From 9c95672beeb0c34104c687c78343f837b0ed0f03 Mon Sep 17 00:00:00 2001 From: HumanoidSandvichDispenser Date: Sun, 15 Feb 2026 23:39:05 -0800 Subject: [PATCH] Add AS2 --- as2/CMakeLists.txt | 16 ++++++ as2/README.md | 65 +++++++++++++++++++++++ as2/as2.cpp | 126 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 207 insertions(+) create mode 100644 as2/CMakeLists.txt create mode 100644 as2/README.md create mode 100644 as2/as2.cpp diff --git a/as2/CMakeLists.txt b/as2/CMakeLists.txt new file mode 100644 index 0000000..8f364af --- /dev/null +++ b/as2/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.18) +project(as2 CXX) + +set(CMAKE_CXX_STANDARD 20) + +# adding this option to make clangd work +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +add_subdirectory(../raylib-cpp raylib) + +add_executable(as2 as2.cpp) +target_link_libraries(as2 PUBLIC raylib raylib_cpp) + +configure_file(../assets/models/penguin.glb models/penguin.glb COPYONLY) +configure_file(../assets/models/eagle.glb models/eagle.glb COPYONLY) + diff --git a/as2/README.md b/as2/README.md new file mode 100644 index 0000000..d0abf19 --- /dev/null +++ b/as2/README.md @@ -0,0 +1,65 @@ +# Building and Running + +Clone the repository, navigate to the root of the project, and initialize the +submodules: + +```sh +git clone https://github.com/humanoidsandvichdispenser/cs381.git +cd cs381 +git submodule update --init --recursive +``` + +Navigate to the `as2` directory, create a build directory, and run CMake to +generate the build files: + +```sh +cd as2 +mkdir -p build +cd build +cmake .. +``` + +Compile the code using `make`: + +```sh +make +``` + +This should create an executable named `as2` in the `build` directory. You can +run the executable with the following command: + +```sh +./as2 +``` + +# Extra credit + +The models spin at a rate of 10 degrees per second. + +# Readme Question + +The `DrawBoundedModel` function takes a model and transformer lambda function +as parameters. We use this because the lambda defines the transformation for +this particular draw call without affecting the model's original transformation +matrix. This allows us to apply different transformations to the same model +when drawing it multiple times. It also consistently draws the model with a +bounding box that respects the transformations. The function does both of these +operations atomically, so you don't have to worry about forgetting to apply the +transform or draw the bounding box separately. + +The same function could be used in the future after the model has been loaded +to apply transforms that are not known at load time and temporary transforms +that only apply under specific conditions. For example, we could make the model +spin by constantly applying a transformation on the identity transform with a +rotation value that changes over time that only applies if a specific key is +being held. + +The same function can also be used for transforms relative to the parent. In +the lambda, we could multiply the transform passed to the function by a parent +transform to get a final transform that is relative to the parent. + +The leftmost penguin looks funny because the model only has half of the +penguin. In its default orientation, the faces of the model are away from the +camera (the surface normal points away), so the back faces are culled and the +model almost appears invisible. When the model is rotated, the faces are no +longer culled and the model looks normal. diff --git a/as2/as2.cpp b/as2/as2.cpp new file mode 100644 index 0000000..8b36dd4 --- /dev/null +++ b/as2/as2.cpp @@ -0,0 +1,126 @@ +#include "Color.hpp" +#include "Keyboard.hpp" +#include "Matrix.hpp" +#include "Model.hpp" +#include "raylib.h" +#include +#include + +static bool scaleModel = false; + +void DrawBoundedModel(raylib::Model &model, auto transformer) { + // store the original transform to apply a different transform to the + // model without affecting the next time we draw + raylib::Matrix oldTransform = model.GetTransform(); + + // apply the transform that we get from whatever the transformer callback + // gives us + raylib::Matrix transform = transformer(model.GetTransform()); + + // default scale doesn't render correctly, so scale the model by 10 if the + // spacebar is pressed + if (scaleModel) { + transform = transform.Scale(10); + } + + // apply the transform that we got from the transformer to the model + model.SetTransform(transform); + + // draw the model, passing the origin and default scale as arguments since + // the transform is already applied to the model + model.Draw({ 0, 0, 0 }, 1.0f, raylib::Color::White()); + + // get the bounding box of the model after applying the transform + auto box = model.GetTransformedBoundingBox(); + + // draw the bounding box of the model using raylib's built in function + DrawBoundingBox(box, raylib::Color::White()); + + // restore the model's transform to its original state so that the next time we + // draw the model, it doesn't have the previous transform applied to it + model.SetTransform(oldTransform); +} + +int main() { + raylib::Window window(800, 600, "CS381 - Assignment 2"); + window.SetState(FLAG_WINDOW_RESIZABLE); + + raylib::Model penguin("models/penguin.glb"); + raylib::Model eagle("models/eagle.glb"); + + raylib::Camera3D camera( + { 0, 120, 500 }, + { 0, 0, -1 }, + { 0, 1, 0 }, + 45.0f); + + window.SetTargetFPS(60); // save cpu cycles + + while (!window.ShouldClose()) { + window.BeginDrawing(); + + window.ClearBackground(raylib::Color::Gray()); + camera.BeginMode(); + + if (raylib::Keyboard::IsKeyPressed(KEY_SPACE)) { + scaleModel = !scaleModel; + } + + // in addition to the required transforms, the models will rotate 10 + // degrees per second (extra credit), and the penguin is scaled by 30 + // eagle scaled by 10 + + // Draw one eagle located at (0, 0, 0), default scale, and with default + // orientation (10 points). + DrawBoundedModel(eagle, [](raylib::Matrix transform) { + return transform + .RotateY(GetTime() * raylib::Degree(10.0f)) + .Scale(10.0); + }); + + // Draw one eagle located at (−100, 100, 0), scaled by (1, −1, 1), and + // yawed by 180 degrees (10 points). + DrawBoundedModel(eagle, [](raylib::Matrix transform) { + return transform + .Translate(-100.0f, 100.0f, 0.0f) + .Scale(1.0f, -1.0f, 1.0f) + .RotateY(raylib::Degree(180.0f)) + .Scale(10.0) + .RotateY(GetTime() * raylib::Degree(10.0f)); + }); + + // Draw one penguin located at (−200, 0, 0) with default orientation (10 + // points). + DrawBoundedModel(penguin, [](raylib::Matrix transform) { + return transform + .Translate(-200.0f, 0.0f, 0.0f) + .Scale(30.0f); + }); + + // Draw one penguin located at (200, 0, 0) and be yawed 90 degrees (10 + // points). + DrawBoundedModel(penguin, [](raylib::Matrix transform) { + return transform + .Translate(200.0f, 0.0f, 0.0f) + .RotateY(raylib::Degree(90.0f)) + .Scale(30.0) + .RotateY(GetTime() * raylib::Degree(10.0f)); + }); + + // Penguin 3: at (100,100,0), scaled by (1,2,1) and yawed 270 degrees + DrawBoundedModel(penguin, [](raylib::Matrix transform) { + return transform + .Translate(100.0f, 100.0f, 0.0f) + .Scale(1.0f, 2.0f, 1.0f) + .RotateY(raylib::Degree(270.0f)) + .Scale(30.0) + .RotateY(GetTime() * raylib::Degree(10.0f)); + }); + + camera.EndMode(); + + window.EndDrawing(); + } + + return 0; +}