diff --git a/graphics/opengl/build.gradle b/graphics/opengl/build.gradle new file mode 100644 index 0000000..bdb7b07 --- /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 0000000..1ce6c4c --- /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 0000000..343e0d2 --- /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 0000000..cb5a946 --- /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 0000000..157e4fa --- /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 0000000..ef5a4cd --- /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 0000000..97a9288 --- /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 0000000..c3c4d11 --- /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 fe8f07b..83195e1 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 685f99e..fe891b5 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 3476c5a..8a214ce 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