# Making Worlds Feel Alive with Procedural Systems in Blender ## Abstract This hands-on class is focused on progressively building a simple space traffic system, using proceduralism. The two main building blocks of this system are the spaceship generator and the traffic paths. The goal of the system is to provide a believable behavior for the ships, making the background of a scene feel alive. The spaceship generator will use procedural geometry modifications to achieve a variety of sci-fi looking vessels, using common geometry manipulation techniques, and simple shapes using boolean operations. The traffic paths will be a simulation driven particle system, exposing parameters such as density, speed, etc. While the spaceship system will be used as a practical example, the concepts are more generally applicable to similar scenarios. There is no requirement of previous experience with the Geometry Nodes system, while a base level familiarity with Blender is encouraged to follow along. ## Files [Files are here.](https://drive.google.com/drive/folders/192oUAFUdoQRXgPXEu4dJAbJtnICpgiAm?usp=sharing) ## Shortcuts Cheatsheet - Move selected nodes: `G` - Viewer: `CTRL + SHIFT + LMB` - Add node: `SHIFT + A` -> search - Add node from socket: `LMB drag + let go` -> search - Mute node: `M` - Frame: `F` - Rename: `F2` - Add reroute node: `SHIFT + RMB drag` - Cut links: `CTRL + RMB drag` - Mute links: `CTRL + ALT + RMB drag` - Toggle Maximize Editor: `CTRL + Spacebar` ## STEP by STEP guide ### I. Spaceship Generator ![spaceship_generator-target](https://hackmd.io/_uploads/rktoZGcDll.png) #### 1. Base Layer ![image](https://hackmd.io/_uploads/HJHOfM5wxe.png) ![image](https://hackmd.io/_uploads/BymMefcPxx.png) <details> - scatter points on base plane (`Distribute Points on Faces`) - create collection with set of base meshes - instance base meshes on the generated points (`Collection Info`, `Instance on Points`) - enable `Separate Children` and `Pick Instance` - merge instances into one mesh (`Mesh Boolean` - Union, Manifold) - expose `Seed` as modifier input (drag link and search for `Group Input`) </details> #### 2. Rotate + Bisect ![image](https://hackmd.io/_uploads/Byyoyz9Pee.png) <details> - after Base Layer - rotate randomly (`Transform Geometry`; `Random Value` - Vector) - use `Seed` input from modifier as `ID` to generate single value - create cube mesh for intersection (`Cube` - size 200; `Transform Geometry` - `X = -100`) - generate difference mesh ('Mesh Boolean' - Difference, Manifold) - delete inside faces (`Delete Geometry` - Face; `Capture Attribute` - Face) - Capture a `True` boolean on the cube before intersectionand use the captured result as selection for deleting faces after </details> #### 3. Mirror ![image](https://hackmd.io/_uploads/H1WMkfcvxx.png) <details> - at the end of the node-tree - flip mesh on X axis (`Transform Geometry` - `Scale = [-1,1,1]`) - join with original (`Join Geometry`) </details> #### 4. Subtraction Layer ![image](https://hackmd.io/_uploads/rJWFefcPgl.png) <details> - after the Base Layer, before Rotate + Bisect - scatter base meshes on base layer mesh (`Distribute Points on Faces`; `Instance on Points`) - use same instances as before - enable `Pick Instance` as before - subtract from base layer mesh (`Mesh Boolean` - Difference, Manifold) </details> #### 5. Greeble ![image](https://hackmd.io/_uploads/HkOcCWqPle.png) <details> - after Rotate + Bisect - extrude random faces (`Extrude Mesh` - Offset = 0; `Random Value` - Boolean, Probability = 0.5) - scale extruded faces down (`Scale Elements` - Scale = 0.9) - `Top` as selection - extrude again with random offset (`Extrude Mesh`; `Random Value`) - previous `Top` as selection - random value as `Offset Scale` - range negative to positive (e.g. 0.05) - repeat whole process - add repeat zone - integrate geometry data flow into the repeat zone - use repeat iteration index as Seed for randomization - don't use side faces for further extrusion - `Side` from 1st extrusion `Or` `Side` from 2nd extrusion - `Not` - `And` - pass result into new repeat output - make that repeat input `True` and pass into second `And` input </details> #### 6. Wires ![image](https://hackmd.io/_uploads/SJNyWzcDlx.png) <details> - after Rotate + Bisect in parallel to Greeble - generate convex hull (`Convex Hull`) - convert to curves (`Mesh to Curve`) - create tubes from curves (`Curve to Mesh` - Scale = 0.002; `Curve Circle` - Resolution = 4) - circle as profile curve - add sagging deformation - `Subdivide Curve` between `Mesh to Curve` and `Curve to Mesh` (Cuts = 20) - Deform using `Set Position` - `Spline Parameter` - `Factor` into `Float Curve` into `Combine XYZ` - `Z` into `Offset` - float curve points: `[[0,0], [0.5, 0.25], [1, 0]]` - `Scale` the offset vector by `-0.5` - turn each edge into its own curve (`Duplicate Elements` - Edge) - use a random selection (`Random Value` - Boolean, Probability = 0.5) - `Scale` the sagging offset vector by `Spline Length` - assign different materials to main and wires geometry - join with main mesh (`Geometry to Instance`) </details> ### II. Spaceship Zoo ![image](https://hackmd.io/_uploads/ryO1Bv9Dll.png) <details> - create `Grid` - generate spaceship for each point of the grid - add `For Each Element` zone - connect grid to zone input - add spaceship generator node-group in zone and connect to group geometry input and zone geometry ouput - connect zone index to seed - move each to its grid point position - convert `Geometry to Instance` - `Transform Geometry` of instance - add `Position` to zone inputs - use position of the point inside the zone for the translation - make seed dependant on modifier input - add `Hash Value` node to zone `Index` - expose `Seed` input to group inputs - name each spaceship geometry by its used seed - convert hashed `Value to String` - `Set Geometry Name` from that string - `Join Strings` with new `String` ('Spaceship ') - store used seed as attribute - pass hash result as attribute on zone output geometry - set attribute domain to `Instance` in zone node properties - add `Store Named Attribute` (Integer, Instance) outside of zone - connect hash attribute to value - give attribute name 'seed' </details> ### III. Spaceship Swarm ![spaceship_swarm-target](https://hackmd.io/_uploads/Sk064qkdle.png) #### 0. Starting Curve ![image](https://hackmd.io/_uploads/Bkc2ock_gl.png) <details> - create curve - set cyclic - set resolution to `64` </details> #### 1. Create Points ![image](https://hackmd.io/_uploads/BkKbm5kuxl.png) <details> - `Points` node - expose `Count` - randomize `Set ID` - `Index` into `Hash Value` - `Sample Curve` for random position per point - `Random Value` into `Factor` - `Position` into `Points` `Position` - expose `Seed` </details> #### 2. Instancing ![image](https://hackmd.io/_uploads/r1r9kjJ_eg.png) <details> - get input instances - `Object Info` of spaceship object - extract individual spaceship instances - `Separate Components` - `Instances` - reset instance positions - `Set Position` to `Vector` [0,0,0] - `Instance on Points` - `Pick Instances` = `True` - `Align Rotation to Vector` - `Y` into `Rotation` - use sampled curve `Tangent` as alignment `Vector` (for now) </details> #### 3. Basic Simulation Setup ![image](https://hackmd.io/_uploads/HJmurqkdex.png) <details> - add `Simulation` zone - add `Set Position` node - add `Velocity` vector to simulation - `Scale` velocity by `Delta Time` and use as `Offset` - accelerate every frame with constant force - `Add` to velocity and pass it to the simulation output - `Scale` constant `Vector` by `Delta Time` and plug into `Add` </details> #### 4. Curve Sampling ![image](https://hackmd.io/_uploads/H1bIbcJOxe.png) <details> - `Sample Index` - `Vector` on curve - `Curve to Points` - `Evaluated` - `Sample Nearest` as `Index` for `Sample Index` - `Tangent` as `Value` input - duplicate to sample `Position` as well </details> #### 5. Speed Alignment ![image](https://hackmd.io/_uploads/ByMzr9kOel.png) <details> - `Scale` sampled curve tangent randomly - `Random Value` between `20` and `50` as different target speed per point - `Mix` (`Factor` = `.1`) into `Velocity` to align to target speed along curve </details> #### 6. Curve Attraction ![image](https://hackmd.io/_uploads/SJae89J_el.png) <details> - `Subtract` `Position` from Nearest Curve Position - `Scale` by its `Length` to the `Power` of `2` - use result as acceleration force </details> #### 7. Evade Neighbors ![image](https://hackmd.io/_uploads/H1By8qJdel.png) <details> - duplicate 'Curve Attraction' setup - instead of nearest curve tangent, use `Evaluate at Index` of `Position` at `Index of Nearest` - invert strength falloff - `Divide` `25` by (existing) `Length` - `Add` result to total forces </details> #### 8. Clamping ![image](https://hackmd.io/_uploads/S10THqkdll.png) <details> - `Normalize` total force vector - `Scale` by its `Length` clamped by `Smooth Minimum` with `100` </details>