master
John Montagu, the 4th Earl of Sandvich 2026-03-01 10:55:19 -08:00
parent c8574ac85e
commit e034b97f4a
Signed by: sandvich
GPG Key ID: 9A39BE37E602B22D
10 changed files with 839 additions and 0 deletions

24
as4/CMakeLists.txt 100644
View File

@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 3.18)
project(as3 CXX)
set(CMAKE_CXX_STANDARD 20)
# adding this option to make clangd work
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_subdirectory(../raylib-cpp raylib)
include(../assets/includeable.cmake)
add_executable(as3 CO.cpp skybox.cpp)
target_link_libraries(as3 PUBLIC raylib raylib_cpp)
make_includeable(../assets/shaders/skybox.vs generated/skybox.vs)
make_includeable(../assets/shaders/skybox.fs generated/skybox.fs)
make_includeable(../assets/shaders/cubemap.vs generated/cubemap.vs)
make_includeable(../assets/shaders/cubemap.fs generated/cubemap.fs)
configure_file(../assets/models/penguin.glb models/penguin.glb COPYONLY)
configure_file(../assets/models/eagle.glb models/eagle.glb COPYONLY)
configure_file(../assets/textures/skybox.png textures/skybox.png COPYONLY)
configure_file(../assets/textures/snow.jpg textures/snow.jpg COPYONLY)

242
as4/CO.cpp 100644
View File

@ -0,0 +1,242 @@
#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 <concepts>
#include <functional>
#include <memory>
#include <optional>
#include <raylib-cpp.hpp>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <vector>
#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<std::shared_ptr<Component>> components;
template<std::derived_from<Component> T>
T &AddComponent() {
auto out = components.emplace_back(std::make_shared<T>());
out->e = this;
return out;
}
template<std::derived_from<Component> T>
std::optional<std::reference_wrapper<T>> GetComponent() {
for (auto &c : components) {
T *cast = dynamic_cast<T *>(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<Entity> entities;
Entity &e = entities.emplace_back();
e.AddComponent<TransformComponent>();
e.AddComponent<DrawModelComponent>();
// 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;
}

58
as4/README.md 100644
View File

@ -0,0 +1,58 @@
# 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 `as3` directory, create a build directory, and run CMake to
generate the build files:
```sh
cd as3
mkdir -p build
cd build
cmake ..
```
Compile the code using `make`:
```sh
make
```
This should create an executable named `as3` in the `build` directory. You can
run the executable with the following command:
```sh
./as3
```
# Instructions on how to use the program
Hold W and S to accelerate the penguin forward and backward. Use A and D to
change heading direction. This allows you to steer the penguin around the
environment.
Use the arrow keys to change the camera's rotation (pitch and yaw) with respect
to the penguin, allowing you to look around while the penguin moves.
# Readme Question
`dt` is delta time, which is the time elapsed between each frame, which can be
found by taking the current time and subtracting the time from the previous
frame. Raylib provides a function `GetFrameTime()` that returns the time
elapsed since the last frame, which can be used to calculate `dt`. By using
`dt`, it ensures that physics calculations are independent of frame rate, since
otherwise the velocity would be in terms of units per frame rather than units
per second. By multiplying the velocity by `dt`, it converts the velocity to
units per second, allowing for consistent movement regardless of the frame
rate.
# Extra Credit
The camera moves with the penguin and can be rotated around the penguin using the arrow keys.

185
as4/as3.cpp 100644
View File

@ -0,0 +1,185 @@
#include "AudioDevice.hpp"
#include "Color.hpp"
#include "Keyboard.hpp"
#include "Matrix.hpp"
#include "Mesh.hpp"
#include "Model.hpp"
#include "raylib.h"
#include <raylib-cpp.hpp>
#include <cmath>
#include <algorithm>
#include <iostream>
#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);
}
struct Entity {
enum Type {
Penguin = 0,
Eagle = 1,
} type;
raylib::Vector3 position;
raylib::Vector3 velocity;
raylib::Degree heading;
};
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<Entity> entities;
auto &e = entities.emplace_back(Entity::Penguin, raylib::Vector3 { 0, 0, 0 }, raylib::Vector3 { 0, 0, 0 }, 0);
// 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;
}

