ilo's - get reacquainted with
malloc(...)
- get acquainted with basic_draw(...)
, imgui_slider(...)
, and input
- get acquainted with data-oriented design
- challenge yourself
code reading a stretchy buffer, aka a dynamic array, is a useful data struture. it holds a pointer to a heap-allocated array (the internal array), and has a function to push back (append) new elements. as new elements are pushed back, the internal array automatically grows (stretches). a reasonable implementation might allocate an internal array of length 16 on the first push back, and then double the length of the internal array as needed.
std::vector
is one implementation of a stretchy buffer. here is another: http://nothings.org/stb_ds/
- the capacity is the length of the internal array (the total number of slots elements *could* fill)
- the length (aka size) of the stretchy buffer is the number of elements that are currently stored in the internal array (the number of slots that are actually filled)
- ✅ the capacity of a stretchy buffer is always greater than or equal its length
code hw note: a single build and run of your submission should demonstrate the functionality for all parts of the code hw (including any extra credit). please write your hw.cpp such that i can verify everything works without making edits to hw.cpp or having to build and run multiple times 🙂👍 note: i will sometimes give you the length of my solution to the problem as a hint. fewer lines is not necessarily better!
/* alright let's get crack-a-lackin'. to start, please clone a fresh copy of the current codebase. each week there will be updates and bugfixes to cow.cpp
and snail.cpp
, as well as an updated hw.cpp
with this week's documentation and starter code */
a. (20 pts) revisit hw0
spec
- draw the same red square outline from hw0a, only this time make it so you can also drag the vertices around
hint
- see the documentation forwidget_drag(...)
- the vertex positions will need to persist across frames. assuming you will store them in a fixed-size array (a fine choice, since there are only four vec2's to be stored), you can accomplish this either by declaring the array before the while loop, or by declaring the array to be static (locally-persistent) inside the while loop. as long as you understand what you're doing, either way is fine by me :)
- it may be easier to draw the red squre outline with basic_draw(...)
than with gl_begin(...)
, gl_end()
hint
- see the documentation forimgui_slider(...)
- it may be easier to draw the green circle outline with gl_begin(...)
, gl_end()
than with basic_draw(...)
basic_draw(primitive, ...)
draws first N vertices with color specified per-vertex in a given size (size is the same for all primitives)
- provide a slider to scrub through which primitive we're using to draw the data
- POINTS, LINES, LINE_STRIP, LINE_LOOP, TRIANGLES, TRIANGLE_STRIP, TRIANGLE_FAN, QUADS, TRIANGLE_MESH, QUAD_MESH
- hotkeys J and K to decrement and increment the primitive respectively
- slider should loop when using hotkeys (see documentation)
- provide a slider to scrub the number of vertices we're drawing
- (all the vertices will still "exist", but we will only draw the first N)
- the slider for N should range from 4 to 100,000
- when the app first starts, N is 12. (so i can see collision-handling is correct)
- provide a slider to scrub the size of the primitives from 0.0 to 50.0
- (no effect on TRIANGLES, TRIANGLE_STRIP, TRIANGLE_FAN, or QUADS)
- pause (optional; ungraded)
- pressing the P key should pause and unpause the simulation (you must still draw the simulation when paused)
- pressing the . key (period) while paused should step forward one frame
- the app runs at ~60fps in the Ward Lab while drawing all 100,000 vertices (this is just a sanity check on your implementation; makes sure you're not e.g. calling malloc every frame)
hints
- my solution is ~50 lines long and doesn't actually use anything from snail (you are more than welcome to use snail in your solution) - to handle collisions, first think about the problem in 1D - you definitely don't have to, but it is possible to draw the wireframe double unit box with a single somewhat bizarre call tobasic_draw(LINE_STRIP, ...)
std::vector
: implement a very basic stretchy buffer API that makes the usage code currently commented out in hw1b()
work
spec (enforced by ASSERT's)
- the stretchy buffer capacity (length of the internal array) should...
- start at 0
- be 16 after the first push_back
- double as needed
- freeing the stretchy buffer should...
- free the internal array
- set the pointer to the internal array to NULL
- set the length of the stretchy buffer to 0
- set the capacity to 0
note: workarounds that don't actually implement a working stretchy buffer will not score points
hints
- my solution is ~20 lines long - i implement -struct StretchyBuffer { ... };
- void sbuff_push_back(StretchyBuffer *buffer, vec2 point) { ... }
- void sbuff_free(StretchyBuffer *buffer) { ... }
- i use malloc
, realloc
, and free
std::vector
unless you've already done the stretchy buffer refactor extra credit; if you're feeling bold, use stb_ds 🚀
spec:
- left mouse click to start drawing stroke
- left mouse hold to continue drawing stroke
- left mouse release to finish drawing stroke
- press the X key on the keyboard to clear all strokes
- hints
- my solution is ~20 lines long - astd::vector<std::vector<vec2>>
may be convenient
- calling basic_draw(...)
once per stroke per frame is A OK
❗❗❗ please grab an updated copy of cow.cpp from the Github before attempting the extra credit (it has bugfixes that you'll need) 🤙. (1 pt) stretchy buffer refactor: templatize your stretchy buffer API so that you can create, e.g., a
StretchyBuffer<vec2>
or a StretchyBuffer<vec3>
. additionally, implement the method T &operator [](int index) { ... }
so you can access a stretchy buffer like you would a regular array; e.g., buffer[3] += buffer[4] * 2;
. if you like, you may also add a push_back(...)
method. congratulations, you have created a not-particularly-featureful replacement for std::vector
that you can actually inspect in a debugger 🎉 note: to score points for this question you must have usage code. i recommend writing the usage code first.
✏. (1 pt) annotation tool refactor: refactor your solution to hw1d()
into a function void widget_annotate(mat4 PV, vec3 color = monokai.blue) { ... }
that you can call in the while loop of any app to annotate that app. call it in hw1a()
to prove it works
video of correct behavior: https://www.youtube.com/embed/XV30COToGDM?rel=0
for full credit
- widget_annotate(...)
must always draw over everything (besides maybe the gui)
- it must play nicely with widget_drag(...)
- "you must not annotate if dragging, and not drag if you are annotating"
- this involves the global variable widget_active_widget_ID
- you may start drawing a new stroke if and only if it is 0
- when you start drawing a stroke, you must set it to WIDGET_ID_ANNOTATE
- you may continue drawing a stroke if and only if it is WIDGET_ID_ANNOTATE
- you may finish drawing a stroke if and only if it is WIDGET_ID_ANNOTATE
- when you finish drawing a stroke, you must set it back to 0
example usage code: if (input.key_toggle['z']) { widget_annotate(); }
🧁. (up to 3 pts) vaporwave terrain flyover: using what we learned this week, make something like this https://youtube.com/embed/8kIMjwWdYz4?rel=0
hint: the line imgui_readout("camera", &camera);
may help you line up your camera
optional soundtrack