aboutsummaryrefslogtreecommitdiff
path: root/apps/openmb/renderer/Skybox.cpp
diff options
context:
space:
mode:
authorEthan Morgan <ethan@gweithio.com>2026-02-14 16:44:06 +0000
committerEthan Morgan <ethan@gweithio.com>2026-02-14 16:44:06 +0000
commit54409423f767d8b1cf30cb7d0efca6b4ca138823 (patch)
treed915ac7828703ce4b963efdd9728a1777ba18c1e /apps/openmb/renderer/Skybox.cpp
move to own git serverHEADmaster
Diffstat (limited to 'apps/openmb/renderer/Skybox.cpp')
-rw-r--r--apps/openmb/renderer/Skybox.cpp155
1 files changed, 155 insertions, 0 deletions
diff --git a/apps/openmb/renderer/Skybox.cpp b/apps/openmb/renderer/Skybox.cpp
new file mode 100644
index 0000000..fea0021
--- /dev/null
+++ b/apps/openmb/renderer/Skybox.cpp
@@ -0,0 +1,155 @@
+#include "Skybox.hpp"
+
+#include "Shader.hpp"
+#include <stb_image.h>
+
+#include <algorithm>
+#include <filesystem>
+#include <iostream>
+
+namespace Fs = std::filesystem;
+
+namespace renderer {
+Skybox::Skybox () : mTexID( 0 ), mVAO( 0 ), mVBO( 0 ), mInitialized( false ) {}
+
+Skybox::~Skybox () {
+ if( mVBO )
+ glDeleteBuffers( 1, &mVBO );
+ if( mVAO )
+ glDeleteVertexArrays( 1, &mVAO );
+ if( mTexID )
+ glDeleteTextures( 1, &mTexID );
+}
+
+static std::string toLower ( const std::string& s ) {
+ std::string out = s;
+ std::transform( out.begin(), out.end(), out.begin(), [] ( unsigned char c ) { return std::tolower( c ); } );
+ return out;
+}
+
+bool Skybox::loadFromDirectory ( const std::string& dirPath ) {
+
+ std::vector<std::string> faceTokens = { "right", "left", "top", "bottom", "front", "back" };
+ std::vector<std::string> faces( 6 );
+
+ if( !Fs::exists( dirPath ) || !Fs::is_directory( dirPath ) ) {
+ std::cerr << "Skybox directory does not exist: " << dirPath << std::endl;
+ return false;
+ }
+
+ for( auto& entry : Fs::directory_iterator( dirPath ) ) {
+ if( !entry.is_regular_file() )
+ continue;
+ auto name = entry.path().filename().string();
+ std::string lname = toLower( name );
+ for( size_t i = 0; i < faceTokens.size(); ++i ) {
+ if( lname.find( faceTokens[i] ) != std::string::npos ) {
+ faces[i] = entry.path().string();
+ }
+ }
+ }
+
+ for( size_t i = 0; i < faces.size(); ++i ) {
+ if( faces[i].empty() ) {
+ std::cerr << "Missing skybox face for token: " << faceTokens[i] << std::endl;
+ return false;
+ }
+ }
+
+ return loadFaces( faces );
+}
+
+bool Skybox::loadFaces ( const std::vector<std::string>& faces ) {
+ glGenTextures( 1, &mTexID );
+ glBindTexture( GL_TEXTURE_CUBE_MAP, mTexID );
+
+ stbi_set_flip_vertically_on_load( false );
+ for( unsigned i = 0; i < faces.size(); ++i ) {
+ int w, h, ch;
+ unsigned char* data = stbi_load( faces[i].c_str(), &w, &h, &ch, 0 );
+ if( !data ) {
+ std::cerr << "Failed to load skybox face: " << faces[i] << std::endl;
+ glBindTexture( GL_TEXTURE_CUBE_MAP, 0 );
+ return false;
+ }
+ GLenum format = ( ch == 4 ) ? GL_RGBA : GL_RGB;
+ glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format, w, h, 0, format, GL_UNSIGNED_BYTE, data );
+ stbi_image_free( data );
+ }
+
+ glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+ glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+ glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
+ glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
+ glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE );
+
+ glBindTexture( GL_TEXTURE_CUBE_MAP, 0 );
+
+ initMesh();
+ mInitialized = true;
+ return true;
+}
+
+void Skybox::initMesh () {
+
+ float skyboxVertices[] = { -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f,
+ 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
+
+ -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
+ -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f,
+
+ 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f,
+
+ -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f,
+
+ -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f,
+
+ -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f,
+ 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f };
+
+ glGenVertexArrays( 1, &mVAO );
+ glGenBuffers( 1, &mVBO );
+ glBindVertexArray( mVAO );
+ glBindBuffer( GL_ARRAY_BUFFER, mVBO );
+ glBufferData( GL_ARRAY_BUFFER, sizeof( skyboxVertices ), &skyboxVertices, GL_STATIC_DRAW );
+ glEnableVertexAttribArray( 0 );
+ glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof( float ), (void*)0 );
+ glBindVertexArray( 0 );
+}
+
+void Skybox::draw ( const glm::mat4& view, const glm::mat4& proj ) {
+ if( !mInitialized )
+ return;
+
+ static Shader s;
+ static bool shaderLoaded = false;
+ if( !shaderLoaded ) {
+ s.fromFiles( "apps/openmb/resources/shaders/skybox.vert", "apps/openmb/resources/shaders/skybox.frag" );
+ shaderLoaded = true;
+ }
+
+ glDepthFunc( GL_LEQUAL );
+ glDepthMask( GL_FALSE );
+
+ s.use();
+
+ glm::mat4 viewNoTrans = glm::mat4( glm::mat3( view ) );
+ s.setMat4( "view", viewNoTrans );
+ s.setMat4( "proj", proj );
+
+ glActiveTexture( GL_TEXTURE0 );
+ glBindTexture( GL_TEXTURE_CUBE_MAP, mTexID );
+ s.setInt( "skybox", 0 );
+
+ glBindVertexArray( mVAO );
+ glDrawArrays( GL_TRIANGLES, 0, 36 );
+ glBindVertexArray( 0 );
+
+ glDepthMask( GL_TRUE );
+ glDepthFunc( GL_LESS );
+}
+
+} // namespace renderer