View File

@ -0,0 +1,30 @@
R"for_C++_include(#version 330
// Input vertex attributes (from vertex shader)
in vec3 fragPosition;
// Input uniform values
uniform sampler2D equirectangularMap;
// Output fragment color
out vec4 finalColor;
vec2 SampleSphericalMap(vec3 v)
{
vec2 uv = vec2(atan(v.z, v.x), asin(v.y));
uv *= vec2(0.1591, 0.3183);
uv += 0.5;
return uv;
}
void main()
{
// Normalize local position
vec2 uv = SampleSphericalMap(normalize(fragPosition));
// Fetch color from texture map
vec3 color = texture(equirectangularMap, uv).rgb;
// Calculate final fragment color
finalColor = vec4(color, 1.0);
})for_C++_include"

View File

@ -0,0 +1,20 @@
R"for_C++_include(#version 330
// Input vertex attributes
in vec3 vertexPosition;
// Input uniform values
uniform mat4 matProjection;
uniform mat4 matView;
// Output vertex attributes (to fragment shader)
out vec3 fragPosition;
void main()
{
// Calculate fragment position based on model transformations
fragPosition = vertexPosition;
// Calculate final vertex position
gl_Position = matProjection*matView*vec4(vertexPosition, 1.0);
})for_C++_include"

View File

@ -0,0 +1,30 @@
R"for_C++_include(#version 330
// Input vertex attributes (from vertex shader)
in vec3 fragPosition;
// Input uniform values
uniform samplerCube environmentMap;
uniform bool vflipped;
uniform bool doGamma;
// Output fragment color
out vec4 finalColor;
void main()
{
// Fetch color from texture map
vec3 color = vec3(0.0);
if (vflipped) color = texture(environmentMap, vec3(fragPosition.x, -fragPosition.y, fragPosition.z)).rgb;
else color = texture(environmentMap, fragPosition).rgb;
if (doGamma)// Apply gamma correction
{
color = color/(color + vec3(1.0));
color = pow(color, vec3(1.0/2.2));
}
// Calculate final fragment color
finalColor = vec4(color, 1.0);
})for_C++_include"

View File

@ -0,0 +1,24 @@
R"for_C++_include(#version 330
// Input vertex attributes
in vec3 vertexPosition;
// Input uniform values
uniform mat4 matProjection;
uniform mat4 matView;
// Output vertex attributes (to fragment shader)
out vec3 fragPosition;
void main()
{
// Calculate fragment position based on model transformations
fragPosition = vertexPosition;
// Remove translation from the view matrix
mat4 rotView = mat4(mat3(matView));
vec4 clipPos = matProjection*rotView*vec4(vertexPosition, 1.0);
// Calculate final vertex position
gl_Position = clipPos;
})for_C++_include"

168
as4/skybox.cpp 100644
View File

