setzer22 / blackjack

A procedural, node-based modelling tool, made in rust 🦀
Mozilla Public License 2.0
1.42k stars 64 forks source link

More geometry primitives #60

Open HenryWConklin opened 2 years ago

HenryWConklin commented 2 years ago

A few common shapes are bit tricky to create with the existing nodes, it would be nice to have more built-in geometry primitives. Matching the primitives in Blender would be a good starting point.

setzer22 commented 2 years ago

I agree, all these nodes would be quite useful :smile:. Is this something you'd like to tackle? I can offer some guidance. Otherwise I can look at this after I get the next big feature merged.

HenryWConklin commented 2 years ago

Yeah, I'm interested in giving it a try. Seemed like a good starting project. I'd definitely appreciate some pointers if you have the time. Any similar stuff you think is worth adding to the list?

setzer22 commented 2 years ago

Great! :smile: Primitives are one of the most approachable bits of the codebase, so it's indeed a great way to get to know more about the project.

If you want to define new mesh primitives, you can find the implementation in src/mesh/halfedge/primitives.rs. From there, you will see the structure of the file is pretty self-explanatory:

  1. You should make one new struct for each new primitive kind (e.g. Cylinder)
  2. Then, in an impl block, implement constructor methods for that primitive. The implementation doesn't (necessarily) have to delve into the more gnarly details of the HalfEdgeMesh structure, because most primitives can be constructed via two buffers (vertex positions and polygon indices). As you will see, most operations work like that, and use the HalfEdgeMesh::build_from_polygons(vertices, polygons) to build a mesh in Blackjack's mesh format.
  3. Once you have your constructor, you need to add its Lua binding. If you scroll to the bottom of the file you'll see several examples. Normally, the only thing to bear in mind is that you need to wrap Vec3 in LVec3 in function signatures.
  4. After you do this, your function will be callable from Lua (high-level glue logic for the nodes is defined in Lua).
  5. Look for the file blackjack_lua/run/core_nodes.lua, and look for the line where local primitives is defined, you can define new nodes at the bottom of that list. You should be able to follow the other examples from the list. You'll see most nodes immediately delegate to the Rust implementation. But sometimes you can have a bit of Lua logic to do dynamic stuff (like have a node with configuration options, and call different Rust functions depending on the values).

Once you define your node in Lua, you should be able to add it to a graph using the UI. A tip for quicker development is that the Lua code hot-reloads from disk, so once you're defining the nodes in Lua, you don't need to close blackjack to introduce new node definitions, just save the file and it will pick it up (you will also see any errors on the terminal). However, if you change a node's parameters you will have to delete that node and re-add it again (This is a limitation I just fixed on my dev branch, btw :smile:).

Filled in circle/regular N-gon (possibly as an option on the Circle node)

Yes! In fact, you'll see the Circle primitive already has the two variants defined in Rust, you just need to change the node a bit to handle that. A good example to look at here would be the Subdivide node, which exposes an enum parameter and calls different Rust functions based on that. This will show on the UI as a dropdown menu.

Any similar stuff you think is worth adding to the list?

I'm out of ideas at the moment :thinking:, but I will post if I think of something!

Good luck! And make sure to post any doubts here. I'd be happy to help :)

HenryWConklin commented 2 years ago

Thanks! That was really helpful. It would be worth copying that to a doc somewhere as an example of how to add new nodes.

setzer22 commented 2 years ago

Thanks a lot for the PR! Let's keep the issue open until we get a few more primitives :smile:

Thanks! That was really helpful. It would be worth copying that to a doc somewhere as an example of how to add new nodes.

Will do! Let me find a good place for this...