3d content creation tools don't have to be complex- sometimes what you need is a special-purpose tool that solves a specific problem and gives you exactly the model you need. Building such tools using modern web technologies is easier than you think. In this talk, we'll walk through the basics of writing a custom web-based tool that can export 3d models.
Creating Custom CAD Tools on the Web with ThreeJS
AI Generated Video Summary
Today we're going to be talking about creating custom CAD tools on the web with 3JS. We'll explore the reasons why you should make web-based tools, including their novice-friendly nature and their suitability for user-generated content. We'll learn how to create custom and parametric geometry using Three.js, set up geometry and material in Three.js, and improve visibility by adding normals to the geometry.
1. Introduction to Creating Custom CAD Tools with 3JS
Today we're going to be talking about creating custom CAD tools on the web with 3JS. We'll explore the reasons why you should make web-based tools, including their novice-friendly nature and their suitability for user-generated content. I'll also share an example of a tool I made using Three.js, a fantastic library that simplifies the process of creating 3D models.
♪♪ Hi, everybody. My name is Adrian Herbez and today we're going to be talking about creating custom CAD tools on the web with 3JS. I'm a web developer and a game developer and I also make toys, 3D printed stuff mainly for action videos. As such, I've used a lot of 3D tools over the years and I generally love them all except they're really complicated. So I think you should make some new ones.
Now why would you want to do that? There's a lot of reasons, but the most significant ones to me are that you can make truly novice-friendly tools and you can make tools that are great for user-generated content. It's also not as hard as you might think. So why make a web-based tool? Well, because the web's the best platform. It's easy to distribute, it's inherently cross-platform, and again, this all goes to being very approachable for novices.
So here's an example of the kind of thing I mean. I wanted to make it easy to make Reebly's, sci-fi panels of stuff, and so I made this tool that runs in a browser and instead of moving points or vertices or polygons around, you just draw a rectangle on the surface of the base shape and you can add a parametric feature of a few different types. You can add extrusions, you can add arrays of buttons, you can add handles of the type you might see on rack-mounted equipment, and you can add dials. And none of this requires any conventional 3D modeling skills, so it's very approachable. And I made this with Three.js. Three.js is a fantastic library. It's a wrapper around WebGL. It's got a lot of great qualities. It's been around for about 13 years, so it has a really rich ecosystem. It works well with React and other frameworks. And I, at least personally, feel it has a great level of abstraction.
2. Creating a Cube and Defining Geometry
Today we're gonna make a cube and learn how to create custom and parametric geometry using Three.js. Geometry consists of vertices and faces, which allow us to build shapes. We'll start by ordering the vertices and specifying their positions. Then, we'll set up our vertex data using a one-dimensional array. Finally, we'll define the faces by referencing the vertices in the desired order.
So today we're gonna make a cube and we're gonna take this sort of slightly roundabout way to get there, but in doing so, we'll learn everything we need to know to make custom and parametric geometry of your own.
But before that, there is a little bit of boilerplate. So I know this code is probably too small to see. I'll have a link to a GitHub repo at the end, but what I wanna impress upon you is this is all that you need to set up a scene in Three.js. There's only about 30 lines here. It's all very straightforward.
So having gotten that out of the way, let's talk about geometry. So geometry can contain a lot of data, but the two most important types are vertices, so points that are positioned in 3D space, and then faces, surfaces that link those points together. There's a lot of other stuff that we won't have time to talk about today, but with vertices and faces we can make geometry.
So here's a cube. Let's take away its skin, and we'll see that we have eight points. So let's start by giving those points an order. So we'll just give them some numbers, zero to seven, to lay them out in some order. And then once we've done that, let's specify where they are in space. So to keep it simple, we're just using negative ones and ones, that'll give us a two unit cube centered at the origin, which is great. So having done all that, we can specify our vertex data. So to do that, I'm just gonna set up an array and I'm gonna push numbers into it for the X, Y, and Z coordinates of each vertex. Note that this is a one dimensional array. I'm not pushing a full set of a triplet at a time, and this just ends up being one dimensional, which is important later.
So that's our vertex data. Now we're ready to set up our faces. So faces are specified by giving a sequence of vertices in order. So what we do is instead of having actual data, we just reference the data that already exists in the vertex list. So the way the order in which we specify the vertices matters a lot. So in 3D graphics, a big part of getting things to be performant is to not draw things you don't need to see. And one of the ways that we do that is by only drawing one side of faces by default. So faces only really exist from one direction. And the way we specify which direction that is is the order in which we specify the vertices. So it's important that we specify our vertices in counter-clockwise order. So this blue triangle, for example, involves the vertices zero, four, and three.
3. Setting up Geometry and Material in Three.js
To ensure the correct rendering of faces in 3D graphics, we need to specify them in counter-clockwise order. This can be done by entering three integers as indices into the vertex data for each triangle. Once the face data is specified, we can set up the geometry in Three.js. Objects in Three.js consist of geometry and a material. In this case, we're using a mesh basic material with a bright green color. For efficient data transfer from the CPU to the GPU, we use buffer geometry and fill buffers with typed binary data. For vertices, we use float32 as the data type, representing the position in three-dimensional space.
But if we specify them in zero, four, three order, that would be clockwise. And this face would end up pointed in the cube instead of toward the outside of the cube. And we'd end up not seeing anything and wondering why nothing's rendering, which is a very standard state in 3D graphics.
So instead, we need to make sure we specify them in counter-clockwise order, like three, four, zero. So having said that, we can specify our face data. So we start again with just an array. And for each triangle, we enter three integers that are indices into the vertex data. So we just do that once for every triangle we have.
And with that, we're ready to actually set up some geometry in Three.js. So objects in Three.js always consist of geometry and a material. So let's set that up. So we're gonna use a mesh basic material. There's a lot of different material types in Three.js. This is the simplest one, but they all work in a similar way in that you make them and pass in an object with parameters. So in this case, we're just passing in a color, bright green, and for geometry, we're gonna use a buffer geometry.
Now there is also just a vanilla geometry in Three.js, but that's a little outdated and it's a little less efficient. So I want all the all to be set up really well for the future. And so we're gonna use a buffer geometry, which means we have to fill buffers. So what a buffer is is it's typed binary data that can be used to efficiently move data from the CPU to the GPU. So for vertices, we wanna use float32 as our data type. Because we're talking about floating point numbers representing the position in three-dimensional space. So we first make a float32 array from our vertices. Then we make a new buffer attribute. And this may look a little cryptic. We pass in the vertex data, right? But then we pass in this three. And what that has to do with is the buffer attributes are always on a per vertex basis. So as we pass in data, we have to specify how many numbers or how fast we consume data per vertex. So in this case, it's position data. So we have an X, a Y, and a Z. So we have three numbers per vertex.
4. Setting up Geometry and Normals in Three.js
We set the buffer attribute as the position and define the indices. With the geometry set up, we can improve visibility by adding normals. Normals are vectors that point away from a surface and can be computed by Three.js. Using a mesh normal material, we can assign colors to the vectors, resulting in better detail of the geometry.
So we pass in three. Now that we've got the buffer attribute, we set the attribute as the position. And now we're ready for the indices, which is similar, except we use an unsigned int instead. And when we make the buffer attribute, this is one index per vertex, so we pass in one. And then we set index. And with that, we have geometry.
But it's a little hard to see, because it's all just bright green. So we can fix that really quick with normals. So what's a normal? A normal is a vector that points away from a surface. You can have vertex normals like on the left or face normals like on the right. And you can pass this in as buffer attributes, but we'll do it a little easier, and then we'll just have Three.js compute them for us with compute vertex normals. And once we've done that, we can use a mesh normal material, which works by taking each vector, a normal is a vector with an x, y, and z component. And if we interpret the x, y, and z as an RGB value, we get colors. And with those colors, we can get something that we can see and get a lot better detail of the geometry that we've made. And that's it.