@ -0,0 +1,168 @@
/*******************************************************************************************
*
* raylib [models] example - Skybox loading and drawing
*
* Example originally created with raylib 1.8, last time updated with raylib 4.0
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2017-2023 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
#include "skybox.hpp"
#include <iostream>
#include "rlgl.h"
namespace cs381 {
SkyBox& SkyBox::Init() {
// Load skybox model
cube = raylib::Mesh::Cube(1.0f, 1.0f, 1.0f).LoadModelFrom();
// Load skybox shader and set required locations
// NOTE: Some locations are automatically set at shader loading
shader = raylib::Shader::LoadFromMemory(vertexShader, fragmentShader);
cube.materials[0].shader = shader;
shader.SetValue("environmentMap", (int)MATERIAL_MAP_CUBEMAP, SHADER_UNIFORM_INT);
return *this;
}
SkyBox& SkyBox::Load(const std::string_view filename, bool isEnviornment/* = false*/) {
if(shader.id == 0) Init();
shader.SetValue("doGamma", int(isEnviornment ? 1 : 0), SHADER_UNIFORM_INT);
shader.SetValue("vflipped", int(isEnviornment ? 1 : 0), SHADER_UNIFORM_INT);
if(isEnviornment) {
if(cubemapShader.id == 0){
cubemapShader = raylib::Shader::LoadFromMemory(cubemapVertexShader, cubemapFragmentShader);
cubemapShader.SetValue("equirectangularMap", int(0), SHADER_UNIFORM_INT);
}
// Load HDR panorama (sphere) texture
texture.Load(filename);
// Make sure that things aren't sampled in a pixelated manor!
texture.SetFilter(TEXTURE_FILTER_BILINEAR);
// Generate cubemap (texture with 6 quads-cube-mapping) from panorama HDR texture
// NOTE 1: New texture is generated rendering to texture, shader calculates the sphere->cube coordinates mapping
// NOTE 2: It seems on some Android devices WebGL, fbo does not properly support a FLOAT-based attachment,
// despite texture can be successfully created.. so using PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 instead of PIXELFORMAT_UNCOMPRESSED_R32G32B32A32
cube.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = GenTextureCubemap(cubemapShader, texture, 1024, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8);
} else {
raylib::Image img(filename);
texture.Load(img, CUBEMAP_LAYOUT_AUTO_DETECT);
// Make sure that things aren't sampled in a pixelated manor!
texture.SetFilter(TEXTURE_FILTER_BILINEAR);
cube.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = texture; // CUBEMAP_LAYOUT_PANORAMA
}
return *this;
}
SkyBox& SkyBox::Draw() {
// We are inside the cube, we need to disable backface culling!
rlDisableBackfaceCulling();
rlDisableDepthMask();
cube.Draw({});
rlEnableBackfaceCulling();
rlEnableDepthMask();
return *this;
}
// Generate cubemap texture from HDR texture
TextureCubemap SkyBox::GenTextureCubemap(Shader shader, Texture2D panorama, int size, int format) {
TextureCubemap cubemap = { 0 };
rlDisableBackfaceCulling(); // Disable backface culling to render inside the cube
// STEP 1: Setup framebuffer
//------------------------------------------------------------------------------------------
unsigned int rbo = rlLoadTextureDepth(size, size, true);
cubemap.id = rlLoadTextureCubemap(0, size, format, 1);
// unsigned int fbo = rlLoadFramebuffer(size, size);
unsigned int fbo = rlLoadFramebuffer();
rlFramebufferAttach(fbo, rbo, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0);
rlFramebufferAttach(fbo, cubemap.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X, 0);
// Check if framebuffer is complete with attachments (valid)
if (rlFramebufferComplete(fbo)) TraceLog(LOG_INFO, "FBO: [ID %i] Framebuffer object created successfully", fbo);
//------------------------------------------------------------------------------------------
// STEP 2: Draw to framebuffer
//------------------------------------------------------------------------------------------
// NOTE: Shader is used to convert HDR equirectangular environment map to cubemap equivalent (6 faces)
rlEnableShader(shader.id);
// Define projection matrix and send it to shader
Matrix matFboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_PROJECTION], matFboProjection);
// Define view matrix for every side of the cubemap
Matrix fboViews[6] = {
MatrixLookAt(Vector3{ 0.0f, 0.0f, 0.0f }, Vector3{ 1.0f, 0.0f, 0.0f }, Vector3{ 0.0f, -1.0f, 0.0f }),
MatrixLookAt(Vector3{ 0.0f, 0.0f, 0.0f }, Vector3{ -1.0f, 0.0f, 0.0f }, Vector3{ 0.0f, -1.0f, 0.0f }),
MatrixLookAt(Vector3{ 0.0f, 0.0f, 0.0f }, Vector3{ 0.0f, 1.0f, 0.0f }, Vector3{ 0.0f, 0.0f, 1.0f }),
MatrixLookAt(Vector3{ 0.0f, 0.0f, 0.0f }, Vector3{ 0.0f, -1.0f, 0.0f }, Vector3{ 0.0f, 0.0f, -1.0f }),
MatrixLookAt(Vector3{ 0.0f, 0.0f, 0.0f }, Vector3{ 0.0f, 0.0f, 1.0f }, Vector3{ 0.0f, -1.0f, 0.0f }),
MatrixLookAt(Vector3{ 0.0f, 0.0f, 0.0f }, Vector3{ 0.0f, 0.0f, -1.0f }, Vector3{ 0.0f, -1.0f, 0.0f })
};
rlViewport(0, 0, size, size); // Set viewport to current fbo dimensions
// Activate and enable texture for drawing to cubemap faces
rlActiveTextureSlot(0);
rlEnableTexture(panorama.id);
for (int i = 0; i < 6; i++) {
// Set the view matrix for the current cube face
rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_VIEW], fboViews[i]);
// Select the current cubemap face attachment for the fbo
// WARNING: This function by default enables->attach->disables fbo!!!
rlFramebufferAttach(fbo, cubemap.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X + i, 0);
rlEnableFramebuffer(fbo);
// Load and draw a cube, it uses the current enabled texture
rlClearScreenBuffers();
rlLoadDrawCube();
// ALTERNATIVE: Try to use internal batch system to draw the cube instead of rlLoadDrawCube
// for some reason this method does not work, maybe due to cube triangles definition? normals pointing out?
// TODO: Investigate this issue...
//rlSetTexture(panorama.id); // WARNING: It must be called after enabling current framebuffer if using internal batch system!
//rlClearScreenBuffers();
//DrawCubeV(Vector3Zero(), Vector3One(), WHITE);
//rlDrawRenderBatchActive();
}
//------------------------------------------------------------------------------------------
// STEP 3: Unload framebuffer and reset state
//------------------------------------------------------------------------------------------
rlDisableShader(); // Unbind shader
rlDisableTexture(); // Unbind texture
rlDisableFramebuffer(); // Unbind framebuffer
rlUnloadFramebuffer(fbo); // Unload framebuffer (and automatically attached depth texture/renderbuffer)
// Reset viewport dimensions to default
rlViewport(0, 0, rlGetFramebufferWidth(), rlGetFramebufferHeight());
rlEnableBackfaceCulling();
//------------------------------------------------------------------------------------------
cubemap.width = size;
cubemap.height = size;
cubemap.mipmaps = 1;
cubemap.format = format;
return cubemap;
}
raylib::Shader SkyBox::cubemapShader(0);
}

