Skip to content

CIS-566-Fall-2021/hw04-l-systems

Repository files navigation

Homework 4: L-systems

For this assignment, you will design a set of formal grammar rules to create a plant life using an L-system program. Once again, you will work from a TypeScript / WebGL 2.0 base code like the one you used in homework 0. You will implement your own set of classes to handle the L-system grammar expansion and drawing. You will rasterize your L-system using faceted geometry. Feel free to use ray marching to generate an interesting background, but trying to raymarch an entire L-system will take too long to render!

Base Code

The provided code is very similar to that of homework 1, with the same camera and GUI layout. Additionally, we have provided you with a Mesh class that, given a filepath, will construct VBOs describing the vertex positions, normals, colors, uvs, and indices for any .obj file. The provided code also uses instanced rendering to draw a single square 10,000 times at different locations and with different colors; refer to the Assignment Requirements section for more details on instanced rendering. Farther down this README, we have also provided some example code snippets for setting up hash map structures in TypeScript.

Assignment Requirements

  • (15 points) Create a collection of classes to represent an L-system. You should have at least the following components to make your L-system functional:

    • A Turtle class to represent the current drawing state of your L-System. It should at least keep track of its current position, current orientation, and recursion depth (how many [ characters have been found while drawing before ]s)
    • A stack of Turtles to represent your Turtle history. Push a copy of your current Turtle onto this when you reach a [ while drawing, and pop the top Turtle from the stack and make it your current Turtle when you encounter a ]. Note that in TypeScript, push() and pop() operations can be done on regular arrays.
    • An expandable string of characters to represent your grammar as you iterate on it.
    • An ExpansionRule class to represent the result of mapping a particular character to a new set of characters during the grammar expansion phase of the L-System. By making a class to represent the expansion, you can have a single character expand to multiple possible strings depending on some probability by querying a Map<string, ExpansionRule>.
    • A DrawingRule class to represent the result of mapping a character to an L-System drawing operation (possibly with multiple outcomes depending on a probability).
  • (10 points) Set up the code in main.ts and ShaderProgram.ts to pass a collection of transformation data to the GPU to draw your L-System geometric components using instanced rendering. We will be using instanced rendering to draw our L-Systems because it is much more efficient to pass a single transformation for each object to be drawn rather than an entire collection of vertices. The provided base code has examples of passing a set of vec3s to offset the position of each instanced object, and a set of vec4s to change the color of each object. You should at least alter the following via instanced rendering (note that these can be accomplished with a single mat4):

    • Position
    • Orientation
    • Scaling
  • (55 points) Your L-System scene must have the following attributes:

    • Your plant must grow in 3D (branches must not just exist in one plane)
    • Your plant must have flowers, leaves, or some other branch decoration in addition to basic branch geometry
    • Organic variation (i.e. noise or randomness in grammar expansion and/or drawing operations)
    • The background should be a colorful backdrop to complement your plant, incorporating some procedural elements.
    • A flavorful twist. Don't just make a basic variation of the example F[+FX]-FX from the slides! Create a plant that is unique to you. Make an alien tentacle monster plant if you want to! Play around with drawing operations; don't feel compelled to always make your branches straight lines. Curved forms can look quite visually appealing too.
  • (10 points) Using dat.GUI, make at least three aspects of your L-System interactive, such as:

    • The probability thresholds in your grammar expansions
    • The angle of rotation in various drawing aspects
    • The size or color or material of the plant components
    • Anything else in your L-System expansion or drawing you'd like to make modifiable; it doesn't have to be these particular elements
  • (10 points) Following the specifications listed here, create your own README.md, renaming the file you are presently reading to INSTRUCTIONS.md. Don't worry about discussing runtime optimization for this project. Make sure your README contains the following information:

    • Your name and PennKey
    • Citation of any external resources you found helpful when implementing this assignment.
    • A link to your live github.io demo (refer to the pinned Piazza post on how to make a live demo through github.io)
    • An explanation of the techniques you used to generate your L-System features. Please be as detailed as you can; not only will this help you explain your work to recruiters, but it helps us understand your project when we grade it!

Writing classes and functions in TypeScript

Example of a basic Turtle class in TypeScript (Turtle.ts)

import {vec3} from 'gl-matrix';

export default class Turtle {
  constructor(pos: vec3, orient: vec3) {
    this.position = pos;
    this.orientation = orient;
  }

  moveForward() {
    add(this.position, this.position, this.orientation * 10.0);
  }
}

Example of a hash map in TypeScript:

let expansionRules : Map<string, string> = new Map();
expansionRules.set('A', 'AB');
expansionRules.set('B', 'A');

console.log(expansionRules.get('A')); // Will print out 'AB'
console.log(expansionRules.get('C')); // Will print out 'undefined'

Using functions as map values in TypeScript:

function moveForward() {...}
function rotateLeft() {...}
let drawRules : Map<string, any> = new Map();
drawRules.set('F', moveForward);
drawRules.set('+', rotateLeft);

let func = drawRules.get('F');
if(func) { // Check that the map contains a value for this key
  func();
}

Note that in the above case, the code assumes that all functions stored in the drawRules map take in no arguments. If you want to store a class's functions as values in a map, you'll have to refer to a specific instance of a class, e.g.

let myTurtle: Turtle = new Turtle();
let drawRules: Map<string, any> = new Map();
drawRules.set('F', myTurtle.moveForward.bind(myTurtle));
let func = drawRules.get('F');
if(func) { // Check that the map contains a value for this key
  func();
}

TypeScript's bind operation sets the this variable inside the bound function to refer to the object inside bind. This ensures that the Turtle in question is the one on which moveForward is invoked when func() is called with no object.

Examples from previous years (Click to go to live demo)

Andrea Lin:

Ishan Ranade:

Joe Klinger:

Linshen Xiao:

Useful Resources

Extra Credit (Up to 20 points)

  • For bonus points, add functionality to your L-system drawing that ensures geometry will never overlap. In other words, make your plant behave like a real-life plant so that its branches and other components don't compete for the same space. The more complex you make your L-system self-interaction, the more points you'll earn.
  • Any additional visual polish you add to your L-System will count towards extra credit, at your grader's discretion. For example, you could add animation of the leaves or branches in your vertex shader, or add falling leaves or flower petals.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published