#include "AudioDevice.hpp" #include "Color.hpp" #include "Keyboard.hpp" #include "Matrix.hpp" #include "Mesh.hpp" #include "Model.hpp" #include "RadiansDegrees.hpp" #include "raylib.h" #include #include #include #include #include #include #include #include #include #define SKYBOX_IMPLEMENTATION #include "skybox.hpp" 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()); // 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); } class Component { public: struct Entity *e; virtual void Setup() = 0; virtual void Update(float dt) = 0; virtual void Cleanup() = 0; }; struct Entity { std::vector> components; template T> T &AddComponent() { auto out = components.emplace_back(std::make_shared()); out->e = this; return out; } template T> std::optional> GetComponent() { for (auto &c : components) { T *cast = dynamic_cast(c.get()); if (cast) { return *cast; } } return { }; } }; struct TransformComponent : public Component { raylib::Transform t; void Setup() override { } void Update(float dt) override { } void Cleanup() override { } }; struct DrawModelComponent : public Component { raylib::Model *model; void Setup() override { } void Update(float dt) override { //DrawBoundedModel(model, []()); } void Cleanup() override { } }; raylib::Degree angle_normalize(raylib::Degree angle) { float decimal = float(angle) - int(angle); int normalized = (int(angle) % 360 + 360) % 360; return raylib::Degree(normalized + decimal); } int main() { raylib::Window window(800, 600, "CS381 - Assignment 3"); window.SetState(FLAG_WINDOW_RESIZABLE); raylib::AudioDevice audio; raylib::Model penguin("models/penguin.glb"); // behind and above the penguin (in penguin-local space) const float CAM_DIST = 512.0f; const float CAM_HEIGHT = 256.0f; const float CAM_ANGULAR_VELOCITY = 2.0f; const float CAM_PITCH_MIN = -0.5f; const float CAM_PITCH_MAX = 1.5f; float camYaw = 3.14f; // offset by 90 deg so it faces in the proper direction float camPitch = 0; raylib::Camera3D camera( { 0, CAM_DIST * std::sin(camPitch), CAM_DIST * std::cos(camPitch) }, { 0, 0, 0 }, { 0, 1, 0 }, 45.0f); raylib::Model ground = raylib::Mesh::Plane(10000, 10000, 50, 50, 25); raylib::Texture snowTexture("textures/snow.jpg"); ground.GetMaterials()[0].maps[MATERIAL_MAP_DIFFUSE].texture = snowTexture; cs381::SkyBox skybox("textures/skybox.png"); std::vector entities; Entity &e = entities.emplace_back(); e.AddComponent(); e.AddComponent(); // penguin physics raylib::Vector3 position = { 0, 0, 0 }; raylib::Vector3 velocity = { 0, 0, 0 }; float heading = 0.0f; float speed = 0.0f; // units/s const float ACCELERATION = 100.0f; // in radians const float ANGULAR_VELOCITY = 3.14f; window.SetTargetFPS(60); // save cpu cycles while (!window.ShouldClose()) { window.BeginDrawing(); window.ClearBackground(raylib::Color::Gray()); float dt = window.GetFrameTime(); position += velocity * dt * 0.5f; // movement for penguin if (IsKeyDown(KEY_W)) { speed += ACCELERATION * dt; } if (IsKeyDown(KEY_S)) { speed -= ACCELERATION * dt; } if (IsKeyDown(KEY_A)) { heading += ANGULAR_VELOCITY * dt; } if (IsKeyDown(KEY_D)) { heading -= ANGULAR_VELOCITY * dt; } if (IsKeyDown(KEY_SPACE)) { speed = 0.0f; } velocity = raylib::Vector3 { std::sin(heading) * speed, 0.0f, std::cos(heading) * speed }; // ds = 1/2 * (v0 + v1) * dt position += velocity * dt * 0.5f; // movement for camera if (IsKeyDown(KEY_LEFT)) { camYaw += CAM_ANGULAR_VELOCITY * dt; } if (IsKeyDown(KEY_RIGHT)) { camYaw -= CAM_ANGULAR_VELOCITY * dt; } if (IsKeyDown(KEY_UP)) { camPitch += CAM_ANGULAR_VELOCITY * dt; } if (IsKeyDown(KEY_DOWN)) { camPitch -= CAM_ANGULAR_VELOCITY * dt; } // clamp the angle between camPitch = std::clamp(camPitch, CAM_PITCH_MIN, CAM_PITCH_MAX); // x = cos(pitch) * sin(yaw) // y = sin(pitch) // z = cos(pitch) * cos(yaw) float yaw = camYaw + heading; // follow penguin raylib::Vector3 camOffset = { CAM_DIST * std::cos(camPitch) * std::sin(yaw), CAM_DIST * std::sin(camPitch) + CAM_HEIGHT, CAM_DIST * std::cos(camPitch) * std::cos(yaw) }; camera.SetPosition(position + camOffset); camera.SetTarget(position); camera.BeginMode(); skybox.Draw(); ground.Draw({ 0, 0, 0 }, 1.0f, raylib::Color::White()); DrawBoundedModel(penguin, [&position, &heading](raylib::Matrix transform) { return transform .RotateY(heading) .Scale(40, 40, 40) .Translate(position); }); camera.EndMode(); window.EndDrawing(); } return 0; }