diff --git a/graphics/opengl/build.gradle b/graphics/opengl/build.gradle
new file mode 100644
index 00000000..bdb7b07c
--- /dev/null
+++ b/graphics/opengl/build.gradle
@@ -0,0 +1,96 @@
+import org.gradle.internal.os.OperatingSystem
+
+plugins {
+ id 'java'
+ id 'io.freefair.lombok' version "${pluginLombok}"
+}
+
+// Determine LWJGL native stuff
+switch (OperatingSystem.current()) {
+ case OperatingSystem.LINUX:
+ project.dependencyLwjglNatives = "natives-linux"
+ def osArch = System.getProperty("os.arch")
+ if (osArch.startsWith("arm") || osArch.startsWith("aarch64")) {
+ project.dependencyLwjglNatives += osArch.contains("64") || osArch.startsWith("armv8") ? "-arm64" : "-arm32"
+ } else if (osArch.startsWith("ppc")) {
+ project.dependencyLwjglNatives += "-ppc64le"
+ } else if (osArch.startsWith("riscv")) {
+ project.dependencyLwjglNatives += "-riscv64"
+ }
+ break
+ case OperatingSystem.MAC_OS:
+ project.dependencyLwjglNatives = System.getProperty("os.arch").startsWith("aarch64") ? "natives-macos-arm64" : "natives-macos"
+ break
+ case OperatingSystem.WINDOWS:
+ def osArch = System.getProperty("os.arch")
+ project.dependencyLwjglNatives = osArch.contains("64")
+ ? "natives-windows${osArch.startsWith("aarch64") ? "-arm64" : ""}"
+ : "natives-windows-x86"
+ break
+}
+
+dependencies {
+ // -> Runtime <-
+ // Lombok
+ compileOnly 'org.projectlombok:lombok:' + project.dependencyLombok
+ annotationProcessor 'org.projectlombok:lombok:' + project.dependencyLombok
+
+ // JetBrains Annotations
+ compileOnly 'org.jetbrains:annotations:' + project.dependencyJetbrainsAnnotations
+
+ // LWJGL
+ implementation platform("org.lwjgl:lwjgl-bom:${dependencyLwjgl}")
+ implementation "org.lwjgl:lwjgl"
+ implementation "org.lwjgl:lwjgl-bgfx"
+ implementation "org.lwjgl:lwjgl-egl"
+ implementation "org.lwjgl:lwjgl-glfw"
+ implementation "org.lwjgl:lwjgl-ktx"
+ implementation "org.lwjgl:lwjgl-opengl"
+ implementation "org.lwjgl:lwjgl-opengles"
+ implementation "org.lwjgl:lwjgl-shaderc"
+ implementation "org.lwjgl:lwjgl-sse"
+ implementation "org.lwjgl:lwjgl-vma"
+ runtimeOnly "org.lwjgl:lwjgl::${dependencyLwjglNatives}"
+ runtimeOnly "org.lwjgl:lwjgl-bgfx::${dependencyLwjglNatives}"
+ runtimeOnly "org.lwjgl:lwjgl-glfw::${dependencyLwjglNatives}"
+ runtimeOnly "org.lwjgl:lwjgl-ktx::${dependencyLwjglNatives}"
+ runtimeOnly "org.lwjgl:lwjgl-opengl::${dependencyLwjglNatives}"
+ runtimeOnly "org.lwjgl:lwjgl-opengles::${dependencyLwjglNatives}"
+ runtimeOnly "org.lwjgl:lwjgl-shaderc::${dependencyLwjglNatives}"
+ runtimeOnly "org.lwjgl:lwjgl-sse::${dependencyLwjglNatives}"
+ runtimeOnly "org.lwjgl:lwjgl-vma::${dependencyLwjglNatives}"
+ if (project.dependencyLwjglNatives == "natives-macos" || project.dependencyLwjglNatives == "natives-macos-arm64") runtimeOnly "org.lwjgl:lwjgl-vulkan::${dependencyLwjglNatives}"
+
+ // -> Testing <-
+ // Jetbrains Annotations
+ testCompileOnly 'org.jetbrains:annotations:' + project.dependencyJetbrainsAnnotations
+
+ // JUnit
+ testImplementation platform('org.junit:junit-bom:5.' + project.dependencyJunit)
+ testImplementation 'org.junit.jupiter:junit-jupiter'
+ testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
+
+ // jOOR
+ testImplementation 'org.jooq:joor:' + project.dependencyJoor
+
+ // -> Project <-
+ implementation project(":base")
+ implementation project(":graphics")
+}
+
+javadoc.options {
+ setMemberLevel(JavadocMemberLevel.PUBLIC)
+ setOverview("src/main/javadoc/overview.html")
+ setLocale("en_US")
+ setJFlags([
+ // Force Javadoc to use English translations
+ "-Duser.language=en_US"
+ ])
+}
+
+test {
+ useJUnitPlatform()
+ testLogging {
+ events "passed", "skipped", "failed"
+ }
+}
diff --git a/graphics/opengl/gradle b/graphics/opengl/gradle
new file mode 120000
index 00000000..1ce6c4c1
--- /dev/null
+++ b/graphics/opengl/gradle
@@ -0,0 +1 @@
+../../gradle
\ No newline at end of file
diff --git a/graphics/opengl/gradlew b/graphics/opengl/gradlew
new file mode 120000
index 00000000..343e0d2c
--- /dev/null
+++ b/graphics/opengl/gradlew
@@ -0,0 +1 @@
+../../gradlew
\ No newline at end of file
diff --git a/graphics/opengl/gradlew.bat b/graphics/opengl/gradlew.bat
new file mode 120000
index 00000000..cb5a9464
--- /dev/null
+++ b/graphics/opengl/gradlew.bat
@@ -0,0 +1 @@
+../../gradlew.bat
\ No newline at end of file
diff --git a/graphics/opengl/src/main/java/de/staropensource/sosengine/graphics/opengl/OpenGlManagement.java b/graphics/opengl/src/main/java/de/staropensource/sosengine/graphics/opengl/OpenGlManagement.java
new file mode 100644
index 00000000..157e4fab
--- /dev/null
+++ b/graphics/opengl/src/main/java/de/staropensource/sosengine/graphics/opengl/OpenGlManagement.java
@@ -0,0 +1,40 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Contributors
+ * Licensed under the GNU Affero General Public License v3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.staropensource.sosengine.graphics.opengl;
+
+import de.staropensource.sosengine.base.classes.Vec2i;
+import de.staropensource.sosengine.graphics.classes.ApiManagementClass;
+import de.staropensource.sosengine.graphics.opengl.classes.Window;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * The OpenGL API management class.
+ *
+ * @since 1-alpha0
+ */
+@SuppressWarnings({ "unused" })
+public class OpenGlManagement implements ApiManagementClass {
+ /** {@inheritDoc} */
+ @NotNull
+ @Override
+ public Window createWindow(String title, Vec2i size) {
+ return new Window(title, size);
+ }
+}
diff --git a/graphics/opengl/src/main/java/de/staropensource/sosengine/graphics/opengl/OpenGlSubsystem.java b/graphics/opengl/src/main/java/de/staropensource/sosengine/graphics/opengl/OpenGlSubsystem.java
new file mode 100644
index 00000000..ef5a4cd5
--- /dev/null
+++ b/graphics/opengl/src/main/java/de/staropensource/sosengine/graphics/opengl/OpenGlSubsystem.java
@@ -0,0 +1,171 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Contributors
+ * Licensed under the GNU Affero General Public License v3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.staropensource.sosengine.graphics.opengl;
+
+import de.staropensource.sosengine.base.annotations.EventListener;
+import de.staropensource.sosengine.base.classes.EventPriority;
+import de.staropensource.sosengine.base.logging.LoggerInstance;
+import de.staropensource.sosengine.base.types.LogIssuer;
+import de.staropensource.sosengine.base.utility.Miscellaneous;
+import de.staropensource.sosengine.graphics.GraphicsSubsystem;
+import de.staropensource.sosengine.graphics.classes.ApiMainClass;
+import de.staropensource.sosengine.graphics.classes.ApiManagementClass;
+import de.staropensource.sosengine.graphics.opengl.events.GraphicsErrorEvent;
+import lombok.Getter;
+import org.lwjgl.glfw.GLFWErrorCallback;
+import org.lwjgl.system.MemoryUtil;
+
+import static org.lwjgl.glfw.GLFW.*;
+
+/**
+ * The main object for the OpenGL Graphics API.
+ *
+ * @since 1-alpha0
+ */
+@SuppressWarnings({ "unused", "JavadocDeclaration", "JavadocBlankLines" })
+public final class OpenGlSubsystem implements ApiMainClass {
+ /**
+ * Instance.
+ *
+ * @since 1-alpha0
+ *
+ * -- GETTER --
+ * Returns the {@link OpenGlSubsystem} instance.
+ *
+ * @return {@link OpenGlSubsystem} instance unless uninitialized
+ * @since 1-alpha0
+ */
+ @Getter
+ private static OpenGlSubsystem instance = null;
+
+ /**
+ * Logger instance.
+ *
+ * @see LoggerInstance
+ * @since 1-alpha0
+ */
+ private final LoggerInstance logger = new LoggerInstance(new LogIssuer(getClass()));
+
+ /**
+ * The Graphics API's management class.
+ *
+ * @see ApiManagementClass
+ * @since 1-alpha0
+ *
+ * -- GETTER --
+ * {@inheritDoc}
+ */
+ @Getter
+ private ApiManagementClass management;
+
+ /**
+ * Constructor.
+ *
+ * @since 1-alpha0
+ */
+ public OpenGlSubsystem() {
+ // Check if subsystem has already initialized
+ if (instance == null)
+ instance = this;
+ else {
+ instance.logger.crash("The subsystem tried to initialize twice");
+ }
+
+ // Register Graphics API
+ GraphicsSubsystem.getInstance().registerGraphicsApi(this);
+
+ logger.info("Initialized subsystem");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void initializeApi() {
+ logger.verb("Initializing Graphics API");
+
+ long initTime = Miscellaneous.measureExecutionTime(() -> {
+ // Set error callback
+ try (GLFWErrorCallback errorCallback = GLFWErrorCallback.create(new GraphicsErrorEvent())) {
+ errorCallback.set();
+ }
+
+ // Initialize GLFW
+ if (!glfwInit())
+ logger.crash("Unable to initialize GLFW");
+
+ // Initialize management class
+ management = new OpenGlManagement();
+ });
+
+ logger.info("Initialized Graphics API in " + initTime + "ms");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getApiName() {
+ return "OpenGL";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isCompatible() {
+ // Set required version
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+
+ // Create test window
+ long window = glfwCreateWindow(0, 0, "sos!engine OpenGL API compatibility test", MemoryUtil.NULL, MemoryUtil.NULL);
+ boolean failed = window == MemoryUtil.NULL;
+
+ // Destroy window
+ if (!failed) glfwDestroyWindow(window);
+
+ return failed;
+ }
+
+ /**
+ * Called when a graphics error occurs. This method just logs the error and returns.
+ *
+ * @param error graphics error
+ * @since 1-alpha0
+ */
+ @EventListener(event = GraphicsErrorEvent.class, priority = EventPriority.EXTREMELY_IMPORTANT)
+ public static void onGraphicsError(String error) {
+ instance.logger.error("An error occurred: " + error);
+ }
+
+ /**
+ * Shuts the graphics subsystem down.
+ *
+ * @since 1-alpha0
+ */
+ public static void shutdownApi() {
+ LoggerInstance logger = instance.logger;
+ logger.verb("Shutting down");
+
+ @SuppressWarnings("resource")
+ long shutdownTime = Miscellaneous.measureExecutionTime(() -> {
+ glfwTerminate();
+ //noinspection DataFlowIssue
+ glfwSetErrorCallback(null).free();
+ });
+
+ logger.info("Shut down in " + shutdownTime + "ms");
+ }
+}
diff --git a/graphics/opengl/src/main/java/de/staropensource/sosengine/graphics/opengl/classes/Window.java b/graphics/opengl/src/main/java/de/staropensource/sosengine/graphics/opengl/classes/Window.java
new file mode 100644
index 00000000..97a92887
--- /dev/null
+++ b/graphics/opengl/src/main/java/de/staropensource/sosengine/graphics/opengl/classes/Window.java
@@ -0,0 +1,105 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Contributors
+ * Licensed under the GNU Affero General Public License v3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.staropensource.sosengine.graphics.opengl.classes;
+
+import de.staropensource.sosengine.base.classes.Vec2i;
+import de.staropensource.sosengine.base.logging.LoggerInstance;
+import de.staropensource.sosengine.base.types.LogIssuer;
+import lombok.Getter;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.lwjgl.system.MemoryUtil.NULL;
+import static org.lwjgl.glfw.GLFW.*;
+
+/**
+ * A window on your screen.
+ *
+ * @since 1-alpha0
+ */
+@SuppressWarnings({ "unused", "JavadocDeclaration", "JavadocBlankLines" })
+public class Window implements de.staropensource.sosengine.graphics.classes.Window {
+ /**
+ * A set of all active windows.
+ *
+ * @since 1-alpha0
+ *
+ * -- GETTER --
+ * Returns a set of all active windows.
+ *
+ * @return set of all active windows
+ * @since 1-alpha0
+ */
+ @Getter
+ public static Set<@NotNull Window> windows = new HashSet<>();
+
+ /**
+ * Logger instance.
+ *
+ * @see LoggerInstance
+ * @since 1-alpha0
+ */
+ private final LoggerInstance logger = new LoggerInstance(new LogIssuer(getClass()));
+
+ /**
+ * The window name.
+ *
+ * @since 1-alpha0
+ *
+ * -- GETTER --
+ * Returns the window name.
+ *
+ * @return window name
+ * @since 1-alpha0
+ */
+ @Getter
+ private String windowName;
+
+ /**
+ * The window size.
+ *
+ * @since 1-alpha0
+ * -- GETTER --
+ * Returns the window size.
+ *
+ * @return window size
+ * @since 1-alpha0
+ */
+ @Getter
+ private Vec2i windowSize;
+
+ /**
+ * Constructor. Creates a new window.
+ *
+ * @param title window title
+ * @param size window size
+ */
+ public Window(String title, Vec2i size) {
+ logger.diag("Creating new Window with title \"" + title + "\"");
+
+ windows.add(this);
+ windowName = title;
+ windowSize = size;
+
+ glfwCreateWindow(windowSize.getX(), windowSize.getY(), windowName, NULL, NULL);
+ }
+}
diff --git a/graphics/opengl/src/main/java/de/staropensource/sosengine/graphics/opengl/events/GraphicsErrorEvent.java b/graphics/opengl/src/main/java/de/staropensource/sosengine/graphics/opengl/events/GraphicsErrorEvent.java
new file mode 100644
index 00000000..c3c4d114
--- /dev/null
+++ b/graphics/opengl/src/main/java/de/staropensource/sosengine/graphics/opengl/events/GraphicsErrorEvent.java
@@ -0,0 +1,42 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Contributors
+ * Licensed under the GNU Affero General Public License v3
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.staropensource.sosengine.graphics.opengl.events;
+
+import org.lwjgl.glfw.GLFWErrorCallbackI;
+
+/**
+ * Called when a graphics error occurs.
+ *
+ * @since 1-alpha0
+ */
+@SuppressWarnings({ "unused" })
+public class GraphicsErrorEvent extends de.staropensource.sosengine.graphics.events.GraphicsErrorEvent implements GLFWErrorCallbackI {
+ /**
+ * Constructor.
+ */
+ public GraphicsErrorEvent() {}
+
+
+ /** {@inheritDoc} */
+ @Override
+ public void invoke(int error, long description) {
+ callEvent(description + " (" + error + ")");
+ }
+}
diff --git a/settings.gradle b/settings.gradle
index fe8f07b4..83195e11 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -3,5 +3,6 @@ rootProject.name = 'sosengine'
include 'base'
include 'slf4j-compat'
include 'graphics'
+include 'graphics:opengl'
include 'graphics:vulkan'
include 'testapp'
diff --git a/testapp/build.gradle b/testapp/build.gradle
index 685f99e3..fe891b5e 100644
--- a/testapp/build.gradle
+++ b/testapp/build.gradle
@@ -19,6 +19,7 @@ dependencies {
implementation project(":slf4j-compat")
implementation project(":graphics")
implementation project(":graphics:vulkan")
+ implementation project(":graphics:opengl")
}
javadoc.options {
diff --git a/testapp/src/main/java/de/staropensource/sosengine/testapp/Main.java b/testapp/src/main/java/de/staropensource/sosengine/testapp/Main.java
index 3476c5ad..8a214cec 100644
--- a/testapp/src/main/java/de/staropensource/sosengine/testapp/Main.java
+++ b/testapp/src/main/java/de/staropensource/sosengine/testapp/Main.java
@@ -26,6 +26,7 @@ import de.staropensource.sosengine.base.types.LogIssuer;
import de.staropensource.sosengine.graphics.GraphicsSubsystem;
import de.staropensource.sosengine.graphics.classes.ApiMainClass;
import de.staropensource.sosengine.graphics.classes.ApiManagementClass;
+import de.staropensource.sosengine.graphics.opengl.OpenGlSubsystem;
import de.staropensource.sosengine.graphics.vulkan.VulkanSubsystem;
import de.staropensource.sosengine.slf4j_compat.Slf4jCompatibilitySubsystem;
import lombok.Getter;
@@ -102,6 +103,7 @@ public class Main {
// Initialize subsystems
new Slf4jCompatibilitySubsystem();
new GraphicsSubsystem();
+ new OpenGlSubsystem();
new VulkanSubsystem();
// Choose Graphics API to use