3D Web Development with Blender and Three.js – Create a Cozy Café Portfolio

💬 Chat with this Video
Ask anything about this video…
Take your creative web development to the next level by building a stunning interactive 3D cozy cafe portfolio from scratch using Blender and 3JS. This course covers professional workflows covering everything from subdivision modeling and texture painting to implementing a robust singleton architecture in your code. You will explore cutting edge techniques like AI assisted scripting for Blender, TSL shaders for dynamic lighting, and mobile responsive raycasting to create a truly immersive user experience. By the end of this tutorial, you'll be able to transform your unique ideas into high-performance play ready 3D web projects. Andrew Wan developed this course. Hello everyone. Welcome back to another video. So, I'm just going to demonstrate it a little bit and then talk about some of the things we'll cover. So, we'll cover the basic interaction over here where I scroll and there's like some things animating as I scroll. Also, hover and pop up a Gab animation model over here. And these just go to external links and things like that. There are a lot of other features I don't cover step by step, but I do talk about them like this slider over here and converting between scenes as well as night and dark mode and some of the interactions and how I set that up. So, we're going to model a single cafe together. And by the end, you'll know how to model everything. Everything else is just a combination of the same techniques. And yeah, that's pretty much it. So, without further ado, let's get straight into the video. Of course, all source files are here. Blender files here. You don't need to model if you don't want to model. And all the credits and everything are here as well. So, there are some prerequisites. You can read them here. I will say if you don't have any knowledge of 3GS, you can watch the intro course that is also on the free code camp channel as well as my own channel. And I go about I would say I go about two or three times faster in this video compared to the intro course. So, if you ever find anything too difficult in this video, definitely give the intro course a little bit of a watch. I I go significantly slower in there, but it'll give you all the prerequisites you need to watch this video. So, this course is a completely standalone course, but I do consider it quote unquote part two because I kind of want to build upon this sort of project over here from the intro course. But as long as you have these prerequisites, it's fine just to watch this one. You don't have to watch this one at all. If you ever have any issues, come join and meet me on Discord or my just feel free to email me as well. So, in the first series of this intro course, we worked primarily with hard surface models that are very, very blocky and have like sharp edges. Now, all of those techniques definitely carry over and we're going to keep a lot of them in this video, but we're also going to add what we also called subdeling or subdivision modeling, which is basically the idea. You kind of just model like you would model one of these, but instead you add a subdivision surface modifier that will basically cut up your mesh into a bunch of other squares. And because you have so much more topology or so many more squares to work from, it gives it a sort of bendy and wavy shape that you can play around with. Now, that might sound a little bit scary, but don't worry about it. I'll go slow and if you don't get anything, feel free to reach out anytime. Okay, so I just want to talk a little bit briefly about idea conception and analyzing models to help you kind of get unstuck in that like low motivation point and like what am I doing sort of thing going online? Uh this probably will make sense in retrospect more so than now, but I feel like little bits and pieces would pick up along the way. So, when you're looking at models online and you like, for example, on Sketch Fab, which might be Fab in the future, if you come over to model inspector and you go into like wireframe, what you'll actually notice, it looks really intimidating, is that there's a lot of different patterns here. You can see that if I look at this sort of object over here, it actually does look like it has the same exact pattern as this head over here. There's like this kind of wavy corner to each of them. like here. This it it's more stretched out, but you can also see kind of like a wavy corner here. And then same with this one. It has less squares, but there's also kind of like this wavy corner over here. And that's because all of them are actually just cubes. Same with this one over here. This is also just a cube with a subdivision surface modifier on it. And then over time when you go on to other things, you'll see these patterns often everywhere, even on more quoteunquote complicated models. So if we go to wireframe here, you can see even on the bottom over here, it kind of looks like a subdivided cube almost. The artist added some extra topology, of course, but but the whole idea is you'll see these patterns a lot. So I guarantee you probably this mouse or something. Well, yeah, here you can see it's a stretched out subdivided cube. And I don't think these Yeah, these are not subdivided cubes, but again, you don't always have to use subdivided cubes. You could totally make this exact same exact shape with a subdivided cube, but but what the artist did here is also perfectly fine. So when it comes to idea generation for a particular style, I try to look for everything that is very related to that specific sort of style and then also other ones that maybe I could potentially bring in from another style into this style just to make it a little bit more unique and tangential. So this one is probably the most biggest outlier out of the ones that I'm showing over here. Um, I'm not sure why I opened this other than just to show the subdoming, but anyway, when I look at references for a particular style, I try and analyze the different variations and moods and commonalities between all of them. So, like what are some of the things common within the style? It's like the way they handle outlines, which I'll talk in a little bit later in the video. You can see all the outlines are handled in very similar ways across the different styles. And then also the proportions of emotions that are trying to convey in each single one. So let's say I want to analyze this scene over here. I'd probably give this like a whimsical score of like um relative to the other ones probably like 80 out of 100. And then humor. There's definitely some sense of humor over here in the fantasy realm of like having oranges in your backpack. So, I would probably give humor like 50. It's not like the main driver. I feel like whimsical is like the main driver here. But humor is definitely up there for sure. It's not like an out of your belly laughs sort of thing. It's kind of just like chuckle like wow that's really cool. So, this one if I had to give whimsical, I think whimsical would probably be like a 70 out of 100. And then humor is probably also like a maybe like a 80 out of 100. Same thing with Oh, well, I'm going to skip this style. This one over here. I feel like um we could probably introduce like a fantasy component, which is also kind of whimsical, but I I'd consider it different. So, this is probably like fantasy relative to other ones, not like in general or whatever. And of course, these are just so arbitrary to what I feel and what I think about them. You'll have your own internal mode of working. And that's exactly how you develop, you know, your own unique style. So fantasy, a little bit of whimsicalness cuz her mouth looks open like, "Wow, I'm discovering a fox for the first time." I think it's just a bad angle where you can't tell on this side, but it definitely gives off that vibe. So like intrigue, familiarity, whimsical, a little bit of humor probably. I I wouldn't put that front forward, but the style itself is also kind of like humor, but it's not like I want to make you laugh humor. It's more just like, "Wow, that's kind of cool." Um, if you look at it sort of thing. And then this one is very very similar to this one as well. So I'd probably give a very similar score here. Uh these two. Anyway, once you have like your proportional breakdown of what you feel and your emotions of each scene, you can use that to shape the way that you want to create your your yours. You can use that proportional breakdown and change those values and shape your modeling based off of the values that you start. So, if I want to start with like 80 humor, 80 out of 100 humor, and then maybe like 20% absurdity, and again, it doesn't matter if they go overflow or whatever, like 70% whimsical, like just have your emotional breakdown of what you want someone to feel. And then work your way up to all those design patterns that you see in other people's work that can help you shape your emotions that you want to convey in your artwork. And this is kind of just like how I approach every single one of my projects intuitively. Now, this is a very intuitive process. So, you might not be able to articulate a lot of your decisions verbally, but over time, the more you do it and depending on how strong you are in a particular emotion or particular style, you just develop the ability to articulate it over time. Other commonalities in design decisions you'll notice is a lot of stars and particles in all them. So, here you can see stars over here. Well, this one doesn't have one. Um, but these sort of bubbles in the bushes kind of act like those background stars and it already has a lot of detail, so it's probably not really needed for the composition. Here you can see these dots are kind of like the background. And same with this one. We also have like pluses and a lot of background elements over here and then circles as well. So that's a commonality to look out for as well. And also if you go to base color, you'll notice that most of these are hand painted. Yeah, this is also hand painted. Same thing. Probably this one is probably also hand painted. Yeah, also hand painted. So, all the highlights, all the lighting is hand painted. If you want to hand paint and paint the highlights, like for the hair and everything, you can totally do that, but I'm not going to be doing too much of that in this video. But yeah, now that we got a good starting point, we got our emotional breakdown. And actually, whimsical is probably going to be like 10% and then I would probably also put satire for this video. Uh, I think satire is probably at 70%. So, we got my emotional breakdown and we got our references and inspirations. So, we could probably start from there. So, I'm in a new Blender file here. On the left, you'll see all the hotkeys that I'm using. Well, not all of them show there. So, I'll try and speak out loud what I'm doing and thinking at the same time, but if I ever miss something, just feel free to drop a comment and I'll get back to you or send me an email, reach out, however. But you might be able to pick it up on the bottom left over here. Okay. Okay. So, the first thing I want to do is just clean up the viewport. So, there's a lot of ways. You can come to the divisor here, hit rightclick, and you can see you have a bunch of options over here. You can also hover over and hit F3 for the global menu and search for area. And then you have a lot of options here like close area and so on so forth. Now, you can also do that like vertical split, so on so forth. Right click. You can also do the typical way, which is get come to the corner, wait for the the crosshairs, and then just drag over there like so. That way, it'll just replace that area and so on and so forth. So, lot of lots of options there to create the viewport that you want in Blender. So, there's a running joke to delete the cube and just read the cube, but we're not going to do that. We're just going to delete the cube and start with a plane. And I'll show you why I like to typically start with a plane. And the reason for that is because the origin point is at the bottom. That way, whenever I scale it, I don't have to worry about it like going through the floor and things like that. You can always adjust the origin point, but it's just faster just to start with a plane. So, I'm going to start with a plane just like so. And we will be adjusting the origin point a lot. So, don't worry about that. And now I'm going to go into edit mode. By the way, I have the extra tab menu items. If you don't have that, you can come over to edit preferences over here. Go into key map and do tab for pi menu. You'll definitely get used to it. If you are on regular Blender, you'll just hit tab to go back between edit and object. Or you can come up here to change modes as well. But I like to use the pi menu here. So, I'm going to go in edit mode. Hit A to select everything. Hit two to go into edge select. And I'm just going to hit E to extrude upwards. And it'll lock by default on the Z-axis, which is really nice. And now you can see when I scale, it's going to scale from the bottom there. Oh, and sorry, I should just clarify. We didn't have to be in edge select. I was just thinking in terms of edges, but if you're in vertex or face, it would also work as well because we're selecting all of them. Now, traditionally, when we go for a cutout, people like to use the boolean modifier. So, if I shift right click to move my cursor on the front over here and then shift A and start typing for a cube and add a cube and then scale it, you know, wherever I want to cut out my cafe. You do you for sure though. And then I come over onto my original body object over here. Come over here and add a boolean modifier. So, we can search for a boolean modifier. Sorry, not a boolean modifier. Use the eyedropper tool over here and select our cutout cube. Now typically what happens when you use a boolean modifier is this. This will show a wireframe. But since it's not we can go into object over here. We can come into viewport display and instead of textured we can change this to bounds. And now you can see that this is typically the traditional way to make a cutout. Now this is perfectly fine for a lot of use cases especially in simple modeling. I'm just going to duplicate it and apply the modifier just to show you. But the reason I don't like doing that is because it creates a little bit of what we call bad topology. It's nothing you really have to concern when you're first starting out, especially with simple models, but it just looks a little bit worse than keeping everything as quads or having four vertices. So, let me show you some other ways, some really other common ways to create that as well. So, I'm going to select this cube. I'm going to delete it. The other way is just to select the face in edit mode, hit I to insert and hit E to extrude it in. If you ever have like weird proportions, it's likely because the scale is wrong. So, we can hit N, come to item, and here you can see the scale is a little bit off. So, we can control A, apply our scale, and now do inset. And now you can see it's going to be even on each side. And then we can just scale that down, and then hit E and extrude it in. So, this is going to be perfectly fine for us as well. It really depends on your needs. The way I'm going to do it is actually with loop cuts. So, we can use control. It might be command R on Mac. And I'm just going to add two loop cuts. And then I'm just going to rightclick to cancel movement. And I'm going to hit S Y. And it'll just scale evenly across however wide that I want them. And I'm going to do that for the same on the Z-axis. So, control R, middle mouse button up, or you can just hit two on your keyboard, right click the movement to cancel the movement, and then S and just move it up. Just like so. And then actually, I think this one I'm going to hold alt to select that whole loop right there. And I'm just going to GZ and move that one maybe probably about right there. And of course, we can always just shift alt to select both these. S Y and keep adjusting until we need to finish. And instead of deleting this, I'm going to hit P and separate by selection. Just so we might be able to use this later. I'm not sure, but for now, we can keep it there. Let's just keep organized. So, I'm going to hit M and create a new collection. And I'm going to call that hidden. I'm going to create that. And I'm just going to hit this check mark over here. So, we no longer see it. So, now we have the start of our cutout cafe. And here you can see we have a little bit cleaner topology here with all quads. Now, as we take a look at some cafes over here, they typically have like some of these overhangs over here. So, there's quite a few ways to make those. I'm going to show you some of the two easiest ways for beginners to make those. So the first one is adjust to create a plane from itself. So we can actually start from this one right over here. So we can do E and we can lock it on the X-axis. If you want to go to an ortho or orthographic view like the side view if that helps, you can totally do that as well. Make sure you're in wireframe. So you also select the one in the back. If you don't like wireframe, you can also do alt z to enter x-ray mode, which you can also come up here to do as well. And this way we can also select all them. If for any reason you ever see it like acting like this, it means you probably didn't select the one in the back or you select the one in back but not the one in the front. So just be aware of that. Then we can select it and we can just bring it down just like so. And then we can select this face and face select and let's just P separate that by selection so it becomes its own object. Go into edit mode. Add loop cuts as many as you want. Maybe something like so. And I'm going to right click and just cancel the movement. If you ever need to adjust it after, you can come over to this menu over here and adjust the number of cuts here as well. So, that is really dependent on you. And I forgot to get rid of the modifiers. So, we can just get rid of the modifiers right now. Okay. Let's turn off X-ray mode for a little bit. And let's just go into edit mode here. And what I'm going to do is hit two to go into edge select. Select this over here. Hold control or command and select this. And it'll pick the shortest path between your two selections. If you want to, you can also do vertex select here and just box select just like so. It'll do the same exact thing if you wanted to. And now we can just hit E and extrude and lock it down on the Z-axis. It might be a little bit offset IRL. So, we could probably do like GX or something just like so. Now, still in edit mode and in vertex select, we can come up to select over here and we can do checker deselect. And it'll just select every other one. And then we can do GZ and just move it up just like so. And there we have the beginning of our cafe hood. Of course, it looks a little bit jaggedy for the style that I want to go for. So, we can add a subdivision surface modifier here, like so. And we can crank that up to two, but there's a faster way to do that. You can just do control 2 or control 1, two, or however many you want to adjust the amount of subdivision here. I'm going to do for two for now. And if we ever need to change that, we can change that. Now, one issue here though is that it looks a little bit off. Now, the hard edges are kind of bending and it doesn't really work. Well, there's quite a few solutions to that. So, if we go into edit mode and I select this edge over here, hold control, select this edge over here, and do Ctrl +B to bevel it, and use my middle mouse button to add a little bit of bevel like so. You can see that kind of resolves this portion over here. But what about this portion over here? Well, we can actually do this thing called called creasing. If I hit N and come over to crease, you can see that's over there. So, I'll do control and same thing over there. Now the hot key for crease is shift E. Now it's not doing anything because we need to crease some other edges along there. So we will just select these two and do shift E and just move our mouse button to the right. And now you can see that we have this perfectly flat. So similarly if I undo all of that and come back here. Let's undo the bevel as well. Like so. If I do shift E and crease over here, you can see it's going to do the same exact thing over here. So definitely multiple ways to handle that. You can also totally add manually bracing or guard loops or edge loops. There's a lot of terms for these. And basically you just control R and slide it along the verticy and just bring it up like so. Same thing here. And you just bring it up like so. You might want one here as well just to adjust it like so. And that's another way to do it. You can totally manually play around with it. The shortcut to dissolve one is Ctrl + X alt controll X alt controll X just like so alt just selects the entire loop right so this is just a line but it does effectively the same thing crl x so I could have just been using alt instead of control as well but I just wanted to show the shortest path selection as well okay before moving on I just want to show you the other really common way to do it and that's with an array modifier so I'm going to hit H to temporarily hide this and I'm going to do the same thing that we did last time e X GZ 3 select P separated by selection and I'm just going to control R maybe type 12. I don't remember what I did last time. Right click cancel movement. If you ever click and can't find that menu back, you can actually hit F9 to get it back. But that's only if you never did anything else. Otherwise, it won't show up anymore. So, click hit F9. You can see the menu comes right back where your cursor is. We can just select all these in face select control shortest path and delete those with X. And now we're just left with this over here. And we can just give it an array modifier. This is going to be in the Y-axis. And we can get rid of the Z-axis. And then we can just bring this up. Or we can come over here in Blender 5.0 and just drag that up there to 13, I guess. And that's like So just select this one, E, scale it down like so. Maybe S Y. Do something a little cool like that. Maybe add a like a loop cut here with control R and G and adjust it like so. Whoops. Sorry. I should probably select this one too and do SY or something like that. And this is another perfectly valid way to do it. And then if you control 2 to add a subdivision surface modifier. Yeah, you probably want to shift E and shear those up just a little bit. And for the style that we're going for, I think it's actually okay if you have a little bit of overlap. I don't think there will be any Z fighting issues, but if there are, you can always resolve that later. But again, that really depends on you. But you can also actually just only apply subdivision to this over here. So we can get rid of the crease and everything. And we can just select these, hit P, separate by selection. And then for these, we can get rid of the subdivision over. And then over here we can do the shift E crease just like so. And now we have this separate one from this over here. Pretty similar. So here we can do like, you know, sy and so on so forth. So we kind of have some that's independent of this one. So, if you like that method, you can totally do that as well. I'm actually going to delete that and go back to our original method. And here, I'm going to do the same thing. I'm going to shift E just one more time just to do that. And then here, I think I'm going to manually add some bracing edge loops rather than a bevel. I'm essentially beveling here, but I'm just going to manually place them just so now typically it's not very one-dimensional like this. It's a little bit spread out just for a little bit realism. So, what we can do, I think, is add some a little bit more loop cuts over here. I don't know, maybe about four. Again, it's really up to you. I encourage you to explore on your own. And I might just go into front orthographic view. You can click here. You can also see the shortcut there, control numpad 3 if you have a numpad. And maybe I'm just going to do like SY. I'm going to turn on proportional editing and choose smooth, I think. And then let's do SY. And I'm going to use my middle mouse button just to adjust it a little bit. So again, it's really up to you. And that'll just change your sphere of influence, like how much it impacts your other weird sissies. I'm probably going to adjust it off camera as well. So, but now we have a little bit just like so. And if it looks a little bit odd, you know, always undo it. And S Y. You might have to increase your include increase your sphere of influence just a little bit. Just like so. Yeah. And that looks a little bit more better. It's just a little bit expanded outwards. Cool. Now, one issue I have with this, if we go into side view, it's really easily to be edible. Sorry. Let me go into wireframe or X-ray. You can see we can edit it pretty well. So, it's not terrible. Now, one thing I don't like about this though is when I scale it, it's going to scale from the original origin point that it was separate from. So, we can fix that by going into edit mode. And I think this is the center. So, we can just select this shift S cursor to selected. We have to go back into object mode. And we can come over to object set origin origin to 3D cursor. And now if I go into side view and I scale this, you can see that it's going to scale from there. And if I hit R, it's going to rotate from there as well. So that's just really nice way of adjusting that for later. Oh, I just remembered one thing before I move on. I want to talk about that array method one more time because there's something that is I think new in Blender 5.0. So let me just quickly duplicate this P separate by selection. You don't have to go back into object mode and go into edit mode to go into edit mode for the next object. You can just hit alt Q and you can see now I'm in edit mode for my separate object over here. So I can just do alt Q to keep going back and forth just like that. Anyway, this looks a little bit distorted. That is beyond the point. We can also just hit O to get rid of proportional editing rather than clicking up there. I'm just going to make it a little bit more even. Um but let's also just hit H to hide this. And basically when you have an array here and let me just do that again. Sorry. Instead of having to crease the edge to make them work, you can actually come over here and see turn on this merge thing over here. So now when there are overlapping, you don't have to worry that when you apply it's going to be okay. So let me just come over here and change all the creases to zero except this one. I think this one we probably still need to crease there. But yeah, if we change this up to, I don't know, maybe like 05, you know, the edges are going to merge and it's going to fill up that gap. That way, you won't have to worry about Z fighting anytime later. So, when I apply that array, you can see, oh, I probably should apply the subdivision first. Sorry, I'm going to apply subdivision and then array, you can see that it's pretty much what we had originally and there's nothing overlapping here. They all are shared there. So, that's just something I want to mention about the array method, which again, if you want to go for, you can totally go for it as well. Okay, so I'm going to go back to the other one. I'm going to hit F2, and I'm just going to call this roof cover. Uh, I don't know what to actually call that. And if you ever want to use this method, you can totally do that as well. So, you know, don't don't definitely don't limit yourself at all. I'm just going to drag that up there. And I'm going to move this one into hidden if you ever want to use this one. Cool. And then let's reshow our original one. Sweet. Let's be a little bit more organized. Let's select our camera and hold control to select our point light. Let's hit M and create a new collection here called LCA for lights camera action. You know what the drill is. And let's just hide that for now. Now, if you ever want to adjust these, don't hesitate to adjust both of them at the same exact time. You can select both of them with a shift, go into edit mode, like a side view here, wireframe, and just select all them again. or go into Alt Z for X-ray. Make sure you're in solid view. If you go into wireframe with X-ray, it's going to show a little bit of a glitch here. But other than that, it doesn't really matter. Just make sure you select everything. And then now you can just like GZ and just move it up just like so. And hey, if you got a delete, you got to delete. Like let's say you want to delete something like so, you can make sure it's like so. Just make sure to control and deselect those back ones just like so. And then I think I also want to delete this. Maybe that's a little bit too much. And we can just delete those vertices. Select this and we can do gy and move it over here. Now I don't like eyeballing here. So what I'm going to do is come over here and turn on snapping. So if I turn on snapping here, you can see we have this thing called vertex snapping. So I'm going to hit vertex just there. Go into edit mode. And if I do gy and with this on, you can see that when I hover over the other vertex, it's just going to snap directly to that vertex along the y-axis. And it's just going to lock it directly there. Super super cool. Now, the hotkey for this to toggle it on and off is shift tab. You can see it toggling it on and off. But when it's off, you actually don't have to hit shift tab. You can just do G Y and then hold control on your keyboard and it will toggle it on as if it was off. Even though it doesn't show that it's on, it's actually activating right now. So, it might be command on Mac, but that is the shortcut there. So, now we just made our cafe a little bit smaller. But again, that is also totally up to you. So, I think we can just have fun a little bit with like the composition. And so you already know what I'm going to do. Probably add some loop cuts over here. Maybe another one over here just so we have like a little bit of a window. Something like that. Again, I'm going to P separated by selection. And I don't want to I'm going to move that into a hidden collection. Just hit M to quickly open that. That way in case we ever need it, we can always come back. Then maybe I'll come over over here and instead of selecting a corner and control shift and select the other corner, which is a really quick way to select all the faces in between. Or you could go into top view and box select which would also do the same thing. You can also just select a face shiftG and select slimmer co-planer and all the faces that are on the same level will also be selected. So you have multiple ways. Again that is a box select and then hold shift just to select the rest of them. Select one corner control shift to select the others or select one. Shift G co-planer just like that. Shift GC if you want to do the hockey way. And then maybe I'll just extrude upwards. I think we can hit alt to select this. Hit alt e to extrude along normals. Hit s to make sure it scales evenly. If you need to reply your scale, make sure to do that. Then you can just hit alt s if you ever want to scale it in or out. Then hit s again to make it scale evenly. Hold shift to make it go a little bit slower. Just like so. Cool. And I think there is the start of our roof there. Might add one over here too. We'll see in a little bit. But that that's there for now. Let's start setting up our scene as well. So I'm going to shift S and let's just set our cursor toward origin. Shift A and add a plane. And I'm just going to scale that up temporarily just so we can have a frame of reference. Kind of try getting our composition in a little bit. Now I want to make one of those quick floor signs that a lot of cafes have. I'm just going to shift A somewhere and I'm going to add a plane just like so. I'm going to bring it up. I'm going to add two loop cuts just like so. I'm going to do S Y and kind of just bring it like so. And then SX and stretch it out. Then I'm going to select these into edit mode with a shift select. Hit E. Lock it on the X-axis over here. If you're working from the other axis, you might have to lock it on a different one. I'm going to do control period. It might be command period on Mac. Do GX and snap it. Hold control to snap it to this vertex over here. That way we can move our origin point very easily to the edge there. And then control period just to confirm that. And I'm going to do go into side view and R Y and try and get an angle that I like for the sign, which is all I'm doing right here. And that looks pretty good. And now what I'm going to do is add an mirror modifier to this. Now it's taking in account its local axis. So what we need to do to make it go to the other side appropriately is apply our rotation. And now you can see that is now on the side there. And now we should just be able to command period again. And then this time I'm going to do GZ and snap it down to this bottom vertex over here. Control period to command that. And now we just have this over here. And now what we can do from here is hit A to select everything. E just extrude it out just a little bit. Now I'm just going to hold control and select the shortest path and hold shift to select that last one. I'm just going to hit E one more time just to bring that out a little bit. Now we kind of have our sign there. Let's model some stools so we got a little bit more stuff to work with with the composition. You might see a lot of stuff moving around, but anyway, let's shift A plane. Scale it down maybe like control 2 to add a subdivision service and E and extrude upwards. Ctrl R to add two bracing edge loops over here. And then SD and scale it up to your liking. I'm going to right click and shade smooth so those faces go away. That's pretty cool. If you ever want to make it a little bit more round, you can like bring those down with SC as well. Sweet. Now, we need the legs. So, I'm just going to come down over here. I'm actually going to go into local view for this. Now, local view is numpad slash, but if you don't have a numpad, you can come over to view over here and go into local view and do toggle local view over there. Same thing. I'm going to select this over here. I'm going to shift D to duplicate that. I'm going to hit P separate by selection becomes its own thing. I'm going to S and scale it down. Let's go into top view into wireframe just to see where it's going to start. I'm going to go into edit mode and let's actually go out of local view here. I'm going to hit A to select everything. Now I'm going to do is E and snap it to the ground. Now we can't snap it to the ground. We have to snap it to a vertex that's on the ground or the ground of the vertex itself. So instead of switching only to vertex, we can hold shift and select edge and face. That way we can also snap to face. So now when I do GZ, remember I already extruded it. So, make sure when you hit E and right click, cancel the movement. Make sure you don't have an extra face there. If you do, you can always just hit A, hit M, and merge by distance. Set it to something like 01. You can see I moved eight vertices, which means I accidentally extruded twice, I think. But that way, now you know you don't have any extra overlapping phases. Let's do it again. A E and then and then hold control just to activate the snapping. And now we can just snap to the face. Cool. We're going to do the same thing here. We're going to add two loops. SZ just like so. And now on the top here, it's going to be covered. And we want to save on memory. So, because we'll never see it, we can actually delete this face. And now we kind of just have a leg like that. And let's just bring these up just a little bit more inside. A little bit of overlap here is okay. And now we should just be able to select this over here, give it a mirror modifier, select our stool as our base, and let's also do it along the Y-axis. And now we should just be able to RX and R Y our way into how we'd like it. And if it helps, you can go in up here, do RR for the full rotation cursor amount if that floats your boat. And once that's done, instead of having to select each of these every single time to move them, we can actually just add them to an empty right now. So I'm going to shift right click. I'm going to shift A and search for an empty. Now, it doesn't really matter what kind of empty you want to go for. or if you want to go for a cube empty, if you want to go for plane axes. I think it's more traditional to go for plane axes. So, I'm going to use that and I'm just going to keep it at that origin point, which is nice. I'm going to select both of these, select the empty, and then do Ctrl + P, set parent to object. So, now when I click on the empty and move the empty, it'll move both pieces while I can still also both move each of these independently as well. And that's a really handy way just to manage multiple objects. For some variety, we might as well create some other kinds of tools. So, I'm going to shift select all these and I'm going to shift D and G Y and just move it out just so we can create a little bit of variation. And here I think I'm going to hit A to select everything. Do alt S to scale it along the normals just a little bit. Just make it there. Again, make sure to hit S to scale evenly. Just like so. We might have to reposition this a little bit more. And then we can probably do GZZ just to move it down a little bit. It's okay if it intersects with the floor a little bit or if it's slightly above. So this way we just have a little bit more variation. And maybe we can make this a little bit taller. Who knows? And yeah, mainly just have fun. GZZ. Going to move that up a little bit. Then I'll maybe I'll make this one a little bit more square than the other ones. So I'm going to add like two loop cuts here. S Y. And just keep scaling it up. Maybe we need another one two over here. SX, you know, something a little more round. It's really up to you and what you want to play around with. But maybe it's a little too thick. So we can make like a thinner version over here. Maybe we can turn this down. Maybe we can do it one more time. And this time what we can do is add a little bit of a ring. So let's make this a little bit smaller here. Maybe SX. That's all I'm doing. And here I think what we can do is maybe move these a little bit closer. So let's go into top view just to see where that's looking. Cool. Yeah, maybe we can make it a little bit closer rather than outside. Then let's select our empty shift S cursor to selected and let's add a circle here and let's make it sure it's a curved circle and let's just scale it down just like so. And let's try and position it in between here. Then give it a modifier and let's do curve to tube modifier just so it looks like a tube. And oh actually the chair itself wasn't centered. So that's fine. We can actually just go and center it now. So I'm going to GX and just move it around here. And I think we also have to try and align this up there. So we're going to do gy and move it in the center there as well. Cool. So now our ring is there. Let's change the scale just a little bit down. I don't know, something like that. And then maybe we have to scale it up just a little bit. And yeah, let's not forget to select this selector empty. Ctrl P. And also set parent to object. So now we kind of have one with a ring as well. And maybe this one's a little bit too thick. And instead of doing a and alt s, we can actually just do ssh shift z shift z and just scale it along its local x and y axis. Now if you don't want to do ssh shift z shift z to go into local mode, you can actually come up here and switch to local right over here. And then now you just do ssh shift z instead of shift z twice. And you can see I'm now working in my local x and y axis. Instead of coming up over here to change that, you can just hit the comma key on your keyboard and you can just change here to local just like so. And then maybe for some of these, like this one, we could also add something. So, I'm going to select this, select this face, shift D, duplicate, and G, and just move it down. Let's do P separate by selection. Let's select it and get rid of that subdivision. Let's scale this. It's going to scale from that origin point, which is fine. We can come up over here and instead of coming up here, we can actually just right click on the object itself, set origin, this time origin to geometry, which will try and calculate the center. And now we can just scale it down just like so. Let's go into edit mode here, hit I to insert just a little bit. Again, if you need to, you can apply your scale first, then hit I to insert it. Maybe just delete that f face. Hit A and E and extrude everything up. Now, the shading's a little bit messed up, so we can rightclick and do shade flat. And now we kind of have this little bridge here. And if you ever need to scale it, you can select one of these inner loops with alt. And then you can do alt ss and just adjust it like so. Or you can also do the exterior one with alt ss as well. Now because we separated this from this original mesh, we actually don't need to reparent this. So if I select this, you can see it's already parented even though it is a separate object. I think what we can do now is probably work on some frames. So, I think the easiest solution h let me think about it. There's multiple ways to do that. I think we can actually just hit E to extrude this out and then hit S just to scale it in a little bit. And then hold alt to select this edge loop over here. And I think I'm going to P separate by selection so it becomes its own thing. And then let's just add a solidify modifier that gives it some thickness over there. I can apply our scale just to make sure. It should be fine though. And then there we kind of have like a frame there. And if we ever want to adjust it, we can always just hold alt edge select and scale that up just like so. I think very similar idea for the other frames. I'm going to add a loop cut here. So we kind of have like a base on the ground. So it's like a foundation they call it. And I'm just going to alt E extrude along normal. Hit S to scale evenly. Just add a little bit of base there. And then maybe we can turn this into like open panable frames. Like that would be kind of sick, wouldn't it? So I think I'm just going to P separated by selection so we can work with it individually. And I'm going to add, let me think. Let's see. It would be cool maybe if one side was like panels and then the other side wasn't. So maybe we can just do like gy. Basically the whole idea for this kind of style is like the more variety you have the better. So over here will be the panels. That might be a little bit too large. So, I might be a little too picky for a tutorial, but you know, it is what it is. I'm going to hit A to select everything and do Alt M. And this will split everything. And we can do faces by edges. And now you can see they're they're each individual object. So now what we can do is hit A and then P separate by loose parts. And now they become their own separate objects over here. If you want to leave them all their separate objects inside edit mode, you can do that temporarily. I think I'm actually just not going to do that. I'm going to P separated by loose parts. So each of them are their own. And here I'm just going to adjust right click and probably set origin origin to geometry. Now because we use rightclick set origin so commonly we can actually come over to set origin rightclick on one of these and add to quick favorites. I have remove because I already have it added but now instead we can just hit Q and it'll show up our quick favorites menu. And you can add basically anything to your quick favorites menu. Sweet. So I think we can just select all these and go into edit mode. Hit all of them. Hit I just to insert a little bit. Yeah, I think that's okay. Let's hit A to and E just extrude it out a little bit. Cool. Now, since we already have everything selected, let's hold control. Let's hold control and box deselect each one of these. And now we can just hit E and extrude this out over here. I don't think we need the back detail because we'll never even be looking there. So, and now we kind of have like these panels over here. And I kind of like how this one is doubled up. It does show a little bit of variety there. Now, I think I want a little bit of like a bar here. So, I think I'm going to go into edit mode, just select this face over here. Shift D, duplicate. GZ, bring it down. I'm going to snap it to a vertex over here. And then GZ, and just scale this down arbitrarily. And then select this face. And then hit E and extrude outwards. Looks a little bit too thick. So, I'm just going to scale that down a little bit. And yeah, that seems about right. We can always change that later. All right. So, maybe we just do the same thing over here. I'm just going to delete this face. That way, we don't have to do it twice. And I'm going to select this over here. Shift D, duplicate that. G Y. And then just snap it to this vertex over there. And then here, I think maybe I want to give one of these an offset. That way it looks a little bit more like it can slide between like behind and in front of each other. Okay, let's make like a flower pot or something. There's so many ways to make a flower pot. I might make more than one here, but we're going to start from a circle. I think 16 is fine. I don't actually know, but whatever. It doesn't really matter. Let's just scale that down. Let's hit E to extrude it upwards. Something like this. Let's scale that up over there. Just in that shape. Hit F to fill. And then let's hit I to inset. And then let's just hit E to extrude down therewards. And we pretty much basically have that a flower pot already. And then we can just like adjust it however we want to adjust it. Then right click shade auto smooth. And there's like a basic flower pot over there. I think if we want to make it a little bit larger, we can like add a loop cut over here. Hold alt to select that. Alt E extrude along normals. Hit S to scale evenly. Although I don't think it affects it here. Yeah. And then we can just like bring that down, scale it down, however we want our pot there. Of course, we want to add some variation. So, let's make another flower pot. I'm going to add a circle. This time, I'm just going to do eight. We only need eight cuz this one is going to be like a round one. So, I'm going to go into edit mode and scale it up. And oh, I'm actually going to make this like sort of like a round, whatchamacallit, like a round pot. Yeah, like so. And then I'm going to start scaling it in like so. And I'm just hitting E and Z. And this time I'm going to do control 2 for a subdivision service modifier shade smooth. We can go into local view over here. And we can play around with the proportional editing. Again, we can turn that on with O and just play around with it. Maybe add more if we need more to get like flower pots. You can even make a vase like this, right? And that would be pretty cool. Like maybe SZ. This is a pretty cool fly pan plot. And yeah, you can just have fun with it. And maybe like instead of the other one, we could do like SZ minus one. Now we need to fix that origin point, which is fine. We can just go in edit mode and just th this time. It's fine just to move it in edit mode to change the origin point instead. So we've got one like this. And that's pretty cool. And maybe we can just delete these vertices so it's a little bit more not this not too much of the same. And we literally have so many options here just to play around and create our own different vases and flower pots. So again, just play around with those. Now, I just realized for some reason the origin pointer messed up again, which is fine because we I guess we can just snap it each time. So I'm just going to control period GZ and snap it over here to the bottom of that one. Control period to confirm that. Same thing over here. I'm going to control period GZ snap it to the bottom of this over here. Control period GZ snap it to the face. GZ snap it down over here. Now we can scale it down. Now if you don't like how it's scaling from the center, you can hit period on your key over here and do individual origins and then hit S to scale it. And now scale on the individual origins, but it doesn't really matter. It's really up to you. You can also come up here and see and change it over there as well rather than hitting period on your keyboard. By default, bounding box center is going to be your default. Same thing over here. I'm just going to shift select both of these. And then I'm going to shift select this to copy. Come over here and copy to se selected, which is just a solidify modifier. I'm going to apply our scale here just to make sure that works. And let's move the solidify modifier before the subdivision surface modifier. Let's turn on even thickness. Although it probably won't change much here. With both of these shift selected, let's hold alt and change the thickness both at the same time. And you can see that's what that does over there. And if you ever don't really like how thick it is, then of course you can apply your solidify modifier. And then you can do the same things we've done before, like select these edges and Ctrl +B to add a bevel. That's a little bit too many. And that gives it a little bit there. Or again, shift E and just give it a crease as well. So you have a lot of options there. For now, I'm just going to leave this here until I decide how I want them to look. So we've actually done quite a bit now. I think it's time to start adding some materials so we can kind of get a feel for our scene. Maybe we go material preview or rendered. I think I'll temporarily instead of using cycles switch to Eevee. And we kind of need some lighting now. So let's find our LCA and let's reactivate that. And I'm not going to use a point light here. I'm just going to add a sunlight. So I'm going to search and I'm going to look for a sunlight like so. And let's just add some sun. I'm just going to change up this strength over here. And I want one more for global illumination. So, this one will be from this side. And we'll definitely change it later, but this is just a temporary thing just to take a look. Great. And I think now we can start giving some materials. So, I'm going to come over here, select this, hit new, and I'll call this flower pot. Name it whatever you want to name it. And I'm just going to give it like a brownish color or something. I don't know. And we can select these. Come over here, Ctrl L. And link materials. So, we have those there. Maybe for the building, we can give it like a white color. So, I'm going to give this one I'll call it building main material. And it'll just be default white. Maybe turn up the roughness a little bit. Same thing with this one. Let's turn up the roughness so it's not as shiny. And then we could probably play around with the colors, you know. Maybe over here we can have uh wood frame. Maybe we can call this wood frame. Default. default and then maybe we can give woodframe default material to this top thing over here. So I'm going to hold alt to select that. Controll numpad plus to increase my selection. If you don't have that you can come over here select more less and you can see that is control numpad plus over there. Hit more there. And then I think for the top here I'm just going to control shift and select that corner there and it'll select everything in between. And we can assign this to wood frame default. Maybe like a yellowish frame or something. And then I feel like it would be more natural if we had like another tier of wood over here for some reason. And then we can do the same thing over here. But yeah, just play around with it until you get something that you like. Actually, going in render view probably doesn't give a good idea because we're going to change up the lighting. So material preview might be a little bit better when you're adjusting your colors. We could probably add like a door or like a window now. So I'm probably going to Yeah, let's do the same thing here. P separate this one by selection. Let's go into edit mode and let's just hit I to insert that. over here. Well, actually before we insert, let's add a loop cut over here and a loop cut over here. Let's select all these and then hit I and hit I again for individual faces over there. You can see on the bottom when I hit I and hit I again you can see it says individual over there and we can just make it something like I don't know maybe something like that. Cool. And then we can we might want to use that for glass later. So I'm going to P separate by selection and keep it like so. We we'll probably join it up later. And then we can select those and we can just give it the wood frame default. And then this one, maybe I'm going to duplicate this one and call it glass and temporarily just bring down the alpha just a little bit. If you ever need to adjust them, you can shift alt, select all these, switch to individual origins, and then scale it like so. I think we're ready to add a little bit of plants. So maybe we can come over here, and I'm just going to shift A and add a plane. And there's so many ways to make it a leaf, but the easiest way is with a plane and a subdivision service modifier. I like to put three in the center over here. And we'll add more a little bit. And this will just pretty much act like our base. So I'm going to do sx. And it's not working because we're on individual origins. Let's change back to bounding box center. Let's just scale that down. And we can just do gy and bring these up. And this one I'm just going to And then these ones we can also bring up just a little bit and just scale it out however we need it. Then here I'm going to add some loop cuts. Then I'm going to flatten them out just a little bit. Maybe sy just to completely flatten them out and kind of just uh you know scale it up until I get a shape that I like. Now if you don't like manually doing it like that's totally fine. You can always just use proportional editing to help you instead. So there's always an option there if you want to. And then we can add like a subdivision surface modifier. And now you can see we kind of have a leaf there. And then we can bring in the center a little bit. GZ, you know. And then now we got like a little bit of a line there. And we can just play around with that until you want to. Of course, lots of other alternatives if you just want to start with a plane. And I'm just going to piece every selection. You just start with a normal traditional plane like so. And whoops. Q origin geometry. Scale up. You can hit A to select everything and subdivide it a few times. And then now you can just play around with proportional editing if you just turn that on with O. And this is another perfectly valid way to make a leaf as well or any sort of plant. Yeah, there's lots of ways to approach the same thing. And that's kind of like the beginning of a flower petal. This one would be a fun flower. So we can give it an array modifier. And here we want to change from line to circle. Now this will not show if you're not on Blender 5.0 Z this new array modifier. So make sure you are on Blender 5.0 if you want to see the same thing. And then we can adjust radius here and the number of count. So maybe like five or so. If you don't like to change the radius instead over here, what you can do is just move the origin point of the pedal itself. So here if I go into edit mode and just do gy and move it to the edge of the origin point, you can see it effectively has the same thing as the radius there. And yeah, so now you can just play around with it. Then I'm going to turn on proportional editing and just make a flower. Maybe add one more loop cut in here. Bring it up a little bit. And then turn on proportional editing. GZ just a little bit. Yeah. And then we can have like the start of a flower that we could use. So this flower we can do shift S cursor to select it. We could probably add, let me think. I think we add a plane here. 2 or 1. Yeah, maybe 2 to subdivision surface. And then just E extrude it outwards. Right click, shade smooth. S, shift Z. Bring it up. Just settle it down just in the center of the flower. And I think we also need whatchamacallit like a stem. So I think what I'm going to do is shift A and add a single verticy. Now if you don't have single verticy, you can come over to the Blender official marketplace. Click get add-on and then you should just be able to drag and drop into Blender and it will install and activate your add-on that way. Now you can just search for add single verticy over here and make sure you're in vertex select when you have the vertex selected and then we can just hit E and then hit Z and lock it on the floor over there. So that creates our stem. Alternatively, if you don't want to install Blender add-on, you can just shift A and add a plane and go into edit mode and then hit M after selecting everything and merge it center. That way you also have a single vertex that way. I'm not going to do that way because I have a single verticy from the extra mesh add-on. And then this time I'm going to F3, convert to a curve. I'm going to come back here, add modifier, and let's do curve to tube like so. And we can just play and we can just play around with the scale as well as the resolution or whatnot. And if you ever need to add more vertices, you can select everything, do subdivide, and just move it like so. Now, if it's a little bit too bendy, you can change the curve type. So, hit A to select everything, set spline type, and choose ners curve over here. Now, it still looks a little bit janky, and that's because we don't have enough. So, if I right click and subdivide again and subdivide again, now you can see we have enough points. So, I think we have five now. One. And then add as many as you need. And then, yeah, play around with the resolution cuz I don't think we need that many. If you want to try some of the other curve types, you can go for it. So, maybe for this one, we can try type to like basier. And now you have like the basier handles if you've ever used these before. Anyway, the nerbs is basically the path curve. If you've ever used that. So, if I add a path, then this is basically what we just created, but with starting as a single verticy rather than this. The reason I don't use this one is because we would have to do like R 90 scale it down. Well, I guess this isn't this bad in in this case, but yeah, we would have to like scale it down and then and then we would have to go in top view and wherever it is, we would have to move it on the center of our flower. I guess it's not that bad to do it that way either. It's more of just a force of habit. Make sure to get rid of caps because it's going to be hidden by the back of the flower. And this one is going to be hidden by the the ground. So, we don't need the caps on either one. If you want ever want to change the thickness at a point, you can do alt s and it will change the thickness over there. And if you want to change the tilt, you can do ctrlt and you can see it'll twist at that point. Now, it's probably not relevant for us because we're just working with a regular stem, but you can do Ctrl T and Alt S if you want to. Let's fix this origin point. Ctrl period GZ. Snap it to the bottom over there. Control period just to confirm that. So, it's like so. You don't always have to use an empty. We could just use regular parenting to this stem. Ctrl P and go to object. And this would work as well. If you don't like seeing those relationship lines, you can actually come over here and turn off relationship lines if that makes it a little bit easier for you to navigate. Maybe we can make some food products or something like that. I don't know, anything. So, let's come over here. And I think what I'm going to do is shift right click somewhere and just add a plane. And I'm just going to go into local view instead. And I don't really know what a bakery looks like or what they have in their shelves. Let's do the same thing as we did last time. We might be able just to get away with the inset method, but this time I'm going to use two loop cuts again just in case. And I'm just going to do SZ and just scale that up there. And now we can just E and extrude this in. Then we can use this as our base launching pad. So I'm actually just going to select these three faces, not the back one cuz it's going to be hidden. And we can just do shift D and GZ. Whoops. I have proportional editing on, so make sure to turn that off. And we can just do GZ and shift DGZ. And this way we can like kind of determine where we want to put our shelves. I don't think I want them completely even, but if you want them even, you can separate by selection and then add an array modifier. I don't want them completely even. I think maybe the bottom shelf should be a little bit bigger. And if you want to select them, you can just hit L when you're hovering over them to select them entirely because they're separated their own object. It's in it's within the same mesh, but it's within its own object, if that makes any sense. Now, when it comes to making the center ones, there's a lot of ways you can add like a loop cut around the base over here, like so. One over here, one over here, snap it to there, shift duplicate this, gy, and just move it. And then E extrude it outwards. I think that is one of the easiest ways to do it. And then shift alt to select it again. Crl X to dissolve it because we no longer need those. L to select it. And then shift duplicate. Gy. For the other ones, we can just duplicate it. It's fine. It doesn't really matter. You can just move it up. If you want to adjust it, you can GZ, select these, GZ, snap it to the face there. Of course, we can always go out of local view and just rescale it how we need to accordingly. All right, let's make another stand. So, let's just shift a add a plane somewhere arbitrarily. Let's give it a mirror modifier like so. Let's go into edit mode and just gy move it to the side. And then let's make this on the Y. Let's just move it arbitrarily. And let's just go in edit mode and scale that down. Let's E extrude that up. And let's add a loop cut with controlr. Make sure we turn on clipping so it doesn't go through. Just select this, hit E, and extrude it outward. And then if we ever need to change it, we can do like alt S and S just to scale it evenly over there. Now, this looks a little bit cramped, which is fine. We can just select it and GZ and move it there. Same with this one. You don't really have to fix that, but if you want to, you can do that. Gy and then snap it over there. So that way it looks a little bit better. Probably need something down here as well. So we can controlr, add another loop cut down over here. Maybe select this face over here. I'm going to hit period key just to jump back cuz I was rotating wrongly. Select this one. Alt extrude along normals and kind of just get like a stand something like so. For the basket, I'm just going to select this shift s cursor to select it and add a plane. And this time I'm just going to scale it. I don't really care if it overlaps. So it's totally fine. Here I think I'm just going to let's see apply scale. Let's go into edit mode. Let's E extrude upwards. And this time I'm just going to insert in. I don't think we need any reason not to. And then hit E and extrude that downwards. SC. Maybe scale it just like so. And then just scale it down a little bit. And we can give it an array modifier. And it's going to be in the Zaxis. If you want this to manually offset it, you can totally do that like we did for the shelf. But for this one, I'm just going to use an array. And we can just arbitrally adjust that. Now, I don't want to rotate it like this. I want to go into edit mode and then lock it on the Y-axis and rotate it like that. It just makes it a little bit easier. And then I can just use the array thing and click and drag that rather than adjust it here as well. And there we kind of have like a shelf that we can use later. Again, if you want to parent this to an empty, you can totally do that or apply modifiers and something like that. We could also make like one of those glass casings. And again, lots of ways to do it, but I think the easiest way is just to have a plane R Y 90° like something like this. And we can just go into local view for this one, I think. And let's go into wireframe to make sure we have everything selected. Let's just hit E and hit X and just extrude it back as far as you want. Select this. Again, make sure you're getting both of those like over there. And we can just do Crl +B and bevel. And we can just use our middle mouse button just to scroll up on there. If your bevels are uneven, make sure to apply your scale first and then go back and bevel again. And if you want to change the profile, you can come over here and adjust the profile shape over here as well. You want, you can also make a custom one, so it doesn't have to be the one that they give you. Might have to add a few more segments for it to look a little bit nicer, but that's also one there. I'm just going to stick with a super ellipse for now. And then for the remaining of it, I'm just going to select this one and probably like GX with proportional editing or something like that. And that's pretty good for our glass casing. But again, you do you and go for the vibe that you want. I'm going to go out of there. And I think this time I'm going to go into local view, add a loop cut down the center. I'm going to delete this half. And I'm going to add a mirror modifier over here. And I'm going to do it along the Yaxis. Sweet. And I'm going to do the same thing on this side. I'm going to select this edge, hold control, and select it all the way over here. I'm going to shift duplicate that and P separate by selection. I'm going to select that edge that we just duplicated. I'm going to F3 and convert that to a curve. So now it becomes a curve. If you can't select on it and have trouble selecting on it, hold alt when you click and it'll show you whatever whatever your cursor is hovering over. And here you can see it's my curve object. So I'm going to click that. And if I hit period on my numpad key to jump to it, you can see it's there. I don't see it selected probably because it's overlapping which is fine but we can add a curve to two modifier. Let's apply our scale just to make sure it's evenly spaced and move that down over there. And now if I go into material preview we can see we kind of have our casing there. And if you want one for the edges, you can totally do the same exact method that we did before. Just duplicate this. P separated by selection one more time. Then with this clicked, we can do F3 convert to curve. Come over here, Ctrl L. And we can just link our modifiers and there we have. And don't forget to apply scale. Now, one thing I don't like about this method is that it's very hard to adjust if you ever want to change it. So, instead, an alternative we could do is actually shift D and duplicate this curve instead. Instead of doing curve to two, we can actually just come here and go into geometry and we can go for the extrude property. Now, it's going the wrong way, which is fine because we can go in edit mode because we can do CtrlT and then we can just type 90°. And now you can see that it's going to be tapering 90°. So, let me just actually hide our original one. Accidentally just duplicated it twice. Anyway, so now we can just use this curve, this singular curve. Fine. But anyway, now we have this separate curve. Like this is also a curve that we can just adjust over here. And let me just include that extrude right until it fits up. Great. And now what we can do is with both curves selected, we can go in edit mode for both of them at the same time. And we can edit them together. Maybe we can also add a donut, which is literally just going to be a Taurus. So we can just add a Taurus. We don't need this many segments. Maybe like we'll probably lower this to like 24. Honestly, we could probably even do 12. It might be fine if it's ever too bendy. I think these are all quads. Yeah. So we can just control one and add a subdivision service modifier. No. Let's see. Is that Is that overkill? Let's see. No, that's fine. Yeah, subdivision is fine just to give it one subdivision. And this is pretty much a doughnut. We can just do alt s to like scale it up and make it like a little thicker doughnut. And then I think for the frosting, we can just like select this edge. Ctrl+ to select the top. Shift DGZ P separated by selection. And then we can just adjust the frosting here. And then that is up to you, I guess, how you want to do it. if you want to like e extrude down one more time and scale it in, you know, it's whatever how you however you want to do your frosting, right? And then let's add a sprinkle. So, let's add a cube over here. Just scale it down. And control 2 to add a subdivision service modifier. I don't know what shape are sprinkles. Oh, I I guess I don't I guess that's kind of like a shape of a sprinkle. Yeah, that's pretty good. And then now in Blender 5.0, there should be one called scatter on surface, which is super cool. And now we can just come to instancing type and then we can select our over here. And then we can scale this. And then let's apply our scale here. And let's turn up that density just a little bit. So we got a little bit more sprinkles. Let's give it a little bit of randomization. So I think that's in transform. Yeah, randomize is over here. Let's randomize the I don't actually know what rotation I'm going to randomize. Okay, I think that's the wrong way. That's not what I want to do. Maybe it's the Z-axis. Maybe. Yeah, 180 on the Z-axis. Okay, cool. Cool. And now we got some sprinkles. We could actually delete this if we wanted to, but I'm not going to risk that. So, what I'm going to do is probably move this into its own collection or something. Or just hit H and hide that. Maybe next we can make like a cake. I think I'm going to start with a plane again. Just E extrude up. You already know what we're going to do. We're going to subdivide and just to like so. Just bring that up. Right click. Shade smooth. I think shade smooth is fine here. And if we ever need to, we can crank up the subdivision to like three just to make it a little bit smoother. I'm just going to probably add another plane. Actually, I'm just going to make another plane for the strawberry. GZ, snap it to the face over there. Go into edit mode, hit E, extrude upwards. Ctrl 3 just to add. Yeah, that looks good. And now we can just go into local view. And let's just shape our strawberry. So, probably gy and probably a little bit something like this. I don't know how a strawberry looks, but I'm just scaling it until we get like a strawberry shape. And I think we should ought to make this a little bit smaller over here. Maybe keep this place a little bit bulkier. And let's turn on X mirroring here. That way we don't have to like add a mirror modifier for it either. We can just adjust it. And since it's mirrored, just like so. Cool. That kind of looks like a decent enough strawberry. And we can do the same thing. We can add an array modifier. Change this to circle. Increase the rad the radius. Increase the count. And then maybe we can flatten out the bottom just a little bit. So sc maybe just a little bit flatter on the bottom. That way it doesn't clip too much with the cake and it kind of just sits on top there. Add a small loaf of bread. So I'm going to shift right click somewhere, add a plane. And we can just Yeah, we can just make it. If you want to make a slice of bread, you can totally do that anyway. And we can just e extrude upwards. And I feel like a bread, all we really need is just loop cuts. Call me crazy or whatnot, but a piece of bread is just a bunch of loop cuts. See, maybe this one is not this one. It's probably in here. And GZ, we probably need one more over here. And then probably one more over here. Uh I'm trying to think about how a bread looks in shape, but it doesn't really matter because you can always adjust it later. So essentially the whole idea is just make it look like a piece of bread for adding a subdivision surface modifier sx. And I think we just do control 2 for a subdivision surface modifier. And now we definitely need two loop cuts over here just to make it look like a loaf of bread. Right click. And then we definitely need one down here just to make it a little bit more loafy. And we definitely need one on the top. Maybe we can add like three at the top over here. And let's turn proportional editing on. Let's make our circle a little bit smaller just so it's cool. And I think we can just round this up just a little bit. So here I'm going to turn on, let's see, X transparency. I mean mirroring. That way we don't have to really do anything. And maybe just turn on proportion editing just a little bit like so. We kind of have one. I think I want the top to be a little bit larger. So I'm just going to select this edge over here. Control shift. Select this edge over here. And I'm just going to GZ and inflate that a little bit. Maybe select these over here and GX a little bit just to make it a little bit more stylized. Yeah, it's really up to you how you want to make your your bread loaf. But that looks like it looks like a decent bread loaf. And I want it to be a little bit stylized. So I might just sy this a little bit. So I might just syy this a little bit. Maybe even add another one over here and sx. Let's make it a little bulky on this side. If we ever want to make a slice of bread, I think we can just select this face, shift G, select similar co-planer. Let's increase this just a little bit. Maybe we can do greater than, and just crank that up a little bit. So, we get all the faces. Okay, I think that's good. Let's shift duplicate and let's do P separate by selection so it becomes its own object. Let's go in GY here. go into edit mode and just e extrude it out. And we might have to add maybe three loop cuts or two loop cuts. And then I think the center one we can just like raise up a little bit. Let's see. Whoops. GZ. Actually, no. I want proportion editing. And remember that's just with O. So we don't actually have to do that. I think I'm going to kind of just inflate it just a little bit. And I think for a slice of this cake, it might be easier just to duplicate it itself and scale this down to like SX. And then probably loop cut over here, I think. Yeah. And that's pretty much a cake slice right there. We can do GG to edge slide the edge. And then maybe we can I don't know. But yeah, now we have like a slice of cake there. Also, don't be afraid to go into sculpt mode and use the inflate tool over here and shift F to adjust the strength and then hit F to adjust the size. And you can just inflate your bread a little bit. So, I can just click on it. It's deflating. So, we can hold control to do the opposite of deflating, which is inflating. Your default might be inflating. And then we can do the same thing, right? We can just do alt Q and then we can inflate around here. So, maybe we want to hold control and just tap it a little bit. just give it some irregular irregularity. So for the bagel, we should just be able to use our donut. I think it has a center ring. Yeah, it does. So we can just hold alt and select that center ring. And we also need to select the center ring on the inside. Then we could hit V to rip. And we'll just right click cancel movement. And we can just P separated by loose parts. And now they become their own. Now we need to move this up and just fill it in here. So we just going to select this, hit F. And now we can just hit F and just keep refilling that. So one more time, go into edit mode, select this over here. Actually, we actually we should just be able to hold alt and shift alt and do control E and do bridge edge loops. Yeah, that would do the same exact thing as what we did up here. And then if you want, you can add like a loop cut and maybe like a subdivision surface modifier cuz I know the flat one is typically like the bottom side of the bagel is a little bit more flat. So we can like round it out a little bit and make it a little bit flatter. and or like peachier. Is that like a little bit thicker, I think. Yeah. So, really up to you there and how you want to do it. Okay. Let's make like a baguette. I'm going to add a plane. Let's go scale it down. Maybe SX. Go into edit mode. E extrude upwards. I'm going to right click uh sorry. Yeah, right click and subdivide everything. And uh let's see. Maybe we can select this edge. I'm going to turn on the mirroring with X. GX. Cool. Hold Alt to select all these. S Y maybe that looks like a nice baguette. And let's just go into local view and select these two. SC. Let's do control 2 for a subdivision service modifier. Shade smooth. Cool. Maybe I just inflate this just a little bit more to get like a baguette shape. Same with the these sides. Actually, just to select these. Probably make it a little bit thicker. Just like so. That looks like a pretty good baguette. And I think we just give it, let's see, a loop cut here. A loop cut here. And it's not working because we have mirroring on. So, let's turn that off. Let's rotate it. But that doesn't look great. So, let's switch to individual origins and then rotate them. That looks pretty good. Let's do Ctrl +B to bevel. And let's hit E to extrude it in. That looks a little bit sus. So, I think what we can do instead is undo that. Select all these faces over here and I to insert first. Make it a little bit smaller. just so we have a little bit of a guard loop there. Now we can select these inner ones over here. Let's just make sure they're a little bit more slanted. They can be a little uneven for realism. Now we'll just select them. We'll do Ctrl +B and something like so. And then we just hit E and extrude it in. And there we got our baguette. And then let's switch back to bounding box center. SX. Make it a little bit more round there. Maybe give it some irregularities and and things like that. Then these interfaces, we could probably give it the light brown. So, I'm going to come over here and just hit assign. And uh yeah, now we can make like one of those smaller ones. I'm going to start with a plane again. Let's go into edit mode. I think E cuz I want a guard loop on the bottom so it looks a little bit square. And then I'm just going to extrude this one way up. Probably scale that down a little bit. I think I need a little bit of puffiness. Control 2 to add a subdivision service modifier. Yeah, I definitely messed that up, but it's fine because we can always adjust it. And now it kind of looks like a Tootsie Roll, but let's just increase that up a little bit. Yeah, make that down a little bit. Actually, this guard loop is is bad. Let me think. Is this bad? Do I need that? Yeah, maybe I do need that. Great. And then we can just bring the top down just a little bit. And I'm going to leave this one over here. and we'll just paint the extra details rather than model the extra details. So, I think we have enough to actually start a little bit of texture painting and composition just to make the scene look a little bit more lively. So, I think what we can do is actually start with this piece of bread over here. And I'm just going to give it a new material and call it circular bread. I don't know what else to call that. And just click and drag this up over here. Change this to shader editor. And I'm going to create a new image texture that we're going to paint on. So, I'm going to create that image texture over there and hook it up into that base color. Now, it's black because we don't have image. So, we can hit new image over here. And I'll rename this circular bread. And for our base color, I'm just going to uh let's see. Let's just create a random base color that kind of looks like bread. So, I'm just going to increase that. Cool. 32 bit float gives a little more color. And we don't need alpha for this one. So, we can uncheck it. But if you want to paint transparency, you can leave that on. And we'll create a new image like so. Now, make sure we have to save it. So, luckily, Blender already has a setup texture paint area for us. So, we can just go to texture paint. And here you can see we have circular bread and just hit alt s and make sure you save. And we want to save it as an EXR file. This way, our color is fine every time we paint and save it. And we'll convert it to a PNG a little bit later. Now, we can't start painting yet. Even though if we hit tab and we show texture painting and we click, it does look like it works. But the issue is we need to update our UV map in order to know how to map our 2D image to our 3D space. So, while you can paint and it'll probably look fine and it'll probably work, the typical thing to do is to apply all your modifiers first, unwrap the model, and then texture paint. So, I'll just duplicate this. if you ever want to create a complete clone of everything and like move it into a new collection called modifiers or whatever. That way, like if you ever need to go back to the modifier one, you always have it. You can just keep that hidden or you can just file saver and command and save a completely new file. That way you always have a fallback. Really up to you. But here I'm going to let's see if we can get away with control one. No, control 2 probably. And I'm going to apply that. Just a refresher on UV mapping. It's literally the idea of, you can imagine trying to gift box wrap a piece of paper or like a globe or anything. You're trying to use a 2D sort of texture and you're trying to map that into a simulated 3D space. So, you can just imagine like seams are where you use scissors to cut to in order to flatten out everything. There's multiple ways to prioritize what kind of UV seams you want to go for and so on so forth, but it doesn't really matter for if you're just starting out. Just focus on having fun. And Blender by default has a default on wrap method that you can use called smart UV project that will try and guess seams for you. And we're just going to be using that. Now, if I start painting, it's actually going to start working already. But the thing is, we want a proper UV unwrap. That way, we know how to map the 2D image texture over here to the 3D space in a proper way. Because this geometry is so simple and the top is so simple, I think it's fine just to paint over it. But what about like the bottom over here? It's showing the bottom that's also painting on the top. And that's because the UVs are overlapping. So, let's undo that. And the first thing we need to do is let's come over here and open up the UV editor just in case we need to see it. I don't think we do, but anyway, let's just go in here and let's make sure to apply scale. And we can just U and smart UV project. Send island margin 03 probably works. It probably doesn't really matter too much because we're texture painting, but you want to have a little bit of gap between the UV islands. And this is a decent thing to texture paint on. But honestly, because this model is so simple, you could come over here and like select this enter one. Ctrl + E to mark the seam and hit A and then U and then I think angle based would probably be fine here. And then you could totally use this. Hit L to select an island. And you could totally use this to paint. If you watch my previous videos, you know that the best way to unwrap a subdivided cube is just like any other regular cube. So something like this. And well this time I'm splitting up into all six spaces. Ctrl E mark seam. And then we can do angle based again. And then this would be another way to unwrap it. Again since this object is so simple, we can just stick I'm going to hit a control E and clear seam. I'm just going to stick with this one. E mark seam. And I'm just going to paint it like this. Again, just hit L to select them and try and make them as big as possible. And if you have UV packmaster, you can also use them to repack the thing. Great. And now when we go back into texture paint and I just paint, you can see it's only going to show at the top and it's not going to show up at the bottom anymore. And there shouldn't be any weird distortion because it's such a simple model. So having a one single unwrap like that should be more than fine. So just some cool hotkeys. Hit F to change the size. Hit shift F to change the strength. You can also come over here and oh, you can use your middle mouse button and click in order to drag this left and right. Play with the fall-off. So, this is something that I'll probably change to the smoother often, like when I'm working with like gradient stuff and things like that. And then, of course, you also have like the custom one. There's a lot of other options over here, like smear, clone, blur. I don't think I'm going to use many of these. The fastest way to sample colors is shift X and you can just hover over anything. And you can see that I'm just selecting the color over here. And now I have that color as part of this. If you want to paint the opposite color or your secondary color, you can just hold control and it will use that color instead. And then if you want to switch your colors, you can just hit X and it will switch the position. You should also hit N on your keyboard to come to the active tool uh active tool workspace. And there's a lot you can do here like add a color palette. So you can create a new palette over here and whatever your active color it's over there and I can just keep adding there. That way I don't have to like completely forget every single time. And yeah, there's a lot of other tools. I'm probably not going to be using many of them other than just the basic paint for this video. Now if you want the color that you see in the viewport, the command is control shiftx. And you can see it'll get a lot darker because I'm sampling it with the lighting and everything else on it instead of my original color. So just make sure to keep that in mind if you ever like the color that you see in your viewport better. So here if I do control shift X it's going to be a lot lighter. You can see the color over there and how it's adjusting as I change over there. Now I will say don't forget to save often. I always keep forgetting always hover over your image over here and hit alt s to save it. But yeah anyway I think we have enough to start with some composition. So I'll see you on the other side. Hello. I'm from the future. I just want to say you can also unwrap multiple objects together. So for example, these over here. Again, just make sure you apply your scale. Go into edit, hit A to select everything, and smart UV object. And now they will all use the same image. Now, because you have a lot more here, you might need to increase the resolution of your image texture. As long as they have the same material and you're using the same image texture, you can paint, you know, two objects or more objects on on the same image texture. So, you can totally go that way as well. Just know the more objects that you add, the higher the resolution of your image you might need to be. I would not recommend going any higher than 4096x 4096 pixels. Just try not to put too many objects on a single texture. Now, there's a lot more nuance there depending on how big the the object is, what kind of material it's using, so on so forth. But for a beginner, I feel like that's just intuition you'll develop over time, like what kind of objects should share, what kind of image packages. But if you want to just save time, I'm sure that none of these models, whether you have like two, three, four, or five, will be a big deal if you all just put it on one texture. But since this is a beginner tutorial, I'm just going to do one item per one texture. But if you do want to see an outline with your painting, let me show you the inverted hall method to create a quick outline first. And we're just going to add a modifier over here and add a solidify modifier. Then we're going to turn up the thickness. And let's turn on even thickness. Let's apply our scale to make sure it's good. For materials, I think this has two materials on it. So, we're going to add a third slot. So, we're going to offset it twice. Create a new material here called outline, which I already created, which is just a black material. So, it's going to use this one. The solidify modifier is going to use this one. And and then we need to flip the normals. This way, we're basically looking inside it. And come over to one last time into material. And then in back face coloring, we need to turn on camera. And now we can adjust how our outline looks. So basically what it's doing is duplicating the entire object and then flipping the normal so we look through it but we're actually looking at the other side. So just to show you what that actually looks like if I apply the solidify modifier. You can see that if I hit alt n and flip this you can see it's just going to be black. So we're doing alt n flip. It's literally just the entire mesh duplicated and then inverted the normals. So you can also come up to over here and turn on face orientation and you can see that the whole thing is red but it's actually the duplicate object we created with the solidify modifier that's red whereas the in side is not red. Now if you ever have the case where something is going outwards while the other one is going inwards with your solidify modifier make sure to go into edit mode alt n and either recalculate or flip manually if you need to. So that's exactly what we're seeing over here. And you can do that with any object you want. So maybe over here I'll ctr L and link materials. And I'll also copy modifiers. And I accidentally got rid of my subdivision. So I'm going to add that back in. I don't think it really matters the order here if we solidify, but I'm going to subd first. And let me turn off face orientation. And you can see we also have that. So let me just apply my scale. Make sure it works. And of course, when you get rid of it, like let's say I copy this and I call it head material, make sure the you also update in here. Even though it seems like it's going to work, we just want to be correct and only offset it once because we only have one material. But yeah, really nice way to create outlines. Now, there is Z fighting here because it's going underneath the floor. But yeah, you can also just adjust for that as well. Hello, I am from the future. Now adding an outline to every single object manually. Even if you do like something like ctr L and copy modifiers or come over here and do copy to selected and copy the modifier over here. It's very tedious and timeconuming to do that. So if you want to you can actually use AI and ask it to generate you a script that will automatically check your object for you and then add an outline material as well as a solidify modifier if you need to. So here I just give it a basic prompt over here. Nothing too special here. Honestly, it's a quote unquote bad prompt, but it gets a point across. Sometimes you don't need to spend time specifying so much stuff for a prompt if it's if it's quote unquote obvious to the AI. Uh so then it didn't work because it was still applying a solidify modifier to the ones that already have solidify modifiers. So I just got rid of that in the second prompt here. And that's pretty much it for that. And if I go to the scripting tab over here, it's just Python. I just run it. And you can take a look at this over here. It doesn't have an outline. And this one. Oh, let me get rid of the outline here as well. And let me get rid of outline here. I was playing around. So, yeah. Okay. So, these three don't have Well, let me get rid of the solidify. But they will after I run the script. So, it's going to loop through my whole scene. It's going to add a solidify modifier that is given the name of outline solidify. So, you can name your modifiers if you want to stay organized. And here you can see they've been applied. And of course I can adjust my thickness. Now if you want to take it one step further, you can do it by color. So here you can see that this is jam and then jam outline. So what I could do is like check for material name and color and add that as a color outline and darker shade. Of course, this is not the actual prompt I would use cuz you need to be more specific here, but you could have AI take it one step further, check for the color in your material if you have one there, and then add an associated colored outline associated with your existing material. So, that is another really handy trick to do. Now, when you're adding outlines, whether manually or with a script, you might run into this error where no matter what you do, no matter the material offset you do or whatever, you're not seeing anything. That's probably because of the object normals. So, you can just go into edit mode. So, I'm just going to go in wireframe, hit A to select everything, do alt N, and just flip. And you can see now it's working. Again, I do want to mention just because this outline method is so popular, there are Blender add-ons out there where you just like select on all the objects you want to give an outline. Hit N and navigate to like your right your edit preferences, Blender add-ons over here. You just install it normally and instead of like Power Ocean, it would be like add border and then you just click on that and it'll automatically do that for you. And then of course you have your manual adjustments either over here or on the panel as well. So if you're really into Blender, you can also make your own custom add-on for this. Oh, another tip. If you go into material preview, you won't see your background color change. Or if I go into rendered, if you want to change the background color without changing your actual lighting, you can go into world in the shader editor and you can search for a color over here. Input color. Look for a mix shader. Mix shader like here. Just slot that in there. Slot this one in here. And look for a is camera ray. Yeah. Right over here. And just plug that in as the factor. And now you have something where you can change the background of the color. So you can use like a color ramp and stuff like that. as the the background lighting is independent. So you can play around with this independently. Of course, you can also use like an actual background object. So maybe like a UV sphere and just texture this instead of the background, but that is an option if you wanted to do that. Maybe we could also do like a coffee mug up here. So I'm going to shift A. I'm going to start with a plane again. I'm going to e extrude upwards. I'm going to controll 2 or 3 for a subdivision surface modifier. I'm going to hit I and then hit I one more time. And then just e extrude it downwards. And let's see. I don't know if I want a loop cut in the Let's see if we Yeah, maybe we do. Nah, I don't think we need that. I'm just going to select that face and bring it down over there. Maybe make it a little bit smaller. The handle. I think I'm just going to also use a subdivided cube. If right click somewhere, let's add a cube. And I'm just going to control to it. And I think here I'm going to hit A and shift duplicate it down and move it to the Z-axis. And then now that it's like this, sorry, let me go into local view to do this. And now what we can do is select this face and select this face. Ctrl + E and do bridge edge loops. And we can increase the number of cuts over here. And you can play around with like the smoothest level and the twist value and a lot of other things. But I'm just going to keep it really simple, just like that. I'm going to right click, shade smooth, and we can go out. Let's just make sure it's back here. So I'm going to shift right click, shift S, selection to cursor. But we should definitely get rid of those internal faces. because we'll never see them. So, let's just delete them right now. And there's the basics of coffee mug. We can always add some more loop cuts to manually adjustment however we want it to be. I will say sometimes it might actually be better to turn off even thickness. Like over here, I like the thickness of the outline, but it's kind of going outwards. So, you might actually find it better to turn off even thickness. Now, I will say you can always adjust it after. Like if I decide to F3, convert to mesh, convert to mesh, and apply all my modifiers really quickly. I can always go into edit mode and adjust the outline afterwards manually if I want to. Now, when it comes to modeling a capy barra, I kind of do recommend you get a reference image. Now, no matter what sort of reference image you get, modeling the modeling is very, very similar. No matter what the style, it's basically just a subdivided cube and then insets for the ears and so on so if we go back and we just start with a subdivided cube. So, we can add a cube. Maybe we can do like control 2 just to add a little bit there. And I think we can go in sculpt mode actually. Well, let's first apply it cuz I just want to use a little bit later. And then we can go to like the grab tool. And I feel like we can just like grab this and let's make sure we turn on symmetry actually. Yeah. And let's turn on symmetry. And then we can just pull this out a little bit and kind of just shape it how we want. And I'm going to do actually go back out and control two one more time for another one. I'm going to do shade smooth here. And I'm just going to shape out my cavi bear head just like this. Maybe a little bit more of the cheeks on the side. Let's go into local view here just so it's a little bit easier. And let's go into material preview. I'm going to make some cheeks over here. You know, push out. This is kind of hard to see. Kind of like just push out and make the front a little bit flat. And honestly, this is already pretty decent for a cavi bear head. You don't really have to do much else unless you want to. And we can just keep pulling. Keep pulling. Hit F to make your brush smaller. Just keep pulling. And that's pretty much it. You just adjust it until you get something you like. And if you want to do the ear, we can just select both of them. And we can just hit I and then hit E to extrude upwards. Then if you want to do the body, I think we can like extrude from here cuz they're very very they they start at the head. Their head definitely starts over there. And then we can just make some adjustments. And then we probably need another loop cut over here. And then at this point, I feel like it's just whatever body shape you want to. I would probably use a reference here just to make it a little bit easier. Turn on proportional editing for sure just uh so it becomes a little bit easier to navigate. And yeah, and at this point it's just about shaping the body how you want to shape the the body. And when you want to get to the legs, let me just do this one more time. So let me just do it one more time just so we get like a fulls size body. I kind of want to keep the quads about even spacing over here. I know it's a little bit distorted, which isn't great. So, we could probably like S Y0 and flatten some of these, but honestly, it's not really that big of a deal. A shape that you like, we can just like select some of them over here. And then before I make the legs, I want to convert them to a loop. And one of the really easy ones is to use this loop tools add-on. So, if I go into edit mode, select these, and rightclick and loop tools, circle, you can see it's going to try putting into a circle. Of course, I only have four, so it's not really going to do that. Now, we can hit I for a guard loop and then just E extrude outwards. And yeah, you can spend some more time polishing this up, but that's pretty much the basics of modeling a capabra. Now, again, it really depends on the style. You could select these two, right click, loop tool, circle, and then hit I to insert here, and then E to extrude outwards this way. Now, going back into sculpt mode, using the flatten tool is actually pretty goated here, especially because of the cavi bear's nose. So if you do like the flatten like look at what it looks like now and then we can like flatten it out a little bit and that definitely looks a little bit more like a heavy bear nose. If you want to smooth something you can just hold shift and it'll smooth but it'll be a little bit too strong. So what you have to do first is go to the smooth tool directly and then you can adjust the strength here and now every time you go to like another tool like grab and hold shift you can see it'll smooth it but it'll smooth it to that level of intensity. And if you want like a sitting one, you don't have to start from the back here. Maybe you can just start from over here and create your caviar from here. Maybe add some loop cuts and then just manually adjust it with probably proportional editing because we want it. So like maybe like a little bit of the back there, a little bit of a belly. And then maybe like one more over here just so we get a little bit of detail. Kind of scrunch that in a little bit. And now we've got like a sitting caviar. So, we might want to grab this out a little bit. Of course, you can also shape this in edit mode. Now, I think inflate might be good here. So, we can also use like inflate over here. This is also a way to make like a bulkiness instead of manually doing it. And then you can like hold shift to smooth out some of the the areas that look a little bit more rectangular. And at this point, if you want to use a mirror modifier, I mean, totally go for it. And same thing, you can just delete the vertices and add a mirror modifier. And we want to do that before we subdivide, of course. And we want to turn on clipping so the vertices don't go through each other. And then if it's like a statue, we could actually just do SZ and like scale this down to zero. And now it's like a cavi bear statue instead. And for the arms, we can just use subdivided cubes again. So I'm just going to add a subdivided cube. Control 2. Let's come over here. Ctrl L. And copy modifiers. And let's just go over here and select the object. So it's over there. Right click, shade smooth. And here we should just be able to extrude it outwards. However you want to do it. Just R Rtate it. And yeah, make our arm. So nothing else really special here. It's just about adjusting stuff until you get like an arm that you like. And don't worry about overlapping cuz our scene is so small. It doesn't really matter. We don't have to waste time deleting the back faces and things like that. We can also just paint our keri vera kabi vera together. So let's create a new material and let's just name it kabi kabi vera. You know I always misspell this. Is it capi? Like cap like no cap? I don't know. But let's do it again. So I'll just start with an image texture over here. Hook it up first. And I'm going to create it in the texture painting. So, let's come over here and let's create a new image. I'm going to make it 4K again because we're going to bake it again. That'll be overkill for a cavi bear. Actually, no. Let's just do 1024 cuz that's all we need. And I'll call this caviar 32 bit float. Don't need the alpha channel. Leaving color is black is fine. New image over here. Let's hit alt s. And I was painting something else here, but again, let's do EXR and let's just save that image. Let's go back to layout over here and let's search for cababra. Cool. And great. Let's go back to texture pane interface over here. And we can't unwrap yet because we need to apply all our modifiers. So, make sure you are good to go. Before applying again, we can duplicate this. Move into modifiers. And let's just apply mirror. apply subdivision. If you want to put this at part of the painting, actually yeah, I think I want to put this as part of the painting as well. So I'm going to apply these as well. So let's select this, select this, ctr L, and link materials. I'm going to go to edit mode, a U. And let's just see what smart UV project gives us. And this is actually not bad. I don't think it's that bad. Let's make sure our scale is one first. Uh, no, it's not. So let's apply our scale and go into edit mode. And let's just do smart UV reject again. Okay, this is a lot better now. It's not perfect, but it's good enough for us and what we're doing. So, we can just deal with this. Sweet. So, we should just be able to go into hit tab and go into texture paint. And I'm going to choose like a car caviar. So, I don't remember if I mentioned this or not, but you can actually use the color picker outside of Blender. So, let me just make this really small over here. And I want to use this brown over here. So, we can come over here, use this color picker, and just tap on that over there. And I'll choose that color. And now we should just be able Now, we should just be able to click on this. And I'll click on this one as well. Well, we can't get there. So, we can do alt Q just to switch quickly to texture paint for that one. Let's go alt Q back here. And if you want, again, you can add it to your color palette in your tool over here. So, we can add this color to our caviara. And then let's just make a slightly darker one over here. Let me just full screen this out. Let's go back to our paintbrush over here. Let's shift F2 to change our strength down just a little bit. And whoops, I changed the color. So, let's bring that back over there. And right over here, just a little bit more brown. Let's hit F just to make that a little bit smaller. And let's just paint. Uh I do have symmetry on, so we could turn that off if you want to. Um, I think I'm just going to turn that off cuz I want it a little bit irregular. Hit X to switch our color palette. And let's create this one as the base color. So, I'm just going to select it here rather than for my palette. And let's make this one slightly like even darker than the other one. And maybe a little bit more saturated like over here. And then maybe we can make the brush a little bit smaller. Something like this for our caviar. And then for the eye, maybe we can just make the eye completely black or a little bit brownish maybe. And let's do shift F. And I want this one to go all the way. So maybe something like that. And I want it a little bit goofy. So that's why I'm leaving off SE symmetrical. But if you want to do it symmetrical, you can leave that on. Maybe you can do shift X and select this one. And then maybe add a little bit of a blush on the sides of the cab bear. And then maybe something over here. We can make it a little bit pink. Let's see. A little bit pink. Cool. Shift F. Make that a little bit smaller. Very very light pink. And oh, let's just hit F and and then let's just hit F to change the scale. And then just dab it on there. Keep tapping until you get like a blush that you like. Tap tap tap tap tap. There we have a pretty good looking caviar. Oh my gosh. It looks a little bit bright because it's really close to the light. So maybe we should choose a darker shade. So let's come back to layout. And cabby bears are not they're very rough cuz of So they're not shiny at all. So we can just turn that probably up all the way to one. And maybe we can add a hue saturation over here. Hue saturation. Plug that in. Maybe we can bump up saturation just a little bit. I'm holding shift so it goes a little bit slower. And then maybe turn down the value a little bit so it's a little bit darker. Yeah, that looks a little bit better. Uh you can also use something like curves and RGB curves, right? You've probably seen this in other software and adjust it accordingly here. So maybe we want a little bit more red in the mid tones. Great. That is more of a cav color. And then we can also give all these outlines. So let's go over here. Let's give it outline. Same thing with here. Actually, we can just ctr L and link materials again. And let's select this one over here. Ctrll L. And let's do cloppy modifiers. And let's just apply our scale one last time in case. And there we have our caviar. I think we're at the point where it's time to talk a little bit about art direction and creativity and things like that. So, I think the first thing when it comes to creativity is to do something that you think is not ever possible. Well, I I guess it depends on the style. Like if you want to do like creative, realistic, semi-realistic, or whatever. But for something like this where it's like playful and goofy, I feel like it's just like completely out of the box thinking. So, for example, like this cafe, why does it have to be a room? Why can't it not be like a piece of bread itself? And then we carve out the piece of bread. Or instead of creating it a piece of bread, we could make the top and the bottom two slices of bread and then have like some peanut butter and jelly, right? It would be like a peanut butter and jelly sandwich sort of cafe smashed between two breads. One of the scariest things about creativity is that you don't know if it'll look good when you do it. But the thing is, sometimes you just have to do it just to see if it looks good or not. And that's exactly what I'm going to do. I'm going to create a top red and a bottom bread, add some jelly and peanut butter, and just see how it looks. Uh, so maybe some other cool details. First, if you want to like make a little raised post here, we can just control R and add a loop cut over here. And now, typically, I think if we were working local axis, we could just like do GZ or G YY or GXX depending on how your plane was oriented. But unfortunately, we can't. And alt S isn't going to work either. So, what we can actually do is use this edge as the pivot point. So if we come over, let me see over here and transform orientation. We can come over here and create an orientation with this edge selected. And now if we select this over here, we can just do gy and it'll get the edges local y-axis. And we can just bring it up just like that. And now if we go into edit mode, let's just move this a little bit somewhere else. Let me change this back to local. Gy gx. Let's see. Yeah. Okay, it's gx. And there you kind of have like a little detail there. Now the topology is a little bit bad here, but we can just control V and add a little bit of more loop cuts there. And I'm going to use my middle mouse button just to scroll it down a little bit. That way we kind of have like a sign like this. And we can just do GX and back. But yeah, that's a really nice handy trick. Of course, we could also just glo global and GXX to go into local view as well. Okay, so I think we can actually start applying some modifiers and just taking our details a little bit to the next level. So for this one, I went back to the array modifier that we set up and got rid of the other one. Or I think I hid it, but if you want to use the other one, that's totally can. So here, I'll apply my subdivision and solidify and my array modifier as well. And now what I want to do is just hit L and select um oh because it's merged. So I can hold L to select a single object anymore which is fine. We can just go and face select and hold shift alt and just select all these individually like that. Luckily we don't have too many so it's not really a big deal. Cool. And we can come over here and give it a new material. Now it's the modifier itself might be on Yeah, it's on material offset too. So, we can give this one to outline. And then this one we can let's just duplicate it and call it cover two or whatever you want to call it. And I'll just make this white. And then I'll hit assign. And there we have a little bit more variation for there. You can also see for the jam and jelly, it's just a subdivided cube with some loop cuts that I stretch around there and gave a outline itself. If you ever see this issue where you have like an outline over here or a mirror modifier, just make sure you delete any hidden empty faces over there and just delete that and it'll resolve that issue. Okay, so I added some extra models here. This is just a extruded out circle with 16 vertices. I think that's overkill. You could probably get away with 14 or 12. The lid is literally just the same object except shorter. This is just a plane that I started with. So, if you can imagine, it's just a plane that I extrude it up. So, I can show you real quick. Extrude it up. Add a loop cut back here. And whoops. Select this face. Extrude this up. Add another loop cut. And extrude this out over here. And then the other thing is just a shift right click and added a circle. Go into edit mode and just extrude it downwards. Hit F to fill and I. And that was pretty much for this one. That was the coffee machine. But what I actually want to do now is give some thickness to our building because we actually don't have any thickness to our building. It's completely flat like a pancake. So let me first disable our solidify modifier which is the outline. And we could just give it a solidify modifier. But the issue with that is you have like a lot of insets. So there would be like a lot of intersecting faces. But I feel like we can just try. So let's add a solidify modifier before I did give it a bevel modifier. You can also not if you don't want to. And this actually is okay. I think if we just let's apply our scale, increase thickness just a little bit. And yeah, this isn't too bad. Now, of course, we have some issues over here on the top because those are like extruded outwards. So, I think one thing we can do is actually delete the top. So, I'm going to select this and control plus control plus and shift alt just to select this. And because this is covered by the bread, I'm just going to delete those faces because we don't need them. Same with over here. I'm just going to select these. Control plus. And I think that's also covered. Yeah, that's also covered by the shift select this as well. Is that accurate? Yeah, it's not never seen. So, we can actually delete these faces, too. And we don't want to solidify this. So, I'm just going to Can we hit L to select it? Yeah, it's not merged with anything. So we can hit L to select that P separated by selection so it becomes its own object and get rid of the solidify modifier cuz this one is already solidified. So only these ones need this solidify modifier. Now we have a few issues here like the solidify modifier is going to start intersecting I believe over here. Or maybe that's cuz of my bevel modifier. Yeah, it's cuz of my bevel modifier. But then there's also this issue here where there's no edge here cuz we split it for the frame. So we could probably like add a loop cut here and add a loop cut here. And sorry, I'm just going to GG and snap it over here so it's equal. And I could just hit F to fill that. Now, there's still some shading issues because we need one here as well. So, I'm going to control R and oh, it's right next to it, which is nice. And then let's hit A to select everything and merge by distance and only get rid of the overlapping ones by putting it really low. But it's not great when we want to bevel it a lot. So, it might actually be better just to start from scratch. But I don't think it's that bad cuz we can kind of I do want to make these like wooden poles anyway. So we can probably just select something like this over here and this one as well. I'm holding control shift again, right, to select everything in between. I'm going to P separated by selection on these. And then for these, I can get rid of this solidify modifier. Well, actually no, I'm going to undo. What I can do, though, is just get rid of the side faces over here. And again, if you have a mirror modifier on, you could probably do that as well. And yeah, I think at this point it's really up to you if you want to start from scratch, if you want to split these up into their own cubes or or not. Uh it's really really up to you. You have a lot of options here. Also, when you want to apply your geometry modifier uh the scatter on surface, the geometry node modifier, we have to come over here and do realize instances. Then we have to apply this one first and then scatter on surface. Now the issue here is that these are no longer instances. All of them are joined into a single object. So what we have to do instead of using object info is use geometry instead. And there there should be random per island and then we can use that instead. So remember if I go into edit mode and I hit L that basically selects an island or in this case it's going to be mesh island. So that's what it's doing. Now you don't see any issue because it only works in cycles. So if I go in cycles you can see that it is working but it does not work in Eevee. So when we bake the diffuse in cycles, it would actually work fine if we use that image texture. Now, do we want to bake in this video is the question. And I don't think I do. So what we probably have to do painstakingly, unfortunately, is go back to our sprinkle material and instead of using this, just add multiple sprinkle materials and manually assign some to the other one. So, I'm just going to duplicate this. We can just delete that with the delete key. And yeah, I'm just going to select some random ones. L L and so on so forth. Are there other ways besides manually doing it? Uh yeah, there's a lot of other manual ways. You could use a gradient texture sheet and just move the UVs for each one. You could offset the UVs with a procedural graph. And and there's also this solution on Blender Stack Exchange if you want to take a look. But again, we would still have to bake it because I don't think this exports into a GB folder. It's only if you're going for EV renders, which we're not going for. So, you do have a lot of ways, but for this beginner course, we're just going to manually assign different ones. The rest of the models that you see here are just subdivided cubes with extrusions and loop cuts. Here you can see this is just a cube. Well, I think I started from a plane here that I extruded up and scaled down and then I inset for the spoon. This is just a duplicate of this. Everything else is very very basic. So, I think we are ready to create our second scene. So, if you want to, you can come up here, file, save incremental, and start in a new Blender file. I'm just going to do it in the same one. So, I'm just going to hit A to select everything. And if you don't want to select your lights or whatever, have different lights and whatnot, that's totally understandable. But um yeah, let's actually I'm gonna hide my camera. And then for the lighting setup, it can be different if you want. I don't know if we're going to use lights yet at this point or not, but it doesn't really matter. And since we have everything, we can actually just come over to collection, rightclick, and duplicate this collection itself. And I can right click over here and change the color just so it's a little bit of um uh you know some some sort of differential hierarchy. We can call this cozy cafe. And then the other one will be Chad Cafe. So this one's going to be Chad Cafe. You can be totally more organized if you want to. And we can just uncheck it and recheck it if you want ever want to go back. Now, if there's ever a chance where you want to delete everything in a cafe, you can rightclick and do delete hierarchy, and that'll delete everything in the collection. So, I can come over here, delete hierarchy. It'll delete the collection and everything inside. Sometimes you might delete the collection, but it will keep all the objects. It'll just delete the collection instead. So, that's just a tip and trick there. And now we can work on our second cafe. Another tip and trick, modeling trick. Let's say I deleted this panel in between these two and I want to snap this to the new panel on the side over here. Using the default snap is very tedious and timeconuming. So what you can actually do is change where you want to basically offset your snap. So in that case we do G and then we hit B and then we can select the edge that we start like somewhere over here and then we can select our ending place that we want to snap to right over here. Now it's going up and down. So we can just lock it on the Y-axis and then we just snap it there. Just another example. It's like really convenient if you want to make like a hook or something. If I do GZ and try to snap it, it's kind of awkward a little bit. But instead, if I do G and then I hit B and then I select uh edge over here and then lock it on the Z-axis and edge over here, you can see that it perfectly snaps just like that. So I don't know if I mentioned this earlier, but you can change modifier properties for every selected object holding alt at the same exact time. You can see they're all changing at the same exact time. So now we can talk about modeling glasses. So modeling all types of glasses are very very similar. Uh when you're first starting out in a low poly style, they're all made with pretty much just subdivided cubes or subdivided planes in this case that you later convert to a cube. So anything just right click, save your image, download it, and once you get your reference image, you can just click and drag it in there. Just make sure it's a compatible image format. Although Blender supports a lot of different kinds of formats, so you should probably be fine. And you can do alt R to reset the location. I mean rotation and alt G to ro reset the location if you want to work from here. If you don't really care where you work, you don't have to. And I'm working from this way, but if you prefer to RX90 and prefer to work from a side view, you can do that as well. Anyway, it's going to be the same logic here. We're just going to add a plane and just scale it down. and I'm on the RX90. And same thing here. We're just going to basically extrude out and match the glasses. So, we can just do that. And again, you can also do control rightclick to quickly extrude if you want to do it that way as well. And that's all I'm going to be doing on this over here. And then for these last two faces, I'll hit F and fill them. And if you ever have like a gap here, just add another loop cut there and shape it. Same thing over here. Again, if you want to, you can add another loop cut right over here. I'm going to hit O to turn on proportional editing and do that. Again, if you like to work in X-ray mode instead, you can do Alt Z, and that will just put you in X-ray mode. And we can just move this back a little bit behind over there. So, Alt Z if you want to work in X-ray mode instead. And then here, let's add a mirror modifier. So, we're just going to add a mirror modifier. Whoops. Sorry, my Chinese keyboard is on. And I'm just going to go into edit mode. And I'm going to use control period to move the origin point just somewhere in the center over here. And I'm just going to control period to confirm that. And now I'm just going to come over here and just extrude this. And extrude it one more time. And whoops. We need to turn on clipping. So it goes inside GX. And just move like that. And then everything else is pretty much just refinement at this point. For these details, you can add them with just by duplicating these over here. And you probably want to give those a subdivision surface modifier as well. Scale those down and just arbitrarily shape it in the way that you need. But again, I don't think anyone's going to be seeing that, so it's probably not necessary. And same thing with the details over here. We can just extrude some of these extra ones over here and over there. Again, it really depends on your glasses. And then for this final one, we could probably just do something like that. all just extruded planes. And now we can just take this and just hit E and extrude it along the Y. If you need a reference image on the side, you can go for one as well, but I don't really need one cuz I'm not going to pay too much attention. I'm just going to hit E one more time. G Y GZ. Probably scale this down. Probably scale this down a little bit as well. Maybe have another one here. Scale that down. And then maybe select all these. I'm going to alt Z cuz I want to go in solid view and kind of just rotate it. Everything else is just adjustments at this point. Great. And that's the basis of glasses. We can do control 1 for a subdivision surface modifier. And then we can give a solidify modifier for the body itself. But let's solidify it first. Increase the just like a little bit. Apply our scale shade smooth. That made it a little way too thick applying our scale. So we can just tone that down just a little bit. And oh, I guess if you want to do the interior, we can do that right now. Before you apply your modifier, select the inner loop with alt, hit F to fill it, and then just select it and P separate by selection and get rid of the subdivision surface modifier. We don't need that anymore. Now you just select with this, right click, and just do shade flat cuz there's no other shading. And there you have your glasses. I don't know if this needs a solidify modifier, so you could probably get rid of that. And then do control 1 to add another subdivision service modifier if you want to. Then in this time you could go into edit mode and just scale it inside here and just to fill up the cracks over there. A little overlap is fine. And yeah, that's pretty much it on how to model any sort of glasses that you want. Another modeling tip and trick is let's say you accidentally have something here and you want to snap it between something that's mirrored. You can select both of the end vertices or faces or whatever. Shift S cursor to selected. Then select this one. Assuming the origin point Q origin to geometry is there and you can just do shift S and selection to cursor keep offset but in this case both have the same result here and then we just S Y there and you've got the snap there. Same thing if you want to like shift Duplicate you can go in edit mode select both of these shift S cursor to selected go over here shift S cursor to selection to cursor. Sometimes when you're using the bevel modifier it like glitches out and does this really funky thing. One thing you can do is try different methods. So if you go into geometry here, you can change these to arc and give that a try. I'm using control and middle mouse button to in order to quickly change. That way I don't have to use my left mouse button to click on it. I can just cycle through it holding control. And here arc seems to resolve that issue. So I can just leave it like that. When it comes to low poly characters that are very stylized, it's very typical that you just start with a subdivision cube. So that's all I did and I just added a loop cut over here for shaping. But as you can see, if I dissolve it, it's just a subdivided cube. If I get rid of this, you can see it's just a cube. So typically, I think working with subdivision 2 and then applying it and then just selecting one of these over here and right clicking doing loop tools to circle and then just hitting E and extruding it outwards is a really common way just to create like arm and then just rotate it however you want it and shape it. You can also add a guard loop depending on the style that you want. So, let me just show you if I do control 1 to add another subdivision surface modifier. You can see that's kind of what it looks like if you were to shape out this one. Maybe E extrude it out one more time. And it's honestly not that bad. Uh you can also before you extrude it out, you can hit I to insert it and then E and extrude it out. So, this is just gives it a little bit more of a hard edge there. And if I do control 1 to add a subdivision surface, you can see that as well. Of course, this is the same thing as adding a loop cut after extrusing, adding a loop cut here and kind of like scaling down. It's pretty much the same thing there as well. Now, with the mirror modifier, we can just mirror it on the other side. And again, we don't actually have to delete that half. So, in this case, it'll be the Y-axis cuz I'm on the Y-axis. And then, we need to see it on this side. So, I'm going to do bisect. And that basically just cuts our mesh. Now, I don't like this because it's showing a ghost method. So, what we have to do is come over here and do on cage. and it'll actually show you the real update on this side this time based on this one. Now, if you want to mirror this side on this side instead, you can do flip. And now you can see it'll mirror this side onto that other side. But this case, I already have the circle over here. So, I'm just going to do that. And now, when I extrude and rotate, you can see that that is my character there. And when you're done, you can probably just I to insert E and extrude inwards just to make a sleeve. And then I think from here you could probably shift duplicate and probably use this as the arm itself and you can just extrude that out. Now it does look like we're getting shading issues here. Oh, I forgot to do bisect. My bad. I must have accidentally turned it off. And we can do control 1 to add a subdivision surface. Now it's a little bit too thin there. So we could probably add two loop cuts to brace it up a little bit if you wanted to do that way. Or again, you could just select both these other shift E, which is also another option. Now, the feet are done exactly the same way. Again, if you want to use a separate subdivided cube, you can totally do that, but just pick some random ones. Hit I to insert them or right click and do circle. Well, sorry, I should have done right click, circle first, and then you can probably just probably move around it actually a little bit closer and then just extrude it outwards. So, same same exact deal with the the feet as well. If you're wondering how I did the hair, it's just subdivided cubes that I went into sculpt mode and just sculpted with elastic grab and grab and a little bit of the smooth brush as well. Also, don't be afraid if you want to like do it separately or like select these faces and dot control plus and P separate by selection and just have them as their individual things. You can totally do that as well. Uh, so here you could probably select this Ctrl+ B separated by selection. And I think given the way we're shading this, it doesn't matter if there's like a hole there. As long as you like plug it in and plug it up, it's probably fine. So, it's really up to you and how you want to do your character. There's really no wrong or right. And just focus on having fun and over time you'll develop, you know, your own methods. Also, I just wanted to mention if you're like texture painting the body and you don't know how to unwrap it, again, just use smart UV project. It doesn't really matter. It won't give you the best sort of cleanest unwrap, but it'll be enough for a painting. Over time, you'll just develop a natural intuition. But if you are curious, the entire idea is kind of imagine where you want to like cut it in real life and flatten it. And generally where that happens is if you can imagine each object is a bunch of different objects. So here you can imagine this is like a cylinder and then this whole entire thing is like a cube and then this is like a cylinder and then these are also their cylinders. So when you want to mark your seams then let me just come over here. You can probably imagine something like this and dot control E and mark your seam over here. Now because we used the mirror modifier both of the sides are identical. So instead of having to select all of those on this side we can actually just do control shift M and select an axis here. In this case it'll be the Y-axis and it'll automatically select it there. We can just do Ctrl + E and mark seam. And that's not to be confused these red lines with our crease lines. I think I call them SH. Sorry, those are crease lines. If you ever want to edit it again and don't want to select both sides like over here, you can turn on this. So in this case, it all mirror on the Y-axis. And it only works if your mesh is identical on each side. Now, if you have like a messed up over here, it'll still work if you adjust it over here. Let me turn that on. Like it'll still here. So it's only like basically you're right next to vertices. Anyway, if you did an inset first, you probably have a guard loop like something like this and then another inset. And you can use the guard loop as the edge here. In this case, I didn't inset. So, we do have a little bit of poor topology here, but it's fine if you're just starting out. There's nothing really here to worry about. And then for models like these, honestly, this topology is also fine as well. I'm just going to control E mark seam. And now, if I do unwrap and angle based, you can see that it's still not going to work. And the reason is because because this body is still a 3D object. We never actually sliced it up. So one thing we can do is probably just hit edge select, hold alt, and probably use this control E mark seam and then use this instead. And you can try some of these other unwrap methods. Uh conformal is typically for like hard surface models where angle based is typically better for what we call organic models where it's like using subdivision surface modifiers whereas conformal is like hard edges that kind of stuff like machines, guns, but it really depends on the project. Now this is definitely better but it's still not completely perfect. And if you can imagine why, like we have a cylinder here, but we're also like cutting the cylinder in half here. But then inside itself also has like a mini cylinder because we extruded it in. If you didn't extrude it in or you deleted this because you'll never see inside, which you actually can totally do actually. I I don't know. I don't think we need that. But you can imagine that this inside is also a cylinder itself. So we could probably mark something like this or maybe one of these edges and control E mark seam. And then we could probably get rid of this cuz we don't need to cut. If you imagine we cut this out, then it'll just be flat like a pancake. So we don't actually need to cut it again down in the center. We could control E and clear seam. And then we probably don't need to slice it over here either cuz we have a slice down on the bottom. Again, there are multiple ways to unwrap the same object. You could arguably go for this one because you might get distortion on this like angled face if that makes any sense. But for all intent purposes, this is an ideal unwrap. If you mirror this tele side, you'll get an idea on unwrap. But again, don't worry about it. Just use smart object and move on and have some fun. Okay, so I have both my scenes done. If I uncheck Chad Cafe and I show Cozy Cafe, I have both of them here. So what we have to do now is prepare our image textures for export. Now, if you did not make any adjustments in your shader editor to your image texture, it's just plugged into principal BSDF. You can simply go into like create your image. Oh, I just go to texture paint UV editor. Oh, this one's image editor. Actually, I think you can also do it in the UV over here. Anyway, go through each of your EXR files and then just do shift alt s, which is also right over here. Save as. And then save it. And the reason we are doing PNG now is because EXR is huge. Look at those file sizes. We don't want to load any of that. And if you want to increase the preview, you can do over here. And then you just want to save it as a PNG. And your colors should be fine. If you ever notice that your colors are not fine, you have to save the image through the compositing tab over here. Create new over here. And this by default is the camera. So if you have a camera and you hit F12 to render, render over here. Render your image. You'll see whatever your camera sees. So I don't know if I have a camera. Oh, I do have a camera. So you here you can see that. So basically, we want to unplug this. This one's just a viewer node. So you can leave that in if you want to, but I'm just going to get rid of that as well. Going to delete that. And we want to hit M to mute this. Shift A and search for an image. Search for an image. And then here we want to open one of ours like over here. Just plug it in over there. And make sure this is set to linear rec 709 which is the default standard in Blender 5.1 I believe for EXR. And you have to take into consideration the size of your image over here. So let's see. It's 1024x 1024. So we have to come over here, leftclick, drag down to select both of them. 1024. and then do F12 to render. And now you can save image, save as, and save it as a PNG here. That's only if your colors do not match up to your EXR files. Now, converting all these and saving them all as a PNG file has two issues. First, saving an image won't keep our changes. If I go into Cozy Cafe and pick on the Cabra over here, what about these? If we just save it as a PNG, we lose these adjustments. Yes, you could bring this into another software like Figma or Adobe Photoshop or whatever and adjust it there and that would be fine as well. You could just do that that way as well. But if you did something really complex in Blender, then how do you save it in Blender rather than put it into external software? Well, that's where baking comes in. So, if just for an example, if I hover Kirby over here, you can see that the shadow number changes. That's so weird. Why is the shadow not changing? Shouldn't it not react to light? Well, that's because there's no real-time lighting on this website. It's all baked into the texture itself. So, here you can see that the image itself is the one that contains the lighting information that I baked in Blender. So, baking is basically the idea that you save some amount of information. That way, you don't have to do realtime calculation for it later. It doesn't just have to be lighting. You can also do like animation and whatnot. So, this is a very common method to do on a lot of creative websites. Here you can see his arm is moving but the shadow is baked in there and stop moving. Now the reason we bake is not only just to save our changes we do in Blender but it's also to optimize cuz we have a lot of textures and each of them have their own respective size and honestly not all of them need the amount of resolution that they do. So the whole idea is to take all these three or how many textures we want and then put it on a single texture itself. That way instead of loading like four separate files, we only load one file and that one image texture contains all these four other paints that we had there. So it will be a little bit hard to follow. If you don't get it the first time, don't even worry about it. But let me just show you what baking is. Right now we know that we have a UV map that maps to our If I go in over here to object data properties under UV maps, we know this is our UV map to map our 2D image to our simulated 3D space here. Now, we also need one for baking. So, I need to add a separate one. And this is the map to go from whatever 3D object is mapped here to map to our shared texture. So, I'm just going to call this baking, but call it whatever you want. And let's say I want to share it with my other image textures here. So, just from experience and my intuition, this is more than fine just to put three different image textures over here on a single texture. In order for that to work though, we also need to give all these baking UVs as well. So, we can do baking over here and then baking for this one as well. Now, if it helps, instead of having to retype it every time, you can just Ctrl + C and copy it and then Ctrl +V and just paste it right over there. Again, you can also write a Blender script to add a second UV called baking to every single one of your objects. You can do that if you want to. It's just a simple LLM prompt, but I only have three here, so I just manually edited all three of them. Now, when you have them highlighted, it means you're going to operate in this domain. This icon me here means it's the one that's showing. No matter which one is showing here, it's not going to change because when you add a new UV map, it's going to take the one above information and use it as that one. So, let me go into the UV editing here. And if I just mess up the baking one with it highlighted, I'm operating in the baking one now. Whoops. Sorry. Let me just obliterate this somewhere else. So, let me actually change this one to caviar so we know where we're going. And as you can see that it's kind of mapping into those black areas into the B. That's why we saw what we did. As you can expect, we're going to do the same thing. So, we can leave this one as the one that's rendering. And then this is the one that we're working in. So, what we want to do is select all three of these. Make sure that baking is selected for all of them. And go into edit mode. Hit A to select everything and U. And we're just going to smart UV project. Add some island margin. Island margin here is actually important. The reason I didn't cover it earlier is because the gap between the painting was probably really big anyway. So there's probably not any sort of issue. Anyway, the reason margin matters here is because of this thing called mit mapping where as you get further and further away your it will automatically optimize and reduce the resolution of your texture. And since because when we bake, there will be like outpouring outside of the UV islands. When we downscale that texture, it's going to use like sampling techniques to basically quote unquote recreate a lower resolution of your original texture in order to save on performance. Cuz when you're far away, right, you don't need as much detail until you get closer. So, let me cancel this real quick. If you imagine when you scale this image down, this black area is going to get smaller and smaller and smaller and then it might eventually overlap if you don't have a decent amount of margin. So there are general guidelines that honestly you can ask LLM to give you probably be pretty accurate. But if you're at 4K, the general device is 32 pixels. Now this is very generous. So you can actually go get away with significantly smaller. Again, it depends on your use case, but that's like general practice. 4K is 32 pixels of margin. And then if you do the math there, it's about 0008. So again, that'll be based on project by project. But we can actually just go over here and smart U reject and set this to 0008 over here and unwrap it like so. And now we are good for baking. But one thing is I don't actually want to bake this or this. And the reason I don't want to bake this is because I want to be able to change the image later. So we can just leave it as is. And if you want to make your life easier, you can sacrifice a little bit of performance by separating this by selection. That way you can just rename it and target it easier in your code. But I'm going to leave it joined just to save on performance. Right? Reduce to draw calls as a single object. So I think the ones that I don't want to change, I want to bake. So not these signs, but this bread here. I'm never going to change this bread. Same with this one and this caviar. Now, if you did bake the changing ones, you could totally change it later if you wanted to, but in that case, you would have to re-ake or rechange the UV map and reexport your object. So, you typically only want to bake something that you know you won't go back and ever change. So, in this case, it's going to be the bread and the caviar. If you want to change the caviar texture to like a smiling one or whatever, different variations, I would not bake the caviara. So, these are the ones I'm not going to be changing. If you have multiple expressions for the cabaria and you have multiple adjustments in Blender, you can bake each one of those. Put it on like a sprite sheet and something like that. If you have no idea what I'm talking about, it's totally fine. But for this tutorial, I'm just going to say if it's static, if you're not changing it, just bake it. Or if you don't want to waste your time baking cuz you didn't make any adjustments, then just do what I did earlier where you just saved it as PNG. Okay, so these are the ones I can bake together. That means we have to give all these baking. Again, if you want to use a script, it's probably time saving now because we have to add it so many times. But now that we all have them baking, we can go here, edit a to select everything. Smart UV project 008 and unwrap. And this will be our unwrap here. And whoops, I selected the wrong object. So, let's do that again. And let's do unwrap over here. Now, it might scare you that the caviar is taking such a small percentage of the map here. Is that going to make my color super blurry? Because it doesn't have as many pixels. I think from experience, the blur that you're going to notice from this sort of size over here from far away is probably not going to be noticeable. But if that ever concerns you, there are ways to adjust your UVs, like selecting an island, hitting L to select some of these, hitting S to scale it down, and then hitting A, and then coming over to UV and doing pack islands over here. And then we can just do pack. and it'll try and repack stuff and rescale your other UVs to match the current UVs. Of course, you can also do it manually yourself. So, here you can select this island, which is definitely a cavi bear island, and just scale it up yourself. And because the spacing in between is so large, it's fine. You can actually even rotate it cuz we have all this extra space over here. And then the ones that you don't need as much pixels, you can make it smaller and so on so forth. So, really up to you. And if you want to look for like a paid add-on, you can come over here and take a look at UV Packmaster 3, which is I think pretty much has a monopoly on UV packing for Blender. It's definitely the best, but you can also use the built-in one over here and and try some of these other options as well. But anyway, for all intended purposes, it's probably just going to be fine. And if you for some reason don't see this, just make sure to turn this on. But yeah, we're actually ready to bake right now. So these look a little bit small. I don't know what they are. But you can probably just select them and see. Oh, it's the ears over here. Yeah. Anyway, doesn't really matter. I just know from experience. Even though this isn't perfect, you can we can just start picking. So, for each of these, we need to create another image texture. So, in over here for bread, bread is the one that's painted. We need to create another image texture. So, I'm going to shift a duplicate this one. Get rid of this one. I'll call this baking bread. Uh here I'm going to do 20480. Again, that's just from past experience. Color doesn't really matter here. We don't have any transparency. Get rid of that 32 boat load. Great. Let's just make sure to save that. So, let's do breaking red over here and do alt s. And then we should be fine to bake as PNG. But just to be safe, I'm going to do EXR and just save it like that. It probably shouldn't matter, but whatever. And now we need to copy this same image texture over here and go into the Cabi Bara painted one and also paint it here. Make sure it's white and highlighted. And then this one we don't actually have to do anything because it actually shares the same sort of one over here. So, now that I think about it, I messed up. We only need to bake these two together. And we can just replace the image section there later because this is an identical copy of this one. So, I can actually go back to UV editing, which is even great because now we can just um actually we don't have to unwrap again. We can just pack now with this. So, if we do UV and pack islands, yeah, now we have a lot more space here. So, these are the only ones we actually have to bake together. Let's change this to breaking bread. I am wasting a lot of texture space here. I know we probably could add other things to bake here, but it doesn't really matter. Yeah. So, what we have is breaking bread highlighted in both of our painted materials. Then we can come over here and we have to change to cycles over here. Now, the outline method is not going to work out of the box. There are ways to make it work in cycles rendered, but for now, we're just going to have to tolerate it. And then we can scroll down over into bake. Simple bake is a goated add-on for baking that just makes it a lot easier. You don't have to like add all those image textures and whatnot. But we're going to do it the native Blender way. And instead of combined, which is what I did in my room one, which includes the lighting of the cycles render engine, we're just going to do diffuse, which is basically just the color. And we don't want to add any lighting influences. We just want the color. So, we're going to get rid of those. And clear image. If you ever need to re-ake, it'll just clear your original image and bake right on top of that. If for some reason you want to bake another thing over it later, then you can like with all our empty texture places over here, we could actually add another object and then bake it at another time. In which case, you don't want to clear the image here. It doesn't really matter for us. And I never had to touch this in my career. So, we're not going to touch this here. Great. So, let's just select our objects. So, I'm going to select this and select this. And we should just be able to hit bake. Great. So, if I go into my UV editing and I go back over here, you can see success. Let me turn off this real quick. And here you can see that there's a lot of bleeding overlap. So, I must have not unwrapped with the proper margins, which is fine. So, we can just do it again. Uh, let's do it again. And sorry, let me deselect it. Oh, okay. So, after I did the pack islands, it actually put stuff a little bit too close. And now you can see the bleeding. So, because the colors are I guess they're yeah, they're not actually overlapping like it might cause issues or you might be able to get away with this, but sorry, I messed up. So, let's do that again. Let's go into edit mode. A U smart U project unwrap. Uh, let's do L. I guess it's good for refresher. Just make that a little bit smaller. We can make the cavi bear ones a little bit larger. So, I'm going to deselect that. Hit L. I'm just selecting random cavi bear ones. Scaling it up a little bit. Hitting A just like all UV. Let's do pack islands here. And oh, it does have the margin there. H. Let's just do pack again. Let's see. Does it work? Yeah, I think it's adding the correct amount of margin. I'm not sure if the Blender one is messed up cuz I usually use UV pack master. It's been years since I used the default Blender pack one. So, I don't know if it's like good or not. But anyway, it probably doesn't really matter just from my experience. You can get away with a very small margin honestly. Great. So now we can just do the same thing again. This time we do want to clear image. So both selected. We can just hit bake. Cool. So now we should just be able to alt S and save our image. So here you can see that we saved our image. Yeah. So this is actually fine. All these colors are fine. So I'm not going to worry about it. Great. Let's go back to layout. Let's change this to Eevee. And I'm going to duplicate this and move it to a new collection. And I'm just going to call that for baking and just hide it. You you could alternatively make the other one for exporting instead. Then for this duplicate one, I'm going to duplicate this and rename it to baked. So we can just do baked. And actually, let me duplicate this one more time just to show you the difference here. So we have regular cavi vera, which is this one. And then this one will be our baked one. So I'm just going to search for PNG over here. And we can plug that in there just to see what we get. And we need to use the baking UV map this time. We can get rid of this one. And here you can see side by side what they look like. Now, it's not exactly the same because this one is still being affected by the lighting. So, what we actually have to do is put this directly into the emission shader. And same with this one. Sorry, I accidentally got rid of it. My bad. It's the same as using an emission node like this. Same exact idea there. I'll just make it a little bit more explicit. So, if we do the same thing over here, let's plug that in real quick. We see the same exact thing over here. Again, if for some reason your colors are not right, you might have to play with the compositing. Other common solutions include going back to cycles and instead of baking with diffuse, you bake with emission. Another thing is, I should have mentioned it earlier, but make sure again to apply all your modifiers before you unwrap. Same thing with here. You can see I have no modifiers when I unwrapped. Also, make sure you apply all your scale. I did not apply the solidify modifier because that's just the outline. And you can add the outline before or after you bake. That's why it was not part of our UV map when we baked. It did throw that warning if you're paying attention that outline does not have an image texture, but that didn't really matter. Other common issues is face orientation. So, always check for face orientation. So, everything seems to look good over here. Now, we do see full red. Sorry, I should go back into Eevee. Now, we do see full red over here because the solidify modifier is the one showing it. So, we can actually just temporarily hide it and just make sure everything that you want to bake is not showing red. Now, it probably doesn't actually matter cuz we're not baking the lighting. we're just baking the diffuse. But that is something to check and consider. Last thing I want to do is probably make a jelly path in a project page, but we already covered that in a previous video. And it's basically just doing this. So if you want to duplicate this and work from that, you could totally do that as well. Okay. So to make the jelly path, we first need to shift A and add a path. And now we just want to orient our path along the wherever we actually want the path. So I'm just going to go over here and come over there. And every time you want to add, if you need one in between, you can right click over here and do subdivide. Or you can also just select, let me just control spacebar, and you can just extrude along. And pretty much just make a path like that. However you want to make your path. And just try and make it a little bit long enough. So depending on your camera view, it won't it'll like be out of frame because we want to make it loop. So something like that is probably fine. And now we should just be able to duplicate like our jelly over here. Scale that down. Sorry, I'm just I just finished eating dinner. Then we can just give it a array modifier. And we want to instead of line do a curve deformation. And whoops, I didn't select the curve. So let me just see the curve. I'm just going to look for path. Yeah, it's right over here. Let's just select that. Let's turn up the count to whatever you want to look at it. I don't want to do a line rotation. Maybe I want to do a line rotation. Let's see. Yeah, align rotation. And in this case, it'll be the Y-axis. Cool. Maybe I can make this just a little bit smaller over there. So, I'm just going to box select both of these. Control and deselect box select. And I'm just going to duplicate that. And let's just do S Y minus one just to flip it really quickly. And then maybe we can use the peanut butter one here. So, I can do Ctrl L and link materials and change it to the peanut butter one instead. And I want to bring these above just a little bit. So, we have a peanut butter trail and we have a jelly trail. Might want to give some variation to the array over here. Maybe a little bit randomize on on some of the scales. Uh maybe a little bit on the rotation and then a little bit on the scale too. H offset probably doesn't matter. Great. And let's make sure to put this back as principal BSDF. I don't know yet if we want to export with emission or principal BSDF. But for now, let's just go back to principal BSDF because if we export with emission, that means we can't use real-time lighting in 3GS to affect like shadows and stuff like that. So, we'll see how it looks first and then decide if we want to go back to a mission or not. Okay, now that I think about it, there's actually two ways to handle this. The peanut butter is on the left side. Basically the pattern is let's just assume I scroll down and going down the page makes you camera go right. If I scroll and let me just hide this one. Pretend this one doesn't exist. What we had originally I scroll it going to go to the purple and by the end of the jam it's going to come back on well this is going to teleport to the other side and start the peanut. So it'll be jelly jelly jelly peanut butter peanut butter peanut butter. And then peanut butter will have its own set of projects. So it would be like this. So this would be project one. This would be project two. And then after you get from the peanut butter, after it's snapped on the opposite side, you go back to the cafe. So this would be a oneway to infinite loop. The other alternative is you put both of them on one side. Let's just say let's just say we only use a jelly one for the infinite loop. In which case, we would put like two projects here. And then we would curve it over here. And then once we get here, we can't see the cafe. So we snap the cafe over here. And then we infinitely loop that way. So when we go back, if we scroll this way, the jelly path just snaps to the left side instead. And then we see these two projects and go back to the cafe that way. Uh I think they both use the same logic. So whichever one floats your boat can totally go for. I think I'm going to do it where they're both on one side. And for that reason, just make sure when you are going back, it curves a little bit back pointing towards the cafe area. So I did the same thing with the Chad Cafe. I just duplicated it and put it into Chad Cafe and change the materials, but all the placements are the same. You don't have to create the same placements if you don't want that, but I'm going to keep them the same. So now what we can do uh is file save incremental. So we have a fallback file in case we ever need it. And I think at this point we can probably show everything. Hit A to select everything and F3 and convert to mesh. So we can just convert to mesh. You'll see that error there that says four of them cannot be converted to mesh. And that's because we have two lights over here. And both of those can't be converted to mesh. So that's what that warning is. And now just like in the previous video, we need to optimize and join objects together that are static that we'll never target. So, like all these building pieces, I guess. If you want the window to move, uh, don't join it. But I don't think I'm going to do that. So, I'm just going to join all these, right, for optimization. If you want to target this and make it move up and down, don't join it with anything else. I feel like this chef hat would be joined with the cavi bear, though. And so on so forth. And of course, if at this point, you can delete things that won't be seen. For example, back faces, possibly on the sides. So, maybe like if I go into edit mode. Well, sorry. I should probably delete before I join just so it's easier. But yeah, we'll probably never see this backside. So, we can delete like those sorts of faces. We could probably even select these. Shift G, select similar normal. And we could probably get away with deleting a bunch of those actually. Yeah, just to save on space. Again, it's kind of optional because our scene is pretty small. So, if you don't decide to delete stuff, it'll work. it'll work fairly fine anyway, but that's just a small optimization stuff. Now, sometimes when you're joining objects together, some of them will turn black. It's just because of the normals. Uh, Blender will like recalculate normals by default. It's fine. Just hit L and Alt N and just flip them. Okay, so basically there's a few things that I joined together. I did join this entire cafe together. Um, I might split it up later if we want to do other animations. You can totally do that if you want, but for for now, I'm not going to do that. Cabby bear is separate. This is separate. If you want to animate, again, make sure you check the origin point and where you want to animate from. I don't know if I'm going to animate this other than up and down. So, that's like a great place to squish to and scale. This is renamed to cozy project one. This one's cozy project 2. And then cozy cafe and caviar. So, we p we primarily only need these. This floor plane over here, we can create in 3GS. Same with the background sphere. We can also just create that in 3GS if you want to save time. and just export as part of GB. You can totally do that as well. So now if I go to Chad Cafe, everything here is joined as well except alpha Chad over here. Beast mode alpha Chad right over there. And then this name is Chad's project one and two. So now I accidentally converted my ketchup trail and our mustard trail a little bit too early because if I go in edit mode and P separated by loose parts, you'll notice that the outline object of the trail is also a thing by itself. So if you do crl J like now it'll become a single object which is what we want but we would have to do it for every single one and that's a little bit tedious. I mean it would probably only take five minutes to do every single one. Just box select control J for so on so forth. But this is exactly why we do save incremental. So we can just fall back if we ever need to. So I'm going to undo that. And what I'm going to do is actually just delete all this. I'm going to rightclick delete hierarchy. Delete would work fine there as well because there's no real hierarchy. And go back into our fallback. And then for all our paths, I put them in temp path chat over here. And each of these is something. So I think that uses nerbs path one and name it peanut butter. So this one is going to be peanut. Peanut butter trail. We can hide this one. This is not mustard. This one is mustard, so we can call this mustard trail. And then this one is ketchup trail. So ketchup trail uses nerves pass 3. I'm going to move that into a new collection. And I'll call that Chad curve. adds curves and then mustard trail uses num nerves path 2. So I'm just going to drag it like there. Great. So if I toggle this on. Great. And now we can go back and go into this and we can append those trails back. So I'm going to go to that file that we saved the collection over there and I'm going to append tempath cafe and Chad's curves. Great. And I'm going to move Chad's curves. And Chad curve goes in Chad Cafe and Tempath Chad goes into Cozy Cafe. And now we just select both of these. We apply their array modifiers. I'm just holding shift select. Go into edit mode. P separated by loose parts first. And now to quickly apply all the modifiers. We just convert to mesh with F3. And now we want to right click set origin origin to geometry or Q if you put it over there. Ctrl period to change the origin point for all them. Control Z lock axis and just snap it to the floor floor there. Control period to confirm that. That way when we scale it's going to come from there. That's also optional depending on how you want to animate. And now we just have a bunch of ketchup trails and a mustard trail. Okay, since I'm creating this plane in 3GS, we can probably I think save incremental at this point. And I'm going to delete that. Same with the sphere. I don't think I ever want that. So, um, yeah, I'm just going to delete that because we have our save incremental anyway. And now what I want to do is just come over here and make sure you turn this on selectable or not and disable it for the lights. So here I have some area lights. I want to disable it for. We could also just disable it for the sphere and floor it as well, but whatever. And I'm just going to file export glTF. Make sure you remember export settings. That way you don't have to do it every time. We only want to include our selected objects. Transform is fine. And for material, I'm changing it from like 80 to 100 because we're going to use the online compression tool. So, we don't want to want to optimize here in in Blender. We don't have shape keys. We don't have skinning. So, we can leave those unchecked. And whether you leave compression on here or not does not really matter because we're going to be using an online tool to compress it. So whether you want to leave that on or off, it doesn't really matter. And then animation, we don't have any. I think I will come back and add one here later. But for now, we can just save it like this. And this one is going not to be Cozy Cafe. It's going to be Chad Cafe. And the other one will be Cozy Cafe. So same deal over here. I'm just going to hit A to select everything. File, export, glTF, Cozy Cafe. We don't need to change anything over here. And just export that. So there are like there are quite a few online compression tools. These are two of the more common ones where you have an image embedded and it will also compress your image rather than like a standalone thing. In the previous video, we uploaded our image textures to Squish and then rehooked it up in Blender and then exported it again. We don't have to do that because our textures are already embedded. These tools actually also optimize your textures and keep it embedded. There are a lot of other more quote unquote developer like tools that you can use for compression like Image Magic. You can do bulk compression, run scripts on all your images and whatnot. There's also chil transform, which is more like a command line tool for compression and a lot of other stuff, but we're just going to stick to the basics for now and use an online compressor. So, I'm just going to use the Simon Dev one, but they're pretty much act very similar. Simon Dev is a famous YouTuber. You might have seen him on YouTube already, but he makes 3JS content as well. So, we can upload Cozy Cafe. Now, it's not going to look like it does in Blender because viewers always have some sort of like lighting already attached to it and it just it'll just look different from Blender. Even when we do, it's going to look a little bit different. Now, these are a little bit more advanced than for this video. So, I'm just going to uncheck all of them. Invertex though, you can see that we are applying Draco or Draco compression here. We don't have to change anything here either. But for texture, you can set the max resolution. So, if you had a 4K texture, it'll downscale it to 102040 by 102040. I don't think I had anything above 1024. I think I had one at 20480, but it'll just be downscaled. And again, all these images that I used were really small, so I could probably take it down to 512, but I'm not going to risk it. I'm just going to leave it at 1024. The format of the texture, we did PNG when we did our export. In the previous video, we exported as WEBP. There's a lot of other formats here that are a little bit more technical. If you are interested, you can check out some of my more advanced videos on my channel. But again, we're just going to do WEBP here. And we're going to click apply our changes. And you will see something really interesting here. So our original file size is 2.54. The file size actually increased, but the memory size is actually lower. So what is the memory size? Well, that's your RAM. Your RAM has a certain amount of memory. If you hover over here, you can see memory usage of this website is over there. That's your RAM usage. where file size is like the actual size of your file. So you can imagine at like at a very very high level file size is like how long your website will take to load that whereas the memory size is the quoteunquote performance during runtime. So if you have using too much memory it might crash and and things like that. So our memory size actually went down but our file size went up. Why does that make sense? Well, we when you compress something, you actually also attach a bunch of information to your GB model in order to optimize it if that makes sense. So that's why in this case the file size is increasing whereas the memory size is significantly decreasing. Most of the time with more complicated models both the file size and memory size will go down. But because we have such simple models it kind of is expected the file size to go up and the memory size to go down. Either way again that also depends on how you compress it, what tools you use, so on so forth blah blah blah blah. That is the explanation at a high level. So we can just download that and use that. And let's do the same exact thing for Chad Cafe and let's just download over there. So let me go to another online tool and just show you if I just did compression. Here's Cozy Cafe is 2.6 megabytes. I come over here. Let's turn on Draco. There's other compression methods. Yeah, but we're just going to stick with the most common one right now. Here you can see it's 2.5. Whereas the So here you can see that we have Cozy Cafe compressed which is from the other tool. And then we have Cozy Cafe-V1 which is from here. And this is actually smaller. But if I click and drag this in here and take a look, you can see the VRAM is 136.8 MB. However, if I use this 2.9541, look what happens to the status here. You can see the VRAM is 49.1 whereas the disk size is a little bit bigger. Of course, whether you compress and optimize for a small project like this probably doesn't really matter, but I just wanted to share some tools that are commonly used for quicker projects. Okay, we are finally ready to start coding. So, make sure you have some sort of JavaScript runtime environment that you can run things on a local host/package manager so you can do like npm install, that sort of stuff. If you're not familiar with that yet, I would recommend um watching a few videos on that before coming back and watching this one. And for our bundler, we're going to be using Vit. And if you don't know what a bundler is, I would also recommend watching a video on what a bundler is. But basically when you are developing in your local JavaScript environment use a lot of tools like SCSS, Tailwind CSS, use a bunch of imports and all that kind of stuff and a bundler just like takes all that development stuff and then optimizes it for the web pretty much. So it might like take really long variable names and change it to like a single letter. It might get rid of extra code that you don't use from your imports, things like that. So that's basically what a bundler is at a high level. all the development tools and then compiles it down to what the browser can actually work with and optimize and vit is just a really popular one which is why we're using it but feel free to use whatever bundler that you want to use in regards to project structure I'll talk about that in a little bit but it's going to be based on a arguably the most famous 3GS creator right now Bruno Simon's template which I'll talk about but let's first start our coding project so just navigate to wherever you want to save stuff cd there and we can just copy and paste this so I'm just going Going to copy that like so. And I'll call this alpha chads cozy cafe. Cozy cafes. And we're just going to use vanilla JavaScript here. And no tides script, just regular JavaScript. And I'll do install with npm and start now. Let's just control C to cancel that. And let's do code dot to open it in our VS code editor. Sorry, we have to go inside there first. So alpha chat base. And now we can do code dot. So before we begin, let's talk a little bit about project architecture first and different design patterns. So technically, just like what we did in the intro course, you can put everything into a single JS file and that would work perfectly fine. The issue is if you have a lot of things, it becomes a lot harder to know where things are, where to add things, and you'd have to keep navigating up and down the page, and that would be kind of tedious. Obviously, you can add like snap points to your code to quickly jump areas easier, but at that point, sometimes we just decide to structure the code differently and take things out into their own separate like util functions that are repetitive or classes and things like that. And there's a lot of different ways to structure your code. And honestly, those are what frameworks exist for. Those are also different ways and methods to structure regular JavaScript code, whether it's like JSX, which just eventually compiles to JS. Anyway, the one we're going to be using is called a singleton design pattern. So, if you don't know what that is, I highly recommend going on Refactor and Guru to take a look. But basically, the whole idea is you can have like one class that all your other subasses refer to. So, let me just show you that if I open some of these files over here at the top, what you'll see is this for each of them or most of them. This experience equals new experience. It's in the renderer. It's in the camera. It's not in resources. When we cover this, I'll talk about why not. And then it's also in world. So why do we see this over and over again? Why are we creating a new experience every single time? Well, we're actually not creating a new experience every single time cuz it's a singleton. We are just getting all the stuff associated with the first experience that was in instantiated. So here you can see the guard clause over here. We're creating an instance of the experience and if it exists then just return the existing one. Otherwise, otherwise create the experience instance and set it to this class. So you can imagine the experience is kind of like a product manager. I think the analogy they give to this is government which is also a great analogy. When you're coding, don't try and look at coding structure any differently than a real life sort of scenario. You have all these like people like the camera needs to know how to render. So instead of like pointing to the renderer and or the camera needs to know which location to go, it doesn't like point at like an object. It just goes to the person who already knows that. So the product manager can just essentially tell you what you need to know, which is basically the idea. Oh, tell me what I need to know here is all it's saying. It's like, hey product manager, talk to me. And so you can think of your code just like a bunch of people talking to each other or talking to the same person for information on these other people. Are there pros and cons to this design pattern? Absolutely. And it still might not make sense, but once we get into the code, it'll be a little bit more clear. Great. So, just like our previous project, we got our index.html, our basic CSS and main.js, and then this extra thing over here that we can delete. We don't need to worry about that. Assets, we can also just delete these. We don't need these either. Ctrl + C to copy this line and then Ctrl+ A and just backspace everything. Ctrl +V to paste that line. You do not have to highlight the line to copy it. you can just have your cursor on that line and it'll highlight there. So, when it comes to a 3GS project, what do we what are the basics? Well, we need a scene, we need a camera, we need a renderer, and then we need a canvas element. All our 3JS stuff lives in that canvas HTML element. And of course, we need some stuff to actually add to our scene. And then we need to update our renderer on a loop and we know that. So, we just take all these different components like the renderer, like the camera, like the scene, and then we split them up into their own different classes. So, let's come over here and let's just uh change this to experience. Well, uh let's call it canvas wrapper. And in here, we want to create our experience canvas. So, I'm just going to do canvas dot experience like that. And that's all we need to get started for now. We can come into the CSS and I think we can get rid of everything. And we can just do global selector for now. And we'll just we can do M0 for margin zero, P 0 for P 0, and box sizing for border box. Just like that. And then we can actually yeah, we can also change this to an ID. And we can do hashtag experience. Hashtag experience, we want to position it fixed. Let's just give it a width of 100 viewport width and height of 100 viewport height. And for the HTML and body, html and body, we're just going to overflow hide so we don't see the scroll bar anywhere. And we actually I think we have a wrapper. Okay, I'm going to call this experience wrapper instead. Let's just copy that over here. And we can Yeah, let's call Yeah, let's highlight this. Shift alt down arrow, shift enter to add a quick space above. Let's change this one to hashtag. Whoops, I accidentally got rid of the hashtag. And this one will be 100 viewport. And then we can make this one 100%. So I'm going to hold there. I'm going to hold alt to click to add two cursors. Just like that. And we should just be able to do this. And I have control shifts to save all my files at the same exact time. So it also saved this one when I did that. And now if I go back over here and go into the inspector with control shift I or rightclick and do inspect and I click on it, you can see the canvas experience takes up the entire screen which is exactly what we want. Okay, so we have our main injection point right over here. So let's do that. We'll do document getelement by ID. We want to grab our experience over here. So we're going to do this and we're just going to slap it in there. And we're going to pass that in into our singleton class in order to reference the canvas. So let's do that now. And we're going to come over here and let's create a new folder called experience. Let's create our singleton or our product manager or our government, however you want to look at it. And I'm going to call it experience. Just like that. So let's come over here. We're going to export class and we're going to call it experience. And we need our typical constructor. So we'll do constructor. Open it up. And let's just set the experience. Equals to this. This basically just referring to this entire thing. Now if it exists, we just want to add that guard clause and just return the original one that we created first. instance. If it exists, then simply just return the one that we already created, which I'm just going to copy and paste there. And of course, if it's just one line, you don't have to have the brackets there. Let's also create an init function where we basically initialize all our stuff like the renderer, the camera, the resources, the loading, and all that sort of stuff. So, I'm going to come over here and we're going to do this. And we're going to create an init function. So, thisinit is just saying, hey class, use your init function. and play that for me, please. So, that's exactly what this is over here. Let's add a canvas here just so it's a little bit more clear what we're doing. And of course, make sure to update that over here. And instead of passing it in as a parameter, we might as well just do it directly in experience itself. So, I'm going to come over here and I'm going to set this classes canvas element equal to the canvas that we're just selecting over here. And let's also put experience canvas just like that. Great. Let's do Ctrl P to quickly search for a file. Let's go to main.js. It might be command P on Mac. And here we need to import our experience. So we can just start typing it and then just hit enter to start typing in. If you ever accidentally get rid of the autocomp completion, you can always come back and do control spacebar and find it again and it will automatically import like that. And now we just need to do new experience just like this. So we instantiate the new experience. Just do a test log. I'm going to need to go back to experience over here. And we should just be able to do console.log. Hello world. Hello world. And we will also just console.log this so you can see what this is. And I'm also going to do experience.instance. So if I go back, make sure you are have a local server. So you can do mpm rundev. And that will run over here in package.json. It'll run npm rundev and start your local development server. And here you can see that experience. And this is the same object over here. And then we also have our hello world showing. So it's activating. Now the canvas element is null because I did not save my index.html file. So make sure to save all that. And now if I go back here, you can see we have a canvas element. So instead of creating our scene first, we want all those imports and vit and our VS code editor to be able to guess that. So let's create a new terminal over here. And let's do mpm i3, which is 3gs. We also want gab for later. So we can also just put gab on the toggle. And if we need anything else, we can put it in a little bit later. So these packages are going to show up in here. You can see three is just there. And now Gab appeared in our node modules. Again, each package is just code someone else wrote. Now you can see it's in our dependencies gab and 3GS in the package.json. Great. I'm going to temporarily hide with control back tick. I'm going to hide the terminal so we have a little bit more space and controls and crl +b to hide the left side menu for a little bit as well. Let's come over here and control W and quickly close those as well. Great. So let's import everything from 3JS. So we're going to import all import all everything as three as a three object and we're going to get that from three. And instead of just three, we're going to use 3 webbgpu. I don't actually know if we're going to use the new web GPU renderers features or not. So if you want to just use three, import all s3 from three, you could probably do that. But for now, I'm going to do webgu. and it's not going to change anything unless I decide to use the new WebGPU features. So, if you didn't watch the previous video, basically we are switching from WebGL to WebGPU as WebGPU is more powerful than WebGL, but WebGL is still here for going to be here for a while. So, WebGPU is just another method of being able to work with graphics and a computer's GPU on the browser. Here you can see WebGPU is a successor to WebGL. Great. So, now we can create our scene. So we'll do this scene. So we're just saying, hey experience, you need a scene and we're just going to grab it from that 3JS object and we're going to create that scene. So let's create our first class and it's going to be a time class. And it's more of a utility class that we're pretty much only going to create once. So let's come over here and create a new folder called utils. And in utils, we're going to create our first class called time.js. I'm just going to copy from experience.js into time.js. And instead of export class, we're going to change this to time. Instead of this innit, let's do ctrl d to select the next instance and do this.date. We don't need anything from the experience. In fact, the time is actually going to be informing the experience. Basically, utilities are informing our product manager rather than the other way around. And let's get rid of this and do our window request animation frame. And we're going to call this.update. So it's calls itself. And then we're going to call itself. This upupdate. This is pretty much all we need in the time class. But it's like okay, we have the time class. So let's create it. This time equals new time. Let's create it over here. But how do we actually use what the time class is giving us? If I console.log time this time. Let's make sure to come over here control spacebar and import this. And I'm also forgetting the new over here as well. And I go back. It's like uh well, we got our update function, but like what do I do with it? How do I access that information? Well, what we can do is from the time class trigger an event that experience listens to. So, how do we trigger an event? Well, you could write an event emitter yourself, but it's easier just to use a package here. And one way to do that is with with the Node.js event emitter. So here you can see when we we can emit a an event and then we can use the on property to listen to that sort of event there. So let's come over here and let's do mvmi events. Let's go back to time.js. Let's import the event emitter from events. We want to be able to use the stuff from that object. So we can extend it over here. Extends event emitter. And then we need to super and write super just for refresher is accessing a project on a super classes constructor. So basically time now becomes a child class of the event emitter class and we can use the parents properties and things like that. And now in our update function here we can do this emit again which is going through the super which is actually using the event emitters this emit function and we can just give that a name called update and then in experience the docs say to use on. So we can come over here let's get rid of the well I'll leave one console log so we can see it. And now over here we can do this time do on and we want to listen to that event called update. And what do we want to do? Well, let's just do a quick console log for now. So I'm going to write that quick arrow function there. Let's just do console.log updating just like that. So if I go back to the browser, take a look. We have a request animation frame that's keep on logging update update update in experience. Great. So instead of just doing the update here, experience itself is also going to have an update function. So we'll create update over here. Basically the product manager learning from his aid time.js is going to tell the other ones to update as well. So let's get rid of this console log over here and let's do this.date again. So every time it emits update, experience listens to it, it's like, oh, thank you my informant. I'm going to tell everything else to update as well. And we'll put stuff here in a little bit later. Let's create another class and it's also a utils called sizes.js and this will just handle like our responsiveness and things like that. So let's just copy from time.js over here. Save us some time. Let's do sizes. We're going to set some stuff. So width equals window.in width of course inner width. And and let's change this to height. And you'll notice when I change the height over here, I didn't have to adjust the h after. And that's an extension. And that's an extension that I'm using. Uh I think it's change case. I don't actually know. Well, it's clearly not change case, but there's a lot of other extensions that do the same exact thing. It'll take into consideration your case so you don't have to manually adjust it later. Anyway, let's go back to the file system here. And what we need to do is create an aspect ratio for our camera. So, we'll do this width by thisheight thisheight. Sorry, my bad. And we'll also put our pixel ratio directly in here as well, cuz why not? Same thing as last time. If you need an explanation on what pixel ratio is, definitely check out the previous video. Pixel ratio is basically the relationship between the amount of physical pixels a device has versus a digital CSS pixel. And then we can just instantiate this.resize over here. We call this resize. And let's just control X. I don't need to get rid of that space. We can put everything over here. I'm going to leave it because we need to emit resize event that we're going to listen on into experience. And we also want to add an event listener here called window add event listener listen into the resize event. Let's just move all our stuff into there. And sorry, this shouldn't be named resize. I'm from the future. It should probably just be in it. And honestly, because we're only doing something simple, you might as well just keep it in the constructor itself. Let's come back over here. Shift alt down arrow. Control D. Sizes. Sizes. Control spacebar and import sizes. And then we can come here. Shift alt down arrow. And we call this dots sizes on resize event. We're going to shift alt down arrow here. Ctrl + D. And I'll call that resize over here. And now I can add a console.log here just to make sure it works. And if I do and come back here and resize the window. You can see it's going to print out our message over there. Now let's create our camera. And this is not a utilities. It's going to go directly into our experience cuz it's kind of tightly coupled in a sense. So I'm going to create camera.js. I'm just going to copy the experience into camera. And let's just get rid of the junk inside over here. Get rid of that. yada yada yada. And now we can change this to camera. Great. And let's create a nit. I'll just call this a netit camera cuz why not? A nit camera. And we'll do this. camera. It's like, hey, create the camera. So we'll just create a this perspective camera equals new three. We'll set a FOV temporarily at 35. And now we need to ask assess the sizes object. So are we going to come over here and say hey sizes bro uh am I going to create a new sizes here? Is that what I'm going to do? And I'm not going to do that because the product manager already knows. So I'm just going to ask the project manager anyway. So how do I do that? Well I import experience from experience. So I'm going to come over here and I'm just going to create experience get its import. And what I'll do is this.experience this.experience experience equals new experience. But again, we're not actually creating a new one. This guard clause is going to trigger over here, and we're going to return the one that was already created. I'll do this. So, this classes sizes is going to equal this.experience. This do.experience sizes. Now, we have this sizes as a property of this class grabbed from the experience sizes which was grabbed and created over here. Now, we can use that. But it does seem a little bit weird that we're creating the own class one and pointing to this one over here. So why don't we just get the instance itself and just directly use that. So we can create a getter function in the experience class itself over here. Just create a static method static. And we'll call that get instance over here. And we'll just return return experience.instance. Now let's go back to camera over here. And instead of this doexperience equals new experience, just do this.experience experience equals experience get instance. We're calling the getter directly. And now for our aspect ratio, we can do this experience.pect ratio. And then let's set the close and the far value. A th00and's obviously overkill, but whatever. It doesn't really matter. And if I just console.log out. Let's go back to experience over here and just create that real quick. So shift alt down arrow one more time. Let's call this camera control spacebar and import that. And I go back over here. Take a look. We have our aspect ratio printing out right over here. Now, of course, what we need to do is update the camera on resize. So, we're going to come over here and we'll just copy this top perspective camera. Every time it resizes, we need to update its projection matrix over here. We have to update its Whoops. its aspect ratio as well. And we're just going to reset it to this experience, which also listens to the resize already, right? The aspect ratio is recalculating here. And camera is just listening to that. Let's go back to experience. And we need to tell the camera to update based on the sizes emitting the vent. We'll do this. And just call the resize function in the camera. If you want to test that, you can console.log it out over here, but I'm going to trust that it works. And let's just add a space right there. And honestly, just for consistency, let's just leave this as in it. That way, we don't have to type it every single time. Next up on the list, let's come over here and create the renderer. Renderer. This is also pretty tightly coupled. So, I'm just going to put it over here. And let's just let's copy our camera. Place it in here cuz ain't nobody got time for that. Ain't nobody got time to time to retype that, right? And let's create our renderer. So, let's come over here. This renderer. I don't know how to spell that, but hopefully that's right. And we're going to create a new 3 webb GPU. We're going to use the web GPU renderer here. And we need to pass in the canvas element. So it will be canvas. And that is going to be from our experience. So this experience canvas element we should call I think that's what we called it. We called it canvas element. And then we want to set anti-aliasing. Anti-aliasing so we don't have those jaggedy edges. Alias to true as well. And then I'm just going to copy and paste some renderer options that we had before. So again, just refresher, this makes the shadows a little bit softer. This just enables shadows. And we set the renderer set sizes equal to the size, width, and height. So it takes up the full width of the canvas and its pixel ratio. And on resize, we need to update both of these. So we can just copy and paste this and replace it in resize as well. Then in the update function, we need to call this renderer. And we actually have to tell it to render on each frame. We need to use this.experience scene from this.experience experience this scene from the experience and this do.experience camera as well. So again this experience is over here but we're getting the instance from over here. So it's basically saying this camera you the renderer use this camera which is this pretty much and then use this scene which we created over here. So now project manager has to tell our renderer hey this renderer on every time our thing is ticking then also update here over here please and we need to create the renderer over here of course renderer come over here control spacebar and import the renderer. Now if we go back you see that we have an error here that says render is called before the backend is initialized which must mean there's something under the hood that's loading and needs to be done loading before we can actually start calling render. So how do we fix that? Well, in our experience class, we can change this to an async function. And instead of going through and just listening to everything, let's just wait for the renderer first. So, we'll do this.init. And now we can just await this. So, we'll just wait for it. And don't do anything over here until this is initialized. Let's do the same thing in our renderer.js. We're going to create this to a insync async function. And we'll do await this.er over here. Don't do anything with the renderer yet until it is done loading. So if I go back and refresh the page, you can see that we don't get that error, but we're now getting another error here. Uh so we're getting an error this.experience camera. Oh, sorry. Because what I'm doing over here is passing in the camera class rather than the instance of the camera. So instead of calling this perspective camera, we can call this instance instance. And for the renderer over here, uh let's go down over here. This camera.instance. So the instance of the camera itself. If you want to leave that perspective camera like we had originally, then it would just be camera.perspective camera. And then whoops, I actually also renamed this. So let's come back over here and change that to perspective camera. And now we have no errors. Is this the only way to do it? Basically, it's saying await this async function. And then this is the one that's actually targeting the created render. Is there another way to do this? Uh, absolutely. You can have like a guard clause here like if this renderer exists and and this.er ready which is like a boolean value you create over here. And then once this is done you can do like this. Ready equals true, right? And then this way you don't need this. You just start the loop and this guard clause will keep on detecting if those are done and then update it. So you could do it that way as well. But yeah, you know it's it's kind of up to you how you want to do it. I'm just going to leave it like this. Okay. So, we should be able to just create our world now. So, I'm going to create a new folder here called world. And this will include our cozy cafe, our Chad Cafe. Well, I guess both of them are Cozy Cafe. I guess Cav Bear Cafe as well. And let's just create world.js. And we're going to add a dummy cube just to test everything first. And I guess you could also put this in experience cuz it's kind of like tightly coupled. When I say tightly coupled, it's like pretty much just directly called in here. like world would just be directly caught in here. It's not like nested deep in in anything else. So, but whatever. It doesn't really matter. And we'll do the same thing over here. Go here. Camera, paste it in world. Let's change this to world. Let's get rid of this junk over here. Ain't nobody got time for that. Ain't nobody got time for that. Let's come back here. Control space bar and import it to the documentation and just copy this cuz well, I guess we could also copy camera position cuz remember last time the camera spawned directly inside the cube. So in order to see it, we have to shove it out a little bit. Uh so so let's just put it in here. And instead of scene, we'll do this.experience scene, right? We want to get the instance of the scene. And for the camera, let's just set its default position over here. Let's get rid of this cuz we ain't need that. Position.Z. And we're just going to set that five initially. Let's go back to world and make sure we update this path to go one directory out. If I go back, voila. Congratulations. We've got our cube. Let's do this, guys. Let's do this. Now, let's enable our orbit controls again. So, we can just pan around. We'll probably get rid of that soon, but let's just go back and initiate it anyway. So, I'm going to shift alt down over here, and I'm just going to do set orbit controls. Orbit controls. Let's just copy this puppy over here down over here. And then we just pass in the orbit controls, the camera instance, the camera instance, and the canvas element. And then we're enabling dampling so it's a little bit smoothing. Then we can update the controls. So this controls update and let's make sure in our experience over here we also called this camera.update which we did not. So let's just shift alt down over here and do this camera.update. Let's make sure to control spacebar and import orbit controls as well. Now if I go back and click and drag and middle mouse button and right click, we have all those controls. Nice. Sweet. So let's go back to world and get rid of this cube. Ain't nobody like you cube. Get out of here. No, I'm just kidding. We love that cube. Okay, so let's create some more utils. And we're going to create one called loaders.js. And this will just have all our loaders. And then we're going to create another one called resources. And this is going to be everything that we load. And then we're going to have one more. And I guess it's less of utility and it's more of like a data. So maybe we can create like data over here. I don't know. Whatever. We'll just shove it in here. And I'll call this assets.js. So first let's upload our assets. Uh so let's come over to public. And ain't nobody need these. Let's get rid of those. And what we want to do instead is create a new folder here. And I'm going to call one for models. And I think I'm going to add one for images for later. Uh, but I don't know yet. And let's just click and drag our models over in here. And I don't like these file names cuz they're too long. So I'm going to hit F2 and just rename this to Chad Cafe. And honestly, Cozy Cafe doesn't make sense. Let's do Cab Vera Cafe for this one. So in assets.js, JS. This is basically where we list all our assets, what type it is, and the path there. That way, we can just when we're loading it, we can just like pass all of those in directly into the loader. Let's come over here and let's export defaults an array of objects with some information. So, we want the name of it. So, the first one we can call Chad Cafe. Chad Cafe. And the type it's going to be a GB model. GB model. And the path is going to be /models/Chad Cafe. Chad cafe.glv and I forgot the comma over here. So by default vit will know it's going to be slashpublic. So all you have to do is slashmodels. If you want to config instead of using public by default, you can create a vit.config.js file and change it to like static or whatever name you want to use it. But I'm just going to use the built-in default name of public. So we have Chad Cafe and then we also have Cab Bear Cafe. Cabar Cappy. Get it? No cap. Like for real, no cap. And then this one will also be Cappy Bear Cafe. So let's go to loaders and create our loaders. So I'm just going to copy from sizes over here. And we're going to come over here. We don't need events here. Uh we're just going to create loaders. Instead of resize, let's control here uh control D to select the next instance and do init loaders over here. And let's create our object of loaders. So we'll call this list.loaders. And we'll just shove all of them into an object. create the glTF loader. So this loaders do gltf loader gltf loader. So every time we refer to this object and then give it a object property over here. We're basically giving it a key. And then once we hit equal, we're creating the value there. So it's going to be new gltf loader over here. And then we need our draco loader cuz we have compression here. So we're going to call this draco loader. Draco loader. And then this is going to be draco loader over here. We need to set the decoder path. So we're going to do that again. Set decoder path. And we need to do Draco. So we need to do the same thing as last time. We need to go into node modules 3. Sorry, not source. It should be examples JSON. And under libs, we should see the Draco. Just Ctrl + C to copy the whole thing and paste it in our public folder. So we're just pathing to the / Draco folder. And we need to tell our glTF loader to use a Draco loader. So we'll just copy this over here. And we'll just do set Draco loader. Set Draco loader to this. Draco loader over here. And I think we're going to add some image loaders a little bit later, but for now, this is going to be enough to load our model. And I don't remember this path being right. This might be right. I don't actually know unless they updated it. But these are the older paths that are typically used. Maybe add-ons is new. I don't actually remember. I would have to look. These are the ones that are on the documentation. So now we can create resources. And I think I'm going to copy from sizes. And let's copy in here because we need the event emitter. And you'll see in a little bit why. So I'm going to hold alt and just get rid of this and this well as well and get rid of that. And of course you already know what we're going to do. We're going to create an init function over here. Now we don't actually need to initialize anything. I guess we can call this init loading like initialize loading. Uh but I'm actually going to call it start loading instead. So once resources is created we're just going to start loading immediately. Now we need to use the loaders. Now, because loaders is only used in resources, we don't have to put it to the singleton. Project manager doesn't need it. Resources basically has its own aid called loaders. And because it's only used once, we probably overkilled splitting this up into a separate file cuz it's only created here, but whatever. It doesn't really matter. We'll just do this. Loaders equals new loaders. And make sure to import that from loaders. And when we create the new loaders, we actually just want the over here this object. So we can actually just directly target it after we instantiate it and it'll just place it into this classes this loaders. Now we also want to set the assets. So this assets and that's going to be the assets that we created here, right? We created this simple data over here. So we're just going to grab that. We call that assets I think. Yeah, from / assets. So we can just get that import there. And we'll create an object that we can refer to in our other files and give this into the experience singleton. This is for our project manager. Every item that we load is going to shove into this object just like our loaders did. And we're just going to refer to this throughout our application. We're going to create a cube, this Q, and it's going to be this assets.length, which is currently two. And we'll create this.loaded, and this is going to be zero cuz we didn't load anything yet. So once the loaded hits the Q or two. So when two equals two, we're going to trigger an event. Hey, everything is done loading. And then we're going to give this item object away to the single print. So let's come into start loading. And we're just going to do, hey, for const asset of this assets. So for each object in our array over here, we're just looping through each one of these objects over here. We're going to check the asset type. So if so, if asset, we're going to check the type. If it equals equals equals GB model, which is right over here, we're just checking, hey, is it type GB model? Great. If if it is, then we're going to do this loaders, and we're going to select the gltf loader, gltf loader, and we're going to load. We're going to take that asset.path that we also plug in in there and we're going to load that asset. But now we want to shove it into this items over here. So let's come over here and what we want to do is do single asset loaded. Single asset loaded. Which means once a single asset is loaded, what are we going to do for it? Well, we're going to shove it into this items object over here. So what we need here is the asset information as well as the actual file that is loaded itself. So we can pass that in in the loader itself. So if you go to the gltf loader documentation, you can see the first one is the string with is the path which is what we've passed in asset.path and on the second one is onload. So once it's done loading, what are you going to do? We're going to trigger a callback function over here. So we're going to just call this pretty much. So once the file is done downloading, we're going to trigger a function over here and that's going to be this single asset loaded. We're going to put pass in the asset name as the key and we're going to pass in the file itself. And then here what we can do is just do this items. Create the key from the asset name that we passed in. And we're going to set the value equal to file just like this. Then we can increment this.loaded because it's done loading this.loaded this.loaded plus+. So every time we load one, we increment by one. And if this.loaded equals equals equals equals this.q, which means how many items are in the queue to load. Then we can this.eit using the event emitter here that resources are ready to go. Great. So, let's go into experience.js over here and let's create the resources. So, we want to create the resources first and then the world. So, we're going to shift alt up arrow and we're going to come over here and we're going to call this resources. Let's make sure to import that. Now, if I go to resources, let's just console.log console.log this items over here. And it should log twice because we're calling it twice. So, here you can see we have Chad Cafe and then the object itself and then caviar as the key which is the asset.name that we gave. So now world can listen to this. So what we want to do is once this dot this experiences once its resources are ready. So on ready what do we want to do? We want to create our stuff. So we're going to do this is where we start creating our room and things like that. So let's just come over here now and let's create our first room which is going to be Chad Cafe. Chad Cafe.js over here. And I'm just going to copy from worlds cuz why not? Ain't no more time that Chad cafe. Let's go, dude. Let's go. And we can get rid of this. And now because it's coming from experience the resources, we can just set the room itself. So I'll do this. Chad cafe. This Chad cafe equals this.experience.resources. And I'll call it Chad Cafe. Actually, it should be capitalized because in assets, if I control P and go to assets, this is capitalized. Let's just lowerase it cuz ain't nobody got time. We should just keep it consistent. You feel me? You feel me? And oh, I called that Cabby Barrett. Sorry. It should be Cabby Barra Cafe. What's wrong with me, man? What's wrong with me? Let's create an environment so we have like lighting and stuff. You can just if you don't want to split up, you can just directly play a world. I mean, nothing wrong with that. You can just put like lighting in here. You don't really need a a second one. And I misspelled that. So, let's just do that. And let's just copy Chad Cafe over here. I don't think we're going to need resize or update here, but whatever. We can just leave that in for now. And all we need in in netit is probably So, let's just create a basic ambient light for first. Ambient light. And I'm going to set it to a new three. Light. We're going to give it a color of # fff. I think that is by default anyway. And intensity of one, which is also default, but I'm just going to set it anyway. And let's just do this.experience. Scene add. And we'll just add this. ambient light just like this. I don't know if we need this here. We could probably just do con ambient light. I don't think we'll ever refer to it outside this class. So, whatever it doesn't it doesn't matter if you use con or whatever. And in world. So, now what we can do is import both the chat cafe and the environment cafe over here. So, let's come over here. Oh, sorry. I should probably rename this first properly. Environment. Let's go back over here. Let's do this. Chad cafe. This Chad Cafe this Chad Cafe equals new Chad Cafe. Get that import over there. Shift alt down arrow. Same thing over here. And we'll do environment. Environment. Environment. Just like that. Let's control space to import that as well. And in Chad Cafe, we also need to add this to the scene. So we'll come over here and we'll do this.experience scene add this chat cafe. And of course from the resources objects, sorry, the Chad cafe is in the items object. So let's go get the resource classes items object and the Chad cafe inside the resources items object. Going to get the scene of the chat cafe object. So if I go back, we see 3. Lights is not a constructor. So yeah, that's cuz I have a typo. So let's go back over here. This should be a capital letter over here. And now if I go back, you can see that indeed we have Chad Cafe over here. Now, I noticed when I resize, it's kind of glitching out. And that's probably because we also need to tell the renderer over here to resize. You can see when I resize, it's fine. And I just realized this. We should wait for the renderer before we create our resources and our world. So, I added a directional light helper as well as a sunlight. You will see me make adjustments to the scene throughout the video without me mentioning it because it's just playing around with lights at this point. Now, there is one issue here. I hate how every time I refresh, I have to start at 0 0. So, we should probably set a default starting position. Another issue I see is Chad's face is not the same color, even though the image used the same color. Now, the reason that's happening is cuz I never saved a PNG version of this. I'm actually still using the EXR version. And that's also why I think my file size was literally twice as big as the Cabra one. And here you can see after I reexported with PNG, it is significantly smaller. Okay, let's add a default camera starting position. So, I'm going to go into let's just go do it in camera. Why not? And every update I'm going to console.log console.log this.instance.position as well as this.cont controls.target. So, we can set a default value. And we're probably going to get rid of this pretty soon, but we might as well just get it now. And I guess this is also the time to determine your FOV. 35 is a little bit of a wide angle lens a little bit. So maybe 45 is a little bit more realistic. Yeah, really up to you. Anyway, once you get a camera angle that you like. So I think I'm going to actually start it directly in front kind of like this. And this one is obviously the target cuz it's really really far away. So we can just come over here in our set orbit controls. We'll do this controls target set. Let's paste this over in here. And it's going to be the really far away one. Great. And then this one for the position. Uh we can just do set here. instead and set all three of them. Same thing. And we'll just add two commas here. And oh, it's actually the other way because I forgot we're spawning at 00 0, which means the camera is the one that's farther away and it's looking at the 00001. So, it should be the other way. My bad. Great. So, let's add a floor and an oval like we said we're going to do. So, it's technically part of the world. So, you could probably put it as separate if you wanted to, but I'm just going to put it as part of Chad Cafe. And then Cozy Cafe is also going to have its own separate one. Now, because they're identical, again, you could probably extrapolate it and then use it as a reusable component for parameters. That way, you don't have to like tweak everything twice, but we're only doing it twice. So, I'm just going to keep it directly in here itself. So, let's get rid of this console log over here for some reason. And you can put init here if you want to. Maybe I call this init. And then we create some more functions called init cafe. And we add the cafe over here. And then we can have some like init plane. Nit plane. Uh again, if you just want to put it all in it, like that would be my guess. It doesn't have to be like this. In it back VG orb or whatever you want to call it. Sphere. Sphere is probably better. And then in init we just called this.init cafe. This.init cafe. And what am I doing? Why am I doing it every single time? I can just copy and paste it like this. just uh shift alt down arrow and then control alt down three times this dot and just like that. I don't know what it's doing. And so we just create a knit once and then init calls all the other knit ones. Too lazy to type out this myself. So I'm just going to go to the documentation to copy the plane cuz ain't no one got time for that. And then we got to copy this. Right. Right. Right. And then we just add the plane there. We don't have to do this plane. It doesn't really matter. We're not going to use the dot plane anywhere else. So, we can just leave it as a const. Like I said, same with the sphere. So, let's go for the sphere. Ain't no got time to type that out. I mean, that's why we got AI for today's AI is supposed to type this out, you know, man. And now we just add sphere here instead. Cool. We can't see our sphere because, well, actually, we can. We see it, right? But we need to flip it. And it's really small. So, we need to make it super super large. And of course, we can't see it cuz it's like Well, sorry. 1,000 is way too far. That's beyond our camera limit. So, I don't know, maybe we can do like 500. And we need to see the inner side of the base. So, we're going to come over to the material and instead of double side, we're going to back side. And the plane itself doesn't need to be double side either. It just needs to be the front side. Front side. Whoops. Sorry. Front side. Great. So, now we have that yellow background. And our plane the issue with the plane is that it's up directly up and down. So what we can do is rotate that puppy. So come over here plane. You got to rotate row. You got to rotate on actually we can just set its rotation itself. So we can do rotation. Let's rotate it 90° on the x axis, zero and zero on the other ones. So it should be flat now hopefully. Yeah. Great. And I don't know why it gives me such a terrible color. This color is so bad. this colors. So, I just temporarily changed the color, but you know, feel free to use whatever color you want to use. And then, of course, make the plane really large as well. I'll probably make this just a slightly different color so I know what I'm looking at. And oh, I don't see the plane because I'm looking that way. So, sorry, this should be minusm.pi / 2. Great. So, we have our plane. And uh, of course, that color is not great, but we'll just change it later. So, now we need to add shadows. We did it before, so I just asked my handy AI assistant over here to do it for me. We need to traverse through all our objects. Check if it's a mesh. If it is, then set those properties to true, basically. And then I asked it to leave sphere untouched, but change plane to mesh standard material in order to be able to cast and receive shadows, except I just had it receive shadow, but not cast shadow cuz the plane isn't going to be casting shadow on anything. So that's all I did. Copied that over there. Now, when you go back, we notice the plane has some issues over here. So we can come and adjust the plane and we can just set it and instead of position we'll do this and then it's going to be in the Y. So we can just try like 2.48. The only reason I know that number is cuz I tried and errored off camera first. And here you can see that's pretty good. And one issue that we have here though is none of these are casting shadows. Probably because our shadow camera is not that big. So let's add a shadow camera helper to our scene over here just so we can visualize it. And here you can see the inside one is the shadow camera and then the other one is our sunlight visualizer helper. So you can see just like in the previous video I'm in my shadow camera really really large and everything is starting to cast shadows now. And of course you will have to play with your normal bias values. Tweak them around until you get something that starts working. And don't forget you can set the target position for wherever your directional light is looking. It doesn't have to be 0000. Anyway, I think we have enough. We can just copy and paste Chad Cafe into Cabby Bear Cafe. If you want to wait to duplicate just in case we have any other thing, you can totally just wait if you want to. Not a big deal. And let's just control shift L to select all the instances of Chad Cafe. And I'm going to call it Kabi Vera. Kabi Kabi Vera cast one needs to be capitalized. Let's go in world. And I'm just going to shift all down arrow. Cababie Vera. Whoops. Vera. Uh, I guess I kind of like camel case here better, but whatever. I'll just leave it lower case. And let's just make sure to import Cabi Vera Cafe, too. Now, if we go back, we see everything overlapping. So, let's come into world. And let's just hide the Chad Cafe for now and just take a look at Cababi Cafe. Great. Now, this one seems a little bit too bright. So, we could have different lightings for each one if we wanted to. And there's some Z fighting over here. Or I think that's just the outline itself. But either way, uh, we probably need to make lighting adjustments per scene. And we can change that in a little bit. Never mind. Screw it. Let's just do it right now. Uh, so what I'm going to do is go into environment and I'm just going to copy the init function. And I'm going to controll X to quickly delete that. If you want to create separate files for chat cafe environment and cavi bear cafe environment, you can totally do that as well, but uh, it doesn't really matter. And I'm going to call this init environment. I know I misspelled that. I'm going to fix that typo, but I'm not going to waste your time doing that uh over here. And I'm just going to controll X and like that. And then I'm going to have one and just copy this over for Cappy Bear Cafe as well. And let's just make sure to shift alt down arrow and change this to in environment for both of them. Technically, the plane and the BG orb is also like part of the environment. So, you can put it in here if you want to, but I'm just going to keep it separate. Now, I just want to talk about a few tips when it comes to environments in 3JS with real-time lights. So, it is in the documentation, so I'm not going to talk about that too much, but one really nice helpful thing is using little GUI. And this is the little interface over here where you can change like light position, intensity, color. That way, you don't have to manually change stuff and it's just a little bit nicer. And then you can change like the target and things like that. So we can just do mpmi little gui-dsave-dev to put it in our uh dev dependencies. It doesn't really matter if you put in dependencies or dev dependencies. It's more just semantics here. Dependencies are the ones that you quote unquote use on deployment and in production where dev dependencies are not. But these are also still used in production too. So it's more just semantic clarity there. There are cases where that matters depending on what text stack you use, but for us it doesn't really matter where it is. And at this point, I feel like if you want to set it up, I would just use AI to help you set it up. Uh, yeah. So, you can see my prompt here. That's literally I just copy it and paste it. Set up a little GI controls for my sun and ambient light. So, you'll get a bunch of folders. It's just a bunch of parameters that you pass in. Make sure you have the onchange here. Sometimes AI will not give you that for this time. Claude did a good job. So onchange is just like every time you change it you need to update some sort of thing. Now if I go back here you can see I can change the target of the light and I can change the position of it the intensity and the color and that's all super cool and we can see that all in real time there. You can also have enable or disable for it as well which I did not add into here but you can toggle like on and off ambient light, toggle on and off sunlight and so on so forth. Another tip I have is to use empties to place your lights and just copy those positions. So if you have a bunch of empties here, like I added a bunch of empty cues on position on these lights, you can do Ctrl F2 to bulk rename the objects that you've selected. And we can just search for empty and replace it with light. And I'm just going to hit okay. And I'll rename those all to light. And we can actually export this as part of the GB file of the other cafe. And we can just look for all the empties in the GB file that way. But for now, I'm just going to export it as a separate GB just to show you what you can do with it separately. But again, I would keep this inside the same GB file. I would not use a separate GB file unless you want to use the same empties for both of them. So, let me just show you. I uploaded our empty object. I added to our assets and then I can just use it directly in chat cafe. All I have to do is refer to that name, right? Because in our resources object, it created that ID for us and it works out of the box. So what I did then was I asked AI to generate me spotlights that are at each empty and pointing downwards and also adding a helper. So you can see traverse light empties and each empty in the scene create a spotlight pointing down with a helper attached to it. And all I had to do was copy this and replace my init environment and it worked directly out of the box. I could probably also ask it to add each spotlight to the little GI but I did not. And of course, you can just again, you can just export it with Chad Cafe and just traverse it with Chad Cafe. That way, you don't traverse two different objects and that way you don't have two separate files and all that kind of stuff. I'm going to undo all those changes. So, don't even worry about it if you don't see them. Um, because it was just a neat little trick to do. So, if you're one of those people that like to hand paint everything like highlights and shadows, you might want to work with emission textures, uh, sorry, emission nodes. And the way to do that quickly is to use AI to generate you a script that will replace every principal BSDF with an emission node instead. So here you can see my prompt over here. Write me a script that changes principal BSDF and then make emission strength one cuz the first time they set all emission to zero for whatever reason. But yeah, you don't even need a good prompt. It will do do this simple task really well. Another tip and trick, if you decide you no longer want the outlines, you can actually ask AI to generate you a Python script that will delete all your outlines. And I only oneshotted it, and it wasn't really even a great prompt anyway, so I just copied it and paste it in here. Now, make sure uh according to this script, everything has to be visible. So, if you accidentally have a hidden one, it's going to throw an error that's saying the name does not exist. But take a look. Everything has an outline. We just run this script and voila. All the outlines are gone. Now, if you want to, you can tailor up the script to put only certain objects in certain collections and remove anything that is in that specific collection, like remove outlines, have Blender, have the AI generate a script to only remove outlines inside that collection. You could totally do that as well. And I think it only gets rid of the outline one, not outline. 01. Again, you can just reprompt the script to get rid of all anything that starts with the text outline. So, that's like a really quick way to do that as well. I guess I should have mentioned it earlier, but to add icons, like contact icons, one of the easiest ways to do that is just finding an SVG icon to download online. Make sure you check the license so you can use it. And then just clicking and drag it in over here and import as SVG or its origin point might be messed up. Yeah, it's origin point's definitely messed up. Either way, let's just do origin geometry. And when it has the Z fighting, just move like some of them up and down. And they're all curves. So, what we can do is convert them to a mesh. But first, if you want to, you could probably extrude it as a curve, but you can also extrude it as a mesh, too. Now, one bad thing about using SVGs as meshes is the topology is often terrible. There's ways to clean it up. Like, you can merge by distance and change it really small till you get rid of overlapping vertices potentially. You can also like select the edge rings, I guess, shift duplicate, GZ, and then use these as the one instead. Well, I guess you don't have to duplicate both of them. Just duplicating one and then extruding outwards S or insetting and deleting would be fine here, too. Uh, so if you want to clean up, you can, but like using one thing that's bad in topology is not really a big deal. And then, of course, you can give it a solid a solid a solidify modifier if you want to. Sorry, I started studying. And it's the same thing as extruding as when it was a curve. If you ever apply your modifier and want to change the width of it, you can just like select the face, hit L, and it'll select the entire outline and then you can just do Alt S and scale it that way. Okay, so I just wanted to mention that I did add a few extra details here. I'll come back and revisit this a little bit later, but I added some contact signs over here that I renamed Twitter contact and this, which I can actually join this because I'm not going to target it. Um, same with Chad Cafe. CH cafe. It's the same thing. And I added a few other objects over here like a car and I named it car wheel, car front, and so on so forth. But don't worry, we'll come back here and I'll make sure every object that I created off camera, I'll make sure to mention it as we continue with the tutorial and you can apply it to your own scene. So, I just reuploaded them. And in our init function, I got rid of the plane because I kept the plane in the model this time because I made a few adjustments to the plane. So you can see if I go back uh I have all my updated models here. I didn't touch anything else. And now I think what we can do is start with the controls. So make sure to set your lighting and then I will see you on the other side. So let's come over here and just do controls.js. All right. So let's just copy from camera.js into controls.js. And I don't need this. Uh we well probably we might need this stuff. I'm just doing Ctrl X to quickly get rid of everything. I don't want this in it. Cool. And let's just call this controls. And let's come over here. Shift alt down arrow and add our controls over here. Make sure to save both files and just find the import of dot /controls. So we're importing it over here. I am getting a init function error and that's because I think in resources uh sorry in environment, yeah, I got rid of init here. So, I don't know if we'll ever need this file, but I'm just going to keep this temporarily. Ignore this error, by the way. This is probably related to one of my browser extensions. Now, you might be asking, why don't we just put controls in camera.js? Why do we have a separate file for it? Can you just put everything in camerajs? And the answer is absolutely you can put everything in camera.js. But the reason I'm splitting it out is because in the real world, you kind of have like a camera, right? And then you also have controls for your camera. Whether that be like your arms to hold the camera, whether that be like a camera rig arm for like a movie or a tripod or something, you typically like have your camera and then you also have a set of controls for your camera. And I kind of represent that through code by splitting it up into two different files. But of course, you could keep all the control logic in camera if you wanted to. And I feel like that is also just personal preference. But I feel like for this sort of interaction, it's more common to bring out controls into a separate file. By the way, I should mention now's a good time to determine if you want to use a perspective or an orthographic camera. You can follow along with either one of them. I'm going to be using a perspective camera. Great. So, let's go back to controls and let's add a event listener over here. And I don't know if I want to do the window or the canvas itself, but I think we actually want the canvas itself. So, let's do this. ments and let's grab that canvas element which is right over here that we created in experience.js. So let's come over here and we want to add and we want to listen to wheel and we'll add a little bit on for the touches. So let's create that function right now that we want to do for the wheel. Let's console.log the event object that comes with this event listener just to see what we can use to move the camera position. Now this is review so I do encourage you to pause the video and give it a try on your own but we will do it together. So as I scroll here, you can see we have this value over here called delta y. That pretty much gives us a value that we can play with. So now we know how to get the camera. So we can do this.experience camera. And we want the instance of the camera. So we'll do that. And we can just do plus equals e. deltayy deltay. And let's multiply it by a factor like 01. So we only move one unit per tick rather than 10 each time. Depending on your computer, this might be a little bit different. And whoops, I forgot to add to the position, not just the camera itself. And let's choose position.x. And now when I scroll, you can see that it is going. It's a little bit jaggedy because it's having the orbit control zoom as well as the movement. So I'm going front and backwards on the X, which means I must have rotated my model when I was modeling. So it might be X for you. I think it's going to be Z for me. When I scroll, you can see. Yeah. Now I'm going left and right on the Z-axis. When I scroll down, it's going to the left, which I do not like. So I'm going to subtract this instead. You can also just multiply by a negative number. Your result will be the same. And now when I scroll down, you can see that we're going right and then we're going left just like that. Now, one issue we do have here is the orbit controls. It is currently conflicting with our custom controls cuz it also has like wheel events and stuff like that. So we can disable it and then we can reenable it in a little bit. But before we do that, we have to figure out a solution for E.L.A.Y. because it's different on every single computer. Now, one way to do that is by clamping or we can use a online package that does this for us called normalize wheel. So, we can just copy this and install that. Again, controlback tick to open this. Create a new terminal and just install that. And now what we can do is import normalized wheel from normalized wheel and then normalize that event object. So, let's come over here. Let's do const normalize wheel equals normalized wheel. Grab that event object there. And now we can just use this instead of the event object delta y. Now if you console.log normalized wheel console.log, you can see it gives us pixel y instead. So let's use pixel y. And let's increase this by a factor of zero. Let's do pixel y. And everything should be the same over here. All right. So let's go back into camera.js and let's get rid of our orbit control. So I'm going to delete that. And I'm also going to sorry not delete it but get rid of that. Now one issue here though is if we ever initiate set orbit controls we kind of like also have to do this. We can put it in little GUI or our own custom interface to toggle it on and off like with an HTML element button. Um the other issue is also once we get rid of set controls we also have to like get rid of this here too. So we can add a guard clause just to save a little bit of time instead of just putting in little GI if this controls. That way we only can just get rid of the function itself and don't have to worry about getting rid of this if we need to cuz this controls doesn't ever exist once we don't create it. Now you can see when I go back uh the camera is that way. So if you modeled your scene that way you probably don't have an issue here. But I need to rotate my camera. So I'm going to do that. So let's go back to my camera over here. And I'm just going to play with the Y rotation. So I'll do this.instance instance.rotation doy whoops equals math.py / 2. Great. And now I'm back looking just like this. Sweet. Okay. Now before we move on, we want to use linear interpolation to smoothly move the camera. Right now it feels a little bit janky that we're just adding to the camera. So this is review from the previous video, but we need a target and we're going to let the request animation frame update do the rest of the work. For those of you who are not familiar with linear interpolation or need a refresher, it's basically the idea that you have your distance and time. So we can say t for time. Instead of going a constant amount every single time we tick to go to our final target position, whatever that target might be, what we do is we create a target and we run a mathematical formula that makes it go infinitely closer over time, closer and closer, and gets like a smooth sort of thing like that. Now, there's a lot of different formulas for looping, but that's the general idea. You have a target position, you have a starting position, and you update the target position and everything in between is just managed by automatically by the mathematical formula rather than constantly ticking it up until whatever value you get to. By the way, you can also use the direction one. I forgot what they called it, but you don't have to use pixel y. Anyway, let's create the target. So, let's come over here and let's just create this.target. And I think now because we're going to refer to the camera so much, we might as well just create a this variable for this class. This camera equals this. That way we can just refer to this camera. This camera. It's just a little bit cleaner. That is totally optional though. We can set the the starting target position equal to the starting position over here. So we'll just come over here and we'll do this camera camera. And that'll be the starting 3GS vector. So this is going to be a 3GS 3 vector. We can uh add some other parameters here like our speed. So we can call this like speed. And we can just right now the speed is at 0001. So we can get we can probably put it over here. I don't know if this needs to be a global variable or we can just leave it in the function. Doesn't really matter. This speed and let's keep time speed here. That's right. Should be this speed. Then we also need our linear interpolation factor. So basically how smooth it is. So we can just do that. And then 01 is a very common one. So we're going to start with that and see if we have to change that later. Now if you didn't catch it, I made an error here. This is currently just a reference to this camera. We need to make a clone of it. So luckily we can just create a clone and create a entire new copy of that initial camera position. Okay. And instead of targeting the camera position itself, we are updating the target position and letting everything do the work it's for. And we're letting the update function do the work for us. So let's go into experience.js here. And let's make sure we update our controls. So I'm going to shift all down over here. And I'll do this. Which remember we created right over here. So now this is going to be infinitely looping and we should just be able to this camera.position position and we can use the built-in lurp function of 3GS and we want to lurp to this dot target position and with our thislurp factor which is going to be the 01 over here. So lurp will take this truck position and slop it in right the resulting vector right directly in here. So you can think of this basically as like an equal pretty much. Um but yeah whatever it just looks like this. So now as I can scroll you can see it's very very smooth. So maybe we can do is probably make it I think 10 times faster. So let's get rid of zero. Yeah. And now we have a very smooth camera. So just play around with it until you get something that you like. So this feels a little bit too smooth. So maybe like that. Yeah. Feels a lot nicer. So yeah, just play around with it until you get something that you like. So the next thing I want to do is make it work with touch events. Now, if you don't want to use the browser one, it's a good time to suggest or sorry, mention that in package.json, if you do vit-host and let's cancel our current development server over here. So, it's going to be this one. And make sure to save that. And you rerun mpm rundev. You're going to get this URL over here. And if your phone is connected to the same internet as your computer, you should just be able to visit this URL on your device and see your application on there. So if you want to test it that way, you can totally do that. I will be using the browser testing tool. So let's go back to controls and we need to listen to touch events. Why are we using touch events instead of pointer events? Isn't it better practice to use pointer events over touch events? And the answer to that is yes. But in specific cases, there are times where pointer events don't really work well on Safari iOS, and we would have to spend some time debugging it. So, it's just easier just to use touch events. But if you want to go down that route and use pointer events, the logic is basically the same. And of course, you can always use pointer events and touch events and like detect the device that you're on to switch between them. But we're for simplicity sake, we're just going to use touch events. And the logic is again basically the same. So, let's just come over here. And at this point, we're going to use canvas a lot as well. So, it's probably just better just to extrapolate that onto a class variable as well. So, I'll do this. Equals this.experience.canvas element. And then this will just be this.vas. This canvas. And let's just copy this. And what we want are touch start. So, the moment we tap down on our screen is going to be to touch start. And we're going to register that position. And then when we move our finger, that's going to be the touch move to update our finger position along as we move across our screen. And then we have a touch end. And then we have a touch end event listener. Sorry, I should probably be uh typing this out as I talk about it. And then we have a touch end one. And that means when we lift off our pointer finger, what do we want to do? And I know I misspelled this. So I'm going to control D to select all of them and just get rid of that. Uh, sorry. Ctrl D is the next instance. So, what do we need? Well, first let's think see what we actually get. I'm just going to hold alt and create two cursors and just drag intr x to get rid of that. Let's just console.log what we have in the vent object for now. And let's also do that in for touch move. So, when I tap down and I drag, let's open this touch event and see what we get. We are interested in this object over here called touches. And then each of these touches that we have here has a lot of information. Reason touches is an object here is because you can have like two fingers, right? Two fingers on the screen at the same time. Then you would have two touch objects in here. But we're only doing one at a time. So that's all we have to account for. Now in this information, we have a lot of things that we can use. So if you want to tap down and scroll up, you can just subtract two coordinates. Similarly, if you want to go from the right and swipe left, you would just subtract two coordinates. So, if I tap over here, you can imagine the client X is a range, right? It's just going to be on the right. So, let me just click over here, get some new touch events, come over here, go into touches, and you can see in over here, the client X is 1050 or very close to this. So, it goes remember the coordinate system is 0 comma 0 over here. So if I start at 179 and I drag over here, that means I end at like about 500, which means it's going to be a positive value. Whereas if I start over here from zero and subtract 500, it'll be a negative value, which means I swipe right, which means I want move my camera to the left. So if you want to do it up and down, the logic is the same. You'll just subtract different coordinates. That is the whole idea of what we're about to do. Let's create our first touchdown variable as our initial starting point. So what we can do is this dot initial touch initial touch equals e touches that touches array and we want to grab the first one and then we want the client x value from there and then as we move we want to create a delta value of how far we've moved to update the target position. So we can come over here and just do const delta x equals this.initial touch minus the new touches object that we get every every time we touch move. We're getting that new touches object with all that new information. And we should probably call this x just to be a little bit more uh specific over here. And then we just simply want to do the same thing over here which is this.target position.z right over here. And we don't need to normalize this. We can just use delta x this time times the speed. And now we need to update the initial touch so we aren't dragging extremely large delta instances. So after we use it, we can just set it equal to the one that we already used right over here. Just like this. That way the next time we touch move, we're going to use the new touch move and the previous one over. Now we do need some guard clauses. You'll notice that sometimes when I drag and cancel it, it kind of jerks a little bit. Now, why is that? Well, it's because the touch move event is actually firing the same exact time as the touch start event. And the other reason we need the guard clause is if we start on an HTML element and drag to the right, going to have an undefined object because my touch start is the one over here that setting the initial touch that we're going to continuously update and subtract. So if I start touch start on an HTML element that's not the canvas, well it's not going to have that to register on touch move, which is fine after the first time we touch move because it will set it over here. But you know, we just want to do the initial one as well. So all we have to do then is just check if thisinitial x equals equals null, then we just have that guard clause and return a little bit early. You'll also notice that I added a touch multiplier over here and I set initial touch X equal to null up here. This is optional, but this is generally best practice when you're creating a class variable just to put it a some sort of default starting value. And then if you want to use different lur factors and different speed factors, by all means, go for it. I just placed a touch multiplier and multiplied it by the speed. But if you want to do this drag speed or whatever, definitely go for it. It's really just personal preference. You'll also notice I got rid of touch and event listener. I don't think we need that, so I got rid of it. Now, what I want to do is when I move my mouse, I want to give the camera a little bit of offset and some sort of parallax. That way, it doesn't feel like I'm just looking at a static image and there's some interactivity with my camera. Now, how do we do that? Well, the easiest way again just like we did with the ray caster in the previous video is to normalize the coordinates to go from the center as 0 comma 0 and left is negative 1, right is one, up is one and bottom is negative 1 rather than starting the coordinate system over here. So you can remember from this video we did that with a recaster example. We normalized the coordinates here. Now, we're probably going to be using this normalization in a multitude of different components. So, it would make sense instead of just doing it one time into the mouse move onto the canvas, we just take it to its own separate class or a math utilities function. So, there are two options here. We can create something over here like math.js or mouse.js and export a utility function that we can import and reuse. Now, the reason you might not want to do that is because the math will calculate every time you run that function across all the files that use that utility function. So, what we could do is just use one class for the mouse. And that's exactly what I'm going to do. It's technically a utility. So, I'm going to put it in here, but it would also make sense because it's to so tightly coupled with like controls and eventually our raycast cer. It might just make sense to put it in the experience instead of utilities. So, it's really up to you, but I think I'm going to put it in experience because it's very related to controls. So, I'll just do mouse.js. Then, let's come over here and copy this into mouse.js. And let's just get rid of update uh sorry, the stuff inside and just determine what we need for now. So, let's come over here and let's create a cursor object. And whoops, for some reason, I accidentally imported this somehow. I probably mistyped something. And what we need is to create a cursor of XY coordinates. And we'll just use a three dot vector 2. Vector 2 and just set it by default at 0 comma 0. Now if you want to use an object like X0, Y0, this is also perfectly valid as well. You don't have to use a three vector 3. You can make your own object. Great. So now this time, do we want to put it on the canvas element? So only when we are moving our mouse over the canvas element or do we want it also when we're on our HTML DOM elements or whatnot? Honestly here I don't think it really matters and we can always change it later. I'm just going to attach it to the entire window. Add event listener and we'll do mouse move. Now again this is where it comes in. Should we use pointer move or should we use mouse move? Well the web standard is generally pointer move rather than mouse move. But I only want it to work on mouses and not mobile devices. So should I use pointer move? Well, actually you can use pointer move anyway and only return if the event type is through the mouse. Otherwise just guard for that and don't execute it. That way it will get rid of mobile devices. So we can do point to move over here and we can add our normalization here. I already explained this in the previous video, but if you need help, uh, you know, feel free to drop a comment. But just spend a little bit of time tracing this and I'm sure you will be able to and you'll be able to figure out how it normalizes the center to be 0 comma 0 and going to the right goes to negative 1 and right. Hey, I'm from the future. I forgot the minus sign over here. Now again, you can just log the e over here if you want to verify it, but we only want to check it if the e.po pointer the event.pointer pointer pointer type equals equals equals mouse otherwise we are just going to return and not execute anything hey from the future I know I said it rightly but I forgot to this is does not equal equal mouse and return if you do equal equal mouse then we don't want to return we just uh want to do everything in between right uh so sorry I accidentally misspoke but I do fix this later to do not equal equal equal great so now we just create this again in experience.js. So let's shift alt up arrow here this time and create a mouse over here because we're referring to mouse and control so it makes sense we do it first. Although for these two specifically order is probably not going to matter but for some of the other ones over here order is going to matter but not for this one. And we might as well get rid of console.logz this. We already know what that is doing. And now we can go in controls and refer to this mouse object over here. So, let's just come over here and let's do this mouse. This mouse equals this.experience.mouse. And I called it cursor, but I don't know if that's good. Maybe we can call it instance just so it's a little bit clearer and consistent with our other stuff. So, mouse.instance, but it's up to you and what makes sense to you. You could also do mouse.pointer and change this to pointer. That would also make some intuitive sense. Or you could change this entire thing. instead of mouse call it a pointer but whatever. Don't forget to rename this to mouse and import it over here as well. Great. So let's go back to controls.js and let's add some logic here. So the basic logic is you have some sort of offset and then you create a new target after adding your offsets and then you lurp to that target. So you get your position that changes every frame. You add the offset and then you lurp to both that position with the new offset. Now the issue with this is it does work but the issue here well I got the axis wrong but it does work. The issue is that so basically going to the offset is really really slow because the lur factor is really really small here. So you could increase this to make it a little bit snappier but then the position changing is also snappier and faster and you might not want that. So you could use two different values for the lur function. But the other issue is also the camera rotation. So later, let's say I move my cursor over to the left. I also want my camera to move that way and then also a little bit to the left. Kind of like this. Now, because my camera is rotated like let's say it's over here. And I don't I don't actually remember what angle I rotated at. But now that it's rotated like this, if I decide to let's say rotate it along the X-axis or Z-axis or whatever axis you want to go for, it'll use the global axis rather than the local camera axis. This will lead to a little bit of issues. Like once I want to, let's say I want to rotate it along the camera's X-axis, it'll actually do it along the global X-axis like this. I want XX. Like in Blender, you get the local axis and rotate it like this. So, what's the easiest way to solve that issue? Well, it's actually to put the camera in the group and animate the group instead of the camera itself. So, that's pretty much just like using an empty. So if I add an empty cube over here, you can imagine this is like the group and we just parent the group or wrap the group. Sorry, wrap the camera with the group and this is the one that I move around and then the camera inside is the one that I rotate. That way I can just basically access its local axes inside. So this is the one I move and then this is the one that I give the offsets to and everything like that. Now, you don't have to do this because there are built-in things in 3GS like getting the world direction. So, even if your camera is rotated, as long as you get the world direction and a perpendicular vector, you can use like the cross productduct of an up and down vector to get the side vector to determine which direction your offset actually is. So, that way you're also quote unquote technically working in the local axis. So if you ever want to do gxx, well, as long as you have the world direction and the up vector and you take the cross productduct, you're going to get a perpendicular one left and right. So that is also another solution instead of using a group. So I'm sure you've seen the cross productduct between two vectors gives a perpendicular vector to both of them before, but we're not going to be doing this method. Not that it's hard, it's just like the easiest thing is just to create a camel group, which is basically a camera rig. Okay, so let's go back here. And what I'm going to do is create a camera rig here or camera group. So right over here, let's do this dot camera rig. You can also call it camera group, whatever. It doesn't really matter. And we'll create a new three dot group over here. And we're just going to add the camera to the group and then add it to the scene. So shift down all arrow over here. And we just add this camera. The camera instance. Remember this camera is pointing to the instance of the perspective camera itself. So if you log it, it should show a perspective camera. And then we just do this.perience. experience scene add this camera rig which includes the camera inside. Now, if it makes more sense for you to add the camera rig inside of camera itself and then just refer to this.experience.camer rig.instance, you could totally do that as well, but it doesn't really matter cuz this is kind of like a one-off thing. So, we can just do it here. But if it makes more sense for you to do it in camera, you can totally do that as well. So, now let's update our code to move the group instead. So, let's come over here and instead of camera, we do camera rig. And that's the one that we want to load to. And let's set a target offset. So, let's come over here and what do we need? Let's see. Target offset. It doesn't matter. We can figure it out as we need it. I think we need this. Offset to lurp to for the camera itself. So, this should also be a new three vector 3. And we need Whoops. And this should be this.target position. Now, we don't have lur target anymore. Now, if I go back over here, you can see that my camera is going farther back because my settings that I did for my camera is now doubling pretty much because I'm moving my camera rig over to the target position, which is this over here. And then my camera is also moving to this relative to the group. So, it's pretty much doubling. Now, there are a lot of solutions to this issue. I think what we can do honestly is instead of cloning the camera position, we set the camera rig in here and just do it there. Uh we could also do it in controls, but I feel like now it would be cleaner to do the camera rig. And then instead of instance over here, uh we can get rid of this. We'll just set it at 0000. I think that's the default. So I don't know if we need that, but whatever. Um and let me just Yeah, let me just paste this. And let me just copy this. And let's just copy this inside over here. And this one will be camera rig over here. And let's just set it up here. Not that I don't think that really matters. Um, but yeah, let's set the positions first. And then add the camera with a 00 over here. Just like this. And then target position is not going to be the camera instance that is copied. It's going to be the camera rig. So let's shift all down arrow here. And let's do camera rig. And this one will also be camera rig. Uh I guess I could have just control D to select the next instance. And now we're copying the camera rig position which is the original camera position. The camera rig does not have that instance variable. That's not what we called it. Sorry. This should be camera rig. And this is no longer this camera. This is this instance over here. And now if I go back and refresh the page, you can see it's working. But now we're moving our group. And our camera is parented inside of our group. And we can just add some offsets to our camera. Great. So, let's start doing our to our offset. So, we just do this.target offset and we're going to set it equal to this dot mouse.x times our offsets strengths. Now, I didn't type that in the video, but it's pretty much just right over here if you didn't notice that. Uh, but you know, be my guest and do whatever you want. And I think this should actually go in the Z-axis, right? We're touching the Z here. So, the X is going to be Z position. And then we want the so the X position isn't going to be moving. It depends on your scene and what your axes are, but this is going to be mine and this should be Y over here. Now, we just need the camera itself. So, this camera instance and we're going to lurp to the this.target offset uh vector that we just created. Oops. This target offset just like this. Uh and sorry, this is no longer instance. It's just position because we already did this camera equals this camera position. Then we need to add a liver factor. So I want it a little bit snappier. So I'm going to do 0.2. If we go back, you can see that it's working and it's a lot faster this time. Not super smooth like the navigation menu itself. And then I also have this inverted. So let's do negative over here. Now when I go right, it goes right and up and up. Cool. If you ever want to increase the distance, feel free to, you know, change the strength. Great. Now before going on for the rotation, we should do the same exact thing for the rotation over here. Instead of rotating the Y over here, we are just going to change this to the camera rig over here. And we can just Ctrl X and bring that down over here. Just like this. Great. And it should be the same. But now the X is wrong because the group is rotating no longer the camera. So let's actually go back here. And now we can just bring this up back over there. And I'm just holding alt and up and down arrow to move the lines really quickly. I think we can get rid of this e this negative as well. Great. And everything is working again. Okay, now we just need to do the same thing except with a rotation. So let's just come over here and instead of target offset, we do target rotation just like this. We'll create a uler here. Set the default rotation to 0 comma 0. And in this case, we can just we should just be able to offset the local X and Y rotation. We can do the same thing over here. And instead of target offset, we do target rotation. And then shift alt up arrow over here. And instead of a position we do rotation. But the issue here, oh sorry, let's put target rotation here. Now the issue here though is you can't rotate. You can't lurp the rotation like this. If you want to do it in one line, you have to use slurp and quanternians which is a little bit more advanced. Not that it's harder to implement. It's just a lot more to explain. So what we can do instead actually is just do each individual component by itself using three math utils. So we can just do this.rotation. rotation and let's just do the x1 first dox and we'll set it equal to 3.mmath utils.lurp and we'll take the this camera current rotation x and we're going to lurp it to the mouse offset x over here over here and we'll have our lurp factor. If you want to extrapolate this or abstract this into a variable you can totally do that but I don't need to do that. And we can do the same thing for the Y. And let's just Ctrl D to control D and do the Y over here. And we can get rid of this. So now we're just doing this individually here. And sorry, I'm so used to React that I just have current here. Sorry, let's get rid of current. I was thinking about using use ref in my head for some reason. Now, if I go back, it is inverted. If I go right and left, it's going the other way. And up and down is left and right, which is fine. All we have to do is just swap it. So this should be Y and this should be X. And then for X uh and then for Y rotation we need to make it negative. Now when I write it's going to the right and when I look left it's going to the left and up and down. And it's a little bit too strong. So I'm going to multiply both of these by another factor of like 50%. Now it's a little bit more subtle. Sweet. So, I hope it makes sense even though we're working in the local axis. Why we need to do X? Cuz let's say I'm moving my U mouse to the right of there, which is the X coordinate. Well, I'm rotating on the Y-axis, right? Well, in Blender it's the Z-axis, but in 3GS it's right. So, if I move my mouse to the right, I'm rotating along the Y-axis in 3GS. And if I go up and down along my phrase, I'm doing on the local X-axis, which is why I'm using the Y-coordinate to drive the X rotation. Let's add a little bit of clamping. That way, they can't scroll off into infinity. And that way, by having set values, we can determine what progress value for between 0 and one uh that we are along our path. And then we can trigger stuff relative to those progress values. So, let's come back here and we'll just console log our camera rig position. console.log camera and we just want the X value. Oh, sorry. It should be the ZV valueue. That's what we want to log and we want to clamp it. Well, actually, we want to clamp the target position, I add. So, we can just uh Well, actually, it doesn't really matter whether we console this or the target position, but we will clamp the target position. Let's just find a good ending point however you want to. 85 is probably a good one to keep note of. So, I'm just going to copy that and I'm going to go to the end and where I kind of want it to like chill out. So, probably around here. Cool. And let's just go down like that's 17. So, I'm going to copy that. So, now we have two values to create. So, we can probably just do this.bounds equals you could do a new three vector too if you want to. I'm just going to make a um well in this case it would be min and uh so let's just if you're on Windows you can do windows v to look at the ones that you copied. So max in this case uh technically goes left but it's going positive which is kind of awkward but whatever. And then min we can do the minus value. If you want to flip those you can totally do that. If it's really bothering you you can just like rotate your entire scene 180°. So, we have our max and min bounds. And now we just clamp the target position in between these bounds. Let's go over here and let's just shift alt down arrow over here. We're going to clamp the result 3utils.clamp. And you can see it takes the value and the min and max value. So, we can just place this value is the one that I want itself. And then I'll just do this.bounds min and this.bounds. And let's do the same thing over here. Now, since we're doing the same thing over here, we could take it into like a helper function, but honestly, it doesn't really matter. Now, you can see when I scroll to the right, it's going to stop because my target position is no longer updating after I hit the threshold. So, it's just going to smoothly stop on the side. Let's get rid of this console log. We don't need it anymore. Great. So, let's create our progress value. And we can instantiate it up here. Um, so we can just put it here. This progress. And we're going to start it with uh this camera rig's starting Z position, which if I remember correctly is 1.2. So this is probably around like 60% of the way uh between 12 and 17. So this progress equals this camera rig.position.z minus the this.bounds.min. Then we divide it by the range. So how do we get the range or the total distance between these two? Well, we just subtract them, right? So we'll do this.range range equals dobbounds domax this do.bounds bounds.m min. So minus minus is just plus basically that will give us the total distance between these two values and who what happened here? My bad. Divided by this range and then we can just copy and paste this in the update function so it updates on every single frame. And it doesn't really matter where we place it. I'm just going to put it at the top there. So let's just console.log this.progress and see where we start. And it's not working because of course order of operations. It's going to divide first. So don't forget these parenthesis. Very important parenthesis over here. So if I go back. Okay, great. Now it's showing me the percentage. So if I go here, it should be one. Yeah, feels kind of awkward. Left is one. Whatever. Doesn't really matter. And then over here should be zero. Nice. So let's go to the console log. We want to animate it. We want to give it a good naming convention. That way it's just easier to navigate to. So here you can see that all my trails are numbered in order. That'll just make coding a little bit easier. It's optional. You can also organize the names in code if you want to. And since we're here, we can also give like IDs to the ones that we want to target. Uh you can always create headboxes if you want to. So I don't remember if we did that in the previous video, but instead of using the actual object itself, you can use a headbox and then just map it to the object name that you want to, right? Just create a map. Uh and you can use the box for the raycaster intersection, which I want to do later. And since we're already here for naming, anything that you want to have a raycaster target on, we might as well attach a raycaster as well. Um, and anything that you think would be relevant. Like if you want a certain type of animation, like up animation, you can also add that like as part of the name. That way you can just search for it through all your objects and everything that has an up animation you give the same animation to. So it's really up to you. But everything that I want to have a raycaster, I think I'm also just going to add right now. So I'll do raycaster. And I'm just going to copy and paste this on everything that I want to be able to click on and interact with. Great. And now all we have to do is reexport everything. Great. Now we can just go in Chad Cafe and find all the trails. Let's create an object here called this.trails. And we'll just have an empty array. and everything that includes trails, we're going to add it to this array to give GSAP to animate. And let's just make sure I think GSAP is already here. Yeah, it is. So, let's just come over in this traverse function. If it's child mesh, so if child is a mesh, if child.name.includes, and we just check for trail. If it does, then we just push it to the array over here. So, let's just copy this.trails over here. Thistrails.push push the child just like this. And we can get rid of this because it's one line. We don't need to use brackets. But if you want to use brackets, we can add brackets there cuz why not? It's just a little bit cleaner. We can create some animations. So let's come over here. Shift alt down arrow. And let's do init animations. Init animations. And we need the progress value that we created in controls.js, which is this right over here. So we can do this.progress. This progress equals this.experience.progress. progress.progress. Oh no, sorry. It's going to be controls.progress controls.progress. My bad. So this is the class and then this is the progress value that we're getting. We're just shoving it into this.progress. And sorry this should be over here. It's just a little bit cleaner that way. Just like set. And then let's also actually uh for every single trail we want to scale the set the scale to start at 0 comma 0 comma 0. So we can also do that right over here. Child.scale set and we set everything 000. And then we're going to use gap to animate that a little bit. Great. So let's just copy init cafe. And let's just change that to init animations. Init animations. And we don't need this. What we can do now is create a gap timeline. So let's import GAP from GAP. Import GAP. Just like that. And let's create a timeline. just on timeline t1 equals gab. timeline. And we want to pause the timeline cuz we're going to manually set the timeline progress ourselves with our own progress value that we calculate over here rather than using the gap timeline one. And then for each of the trails, so this trails do for each of the trails, let's grab its index to give it a little bit of an offset on each single progress. We're going to play an animation for it using Gap tween. So, I'm missing a set of harnesses here. My bad. And sorry, I don't know why I wrote this. There. It should just be trails. Uh, sorry, trail for each trail inside this.trails. I was just typing on autopilot. And then we'll do this. T1.2. And we'll do trail.scale, which again is just an object of vector 3. So, that's our tween target over here. And we can just set uh its scale to to one comma one comma one. So let's do that. And we will do the x to one, the y to one, and the z to one. And the duration can be like 0.2 seconds, I guess. So I added this console log just to show you that there is an issue here. If I go back, the name it's using is actually the mesh data name, not the object name that we gave it into the Blender outliner. Now, why is that happening? Honestly, I don't know. It might be a glTF exporting settings cuz sometimes it will rename to match this and other times it will not. So I think what we need to do is run a Python script over here. Just create a new one. And I'm just going to copy this and paste this AI generated script that will just make the object name the object name that we give it in the outliner. Cool. Now everything has the object name. Sweet. Now we again, yeah, we have to reexport, but whatever. All right. So now we should have all the trails properly. Let's make sure we call the update function in over into world.js because we need to update the cavary scene over here. So let's come over to world.js and uh have some guard clauses in update. So let's copy these over here with alt paste it. Shift get rid of this. Let's do update. But we don't want to uh update them cuz they might not exist and they won't exist by the time this is ready by the time experience calls it. So we need a guard clause over here. If this chat cafe or this cafe we carefy doesn't exist, return it. And of course we need to go to experience.js and shift up arrow and call world.update over here as well. Now one issue though is we haven't created caviar cafe. So we actually can't do this right now because it will always be false and it'll always return. So let's just get rid of that temporarily. Well, actually, let's shift all down arrow first and comment that out because we'll need that later. And let's just get rid of it temporarily with this line here as well. So, back in Chad Cafe, let's just console.log the progress. And you'll notice that it never changes after my first scroll. And that makes sense because I made a mistake. We only it's only referential here. It doesn't update every single time. So, let's see. I think the easiest solution is just get rid of it entirely and just directly refer to experience. progress itself. So that's what I'm going to do over here. And I'm just going to do it like that. Great. And now you can see that's exactly what we wanted. And now we can have the trail trigger the animation across the different progress values. Let's go into enit animations over up here. And let's give it some offset rel relative to the index. So it's I think it was about 0.44. And then we're going to subtract the index multiplied by some value like 01. So this way it'll be like 0.43 so on so forth for every single trail. And it's still not working because I did not actually update the Gab progress itself which we need to do right now. So I'm going to go back all the way down to update. And the timeline might not be created by the time that uh the experience calls world update and update. So we're going to do another guard clause here. If the timeline does not exist, do do anything. If it does, we want to set the progress of this.t1 equal to the this experience controls.progress over here. And now you can see when I scroll, it's animating, but it's not going the right way, which is fine cuz we can just fix that. But at least it's doing something. Okay, so let's come back. And now that I think about it, I don't want a timeline because timelines play sequentially. And I only want to play the trail after I pass a certain threshold back and forth, which means we just need some sort of ID. Sorry, not ID, or I guess you could also take an ID approach, but some sort of flag approach for each trail. And the other issue with the trail is that each trail should be synced up into each other. So, I think what I should do back in Blender, instead of keeping them separate, I should just Ctrl J and just name it trail one for both of them. That way, we don't have two separate trails. Oh well, I'm just going to deal with the fact that we have more than one trail. But if you want to go back and just simplify and make your life a little bit easier, be my guest. Anyway, the logic is basically the same. So, let's come over here and let's get rid of the timeline entirely. We don't need that. We're going to need the progress value to set someone's eventually. And let's go back over here. And I'm just going to collapse a bunch of these. And instead of having a timeline here, we're going to create an array of trail groups to loop through together. So we'll do trail groups. And we need the trails associated with each group cuz we have like 1 2 3 4 5. And that's going to be shoved into here. And then we need a threshold value. So the threshold of what value we pass before we activate it. And I honestly have no idea. I'm just going to start at like 0.45. And then we need to have a flag that says if it's shown or not. So has shown. And we'll just set that to false just like this. Great. And let's make sure this is an array of objects. So let's add that. And sorry, this should be comma. And now we can just copy and paste this thing. How many how many over times? Let's see. One, two, three, four. And I'm just going to increase uh sorry, decrease the threshold because we're actually going the opposite direction. So again, feels a little bit odd, but whatever. You can also dynamically create these uh if if your threshold is like constant amount, you don't have to manually create these. So you could this trail groups and loop through it and manually do everything, but whatever. The trails, we want to push it into a trail group. So this trails for each trail. Now let's console each trail name to determine how we can split up the string. Okay, so these are our names. So what we want to do is split over here and then that will break into mustard underscore and this number underscore into an array. And then we can grab the first index of that array which would be this one cuz remember arrays are index base zero. So this would be the first one. Sorry uh this would be the first. Then we can use parse int to get the index that we want. Here you can see parse int will from 077 will remove leading zeros. So it will just remove the leading zeros and it'll stop at the next underscore for parse int. So that's all we have to do. So let's come over here and let's do const. Our index is going to be parse int trail.name and we're going to split it at trail at the word of trail and we're going to get that second index of that array after splitting and we're going to parse that int. And since we started at one rather than base zero, we're going to have to subtract by one because arrays start at index zero. So let me just console.log the index now. Oh, and sorry, I have a typo here. This should be right after here after we split. And now you can see our index matches up. 1 0 1 0. Again, ignore this underscore over here. 0 two and this is one so on so forth. So basically what we can do is get rid of those of course. And if this trail groups if the index exists basically there's an object here if that exists at the given index and at that given index this trail groups at that index we want to grab the trails. So if this exists grab the trails and then just push that specific trail into that group there. I mean sorry that array there. And because every index is already mapped, it's going to push two of them into there because they share the same index. Now in our update function, we can come over here and ain't nobody need that no more. And now we just do for let I equals Z I less than this trail groups length. We want to go through every trail group. I ++ you know the for loop. Let's create a variable here group and just get the index of the trail group and get the trail group at that specific index. That way it's just a little bit more clean. Of course, that is also optional. And let's create another variable called con should show. And we would determine this if the progress itself. So this experience controls.progress is uh let's see it should be less than or equal to the group. Basically as soon as the progress goes below 0.45, we want to trigger it. Although if you do greater than or equal, actually no, equal would be the thing that triggers it. So, uh, you could just do equal. Um, but if you're going from greater than over to less than, it might make sense to, uh, flip the sign, but honestly, it probably doesn't really matter. But we can figure it out once we get there. So, if uh, let's see, should show, and it has not already shown cuz we don't want to animate it again if it's already shown. So if group dot has shown so if the group has not shown and it should show then we want to show it. So for each trail so for each trail in the trails array of the group object over here just set the scale and no timeline just between set the scale to one comma 1 comma one and then because it's showing we can do group has shown and we'll set that to true. And of course, we just do the opposite over here with an else- if that's over here. Else, and just paste that over there. And now we just grab everything over here. Maybe make it a little bit faster when it goes back to zero. And as shown, goes back to false because it's no longer showing. Now, I used a for loop here because it's technically quote unquote better for performance in an update function every time we're running this on 60 fps. But because we're only dealing with five here, if you want to just use trail groups, trail groups for each, right? And then that way you it's a little bit cleaner. You don't have to do this. It's just like for each group and just refer to that. Oh well, it doesn't really matter. Um, but yeah, that's just something I want to point out as well. I go back and refresh the page. As soon as I hit the threshold, let's see. Hopefully it starts showing. And it does. what all of them show really quickly because uh they're really really close to each other. So I might turn these on into really really large differences uh just to just to see what's up 20. And let's do it. Great. Yeah. And now it's a lot more spaced out just as I expected. And it's not working when I go back. Oh yeah. Yeah. Sorry. What am I doing? I didn't even I didn't even flick this. I'm so dumb. Sorry. My bad. Now we can do it. Cool. And then we can go back as well. Now, if you just wanted to go one direction, well, you can just get rid of the else if uh statement entirely. And I just realized this should be uh sorry, this should be outside of the for each loop. Great. Now, what we're ready to do is add a little bit of ray casting interactions. That way, when we click on this, it opens a new tab. When we click on this, it pops up a little modal. And uh yeah, that's pretty much it. So, we kind of have like two types of ray casting interactions. a little bit of a review, so I'll go a little bit faster. But it's a new system, so let's do that. And you can imagine that we could just put it in Chad Cafe per usual. But since it's so much complexity, I think it's better just to separate out in experience and create another one here called raycaster.js. Let's just copy from mouse to create a raycaster. And of course, let's not forget to rename this. And we don't need all that junk over there. So I think one of the most important things about the raycast cer is we need to actually get those objects somehow. Let's create an array of objects. Remember the raycast cer takes an array of objects that we need to intersect with. So let's create that and I'll do this intersect objects is going to be an empty array. And we need to populate this later. So of course we also definitely need this mouse equals this.experience.mouse right which is our pointer over here cuz we need these normalized coordinates here. And oh, okay, this might be an issue now because we are only checking for mouse and no longer touch events, which is fine. I think we can just add another touch event listener. It's not really a big deal. Or we could pass in a parameter that bypasses the the guard clause like or or um I don't know. There's multiple ways to solve it and it it doesn't really matter which one. Anyway, let's create a function here. And what we need to do, it's like populate intersect objects or something. populate intersect objects and we need to pass in a bunch of objects. So we'll just call this objects and then for each of those objects so objects do for each of them uh we can push it we can push that object into this intersect objects. Now because every intersect object has a different sort of let's see different sort of starting point different sort of data that we need instead of just logging the mesh itself we should take the we should take the original object and some sort of other information over here if that makes any sense because we're going to need that extra information rather than just the object itself. So for each object let's just push it to the array. So this intersect objects and we're going to push an object. So this will be an array of objects. We're going to spread the object itself to make a copy of it. And I think I want to get the original scale as well. So I'll call it original scale. Original scale. I'm going to set that to object.m mesh.scale. And we can clone it. That way when we adjust it, it doesn't affect the original one. And then we also need a boolean value here to because when I hover it, I want to scale up and scale down. So I'll come over here, probably do is scaled or something like that. And we'll set it to false by default. Not everything will have is scaled. So I think uh is is hoverable or or something like that might make sense here. Uh maybe not is hoverable, but like does does hoverable affect or whatever. But for now, I think it's fine if we just scale uh every single one. Cool. Let's go back to Chad Cafe. Uh so let's just control P go to Chad Cafe and let's import the raycaster over here. Actually h I think we can just do it directly. So let's do init raycaster over here. Init ray caster. Let's create this. Uh we can just do it in order, but I don't really care. I'll just do it right over here. Doesn't really matter. Actually, now that I think about it, I don't know if we need to for cuz we're going to need to traverse and add it to an object, which means we would create a intersect objects here. Traverse, push it in there. We might as well just do it part of in cafe then. So, we can just check if the child name includes raycaster. If it does, uh, I guess we can just create con intersect. It'll just be an object and we just make some key value pairs. So we'll create some ids with the child name itself. So intersect objects and we'll create it create the ID from the child name itself and we're just going to set it equal to the mesh object itself. And then I think we can go into let's see raycaster and populate intersect objects and pass in the key value pair of objects. So we can come over here and we'll do this.experience experience raycaster.pop populate and let's just pass in a bunch of objects. So let's just make sure we have those. Uh see now the issue here is if we get the mesh object itself and then set the type of object and then set what we need to do. Might be better to go into Blender and underneath one of these include like if you wanted to just a scale up just do scale. If you wanted to link somewhere else, you can probably do like Twitter link and then that way uh you can just check determined by type based on the string name. That might be a little bit cleaner and automatic in the long run and then you create like a map to each URL and each ID. But I think we can just manually do it because we only have a few so it's not a big issue. But if you want to make it scalable, I would consider renaming here and reexporting to make it a little bit more automatic. Anyway, I think what we can do now is let's just make sure to console.log intersect objects just to make sure we have all of them. Let's also make sure to initialize our raycaster. So, I'm just going to put it over here. Raycaster. And forget to import raycaster. Go back to check FA and let's get rid of this because we got rid of that as well. Now, if I go back, you can see that it's here. But we have an issue. Where are these underscores coming from? And why in glTF report when I look at it, there's not a bunch of objects here. It's the actual names that we give it. Why is this doing this? Well, it's because 3JS per material has to engage in a draw call. That's like some GPU thing that you don't really have to worry about as a beginner. And it will split up. So, because Beast mode alpha Chad has seven different materials, it's going to underscore seven times. Same with these other ones. My YouTube contact has the outline material, the gray material, and then the sign material itself. So, it's split into three for each of those. So, what is our solution here? Well, our solution is just to either bake everything into a single texture and that will just save on draw calls or we just use the group instead of the individual mesh. So, instead of this over here, let's take it outside of if child mesh. Come over here and if child.name.includes raycaster and and child.type type equals equals equals group. Let's put this in a parenthesy over here or or child.ype equals equals equals object 3D. And now we can console.log over here the child. Now when I go back you can see that we just have a few groups for each one. So beast mode alpha we got the group there. So this check is just if in case it's not a group and you have a single material. This is what this is for. But for all of mine they're groups. Let's just save us some copying and pasting time. And let's just do object wrap this so we only show the keys. Sorry, this should be object. And now we can just copy this. And now let's just come here and make a bunch of objects. So what do we want? Well, we want the mesh itself. We want the type of the object. Again, you can automate this with the naming convention. And then uh I guess if it's a link, we can make like a link one. And we'll have like a URL associated with it. And we can have like scale like if we want to hover and scale and then we can do like scale factor. And then the last one is modal. So I'll do modal. We could probably put like a ID value here or or selector selector ID. I don't know like hashtag something like hashtag lol. I might change this a little bit later. I'm not exactly sure at the time of recording this. So let's just paste our stuff over here and move it appropriately. So, I'm going to hold Alt just to quickly get rid of these and backspace them out. Just like that. And alt for the last one. And now that I have six cursors, I'm just going to come over here. Ctrl Alt down arrow to quickly add cursors and just paste it in there. Now, we can just temporarily put w.com over here. So, beast mode alpha chad is not type link, it's a scale. And then we can do scale factor and just make adjustments until whatever. Oh, and yeah. So here you can see that everything I'm doing and I did forget these are just keys. They aren't strings that we want. Well actually now that I think about it we could pass the intersect objects itself rather than the mesh. Well ah whatever it's already here. Let's just control D to select the next instance of mesh over here. And let's just type intersect objects wrap all these strings with a bracket over here. And let's move the comma outside over there. Just like that. And now we're just passing the mesh in directly. And yeah, I guess it's kind of messy. We could probably put all this entire thing into an init recaster function, but we already got rid of it, so I'm not going to add it back in. But if you want to split it up, yeah, you can totally do that. Great. So now we should have access to all the objects. Let's go back to our raycaster.js over here, and let's just store all the objects. So we push to intersect objects over here. We call it, right? And then it shows it in here. And we actually only want to grab the meshes itself from the objects that we passed in. So let's just do let's create another one called this meshes over here equals this.sect objects. Uh so the way this works is for every intersect object inside of intersect object. We get grab the objects mesh property. So if I go in here, it's just grabbing the mesh one. Well, this one's trail. Sorry. Uh yeah, it's just grabbing this. and map just returns an array of the thing that you're selecting which is right here. So this meshes is just going to be an array of our object meshes here. Let's go in our update function because when we hover on objects we want them to pop up and go down. So let's do that and let's just make sure our h let's see our experience is calling up raycaster.update. So let's go up here and let's make sure this is raycaster. Great. And we might have to add a guard clause here later but we'll figure it out once we get there. So, let's do this.caster. This raycaster. Uh, actually, we we didn't create a raycaster yet. So, let's come over here and let's create a raycaster. And then we can do like a this.hovered object. Hovered object. And we'll just set that to null for now. We just need to do the same thing. Set from camera. This, which is basically our normalized coordinate system. And then we need to use the camera. So, this experience. Well, actually, let's just do this.instance. And let's grab it from up here as well. And let's just do this camera equals this experience. Well, uh, sorry. Now we just need this camera. It doesn't have to be this camera instance. And I guess just for semantic clarity, this is probably better just to instantiate it here too, but doesn't really matter. Cool. And this is our review. So we need to grab our intersects array from the raycaster to determine if we have anything that is intersecting. So const intersects and if there's anything in intersect. So if intersect.length, we just want to grab the first one cuz we have nothing behind. We're not doing any sort of layered just console.log intersects just to make sure that we are getting something. All right. Now that I go back, let me just hover over and I think it's showing that because I'm like hovering over Yeah, I'm hovering over a bunch of the meshes inside the group at a single time. But it doesn't matter because we just select the first one. Great. So let's just grab the first one and intersected. So intersected object equals the first one over here. Now we need to do some recursion to get the parent object because our stuff is very very deeply nested. If you remember from bellispark, we went one layer up of the intersect object to get the entire parent. That's not going to work for us cuz we have multiple layers. So we have to do a little bit of recursion and get the actual object that we want to get. So we can create a function for that. And I guess we'll just put it over here and do get and we pass in our intersected objects. Intersected object get parent object of our intersected object. Then just a little bit of basic recursion here. So I'll do let object equals intersected object while the object is not found. Cons parent equal this.sect object. We need our base case. So if parent return our parent and then we set the object equals to the object parent. So object.p parent. So basically the recursion here is we get the object does it match anything in intersect objects? If not go to the next one and so on so forth and then every time it goes to the next one it goes one layer up until eventually we hit the parent value that matches with an intersect object. Well then it exists and then we return the parent object. Cool. So, we can basically use nearly identical uh newly identical logic that we did in here. And this pattern is so common actually. Uh you're probably going to see it a lot. So, I'm just going to copy and paste this in in multitude of different contexts, not just 3GS. Um so, we just want to use gap to play our animation. And let's use a function to get our parent object to animate or in this case our group. And I'll just call this con parent object. And we're doing the same thing here. If parent object this time instead of uh a flag variable, we're just checking if our hovered object is not the intersected object, then we play it. Otherwise, we don't. So we created this hovered object state over here. So we just check that. So if our parent object does not equal equal does not equal equal hovered object, then let's set this.hover object equal to the parent object equal to the parent object. That way it's not going to run again. It's going to go to our else statement. And we just can run gab.2 over here. We'll take our parent objects scale and we can like multiply it by a factor. So by default I think it starts at one but we have this original scale over here. So we can refer to that. It could be parent object. Whoops. It could be parent object dot original scale. And then we can just multiply by a factor here like 1.2. And then this time we can just use else. And then let's check if this.hover object exists because it might not exist exists. Then let's just get rid of this junk over here. Then actually let's just use this. And we just get rid of all this. We don't need this. And then we set this hover object back to null over here after we leave it. And let's get rid of this. Well, I think that's it. We might have some errors, but we can find it when we get it. Don't forget to import gap. Let's see. Uh, of course, we're getting a bunch of errors. I'm sure I mistyped something pretty obvious. Yeah, parent object is like the actual object itself with all that extra data. So, we need to grab the mesh itself. Sorry, my bad. And then this isn't parent object here. This is this hoverboard object. Sorry, my bad. Just like this. This should also be mesh over here. Should be x, y, and z. Sorry, I'm rushing too much. It's a vector 3, so we need to grab those. do that like that. See, hopefully this works. Uh, that's I mean, it's doing something. Oh, this works fine. Oh, I need to fix this origin point. It's kind of awkward putting it on the on the right there. Uh, um, that it's not resetting properly, which means let's see where is this else statement. Uh, oh, sorry. It should be if I'm not intersecting anything, put it back to normal. Sorry. So, else statement should be relative to the intersect length one. My bad. Cool. And now it works. Voila. Oh my gosh, that is so epic. All right. So, just for user experience, let's come over here and change our cursor every time we hover as well. So, we'll just do document.style.cursor. Otherwise, just set it to default over here. Default. And that should work. I'm not even going to bother testing it. Now, if you want, since some of them are not clickable, you can probably do like a clickable here and check for this flag. If it's not clickable, then don't show the hover state. Otherwise, show the hover state. But, uh, I don't really care that it's not clickable. I'm just going to change the pointer anyway. But that's a user experience decision. That's up to you. Okay. So, I think the next thing we can do is create our click function. So, I guess we might as well just do it in init. And let's just come over here and let's just do uh this.experience. Actually, let's just do it what we did before. So, let's just do this. This canvas equals this experience canvas element. And this will just be this canvas. And let's do add event listener. We need the same thing from here. We need our intersect objects over here. So, let's just come over here. Grab our intersect objects during clicking. And if there is no intersects.length, then we just return. Now, this time I'm using a guard clause because over here I'm not using a guard clause because maybe we want more stuff underneath here. So, I'm just using an if statement. Here I'm using a guard clause because I don't want to execute anything in my click function if there's nothing in there. You can also copy this logic over here just like this. And then if no parent object, if no parent object, then just return. And then we can just switch switch the parent object.ype. Then for our cases we have scale. I forgot the types that we uh actually did. So, and actually now that I think about it, I don't want to do scale at all. Yeah, I don't want to do scale. So, I'm going to get rid of scale cuz the hover does the scale. If you want to click on it to scale it, you can have the scale here and run another Gab timeline. But I I don't need to do that. I don't want to do that. So, that means we only need modal and URL. So, I'm going to do modal. Actually, let's do URL first because it's just easier. Let's just do window.open parent object. And then we need those security measures that are very standard for the web. If we go back and try, it should already work. I don't think there's any issue. Yeah, it already works. So, let's just shift alt down arrow and let's do case scale. Now, I think I'm going to change that to animation later, but for now, I'm just going to do return. We're not going to do anything uh with this case. And then now for modals. Okay, this is going to be interesting. So if it's a model, what are we going to do? Well, we have the selector. Well, I guess selector ID. Selector ID kind of makes sense, but it's probably more standard to call like element ID. Now, if we do it the Bellis Park way, right? We did it where we just created one single model and we swap the content for that that single model. And honestly, you could create the model itself in JS, you know, using a string literal and creating like the div in in in JavaScript if you wanted to. I think for us, you can totally take this approach. There's nothing wrong with that, but I'm just going to do it a very easy way. And I'm literally just going to create all four modals and just make them exist uh in inside the HTML element itself. So, so we want a modal and then inside that modal, I'm just going to make it really simple. It'll just be like a model wrapper. And inside the model wrapper, let's do the model title and the model paragraph. And I'll just do that. So, this should probably be an H1. And then this should just be a P tag. Sorry, my bad. Great. And then once you get a design here that you like, you can just shift alt down arrow four times. Uh, and then for each of them, we can give them their unique ID. So, for this one, I can this one I'll call project one Chad. And let's just shift all down arrow and project two chad. And we can add two more for the caviar one later. Uh but yeah, that's pretty much it. And I'm just going to go into just the default style over here and add some styles. Cool. So just very basic styles over here. And I actually forgot, but I also want to give a back button to each of these. So I'm going to do button. And I'll do back button like this. And I'm just going to do close. Oh, sorry. I should probably call it close button. Close button. So, I updated to block element modifier naming convention BM. Very common. Uh, that's also optional. And you can see very, very basic, very basic CSS here. I just centered it. Give it a set height and everything. So, you can see here it's just this and a close button that we can click on. Before we animate it, we kind of wanted to determine, do we want it to be offscreen or do we want to just leave it on on screen? is just leave it on screen and I'm just going to do opacity zero. Just leaving opacity zero still means you can interact with it. So we'll do pointer events and we'll just set them to none. That way we can't interact with the model. But we want to be able to interact with it once it is faded in. So we could add another class list or we could use gap to directly target that and set pointer events to auto. Or if you want to toggle on a class, maybe we could do like um and here you could do pointer events. Pointer events auto. That way we can like scroll and do all that other stuff with the model again. And you can attach that class as the G-Stop animation fades in or you can just target directly with Gab. It's kind of up to you. Anyway, we we can probably get started uh now with that. Now, I do want to mention when it comes to managing a bunch of HTML elements, you typically want to have like a utils over here and like HTML elements or DOM elements, DOM elements. And then basically the whole idea is you just have a bunch of selectors and you just go in and select everything, right? Like document query, select all or whatever. And then you create an object of DOM elements and then the ids or the class names become the same thing. So you have like model wrapper and then it'll like auto index if you have like more than one class right and then for ids well the id itself would be the key value pair. So this would be the id and then the id would be the actual dumb element over here which is like a div or whatever and then this way you just use these elements throughout your code and you don't have to have manage a bunch of selectors everywhere because all the selectors live in here and then you generate an object that you refer to. So, are we going to do that? And the answer is no, because we only have like four things to manage and probably a pre-loader, which we'll also just do directly in the pre-loader. But if you ever want to manage a bunch of other stuff, I do kind of recommend doing that where you have like a DOM element object that you can refer to. So, for us though, we're just going to do it directly in the raycast caster. We can check simply and select over here. We can simply check if it has an element ID. So, we can create a new field here called element. And if element ID exists. So let me go into chat cafe. So if element ID exists then we want to just document document dot getelement by ID and just get the element ID in there. Let me control B just to get that. And should be the object sorry object.element ID and whoops this should be come after. So if element ID exists, set it to the element, the actual DOM element. Otherwise, just set it to null. Great. And now we have access to the ID associated with each object if there's one here. So we do have it. Let's go into index.html over here. And let's get our IDs. So project one Chad. Then cozy project one is going to be project one Chad. And then project two Chad is going to be this one over here, element ID. Let's go to our model over here. Actually, before we do that, yeah, we should probably break here cuz we don't want to check anything after if we already do URL. Same with return. Return will actually cancel our entire function and the rest of it, which I guess is fine cuz we're not really doing anything. But we might as well just stay consistent. Same with model over here. And we can check if the model is open or closed, not based on the class that we add actually. So that was good that we actually added a class over here. So on open. So I I'll just call this model open actually model open. So let's check for that in our array cer const is open equals parent object do element doclass list contains does it does it contain model open if it is it means it's open otherwise it's closed. So if it's open if is open so if it's open we want to fade it otherwise we want to bring it up and then once this function is complete we can do onmplete callback function over here and we can just remove we can just instead of remove contains we just remove it and then otherwise we want to fade it in. So we'll just copy gap.2 and we'll do add over here. Actually this should be on start not on because as soon as we click it it will be on start. Uh we can also directly just leave it outside. We don't have to use the Gab on start, but whatever. I'm just going to put in Gab on start. Let's see. We have an issue here. Element ID is not defined. That's because this should be object. Sorry. This should be object element ID here. Cool. Let's go back. Refresh the page. Should be working nice. Oh, and now we just need to disable raycaster and everything once the model is open. And then we also need to activate the close button. So, let's do the close and then let's disable everything if the model is open. So I created this simple function over here that will take in our object this entire object over here. We'll grab the element inside of this spread operator over here and we'll get the close model of that element. If the close button does not exist because some HTML elements that we pass in won't have close buttons just return and don't do anything. Otherwise if close button does exist attach click event listener. Grab the event object and stop propagation. We don't want to have it bubble up to anything like the canvas itself. Although I don't know if this will actually be necessary for this project, but better just for good practice, we'll leave that in there. And then we just take the object element, so the model itself, and we set its opacity to zero. Same thing here. On complete, remove model open cuz it's now closed. Very, very similar to what we did over here. And now what we can do is just directly initiate it over here. And instead of just doing this in one line, we can take this out into a new one. And let's do const equals this old object and push the new object. And then for every new object if new object then let's just call this.init modal close button and pass in the object itself. Sorry, it should be the new object. Great. Now when I go back, you can see it works just like that. Okay. Now what we need to do is disable the raycaster and everything else while the model is open. So let's quickly do that. That's really really simple. So let's just do this is model open and we'll set it to false initially and everywhere that let's see close button on complete let's create a function over here we could also this actually doesn't need to be on complete you can put it afterwards anyway this is model open is going to be false over here is going to be true and then this one will be yeah this one can be false cool and actually we can just check instead Instead of doing this now, we can just check uh this is modal open or not. We don't need this check anymore. Now, we're still using this for the pointer vents auto to write to add the pointer vents auto again. Although, at this point, I maybe it would just be better just to not have adding and removing lists. I think later I might want to use that class for something else. So, it's it's fine just to leave it. And then this is also fine just to do that. But if you just want to do this and we don't really need this here anymore, that's fine. But we can keep this at least. And now we just want to guard from everything. So click over here. We just do if this this moto open then just return early. And then same thing with the update function. I don't know. Do we have to guard for anything here? I don't think so. We can just actually do it here too. If you have something else, make sure to wrap it. If you also when you want to click on it and the motor is open, if you want to reset this, then put this inside of this here too. That way it goes back to zero. But I don't think I want to do that. Um, and now that I think about it, this doesn't make any sense because it's not going to we're never going to close the model this way. The close button is going to close the model. So this is actually completely unnecessary. We can just do this. See, now when I go back, let's see. See, I click. Cool. Oh, the cursor is not resetting. So, this is an issue. I need to fix that. Go back. And I do want to fix this. So, actually, I'm just going to I'm just going to copy this whole thing over here. Let's just open it up and let's put it here and return. This will make everything scale to zero. And then re revert back to default as well. And yeah, now it's working. Now, one thing I don't like is I don't want to be able to scroll while the model is there. Now, if you want to, that's fine. You can just leave it as is, but I don't want to, right? So, let's go to controls.js and we simply just have to check for it. So, everywhere in our wheel in our touch start and our touch move, let's just add the same guard clause. If this experience ratecaster is modal open, it's true, then just return. Cool. And now when I do it, sweet. Now it feels a little bit odd that the rotation is really large when there. So you could check if the modal is openly you know with a turnary oper operator here. If uh the is modal open is model open check right if if it's open then multiply it by another value otherwise don't um that way it's just a little bit smoother but whatever I'll just leave it on. Okay so the last thing we have to do is make all this stuff work with touch event. So, let's go back to mouse and h now that I think about it, I don't think this is a good idea to restrict. Let's just do the split as usual. Mouse move and touch move. And uh yeah, let's just do touch move. Touch move and touch end. Touch end. And then we need to grab the touch. So, we'll do const touch equal e touches. Just grab the first one. We don't care if they are having two fingers on there or not. Let's change this to touch over here. Now, this is repetitive. You could put it into another function and just call that function in all three of them, but we only have these three, so it's not really a big deal. We can get rid of resize and update. Uh, and sorry, this should be touch start. Okay. Now, I think this is going to cause an issue with our control offset because we specifically checked for that. So, when you tap it wouldn't do the offset. Um, but I think we can just Let's just confirm that. I think. Uh, no. I mean, I know it will, but let's just get it working first and then figure out how to fix it later. So, if I go back, you can see it works. Now, I don't have an iOS device, so I feel like we still have to add touch events just in case this doesn't work on an iOS device. But, you can see we have the issue here. Now, when I tap, uh, there's an offset. So I guess the easiest solution is just to check if it's a mobile device with screen width and then if it's a small screen width then disable offset entirely. But if someone's small screen is like this on desktop then then they wouldn't have the offset but it would work fine. Probably no one's looking at this website that small. Another solution instead of updating it here we can actually just update it in the function itself. That way we can get rid of this and it doesn't affect anything else but whatever. I think the solution I'm going to go for is literally just checking if I'm on a mobile device. So I think we can actually let's see maybe we can do it in the sizes check. I don't think that makes sense. Let's just do device.js and let's come to sizes and change it over to device. Let's just do this is mobile is mobile device. Let's get rid of this. And then the cliche. And then let's just test if we're on a mobile device over here. Cool. And we don't need event emitter here. So we can get rid of that junk. And this honestly doesn't need to be a class. It can just be a utility function. But I might come and add extra stuff here for the stuff later. So this can just come whenever, honestly. Let's make sure to import that. Yeah, I might do device specific things later. I don't know. But for now, it's just going to check if it's a mobile device. Um, sizes can also check if it's a mobile device based on screen width. So, sizes kind of like screen width though it also check like tablets and whatnot or whatever. So, in controls.js, let's just simply check for that. If this experience device, so if it's a mobile device, uh, we just return. We don't have any offset. So, we just return early. But we still want to move the position, but we don't want to do any of the looping offset. Now, when I refresh the page, cool, it's working. Okay, so let's go back to Raycaster just to be safe. Well, just to be safe, let's go to Raycaster. Let's take out this entire function over here. And let's just call it let's just call it handle click uh for whatever. That's a terrible name. Whatever. And we'll just create the function over here. function and just paste it in there. Click and touch just like that. Now, if you prefer to put handle click and touch as a standalone function like this and as part of the class the that's fine as well. Just note that you might have to dobbind this just to make it work, but that will depend on how you want to format it. Anyway, this is kind of a mess, but it's fine. It's understandable. As long as it's readable, that's all that matters. And we'll do touch start or touch end works here. I I feel like I'm going to prefer touch end here. And same thing, just same exact function. And now it should work. Let's give it a try. Oh, it's not working. Yeah, that's I'm dumb. Yeah, we can't use a function here. It has to be an arrow function because then we lose scope to the variable. We could pass in parameters, too, but like it's better just to use an arrow function at this point. And we get rid of that. And I already have it. I can just get rid of the syntax there. It looks like we got an error. Probably one of these brackets is extra. Cool. And great, now it's working. Now, I will say this is the outdated way of detecting if it's a mobile device. We can also detect if it's a touch device and things like that, but for now, this is universally applicable. So, I'm just going to keep doing this. Cool. Last thing on our agenda is a loading and then I'll talk about a lot of other cool stuff a little bit faster because I feel like we've covered enough foundation to speed up. So let's do that. So how do we add a preloader? Well, we can just come in over here and do again as you can expect preloader.js. Now I kind of actually want you to implement the preloader yourself and if and then compare on how I would do it because there's a lot of different ways to approach the preloader as well. And so I think the idea is very similar that once the preloader is done, we need to emit some sort of event. So I'm just going to copy from sizes over here. And uh so I'm just going to copy this from sizes over here. And I'm just going to paste it over there. And let's change this to preloader. So we definitely need access to the resources because this way we know when it's done loading. Let's go to somewhere like mouse and just grab the instance for ourselves over here. And we can get rid of all this stuff because we definitely don't need it. Well, so if we want to make a loading bar, we need somehow to note the amount of Q. Well, of course, we can use this over here. We can actually get rid of this console log. I don't know why I had it for so long. Simply emit the progress value. So, let's do emit progress. And let's add the value over here, uh, which is going to be this.loaded divided by this. Q. That's going to be the progress value that we're going to refer to. So, we can pass in a value. And then we can grab that value in our preloader over here. So let's just shift alt down arrow over here and this is going to be this.resources. Let's do this.resources from progress. We're going to grab the value. So we can grab value from the event emitter. And then here we can also listen to this.resources.onready. And this one doesn't have a value which is fine. We can call this play intro. We'll create this function right now. And then we'll give the progress bar a value here as well. Just get out of the constructor and play intro over here. I guess this kind of depends on you if you want to play an intro or not. You could also do like show button and then click on button and then enter. I'm just going to automatically make it fade as soon as it goes to 100%. But if you want to wait for a button, you can totally do that. And I'm sure you can be able to figure it out. You know, totally believe in yourself. You got this. For me, I'm just going to create a very, very simple one. And I'm going to again, you can create the preloader in your JavaScript if you want to. I'm just going to do it directly in uh my index.html over over here. So, I'm just going to do preloader. So really simple, just pre-loader and then a preloader inner and then a pre-loader progress wrapper and then a progress bar. You can see my style.css. It's literally just centering the the progress bar. So just a bunch of dips and you can see what that looks like over here. And so just make sure the progress bar width itself is 0% or whatever whatever you want to animate. I'm just animating the width on top of the bar. So there's this one div that's the back and then the other bar is going to be animating over there. Really really simple. And let's go to preloader.js and let's just finish this puppy up. So we can do this innit DOM. But honestly, if you don't want to be organized, you don't have to do init elements. Again, this is why you might want to have like multiple like a single source of truth for the DOM elements. But we can just create it directly in over here. So we'll just do this overlay, which is like the entire preloader itself. And I'm going to do document.query selector. You can just do dot the preloader itself. And then the bar itself, the one that we want to animate is going to be that's going to be the bar itself. And then here we can do let's see this.onloading unload. Call it whatever you want to call it. Shift all down arrow. I'm going to move it up because it's up here. And I'll just do onload. Honestly, play intro. It could be like if if you have like a intro animation and then an outro animation, it might say make more sense to call this one play outro. play outro cuz once you first land on the preloader it'll play an intro animation then we have the loading bar then once we hit enter or once you finish loading then we play an outro for the preloader so it makes more sense to say play outro here. So during the load we can do very very basic thing here. We can just do this bar and let me just call this progress bar progress bar just to be a little bit more consistent and we'll just width and we're just going to set it a string literal here. Whoops, I forgot the dollar sign. Value times 100. Basic math over there. And then we need a percentage. And let's make sure in experience.js to create the preloader. We can come over here and do preloader. Make sure to import it at the top. And preloader also needs to import experience. I must have accidentally got rid of this. So, let's import that. And let's fix this typo here. Whoops. Uh, I must have accidentally changed it when I was renaming the bar. Of course, we need to pass in value over here so we can actually use it. Go back and refresh the page. Woo, it's working. Now, we only have two assets, so it goes 0.5 to one. If you want to do some fancy math where there's like a delay in between or some artificial loading, which a lot of creative websites do, you can totally do that. But as you add more assets like images and things like that, it'll, you know, sort itself out. So, let's get rid of that console log. And I'm actually going to rename this to preloader. And now if we play outro, we just make it fade to zero. And on complete, I want to remove it completely from the DOM. So we just do dot remove there. Don't forget to import gap from GSA. And now if I refresh the page, as soon as the bar hits 100, it's going to fade out. Now that's a little bit too fast. So we can probably add a delay here of like 1 second. So we hit the loading bar and then delay 1 second and then fade out. You will notice there's this really awkward thing where it hits the loading, it delays one second, then it plays the opacity, and then it like teleports out and gets removed. Why is that? Well, it's because of my CSS styles. I simply put transition to everything over here. Opacity. So, we can actually get rid of the ease because Gab handles the ease there. So, if you want to custom ease, you can put the ease over here, right? So, now if we go, it loads, waits 1 second, and then fades out just like that. And you can see that remove an action here. It's part of the DOM over here. Once it's done, we remove it from the DOM. So, the last thing I want to do is probably just emit an event that says this. And I'll just You can call it ready again if you want to. There's nothing wrong with using the same variable name here, but I'll just do preloader finished. Preloader done or preloader finished. And we can listen to this in experience.js. So, if you want to, you can listen to this event like this. Preloader. I guess we could move something possibly like the raycaster in here. I don't know. It's it's up to you and what you want to work for. Just make sure nothing else is conflicting with it. And you might have to add some guard clauses here if you're waiting for the preloader to be done before you instantiate something. I don't know if I need that yet, but that is an option there if you need it. If you don't, then just get rid of this, but uh that's also an option as well. So now all we have to do is just deploy it. So create a GitHub account if you don't already. I kind of already assume that you know this, but create a repository if you don't have one already. Let's come over here. I'm just going to cancel ganit get init promote add origin over here get add everything get commit dash m let's go dudes portfolio and let's push that up there oops I forgot to do this only the first time we got to do that and then every other time we can just do get add all get commit and get push let's refresh the page let's go to versell.com sign up for an account the last thing you push push should uh show up over here once you connect your GitHub. Then just hit import and deploy. And we have nothing special, so we probably don't need to do anything here. But you can see it by default runs our mpm run build command, which is the one that we have in here to build out everything into a disc folder. So you can run mpm run build here on your local device just to see the output here. You can see everything is disc in here. Then if you want you can do mpm run preview to preview the built out files in the disc here. But this gets deployed and you can see it's done. We can continue to dashboard. Open this up and you can see that Alpha Chad's Cozy Cafes is now live and we can share it with anyone. All right. So now I'm just going to do a lot of extra content a little bit quicker and just kind of like talk over them because I feel like at this point you can definitely pick up a lot of things a lot quicker and there might be a few gaps here and there that you might have to spend some time trying on. But like don't worry, you know, failure is part of learning and I'm sure you will be able to figure it out. Just make sure to believe in yourself. I know that sounds really cliche but the belief in yourself is actually really powerful. Okay, so the first thing I want to just tackle some general issues out when you're working with uh I guess in mouse.js when you have this instance set over here it's already set because it's belongs to the window itself. So by default your pointer is going to start at 0000 that we normalize into the center. So anything that's directly there even if you don't interact with anything yet might be activated. If that's the case, you can simply have a guard clause until you have a first interaction. Very similar to how you can't play audio unless there's some sort of user interaction. You can just guard for the raycast caster updating or triggering anything until the user does a first interaction, whether that be a click or pointer. That doesn't concern me because the only thing it animates is Chad and it makes his animation play. But it doesn't really concern me. So that's just one way to fix that and something you might encounter later. If you ever see like something scaled up and you're like wondering like why is it scaled up when nothing else is scaled up, it's probably because it's directly at 0, 0. I also want to talk about code smells in general. So I feel like we have a few of these like probably a long method somewhere. I think the update functions in some of our things are a little bit long. You could extrapolate those into other functions. Um sorry, extrapolate. No, like take it out. And then a little bit of shotgun surgery, but it's not really bad. Like sometimes you want the readability over the dry or the, you know, do not repeat yourself principle. Like for example, in mouse.js, we do the same thing here three different times. Does it really matter that we didn't take it out to, you know, its own function and just call that? Honestly, here I feel like the readability just makes a lot more sense than to put it in another function. But if you want to put it in another function, you can totally do that. So here is a little bit of shotgun surgery. If we want to change this here, we also have to change it here and so on so forth. But whatever. Overall though, I think we did a fairly good job with clean code. I feel like there's a few areas where we could make it a little bit cleaner. If this project were to extend beyond this, I would definitely restructure a little bit of it. But for this kind of project, we haven't done too bad because we can change things and we can add things later if we want to. Again, if we had more HTML elements, more interactions, I would consider having like, you know, more helper classes with like HTML elements preparation and maybe like a bunch of animation wrappers that we can reuse or export and just reuse rather than having a bunch of custom G-Soft animations everywhere in multiple different files. But yeah, for us, I don't feel like that's really necessary. Next thing I want to talk about is this day and night mode toggle. Now, there's a few interactions tied here, but I'll give you the general concept. I'm not going to cover the CSS part where the user interface changes color because there's a lot of online free tutorials on on that already. So, uh, first thing I'll talk about the moon and the night. So, you can imagine this is a very simple gap animation, but I do want to mention a few things. If I go over here to Cozy Cafe, I just want to show you how I set this up real quick. So, I modeled the sun and then I did Q origin to geometry and then I just control period just to move it somewhere that I like the rotation of. So, oh sorry, let me confirm it like this. And then when after I modeled the room, it doesn't have the same origin point anymore. But if you do shift S and do cursor to selected, it'll snap to the origin point, not the, you know, where you kind of expect it to. And then you just do Q or, you know, right click, set origin, origin to the 3D cursor. Now both of them share the same origin point. Then you can rotate around. Of course, just make sure you adjust this to get where you like. And uh yeah, now you can just rotate it however you want. Now because they are on opposite sides generally they have a different rotation. So this would have like a rotation of 180 and this one would have zero I guess cuz I just left it there. But because I applied my rotation with control A and applied my rotation over here. See that I actually give them both the same exact animation. If one of them has a 180 rotation while the other one has zero, you might actually have to do two times math divided by pi. one of them might have to go to 360 and then like math.py. So, because I applied my location, I could just give both of them the same exact animation cuz it's rotating relative to its new quote unquote 0000 rotation values. Next thing I want to talk about are these lights. So, how did I make them gradient fall off? Now, this is a little bit advanced for this course, but those are shaders. And we've actually already been working with shaders. Everything that you see in 3GS is pretty much a shader, which is basically a program that runs on your GPU. But basically, when we talk about shaders and generics, we kind of mean like custom math functions or like in Blender, we also call these shader nodes. So, it's pretty much just these because behind every one of these nodes is literally just a bunch of math. So, when we call shaders, it's kind of just like, uh, we wrote some sort of math function. And then shader nodes are just nodes to help people with math. So, there's a lot of that there, right? Like, let's say I just uh let me create a plane real quick and just give it a new material over here. And I hook up a gradient texture over here. Just like that. You can see now there's a gradient on my plane over there. Now, there's a lot of math behind this and why it looks the way that it does on the plane. Now, if you are interested in knowing the math behind it, you can check out this video over here. Some of the imports might be a little bit dated, but I explained all the math behind well maybe not all the math but on the foundational math behind like the gradient texture node here in Blender and also how to recreate that in 3JS. So the concept is basically the same. All I did was recreate those shader nodes using using the 3GS shading language TSL over here. So let me go back to Chad Cafe and you can see that I didn't create any of the shapes actually in here. I just used lights and then generated copied those positions and generated the lights. the same with the car over here. You can see that I just generated the cone in 3GS. Now, why did I do that in well, I actually used a cylindrical geometry instead of a cone. Now, the reason I generated them in 3GS here is one, lower file size, and two, if I ever want to make adjustments, it's easier to dynamically make that in 3GS rather than going in Blender, changing it, reexporting it, and so on so forth. The only time I would consider making a geometry in Blender for something like that is if the model is really complex, but for a simple cylindrical shape that kind of looks like a cone. There's no need to do it in Blender. I mean, if you want to do it in Blender, that's totally fine as well because just want to save time and don't want to play around with parameters, I get that too as well. So here you can see there's this thing called color node. You can imagine that's like a color node in Blender, right? Like this is this is pretty much a color node, right? So basically I'm just creating a custom shader using TSL not actual math like the low lowest level language cuz TSL is an abstraction. So I'm just using their abstracted math to create a custom color node for there. And you can see it's just like a light smooth step kind of like a gradient fade out sort of thing. It looks really scary and intimidating but once you get the basics it won't be that bad. Definitely check out that other video that I have. You'll notice that it's actually not really that scary. It starts getting scary when you get to the more complex shaders. I mean, the Blender shader node system is also pretty scary once you get to the really complicated shader node setups. Another thing to mention is, yes, you can use Gap to animate things like this on targets, on position, all that sort of stuff. The same way we use Little GUI to modify it. We can also modify it with Gap. Again, at the end of the day, all of these are kind of like vectors and JavaScript objects. So, you probably can animate them if you can tweak them some other some some other way. Next I want to talk about are like briefly animations. So when you see this car over here and like this floating icon over here and of course the hover interactions like when it's very very similar or sorry when it's very basic animations I'll just do them in JavaScript directly because adding all that animation information data in the GB file isn't really necessary for simple interactions and they're not as easily tweakable if you do it in Blender beforehand. Now I did give one single animation. Again, I would not do this in Blender, but just to for demonstration here, I animated this up. So, let me go over to timeline and let me just show you that real quick. You can see I have two key frames. So, if you're ever curious on what to do, you just select your object and then just hit K and then you can do rotation and then you can go to some amount of frames like over here and uh set or new rotation and then just hit K and do rotation or whatever thing you're keying again. And just like that. So that's the really really basics of animation. There's like auto keying, drivers, whatever, blah blah blah. But that's like the basics. For very basic ones like these, you can just hit you can just check animation and you'll be able to find the animation once you log out the animation clip. Not every kind of animation in Blender will export. So be careful there. But for very simple transformations like these, you can be pretty confident that they will export through the GB file. For audio, I used Hower.js. The reason I'm using Hower instead of doing it myself is because Hower kind of take cares of a lot of cross compatibility issues with the different browsers and audio which is why I use Hower.JS to convert fonts into W and W 2. For web fonts, I use transferters. So if you would like any of these, just upload it over here, convert. And the really nice thing is it will also give you a stylesheet. So if I go here and open the stylesheet, you can see that it'll set it up all here. All you have to do for us is just control shift L and then just do slashfonts or wherever you saved it in your public folder. I just put in font. So that's all you have to do there. And then you can just use this and import this into your wherever your root is or wherever you want to use it. Here you can see I just imported stylesfont or favicon generator. I use this one but there's a lot of other ones online but this is really really nice cuz it also gives you the index.html element to link up in your head. right over here. They give you all this over here. And if you come into media, you can see it gave me all that OG image I uploaded myself. But this is all given to us. Again, all you have to do is just select the hrefs and do / media or wherever you uploaded the image there for your public folder. Just be careful about using SVGs here cuz it might add like a background even though your stuff is transparent. So just be aware of that. All right. So last on the agenda, I just want to talk about this split over here. So the easiest way to do that is using scissoring. So if I come over here, if you click over here and go to the code over here and type in scissor, you can see that it's using scissoring. And then that's also in the documentation. So if you come over here and look up scissoring, you can see over here. And then you pretty much can just copy this. Now, the downside of this one is that it's only going to be like here, you can see multiv- views. It's just going to be like perpendicular lines rather than custom ones. So what I had to do instead is use render targets. This is a WebGL version, but I did it with TSL or WebGPU, but instead I used render targets. And then I used some custom shaders to make the line and blend between the render targets a little bit distorted and funny. Whereas you can't do that as easily with a scissors. There's like there are some methods where you can use a scissor test and a custom like stencil buffer to have like jaggedy edges, but it's not going to be as easy as using render targets. So if I come to my renderer folder over here, you can see that it's just some math like I have a slider and then based on how far I am from that slider coordinate which I got from my HTML element and my mouse events and mouse move or pointer as well. I calc do some distortion pretty much like this is all just random math that I generated with AI to give some sort of distortion around the slider. Other than that it's basically just the slider itself with some added distortion. Again, it looks really scary, but if you convert it into like blender nodes and things like that, it won't be as intimidating as it currently looks. So, the whole idea with the render target is you can imagine you have like two different scenes that you render to what they call offscreen textures and then you create a custom shader to determine how to to smash or composite those scenes together. Last thing I want to talk about are the image textures that you can easily replace later. Here you can see I no longer am attaching the export over here. I got rid of it. But you can see the project name is still there. And the thing with materials is that it actually keeps the space unlike mesh names. So when you search for it in like chat cafe and go to project, you can see that I'm actually searching it with the space over here. And then I'm just setting it into our assets. Right. So here I am now uploading my images and loading them here, which means we have to update our loaders and add a texture loader here. I'm doing this because I'm using WebGPU, but if you're on WebGL, you're going to use the image texture loader. And then you can imagine in resources we just add check the asset type and then you use the respective loader that we can later access and it just puts it in our items folder. So every time you want to load something new like maybe a cube texture you can do cube texture and so on so forth. I just want to recap on some of the ways that we accessed one file to another file. So one of them was to emit an event. Another one was to call a public method from another class. We did that with register objects in ray caster. Right in Chad and Cababie Bear Cafeway, we both called raycaster's public method of register objects. And then second, we also did something like refer to the variable itself through experience. Like when we were checking if it was mobile or not. Now speaking of mobile, I just want to talk about like sizes over here. If you want you can do something like you know if if window is like less than if window inner width is less than like 364 you can do like this emit and mobile and then obviously the converse of that is like this emit desktop and then you could listen to that throughout your application as well. I didn't have to do that cuz we only check things but like it's really really case by case basis. There's multiple methods to interact with each other. Now, when it comes to quoteunquote best practices generally in JavaScript, the way to denote like a private function is to give it an underscore. So, this populate intersect objects is a public method. So, you can leave it exposed like that. Well, I mean, they're technically all callable cuz in JavaScript it's not like strictly there. So, for everything that's like internal, you can label with an underscore. So, that's just like a naming convention thing. Although, now I think you can use the hash uh the hashtag to make a private method in JavaScript. Although I don't know how common it is. I usually still see the underscore. Typically though, this is why people prefer TypeScript. Okay, so now I just want to talk about what's next. We did the intro course, we did this course, and we know how to structure a little bit larger projects. What comes after? Well, if you want a very comprehensive course, I recommend doing 3JS Journey by Bruno Simon, which the code was based off. He's arguably the most famous 3GS developer right now in the entire world. So definitely check him out. He's got his own course and it's only $95 for a ton a ton of content. So, he's a very generous person. So, if you want to actually think about becoming a 3GS developer full-time career, maybe you can check that out. Or even if you're just a hobbyist, if you want to do a little bit more Blender stuff and like get into realism and know a little bit more about Blender shader nodes, you can check this one out. If you're more curious about going in shaders in general for WebGPU, you can check this out. Right now, it's still dominant with WebGL and GLSL resources. So, you'll definitely find more on WebGL, but if you want to go for the more futurist quote unquote futuristic stuff, you can check out that video. So, if you're more into shaders, you can go here. Although, this does provide somewhat of a prerequisite for this one as well. Just one section here, but but honestly, you might not need it depending on your level with Blender. But honestly, I don't actually recommend you go for any of these. Sorry, that I know that looks really bad. I honestly don't recommend you go for any next video. I actually recommend you try your own project and then just learn along the way and try and fail and try and fail. You might find those things in other videos and other courses, but but you can skim videos rather than just like watch them end to end and copy the idea. So just think of something you're really really passionate about and believe in yourself and do it. I'm sure I think you would have I think you have enough knowledge to do a lot of really really cool stuff. Uh, I know it'll be tough, but that's definitely my recommendation. But again, you know, you do you. You know how you learn best. And yeah, that's pretty much it for the video. Thanks very much for stopping by. And as always, take care, peace out, subscribe, and I hope to see you in my next video. Bye.

Generated algorithmically for Search Engine Indexing.

Summarize Another Video