58
as4/skybox.hpp 100644
View File

@ -0,0 +1,58 @@
/*******************************************************************************************
*
* raylib [models] example - Skybox loading and drawing
*
* Example originally created with raylib 1.8, last time updated with raylib 4.0
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2017-2023 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
#include "raylib-cpp.hpp"
namespace cs381 {
struct SkyBox {
constexpr static std::string_view vertexShader =
#include "generated/skybox.vs"
;
constexpr static std::string_view fragmentShader =
#include "generated/skybox.fs"
;
constexpr static std::string_view cubemapVertexShader =
#include "generated/cubemap.vs"
;
constexpr static std::string_view cubemapFragmentShader =
#include "generated/cubemap.fs"
;
static raylib::Shader cubemapShader;
raylib::Texture texture;
raylib::Shader shader;
raylib::Model cube;
SkyBox() : shader(0) {};
SkyBox(SkyBox&) = delete;
SkyBox(SkyBox&&) = default;
SkyBox(const std::string_view filename, bool isEnviornment = false) : SkyBox() {
Load(filename, isEnviornment);
}
~SkyBox() {
if(cube.IsValid())
UnloadTexture(cube.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture);
}
SkyBox& Init();
SkyBox& Load(const std::string_view filename, bool isEnviornment = false);
SkyBox& Draw();
private:
// Generate cubemap texture from HDR texture
static TextureCubemap GenTextureCubemap(Shader shader, Texture2D panorama, int size, int format);
};
}