diff --git a/.gitattributes b/.gitattributes old mode 100644 new mode 100755 diff --git a/.github/workflows/gradle-publish.yml b/.github/workflows/gradle-publish.yml new file mode 100755 index 0000000..f29de7f --- /dev/null +++ b/.github/workflows/gradle-publish.yml @@ -0,0 +1,39 @@ +name: Gradle Package + +on: + release: + types: [created] + +jobs: + build: + + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - uses: actions/checkout@v2 + + - name: Set up JDK 17 + uses: actions/setup-java@v2 + with: + java-version: '17' + distribution: 'temurin' + server-id: github + settings-path: ${{ github.workspace }} + + - name: Set up Gradle + uses: gradle/gradle-build-action@v2 + + - name: Execute Gradle build + run: | + chmod +x ./gradlew + ./gradlew :engine-core:build --stacktrace --info -x test + + - name: Publish to Vega + run: ./gradlew :engine-core:publish --stacktrace --info -x test + env: + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_PASSWORD }} + ORG_GRADLE_PROJECT_vegaUsername: ${{ secrets.VEGA_USERNAME }} + ORG_GRADLE_PROJECT_vegaPassword: ${{ secrets.VEGA_PASSWORD }} diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index d0b46af..d923d13 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,17 @@ /*/.settings +/.vscode + # Ignore Eclipse project files /*/.project /*/.classpath /.project +/.settings # Ignore IDEA project files /.idea /*/.idea +.idea *.log @@ -18,4 +22,8 @@ # Ignore Gradle build output directory /build -/*/build \ No newline at end of file +/*/build + +/bin +/*/bin +/logs/ diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 index 38f4513..205f75f --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019-2020 493msi +Copyright (c) 2019-2022 493msi Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/NEXT_RELEASE_DRAFT.md b/NEXT_RELEASE_DRAFT.md old mode 100644 new mode 100755 index 8a8a01f..0ec0753 --- a/NEXT_RELEASE_DRAFT.md +++ b/NEXT_RELEASE_DRAFT.md @@ -1,16 +1,17 @@ -## Features targeted for 20.2.0.0-alpha.3 -* The initial minimal release of `[PlutoCommandParser]` -* `[PlutoLib]` Completely redo the ModLoader system - * The current implementation is a result of 5 years of feature creep - * Large scale API changes, however the general idea should stay the same - * Rethink the class loader system. -* `[PlutoLib]` Redo the resource system +## Features targeted for 22.0.1.0-alpha.0 +* `[PlutoGUI]` Initial implementation of the new font renderer + * Full rewrite + * High quality font rendering + * Subpixel rendering support [?] + * Possibly a new system for bitmap fonts +* Improve upon the support of thread-local Pluto instances + * The long term goal is to allow an unlimited amount of Pluto applications at any given time -## Features targeted for 20.2.0.0-alpha.4 +## Features targeted for 22.0.1.0-alpha.1 * The stage subsystem * A "stage", in this context, is a set of assets bound together - by programming logic, not necessarily a game level. - Stage switching and asset management are handled by the engine. + by programming logic, not necessarily a game level. + Stage switching and asset management are handled by the engine. * Upon stage switch 1. Unload unused assets 2. Load new assets @@ -22,21 +23,19 @@ 2. Deferred switch * The stage will continue running until all assets load * Assets will load synchronously, but at a slower pace - to avoid frame stutter + to avoid frame stutter 3. Asynchronous switch * Assets will be loaded in asynchronously, where applicable - * Falls back to deferred switching for synchronous loading, - such as OpenGL texture upload + * Falls back to deferred switching for synchronous loading, + such as OpenGL texture upload * Automated asset loading * All asset management will eventually be handled by `PlutoCore` * This includes audio clips, textures, sprites * Add a common interface for all assets * Let the stage system handle audio playback * This API should be as neutral as possible and avoid steering towards - game-only use + game-only use * The stage manager should be relatively low-overhead and allow multiple - instances + instances * Allow stages to be inherited from, creating a stack-like structure -* `[PlutoAudio]` Integrate the Audio API with the Stage API -* Improve upon the support of thread-local Pluto instances - * The long term goal is to allow an unlimited amount of Pluto applications at any given time +* `[PlutoAudio]` Integrate the Audio API with the Stage API \ No newline at end of file diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 3c653db..cb76a79 --- a/README.md +++ b/README.md @@ -25,18 +25,18 @@ version numbers.* * **PlutoCore** - Stable * **PlutoFramebuffer** - Stable * **PlutoGUI** - Stable, awaiting a rewrite - * **PlutoLib** - Mostly stable, the module API still has some quirks * **PlutoMesher** - Stable * **PlutoShader** - Stable + * **PlutoTexture** - Stable * **PlutoSpriteSheet** - Stable, some features are unfinished - * **PlutoStatic** - Stable, collision API nowhere near completion + * **PlutoDisplay** - Stable, collision API nowhere near completion + * **PlutoUSS2** - Stable + * **PlutoLib** - Mostly stable -### Unstable submodules +### Unstable submodules + * **PlutoRuntime** - Somewhat tentative, the module API has been rewritten and might contain bugs * **PlutoAudio** - Somewhat usable, unfinished -### Broken submodules, do NOT use - * **PlutoCommandParser** - Unfinished, broken, unusable - * **PlutoDB** - Broken, unusable ## Current priorities @@ -44,25 +44,31 @@ See `NEXT_RELEASE_DRAFT.md` for details. ### Very high priority [ *Implemented in the current release.* ] - * Rewrite the ModLoader - * Finish PlutoCommandParser * Streamline PlutoLib, remove bad APIs and improve code quality + * Improve image loading capabilities, possibly rewrite PlutoLib#TPL + * The stage system and automated asset loading + * Rewrite PlutoGUI ### High priority [ *Implemented in the next release.* ] * Finish PlutoAudio * Depends on the stage system - * The stage system and automated asset loading ### Normal priority [ *Planned for an upcoming release.* ] - * Rewrite PlutoGUI * The collision system for PlutoStatic - * Improve image loading capabilities, possibly rewrite PlutoLib#TPL ### Low priority [ *Items not required immediately, planned to be implemented eventually.* ] * Allow multiple running instances of Pluto * Alternatively, if this deems too difficult to implement, - prohibit the creation of more than instance per VM to avoid issues - * A networking API \ No newline at end of file + prohibit the creation of more than one instance per JVM to avoid issues + * A networking API + * Re-add support for external mod jars to the ModLoader + * This feature requires a full rewrite and possibly a complete overhaul + * Mods should have limited execution levels, for example restricted file access + or disabled native library loading (this is probably not possible) + * Expand upon the Color API + * Color mixing and blending + * Color transformation + * High-performance serialization \ No newline at end of file diff --git a/UPDATE_NOTES.md b/UPDATE_NOTES.md old mode 100644 new mode 100755 index f743dc1..5ba9f62 --- a/UPDATE_NOTES.md +++ b/UPDATE_NOTES.md @@ -1,6 +1,62 @@ +## 20.2.0.0-alpha.3 +* `[SDK]` Restructured the repository + * All build scripts are now written in Kotlin + * **Added runnabled examples** + * **Upgraded to Java 17** to take advantage of new language features and a more efficient JVM + * **The repostiory now contains examples** + * **Moved all classes to the `org.plutoengine` package** +* `[PlutoComponent]` **Added PlutoComponent as a new module** + * `[PlutoLib]` `PlutoLib` now depends on `PlutoComponent` +* `[PlutoUSS2]` **Added USS2 as a new module** + * `[PlutoLib]` `PlutoLib` now depends on `PlutoUSS2` +* `[PlutoLib]` **Greatly simplified the API and moved PlutoEngine specific classes to `PlutoRuntime`** + * ***Moved* the module system to `PlutoRuntime`** + * *Removed* `ResourceSubscriber`, + * *Removed* `cz.tefek.pluto.io.pluto.pp` + * *Removed* `RAID` + * *Moved* `Logger`, `OutputSplitStream` to `PlutoRuntime` + * *Removed* `Severity`, use `SmartSeverity` instead + * *Removed* `TextIn`, `TextOut`, `ResourceImage` and `ResourceInputStream` + * Use Java's NIO instead + * *Removed* `StaticPlutoEventManager` as the implementation was too obscure + * The module system now uses its own event management + * *Removed* the `EventData` class +* `[PlutoRuntime]` **Added PlutoRuntime as a new module** + * **Completely rewrote the module system** + * *Removed* support for external mods as the feature needs a complete overhaul + * **Revamped resource system now based on NIO** + * *Moved* the logging system from `PlutoLib` to `PlutoRuntime` + * Made `OutputSplitStream` public as it is now reusable + * **Added the Version API** + * Added the `IVersion` interface + * Added support for version objects + * As a result, all fields in `Pluto` except the version string are no longer compile-time constants +* `[PlutoDisplay]` **Renamed `PlutoStatic` to `PlutoDisplay`** + * Added the `ModGLFW` virtual module + * `DisplayErrorCallback` and simplified the callbacks in `Display` +* `[PlutoCommandParser]` **Module discontinued as a part of PlutoEngine, it will still be developed seprately** +* `[PlutoTexturing]` Renamed to `PlutoTexture` + * Removed `Texture#load(String)` and `Texture#load(String, MagFilter, MinFilter, WrapMode...)` + + +* `[PlutoLib]` Added the `@ConstantExpression` annotation +* `[PlutoLib]` The transitive dependency JOML is now provided by `PlutoLib` instead of `PlutoStatic` +* `[PlutoLib]` Created a simple Color API + * `[PlutoLib]` Added the 8-bit RGBA `Color` class as a counterpart to AWT's `Color` class + * `[PlutoLib]` Added the `RGBA` and `RGB` single precision float color objects + * `[PlutoLib]` Added the respective `IRGBA` and `IRGB` read-only interfaces + * `[PlutoLib]` Added the `HSBA` and `HSB` single precision float color objects + * `[PlutoLib]` Added methods to convert between HSBA, RGBA, HSB and RGB + * `[PlutoShader]` Added the `UniformRGBA` and `UniformRGB` shader uniform types +* `[PlutoCore]` Made `PlutoApplication`'s constructor private +* `[PlutoLib]` `MiniTimeParseException` no longer contains a hardcoded String message +* `build.gradle` *Removed* the prepackaged JVM wrapper introduced in the previous alpha + as it caused numerous issues + * In the future, JDKs will be packaged with the SDK + ## 20.2.0.0-alpha.2 * `build.gradle` Extracted the version numbers into separate variables -* `build.gradle` **[experimental]** `gradlew` should now automatically download JDK11 when needed +* `build.gradle` **[experimental]** `gradlew` should now automatically download JDK11 when needed * `build.gradle` Updated the build scripts and added source Maven publication * `[PlutoLib]` Renamed the `cz.tefek.pluto.eventsystem` package to `cz.tefek.pluto.event` * Moved all subpackages @@ -10,7 +66,7 @@ * Renamed `VertexArray#createArrayAttrib` to `VertexArray#createArrayAttribute` * Renamed `VertexArray#getVertexAttribs` to `VertexArray#getVertexAttributes` * `[PlutoCore]` Made `PlutoApplication.StartupConfig` fields private, options -can now only be modified only through public setters + can now only be modified only through public setters * `[PlutoLib]` Added the `ThreadSensitive` annotation * `[PlutoLib]` Renamed `MiniTimeCouldNotBeParsedException` to `MiniTimeParseException` * `[PlutoCore]` Refactored `InputBus` and added several convenience methods @@ -19,27 +75,27 @@ can now only be modified only through public setters ## 20.2.0.0-alpha.1 * `[PlutoLib#cz.tefek.pluto.io.logger]` Refactored the Logger subsystem - * Renamed `Logger#logException` to `Logger#log` to match the rest - of log methods and updated references to this method accordingly - * Streamlined `StdOutSplitStream` and `StdErrSplitStream` into a more generalized - `OutputSplitStream` - * `Logger`'s output filenames now look cleaner with `log--YYYY-MM-DD--HH-MM-SS.txt` - * `[Logger#setup]` can now throw `IOException` - * `[PlutoCore]` As a result, `[PlutoApplication#run]` can now throw `Exception` + * Renamed `Logger#logException` to `Logger#log` to match the rest + of log methods and updated references to this method accordingly + * Streamlined `StdOutSplitStream` and `StdErrSplitStream` into a more generalized + `OutputSplitStream` + * `Logger`'s output filenames now look cleaner with `log--YYYY-MM-DD--HH-MM-SS.txt` + * `[Logger#setup]` can now throw `IOException` + * `[PlutoCore]` As a result, `[PlutoApplication#run]` can now throw `Exception` * `[PlutoLib]` Updated JavaDoc in `ResourceAddress`, `TPL`, `TPNImage` * `[PlutoLib]` Code cleanup in `MiniTime`, `TPL` - * `[PlutoLib]` Deprecated `TPL#load(String)` in favor of `TPL#load(ResourceAddress)`, - `TPL#load(File)` and `TPL#load(Path)` - * `[PlutoTexturing]` Deprecated the `String` variant of `Texture#load` - to reflect this change - * `[PlutoSpritesheet]` Removed the usage of this method - in `DisposablePlaceholderSprite` - * `[PlutoLib]` Added an option to flip loaded images with `TPL#loadImageSpecial` - and added respective `TPL#loadSpecial` for every `TPL#load` - * `[PlutoLib]` *Removed* `TPJImage` - * `[PlutoLib]` Removed `TPL#loadPixels` - * `[PlutoCore]` Updated `GLFWImageUtil` to remove the usage of `TPJImage` + * `[PlutoLib]` Deprecated `TPL#load(String)` in favor of `TPL#load(ResourceAddress)`, + `TPL#load(File)` and `TPL#load(Path)` + * `[PlutoTexturing]` Deprecated the `String` variant of `Texture#load` + to reflect this change + * `[PlutoSpritesheet]` Removed the usage of this method + in `DisposablePlaceholderSprite` + * `[PlutoLib]` Added an option to flip loaded images with `TPL#loadImageSpecial` + and added respective `TPL#loadSpecial` for every `TPL#load` + * `[PlutoLib]` *Removed* `TPJImage` + * `[PlutoLib]` Removed `TPL#loadPixels` + * `[PlutoCore]` Updated `GLFWImageUtil` to remove the usage of `TPJImage` * `[PlutoCore]` `[PlutoApplication]` now properly closes the `Logger` on exit * `[PlutoLib]` Various typo fixes * `[Pluto*]` Deprecated `Severity` for `SmartSeverity` and replaced all usages -* `[Pluto*]` Deprecated `CRLF` with `LF` in all Java source files \ No newline at end of file +* `[Pluto*]` Replaced `CRLF` with `LF` in all Java source files \ No newline at end of file diff --git a/build.gradle b/build.gradle deleted file mode 100644 index ca4732e..0000000 --- a/build.gradle +++ /dev/null @@ -1,83 +0,0 @@ -import org.gradle.internal.os.OperatingSystem - -plugins { - id "me.filippov.gradle.jvm.wrapper" version "0.9.3" -} - -jvmWrapper { - linuxJvmUrl = "https://corretto.aws/downloads/latest/amazon-corretto-11-x64-linux-jdk.tar.gz" - macJvmUrl = "https://corretto.aws/downloads/latest/amazon-corretto-11-x64-macos-jdk.tar.gz" - windowsJvmUrl = "https://corretto.aws/downloads/latest/amazon-corretto-11-x64-windows-jdk.zip" -} - -wrapper { - distributionType = Wrapper.DistributionType.ALL -} - -project.ext.versionYear = 20 -project.ext.versionMajor = 2 -project.ext.versionMinor = 0 -project.ext.versionPatch = 0 - -project.ext.isPrerelease = true -project.ext.prereleaseName = "alpha" -project.ext.prerealeaseUpdate = 2 - -subprojects { - apply plugin: 'java' - apply plugin: 'maven-publish' - - project.ext.lwjglVersion = "3.2.3" - - project.ext.jomlVersion = "1.9.25" - project.ext.steamworks4jVersion = "1.8.0" - project.ext.steamworks4jServerVersion = "1.8.0" - - group = "cz.tefek" - version = isPrerelease ? - "${versionYear}.${versionMajor}.${versionMinor}.${versionPatch}-${prereleaseName}.${prerealeaseUpdate}" - : - "${versionYear}.${versionMajor}.${versionMinor}.${versionPatch}" - - tasks.withType(JavaCompile) { - options.encoding = "UTF-8" - } - - java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } - - - task sourcesJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allJava - } - - publishing { - publications { - maven(MavenPublication) { - from components.java - - artifact sourcesJar { - classifier "sources" - } - } - } - } - - switch (OperatingSystem.current()) { - case OperatingSystem.LINUX: - project.ext.lwjglNatives = "natives-linux" - break - case OperatingSystem.MAC_OS: - project.ext.lwjglNatives = "natives-macos" - break - case OperatingSystem.WINDOWS: - project.ext.lwjglNatives = "natives-windows" - break - } - - repositories { - mavenCentral() - } -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100755 index 0000000..54af541 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,12 @@ +tasks.withType { + distributionType = Wrapper.DistributionType.ALL + gradleVersion = "7.4.2" +} + +subprojects { + group = "cz.tefek" + + tasks.withType { + options.encoding = "UTF-8" + } +} \ No newline at end of file diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100755 index 0000000..061ab51 --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,24 @@ +plugins { + `kotlin-dsl` +} + +repositories { + mavenCentral() +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +tasks.withType { + kotlinOptions { + jvmTarget = "17" + } +} + +dependencies { + implementation("org.jetbrains.kotlin", "kotlin-gradle-plugin", "1.6.20") + + implementation(gradleApi()) +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/org/plutoengine/Versions.kt b/buildSrc/src/main/kotlin/org/plutoengine/Versions.kt new file mode 100755 index 0000000..71ada52 --- /dev/null +++ b/buildSrc/src/main/kotlin/org/plutoengine/Versions.kt @@ -0,0 +1,35 @@ +package org.plutoengine + +import org.gradle.internal.os.OperatingSystem +import org.gradle.api.JavaVersion + +object Versions { + const val lwjglVersion = "3.3.1" + val lwjglNatives = when (OperatingSystem.current()) { + OperatingSystem.LINUX -> "natives-linux" + OperatingSystem.WINDOWS -> "natives-windows" + else -> throw Error("Unsupported operating system!") + } + + const val jomlVersion = "1.10.2" + const val steamworks4jVersion = "1.8.0" + const val steamworks4jServerVersion = "1.8.0" + + const val versionYear = 20 + const val versionMajor = 2 + const val versionMinor = 0 + const val versionPatch = 0 + + const val isPrerelease = true + const val prereleaseName = "alpha" + const val prerealeaseUpdate = 3 + + val versionFull = + if (isPrerelease) + "$versionYear.$versionMajor.$versionMinor.$versionPatch-$prereleaseName.$prerealeaseUpdate" + else + "$versionYear.$versionMajor.$versionMinor.$versionPatch" + + + val javaTargetVersion = JavaVersion.VERSION_17 +} \ No newline at end of file diff --git a/engine-core/.gitignore b/engine-core/.gitignore new file mode 100755 index 0000000..43f2993 --- /dev/null +++ b/engine-core/.gitignore @@ -0,0 +1,28 @@ +/*/.settings + +/.vscode + +# Ignore Eclipse project files +/*/.project +/*/.classpath +/.project +/.settings + +# Ignore IDEA project files +/.idea +/*/.idea +.idea + +*.log + +/.gradle/ + +# Ignore Gradle project-specific cache directory +.gradle + +# Ignore Gradle build output directory +/build +/*/build + +/bin +/*/bin \ No newline at end of file diff --git a/engine-core/build.gradle.kts b/engine-core/build.gradle.kts new file mode 100755 index 0000000..7f43821 --- /dev/null +++ b/engine-core/build.gradle.kts @@ -0,0 +1,70 @@ +import org.plutoengine.Versions + +task("publish") { + dependsOn(":plutoengine:plutouss2:publish") + dependsOn(":plutoengine:plutolib:publish") + dependsOn(":plutoengine:plutocomponent:publish") + dependsOn(":plutoengine:plutoruntime:publish") + dependsOn(":plutoengine:plutodisplay:publish") + dependsOn(":plutoengine:plutotexture:publish") + dependsOn(":plutoengine:plutomesher:publish") + dependsOn(":plutoengine:plutoshader:publish") + dependsOn(":plutoengine:plutoframebuffer:publish") + dependsOn(":plutoengine:plutospritesheet:publish") + dependsOn(":plutoengine:plutogui:publish") + dependsOn(":plutoengine:plutoaudio:publish") + dependsOn(":plutoengine:plutocore:publish") +} + +subprojects { + apply(plugin = "java") + apply(plugin = "java-library") + apply(plugin = "maven-publish") + apply(plugin = "signing") + + repositories { + mavenCentral() + } + + configure { + sourceCompatibility = Versions.javaTargetVersion + targetCompatibility = Versions.javaTargetVersion + } + + configure { + named("main") { + tasks.withType { + from(allJava) + } + } + } + + configure { + publications { + create("maven") { + from(components["java"]) + } + } + + repositories { + maven { + name = "Vega" + url = uri("https://vega.botdiril.com/") + credentials { + val vegaUsername: String? by project + val vegaPassword: String? by project + + username = vegaUsername + password = vegaPassword + } + } + } + } + + configure { + val signingKey: String? by project + val signingPassword: String? by project + useInMemoryPgpKeys(signingKey, signingPassword) + sign(the().publications["maven"]) + } +} diff --git a/engine-core/plutoaudio/build.gradle.kts b/engine-core/plutoaudio/build.gradle.kts new file mode 100755 index 0000000..62b2948 --- /dev/null +++ b/engine-core/plutoaudio/build.gradle.kts @@ -0,0 +1,16 @@ +import org.plutoengine.Versions + +plugins { + java + `java-library` +} + +description = "PlutoEngine's sound subsystem." + +dependencies { + api(project(":plutoengine:plutodisplay")) + + api("org.lwjgl:lwjgl-openal") + + runtimeOnly("org.lwjgl", "lwjgl-openal", classifier = Versions.lwjglNatives) +} \ No newline at end of file diff --git a/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/AudioLoader.java b/engine-core/plutoaudio/src/main/java/org/plutoengine/audio/AudioLoader.java old mode 100644 new mode 100755 similarity index 78% rename from plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/AudioLoader.java rename to engine-core/plutoaudio/src/main/java/org/plutoengine/audio/AudioLoader.java index dedb0a2..8262c96 --- a/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/AudioLoader.java +++ b/engine-core/plutoaudio/src/main/java/org/plutoengine/audio/AudioLoader.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.audio; +package org.plutoengine.audio; import org.lwjgl.stb.STBVorbis; import org.lwjgl.stb.STBVorbisInfo; @@ -10,67 +10,64 @@ import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.nio.ShortBuffer; import java.nio.file.Files; +import java.nio.file.Path; -import cz.tefek.pluto.engine.buffer.BufferHelper; -import cz.tefek.pluto.io.asl.resource.ResourceAddress; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; +import org.plutoengine.buffer.BufferHelper; +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; public class AudioLoader { /** - * Loads an audio track denoted by this {@link ResourceAddress} into memory + * Loads an audio track denoted by this {@link Path} into memory * for from-memory Vorbis decoding and streaming. Good for frequently used - * medium sized audio files, however it is discouraged to use such a track + * medium-sized audio files, however it is discouraged to use such a track * in multiple audio sources at once due to the cost of seeking. */ - public static ISeekableAudioTrack loadMemoryDecoded(ResourceAddress address) + public static ISeekableAudioTrack loadMemoryDecoded(Path path) { - Logger.logf(SmartSeverity.AUDIO_PLUS, "Loading audio file: %s\n", address.toString()); + Logger.logf(SmartSeverity.AUDIO_PLUS, "Loading audio file: %s\n", path); try { - return new MemoryDecodedVorbisTrack(address); + return new MemoryDecodedVorbisTrack(path); } catch (IOException e) { - Logger.logf(SmartSeverity.AUDIO_ERROR, "Failed to load an audio file: '%s'\n", address.toString()); + Logger.logf(SmartSeverity.AUDIO_ERROR, "Failed to load an audio file: '%s'\n", path); e.printStackTrace(); return null; } } /** - * Loads an audio track denoted by this {@link ResourceAddress} into memory + * Loads an audio track denoted by this {@link Path} into memory * for from-memory PCM streaming. Good for frequently used small audio * files. */ - public static ISeekableAudioTrack loadMemoryPCM(ResourceAddress address) + public static ISeekableAudioTrack loadMemoryPCM(Path path) { - Logger.logf(SmartSeverity.AUDIO_PLUS, "Loading audio file: %s\n", address.toString()); + Logger.logf(SmartSeverity.AUDIO_PLUS, "Loading audio file: %s\n", path); try { - return new MemoryPCMTrack(address); + return new MemoryPCMTrack(path); } catch (IOException e) { - Logger.logf(SmartSeverity.AUDIO_ERROR, "Failed to load an audio file: '%s'\n", address.toString()); + Logger.logf(SmartSeverity.AUDIO_ERROR, "Failed to load an audio file: '%s'\n", path); e.printStackTrace(); return null; } } - private static ByteBuffer loadIntoMemory(ResourceAddress addr) throws IOException + private static ByteBuffer loadIntoMemory(Path path) throws IOException { - var path = addr.toNIOPath(); var size = Files.size(path); if (size > Integer.MAX_VALUE) - { - throw new IOException("File '" + addr.toString() + "' is too big to be loaded!"); - } + throw new IOException("File '%s' is too big to be loaded!".formatted(path)); var readData = MemoryUtil.memAlloc((int) size); @@ -108,18 +105,18 @@ public class AudioLoader public static class MemoryPCMTrack extends SeekableTrack { - private ShortBuffer pcmAudio = null; + private final ShortBuffer pcmAudio; private int sampleOffset = 0; - private MemoryPCMTrack(ResourceAddress address) throws IOException + private MemoryPCMTrack(Path path) throws IOException { long handle = MemoryUtil.NULL; ByteBuffer audioBytes = null; try (MemoryStack stack = MemoryStack.stackPush()) { - audioBytes = loadIntoMemory(address); + audioBytes = loadIntoMemory(path); IntBuffer error = stack.mallocInt(1); handle = STBVorbis.stb_vorbis_open_memory(audioBytes, error, null); @@ -127,10 +124,10 @@ public class AudioLoader if (handle == MemoryUtil.NULL) { this.close(); - throw new IOException(String.format("Failed to load '%s', error code %d.\n", address.toString(), error.get(0))); + throw new IOException(String.format("Failed to load '%s', error code %d.\n", path, error.get(0))); } - STBVorbisInfo info = STBVorbisInfo.mallocStack(stack); + STBVorbisInfo info = STBVorbisInfo.malloc(stack); STBVorbis.stb_vorbis_get_info(handle, info); this.channels = info.channels(); @@ -201,11 +198,11 @@ public class AudioLoader private final ByteBuffer encodedAudio; - private MemoryDecodedVorbisTrack(ResourceAddress address) throws IOException + private MemoryDecodedVorbisTrack(Path path) throws IOException { try { - this.encodedAudio = loadIntoMemory(address); + this.encodedAudio = loadIntoMemory(path); } catch (IOException e) { @@ -221,10 +218,10 @@ public class AudioLoader if (this.handle == MemoryUtil.NULL) { this.close(); - throw new IOException(String.format("Failed to load '%s', error code %d.\n", address.toString(), error.get(0))); + throw new IOException(String.format("Failed to load '%s', error code %d.\n", path, error.get(0))); } - STBVorbisInfo info = STBVorbisInfo.mallocStack(stack); + STBVorbisInfo info = STBVorbisInfo.malloc(stack); STBVorbis.stb_vorbis_get_info(this.handle, info); this.channels = info.channels(); @@ -234,7 +231,11 @@ public class AudioLoader this.samplesLength = STBVorbis.stb_vorbis_stream_length_in_samples(this.handle); - Logger.logf(SmartSeverity.AUDIO, "\tSample rate:\t%d\n\t\tChannels:\t%d\n\t\tSamples:\t%d\n", this.sampleRate, this.channels, this.samplesLength); + Logger.logf(SmartSeverity.AUDIO, """ + \tSample rate:\t%d + \t\tChannels:\t%d + \t\tSamples:\t%d + %n""", this.sampleRate, this.channels, this.samplesLength); } @Override diff --git a/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/IAudioStream.java b/engine-core/plutoaudio/src/main/java/org/plutoengine/audio/IAudioStream.java old mode 100644 new mode 100755 similarity index 82% rename from plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/IAudioStream.java rename to engine-core/plutoaudio/src/main/java/org/plutoengine/audio/IAudioStream.java index ae5903b..e919f99 --- a/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/IAudioStream.java +++ b/engine-core/plutoaudio/src/main/java/org/plutoengine/audio/IAudioStream.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.audio; +package org.plutoengine.audio; import java.nio.ShortBuffer; diff --git a/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/ISeekableAudioTrack.java b/engine-core/plutoaudio/src/main/java/org/plutoengine/audio/ISeekableAudioTrack.java old mode 100644 new mode 100755 similarity index 93% rename from plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/ISeekableAudioTrack.java rename to engine-core/plutoaudio/src/main/java/org/plutoengine/audio/ISeekableAudioTrack.java index 26278b4..b154067 --- a/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/ISeekableAudioTrack.java +++ b/engine-core/plutoaudio/src/main/java/org/plutoengine/audio/ISeekableAudioTrack.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.audio; +package org.plutoengine.audio; public interface ISeekableAudioTrack extends IAudioStream { diff --git a/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/al/AudioEngine.java b/engine-core/plutoaudio/src/main/java/org/plutoengine/audio/al/AudioEngine.java old mode 100644 new mode 100755 similarity index 74% rename from plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/al/AudioEngine.java rename to engine-core/plutoaudio/src/main/java/org/plutoengine/audio/al/AudioEngine.java index 4327533..9ff94e6 --- a/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/al/AudioEngine.java +++ b/engine-core/plutoaudio/src/main/java/org/plutoengine/audio/al/AudioEngine.java @@ -1,23 +1,15 @@ -package cz.tefek.pluto.engine.audio.al; +package org.plutoengine.audio.al; import org.joml.Vector3f; -import org.lwjgl.openal.AL; -import org.lwjgl.openal.AL10; -import org.lwjgl.openal.ALC; -import org.lwjgl.openal.ALC10; -import org.lwjgl.openal.ALCCapabilities; -import org.lwjgl.openal.ALCapabilities; -import org.lwjgl.openal.EXTThreadLocalContext; +import org.lwjgl.openal.*; import org.lwjgl.system.MemoryUtil; - -import java.nio.ByteBuffer; -import java.nio.IntBuffer; +import org.plutoengine.Pluto; +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; import javax.annotation.concurrent.ThreadSafe; - -import cz.tefek.pluto.Pluto; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; /** * @author 493msi @@ -26,23 +18,11 @@ import cz.tefek.pluto.io.logger.SmartSeverity; @ThreadSafe public class AudioEngine { - private static ThreadLocal device = new ThreadLocal<>() { - @Override - protected Long initialValue() - { - return MemoryUtil.NULL; - } - }; + private static final ThreadLocal device = ThreadLocal.withInitial(() -> MemoryUtil.NULL); - private static ThreadLocal context = new ThreadLocal<>() { - @Override - protected Long initialValue() - { - return MemoryUtil.NULL; - } - }; + private static final ThreadLocal context = ThreadLocal.withInitial(() -> MemoryUtil.NULL); - private static ThreadLocal capabilities = new ThreadLocal<>(); + private static final ThreadLocal capabilities = new ThreadLocal<>(); public static void initialize() { diff --git a/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/al/AudioSource.java b/engine-core/plutoaudio/src/main/java/org/plutoengine/audio/al/AudioSource.java old mode 100644 new mode 100755 similarity index 95% rename from plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/al/AudioSource.java rename to engine-core/plutoaudio/src/main/java/org/plutoengine/audio/al/AudioSource.java index ad36184..db89ae9 --- a/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/al/AudioSource.java +++ b/engine-core/plutoaudio/src/main/java/org/plutoengine/audio/al/AudioSource.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.audio.al; +package org.plutoengine.audio.al; import org.joml.Vector3fc; import org.lwjgl.openal.AL10; diff --git a/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/al/AudioTrack.java b/engine-core/plutoaudio/src/main/java/org/plutoengine/audio/al/AudioTrack.java old mode 100644 new mode 100755 similarity index 82% rename from plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/al/AudioTrack.java rename to engine-core/plutoaudio/src/main/java/org/plutoengine/audio/al/AudioTrack.java index 2559450..16c8339 --- a/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/al/AudioTrack.java +++ b/engine-core/plutoaudio/src/main/java/org/plutoengine/audio/al/AudioTrack.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.audio.al; +package org.plutoengine.audio.al; import org.lwjgl.openal.AL10; import org.lwjgl.openal.SOFTDirectChannels; @@ -6,8 +6,8 @@ import org.lwjgl.system.MemoryUtil; import java.nio.ShortBuffer; -import cz.tefek.pluto.engine.audio.IAudioStream; -import cz.tefek.pluto.engine.audio.ISeekableAudioTrack; +import org.plutoengine.audio.ISeekableAudioTrack; +import org.plutoengine.audio.IAudioStream; public class AudioTrack extends AudioSource { @@ -29,17 +29,11 @@ public class AudioTrack extends AudioSource { this.track = track; - switch (track.getChannels()) - { - case 1: - this.format = AL10.AL_FORMAT_MONO16; - break; - case 2: - this.format = AL10.AL_FORMAT_STEREO16; - break; - default: - throw new UnsupportedOperationException("Unsupported number of channels: " + track.getChannels()); - } + this.format = switch (track.getChannels()) { + case 1 -> AL10.AL_FORMAT_MONO16; + case 2 -> AL10.AL_FORMAT_STEREO16; + default -> throw new UnsupportedOperationException("Unsupported number of channels: " + track.getChannels()); + }; int bufferSize = track.getChannels() * BUFFER_SIZE_PER_CHANNEL; diff --git a/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/util/AudioUtil.java b/engine-core/plutoaudio/src/main/java/org/plutoengine/audio/util/AudioUtil.java old mode 100644 new mode 100755 similarity index 88% rename from plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/util/AudioUtil.java rename to engine-core/plutoaudio/src/main/java/org/plutoengine/audio/util/AudioUtil.java index c70d6c9..73c6971 --- a/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/util/AudioUtil.java +++ b/engine-core/plutoaudio/src/main/java/org/plutoengine/audio/util/AudioUtil.java @@ -1,10 +1,10 @@ -package cz.tefek.pluto.engine.audio.util; +package org.plutoengine.audio.util; import org.lwjgl.stb.STBVorbis; import org.lwjgl.stb.STBVorbisInfo; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; public class AudioUtil { diff --git a/engine-core/plutocomponent/build.gradle.kts b/engine-core/plutocomponent/build.gradle.kts new file mode 100755 index 0000000..e1d648f --- /dev/null +++ b/engine-core/plutocomponent/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + java + `java-library` +} + +description = "A module acting as glue for all PlutoEngine components." + +dependencies { + api("org.apache.commons:commons-lang3:3.12.0") + api("org.apache.commons:commons-collections4:4.4") + + api("com.google.code.findbugs:jsr305:3.0.2") +} \ No newline at end of file diff --git a/engine-core/plutocomponent/src/main/java/org/plutoengine/component/AbstractComponent.java b/engine-core/plutocomponent/src/main/java/org/plutoengine/component/AbstractComponent.java new file mode 100755 index 0000000..9843741 --- /dev/null +++ b/engine-core/plutocomponent/src/main/java/org/plutoengine/component/AbstractComponent.java @@ -0,0 +1,49 @@ +package org.plutoengine.component; + +import java.util.Objects; +import java.util.concurrent.atomic.AtomicLong; + +public abstract class AbstractComponent implements IComponent +{ + private static final AtomicLong ID_SOURCE = new AtomicLong(); + + private final long id; + + protected AbstractComponent() + { + this.id = ID_SOURCE.getAndIncrement(); + } + + @Override + public long getID() + { + return this.id; + } + + @Override + public void onMount() throws Exception + { + + } + + @Override + public void onUnmount() throws Exception + { + + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || this.getClass() != o.getClass()) return false; + AbstractComponent that = (AbstractComponent) o; + return this.id == that.id; + } + + @Override + public int hashCode() + { + return Objects.hash(this.id); + } +} diff --git a/engine-core/plutocomponent/src/main/java/org/plutoengine/component/ComponentManager.java b/engine-core/plutocomponent/src/main/java/org/plutoengine/component/ComponentManager.java new file mode 100755 index 0000000..f0fd9d3 --- /dev/null +++ b/engine-core/plutocomponent/src/main/java/org/plutoengine/component/ComponentManager.java @@ -0,0 +1,140 @@ +package org.plutoengine.component; + +import org.apache.commons.collections4.MultiValuedMap; +import org.apache.commons.collections4.multimap.ArrayListValuedHashMap; +import org.apache.commons.collections4.multimap.HashSetValuedHashMap; +import org.apache.commons.lang3.ClassUtils; + +import javax.annotation.Nonnull; +import java.util.*; +import java.util.stream.Stream; + +public class ComponentManager +{ + private final Class base; + + protected final MultiValuedMap, R> components; + protected final Map> tokens; + protected final MultiValuedMap, R> implementationProviders; + protected final MultiValuedMap> implementationReceivers; + + public ComponentManager(@Nonnull Class base) + { + this.base = base; + this.components = new HashSetValuedHashMap<>(); + this.tokens = new HashMap<>(); + this.implementationProviders = new HashSetValuedHashMap<>(); + this.implementationReceivers = new ArrayListValuedHashMap<>(); + } + + public T addComponent(@Nonnull ComponentToken token) + { + T component = token.createInstance(); + var clazz = component.getClass(); + + if (component.isUnique() && this.implementationProviders.containsKey(clazz)) + throw new IllegalArgumentException("Cannot have two components of the same class '%s'".formatted(clazz.getCanonicalName())); + + var superclasses = ClassUtils.getAllSuperclasses(clazz); + + for (var superclass : superclasses) + { + if (superclass.isAssignableFrom(AbstractComponent.class)) + continue; + + this.implementationProviders.put(superclass, component); + this.implementationReceivers.put(component, superclass); + } + + this.implementationProviders.put(clazz, component); + this.components.put(token, component); + this.tokens.put(component, token); + + try + { + component.onMount(); + } + catch (Exception e) + { + throw new RuntimeException("An exception has occured while mounting the component", e); + } + + return component; + } + + public Class getComponentBase() + { + return this.base; + } + + public Stream streamComponents(@Nonnull Class componentClazz) + { + var providers = this.implementationProviders.get(componentClazz); + + return providers.stream().map(componentClazz::cast); + } + + public T getComponent(@Nonnull Class componentClazz) throws NoSuchElementException + { + return this.streamComponents(componentClazz) + .findAny() + .orElseThrow(); + } + + public T getComponent(@Nonnull Class componentClazz, @Nonnull Comparator heuristic) throws NoSuchElementException + { + return this.streamComponents(componentClazz) + .max(heuristic) + .orElseThrow(); + } + + public List getComponents(@Nonnull Class componentClazz) + { + return this.streamComponents(componentClazz).toList(); + } + + public void removeComponent(@Nonnull R component) throws IllegalArgumentException + { + var token = this.tokens.remove(component); + + if (token == null) + throw new IllegalArgumentException("Component to token mapping could not be found: %d -> ???".formatted(component.getID())); + + this.components.removeMapping(token, component); + + var classes = this.implementationReceivers.remove(component); + + classes.forEach(clazz -> this.implementationProviders.removeMapping(clazz, component)); + + try + { + component.onUnmount(); + } + catch (Exception e) + { + throw new RuntimeException("An exception has occured whiile unmounting the component", e); + } + } + + public void removeComponents(@Nonnull ComponentToken componentToken) + { + var activeComponents = this.components.remove(componentToken); + + activeComponents.forEach(component -> { + this.tokens.remove(component); + + var classes = this.implementationReceivers.remove(component); + + classes.forEach(clazz -> this.implementationProviders.removeMapping(clazz, component)); + + try + { + component.onUnmount(); + } + catch (Exception e) + { + throw new RuntimeException("An exception has occured whiile unmounting the component", e); + } + }); + } +} diff --git a/engine-core/plutocomponent/src/main/java/org/plutoengine/component/ComponentToken.java b/engine-core/plutocomponent/src/main/java/org/plutoengine/component/ComponentToken.java new file mode 100755 index 0000000..4d7af15 --- /dev/null +++ b/engine-core/plutocomponent/src/main/java/org/plutoengine/component/ComponentToken.java @@ -0,0 +1,50 @@ +package org.plutoengine.component; + +import javax.annotation.Nonnull; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; + +public final class ComponentToken +{ + private static final AtomicLong ID_SOURCE = new AtomicLong(); + + private final long id; + private final Supplier supplier; + + private ComponentToken(Supplier valueSupplier) + { + this.id = ID_SOURCE.getAndIncrement(); + this.supplier = valueSupplier; + } + + public static ComponentToken create(@Nonnull Supplier valueSupplier) + { + return new ComponentToken<>(valueSupplier); + } + + public T createInstance() + { + return this.supplier.get(); + } + + public long getID() + { + return this.id; + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || this.getClass() != o.getClass()) return false; + ComponentToken that = (ComponentToken) o; + return this.id == that.id; + } + + @Override + public int hashCode() + { + return Objects.hash(this.id); + } +} diff --git a/engine-core/plutocomponent/src/main/java/org/plutoengine/component/IComponent.java b/engine-core/plutocomponent/src/main/java/org/plutoengine/component/IComponent.java new file mode 100755 index 0000000..1504aad --- /dev/null +++ b/engine-core/plutocomponent/src/main/java/org/plutoengine/component/IComponent.java @@ -0,0 +1,22 @@ +package org.plutoengine.component; + +public interface IComponent +{ + /** + * Denotes whether this component should be unique. + * Unique components can only exist once per instance + * in a given {@link ComponentManager}. + * + * @return whether this component should be unique + * + * @author 493msi + * @since 20.2.0.0-alpha.3 + */ + boolean isUnique(); + + long getID(); + + void onMount() throws Exception; + + void onUnmount() throws Exception; +} diff --git a/engine-core/plutocore/build.gradle.kts b/engine-core/plutocore/build.gradle.kts new file mode 100755 index 0000000..abfbbd8 --- /dev/null +++ b/engine-core/plutocore/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + java + `java-library` +} + +description = "The foundation module for games and apps built on top of PlutoEngine." + +dependencies { + api(project(":plutoengine:plutogui")) + api(project(":plutoengine:plutoaudio")) +} \ No newline at end of file diff --git a/plutocore/src/main/java/cz/tefek/pluto/PlutoApplication.java b/engine-core/plutocore/src/main/java/org/plutoengine/PlutoApplication.java old mode 100644 new mode 100755 similarity index 69% rename from plutocore/src/main/java/cz/tefek/pluto/PlutoApplication.java rename to engine-core/plutocore/src/main/java/org/plutoengine/PlutoApplication.java index 3746b33..f2c7593 --- a/plutocore/src/main/java/cz/tefek/pluto/PlutoApplication.java +++ b/engine-core/plutocore/src/main/java/org/plutoengine/PlutoApplication.java @@ -1,20 +1,21 @@ -package cz.tefek.pluto; - -import java.util.Locale; +package org.plutoengine; import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GL33; +import org.plutoengine.audio.al.AudioEngine; +import org.plutoengine.buffer.GLFWImageUtil; +import org.plutoengine.component.ComponentToken; +import org.plutoengine.display.Display; +import org.plutoengine.display.DisplayBuilder; +import org.plutoengine.input.InputBus; +import org.plutoengine.l10n.PlutoL10n; +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; +import org.plutoengine.mod.ModLoader; -import cz.tefek.pluto.engine.audio.al.AudioEngine; -import cz.tefek.pluto.engine.buffer.GLFWImageUtil; -import cz.tefek.pluto.engine.display.Display; -import cz.tefek.pluto.engine.display.DisplayBuilder; -import cz.tefek.pluto.engine.input.InputBus; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; -import cz.tefek.pluto.l10n.PlutoL10n; -import cz.tefek.pluto.modloader.ModLoaderCore; +import java.nio.file.Path; +import java.util.Locale; /** * The main entry point for OpenGL applications built around the Pluto framework. @@ -31,6 +32,11 @@ public abstract class PlutoApplication protected abstract void loop(); + protected PlutoApplication() + { + + } + /** * A set of values used to create a new {@link PlutoApplication}. * @@ -111,6 +117,13 @@ public abstract class PlutoApplication private int windowMinHeight = 600; private int vsync = 0; private boolean windowResizable = true; + private Path[] icons = null; + + public StartupConfig icons(Path... paths) + { + this.icons = paths; + return this; + } public StartupConfig coreProfile(boolean coreProfile) { @@ -169,31 +182,58 @@ public abstract class PlutoApplication } } - public final void run(String[] args, StartupConfig config) throws Exception + /** + * TODO: Start the application in a new thread + * */ + public final void run(String[] args, StartupConfig config) { if (config == null) { config = new StartupConfig(); } - Logger.setup(); + var globalComponents = PlutoGlobal.COMPONENTS; + + globalComponents.addComponent(Logger.TOKEN); Logger.log(SmartSeverity.INFO, "Debug mode: " + (Pluto.DEBUG_MODE ? "enabled" : "disabled")); PlutoL10n.init(Locale.UK); + var local = PlutoLocal.instance(); + var components = local.COMPONENTS; + DisplayBuilder.initGLFW(); + DisplayBuilder displayBuilder; + if (config.coreProfile) { - this.display = new DisplayBuilder().hintOpenGLVersion(config.majorOpenGLVersion, config.minorOpenGLVersion).hintDebugContext(Pluto.DEBUG_MODE).hintMSAA(config.windowMSAA).hintVisible(true).hintResizeable(config.windowResizable).setInitialSize(config.windowInitialWidth, config.windowInitialHeight).export(); + displayBuilder = new DisplayBuilder() + .hintOpenGLVersion(config.majorOpenGLVersion, config.minorOpenGLVersion) + .hintDebugContext(Pluto.DEBUG_MODE) + .hintMSAA(config.windowMSAA) + .hintVisible(true) + .hintResizeable(config.windowResizable) + .setInitialSize(config.windowInitialWidth, config.windowInitialHeight); } else { - this.display = new DisplayBuilder().hintOpenGLVersionLegacy(config.majorOpenGLVersion, config.minorOpenGLVersion).hintDebugContext(Pluto.DEBUG_MODE).hintMSAA(config.windowMSAA).hintVisible(true).hintResizeable(config.windowResizable).setInitialSize(config.windowInitialWidth, config.windowInitialHeight).export(); + displayBuilder = new DisplayBuilder() + .hintOpenGLVersionLegacy(config.majorOpenGLVersion, config.minorOpenGLVersion) + .hintDebugContext(Pluto.DEBUG_MODE) + .hintMSAA(config.windowMSAA) + .hintVisible(true) + .hintResizeable(config.windowResizable) + .setInitialSize(config.windowInitialWidth, config.windowInitialHeight); } - this.display.create(config.windowName); + var displayToken = ComponentToken.create(displayBuilder::export); + this.display = components.addComponent(displayToken); + + this.display.setName(config.windowName); + + this.display.create(); this.display.setWindowSizeLimits(config.windowMinWidth, config.windowMinHeight, GLFW.GLFW_DONT_CARE, GLFW.GLFW_DONT_CARE); @@ -201,20 +241,24 @@ public abstract class PlutoApplication this.display.show(); - // TODO Un-hardcode these - var icons = GLFWImageUtil.loadIconSet("data/icon16.png", "data/icon32.png", "data/icon64.png", "data/icon128.png"); - - this.display.setIcons(icons); - this.display.createOpenGLCapabilities(); - InputBus.init(this.display); + components.addComponent(InputBus.TOKEN); AudioEngine.initialize(); - ModLoaderCore.registerMod(this.getMainModule()); + var modLoader = components.addComponent(ModLoader.TOKEN); + + modLoader.registerMod(this.getMainModule()); + + modLoader.load(); + + if (config.icons != null) + { + var icons = GLFWImageUtil.loadIconSet(config.icons); + this.display.setIcons(icons); + } - ModLoaderCore.loadProcedure(); while (!this.display.isClosing()) { @@ -233,17 +277,21 @@ public abstract class PlutoApplication AudioEngine.exit(); - InputBus.destroy(); - - ModLoaderCore.unloadProcedure(); + modLoader.unload(); GL.destroy(); + components.removeComponent(modLoader); + + components.removeComponents(InputBus.TOKEN); + this.display.destroy(); + components.removeComponents(displayToken); + DisplayBuilder.destroyGLFW(); - Logger.close(); + globalComponents.removeComponents(Logger.TOKEN); } public Display getDisplayInstance() diff --git a/plutocore/src/main/java/cz/tefek/pluto/engine/input/CursorPositionCallback.java b/engine-core/plutocore/src/main/java/org/plutoengine/input/CursorPositionCallback.java old mode 100644 new mode 100755 similarity index 96% rename from plutocore/src/main/java/cz/tefek/pluto/engine/input/CursorPositionCallback.java rename to engine-core/plutocore/src/main/java/org/plutoengine/input/CursorPositionCallback.java index 4d2f967..b9563a8 --- a/plutocore/src/main/java/cz/tefek/pluto/engine/input/CursorPositionCallback.java +++ b/engine-core/plutocore/src/main/java/org/plutoengine/input/CursorPositionCallback.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.input; +package org.plutoengine.input; import org.lwjgl.glfw.GLFWCursorPosCallback; diff --git a/engine-core/plutocore/src/main/java/org/plutoengine/input/InputBus.java b/engine-core/plutocore/src/main/java/org/plutoengine/input/InputBus.java new file mode 100755 index 0000000..b0a63b0 --- /dev/null +++ b/engine-core/plutocore/src/main/java/org/plutoengine/input/InputBus.java @@ -0,0 +1,183 @@ +package org.plutoengine.input; + +import org.lwjgl.glfw.GLFW; + +import org.plutoengine.PlutoLocal; +import org.plutoengine.address.ThreadSensitive; +import org.plutoengine.component.ComponentToken; +import org.plutoengine.component.PlutoLocalComponent; +import org.plutoengine.display.Display; + +@ThreadSensitive(localContexts = true) +public class InputBus extends PlutoLocalComponent +{ + public static final ComponentToken TOKEN = ComponentToken.create(InputBus::new); + + private final KeyboardInputCallback keyboard = new KeyboardInputCallback(); + private final MouseButtonCallback mouseButton = new MouseButtonCallback(); + private final CursorPositionCallback cursorPosition = new CursorPositionCallback(); + private final ScrollInputCallback scroll = new ScrollInputCallback(); + private final KeyboardCharInput charInput = new KeyboardCharInput(); + + private long windowPointer; + + private InputBus() + { + + } + + private static InputBus instance() + { + return PlutoLocal.components().getComponent(InputBus.class); + } + + @Override + public void onMount() + { + var display = PlutoLocal.components().getComponent(Display.class); + this.windowPointer = display.getWindowPointer(); + + GLFW.glfwSetKeyCallback(this.windowPointer, this.keyboard); + GLFW.glfwSetMouseButtonCallback(this.windowPointer, this.mouseButton); + GLFW.glfwSetCursorPosCallback(this.windowPointer, this.cursorPosition); + GLFW.glfwSetScrollCallback(this.windowPointer, this.scroll); + GLFW.glfwSetCharCallback(this.windowPointer, this.charInput); + } + + + @Override + public void onUnmount() + { + GLFW.glfwSetKeyCallback(this.windowPointer, null); + GLFW.glfwSetMouseButtonCallback(this.windowPointer, null); + GLFW.glfwSetCursorPosCallback(this.windowPointer, null); + GLFW.glfwSetScrollCallback(this.windowPointer, null); + GLFW.glfwSetCharCallback(this.windowPointer, null); + + this.scroll.free(); + this.mouseButton.free(); + this.keyboard.free(); + this.cursorPosition.free(); + this.charInput.free(); + } + + public static KeyboardInputCallback keyboard() + { + return instance().keyboard; + } + + public static MouseButtonCallback mouseButtons() + { + return instance().mouseButton; + } + + public static ScrollInputCallback scroll() + { + return instance().scroll; + } + + public static CursorPositionCallback cursorPosition() + { + return instance().cursorPosition; + } + + public static KeyboardCharInput charInput() + { + return instance().charInput; + } + + public static void resetStates() + { + var instance = instance(); + + instance.keyboard.resetPressed(); + instance.mouseButton.reset(); + instance.scroll.reset(); + instance.cursorPosition.reset(); + instance.charInput.reset(); + } + + @Override + public boolean isUnique() + { + return true; + } + + @ThreadSensitive(localContexts = true) + public static class Mouse + { + public static boolean clicked(int button) + { + return instance().mouseButton.buttonClicked[button]; + } + + public static boolean released(int button) + { + return instance().mouseButton.buttonReleased[button]; + } + + public static boolean isButtonDown(int button) + { + return instance().mouseButton.buttonDown[button]; + } + + public static double getX() + { + return instance().cursorPosition.getX(); + } + + public static double getY() + { + return instance().cursorPosition.getY(); + } + + public static boolean isInside(int x1, int y1, int x2, int y2) + { + return instance().cursorPosition.isInside(x1, y1, x2, y2); + } + + public static double getDX() + { + return instance().cursorPosition.getDeltaX(); + } + + public static double getDY() + { + return instance().cursorPosition.getDeltaY(); + } + + public static double getScrollX() + { + return instance().scroll.getXScroll(); + } + + public static double getScrollY() + { + return instance().scroll.getYScroll(); + } + } + + @ThreadSensitive(localContexts = true) + public static class Keyboard + { + public static boolean pressed(int key) + { + return instance().keyboard.hasBeenPressed(key); + } + + public static boolean released(int key) + { + return instance().keyboard.hasBeenReleased(key); + } + + public static boolean isKeyDown(int key) + { + return instance().keyboard.isKeyDown(key); + } + + public static String getTypedText() + { + return instance().charInput.getTypedText(); + } + } +} diff --git a/plutocore/src/main/java/cz/tefek/pluto/engine/input/KeyboardCharInput.java b/engine-core/plutocore/src/main/java/org/plutoengine/input/KeyboardCharInput.java old mode 100644 new mode 100755 similarity index 92% rename from plutocore/src/main/java/cz/tefek/pluto/engine/input/KeyboardCharInput.java rename to engine-core/plutocore/src/main/java/org/plutoengine/input/KeyboardCharInput.java index 1a849ca..8b9f04d --- a/plutocore/src/main/java/cz/tefek/pluto/engine/input/KeyboardCharInput.java +++ b/engine-core/plutocore/src/main/java/org/plutoengine/input/KeyboardCharInput.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.input; +package org.plutoengine.input; import org.lwjgl.glfw.GLFWCharCallback; diff --git a/plutocore/src/main/java/cz/tefek/pluto/engine/input/KeyboardInputCallback.java b/engine-core/plutocore/src/main/java/org/plutoengine/input/KeyboardInputCallback.java old mode 100644 new mode 100755 similarity index 88% rename from plutocore/src/main/java/cz/tefek/pluto/engine/input/KeyboardInputCallback.java rename to engine-core/plutocore/src/main/java/org/plutoengine/input/KeyboardInputCallback.java index 4c65fa8..88032cc --- a/plutocore/src/main/java/cz/tefek/pluto/engine/input/KeyboardInputCallback.java +++ b/engine-core/plutocore/src/main/java/org/plutoengine/input/KeyboardInputCallback.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.input; +package org.plutoengine.input; import org.lwjgl.glfw.GLFWKeyCallback; @@ -11,8 +11,8 @@ import static org.lwjgl.glfw.GLFW.GLFW_RELEASE; public class KeyboardInputCallback extends GLFWKeyCallback { private final Set keyPressed = new HashSet<>(); - private Set keyDown = new HashSet<>(); - private Set keyReleased = new HashSet<>(); + private final Set keyDown = new HashSet<>(); + private final Set keyReleased = new HashSet<>(); @Override public void invoke(long window, int key, int scancode, int action, int mods) diff --git a/plutocore/src/main/java/cz/tefek/pluto/engine/input/MouseButtonCallback.java b/engine-core/plutocore/src/main/java/org/plutoengine/input/MouseButtonCallback.java old mode 100644 new mode 100755 similarity index 95% rename from plutocore/src/main/java/cz/tefek/pluto/engine/input/MouseButtonCallback.java rename to engine-core/plutocore/src/main/java/org/plutoengine/input/MouseButtonCallback.java index 503751c..87b5b07 --- a/plutocore/src/main/java/cz/tefek/pluto/engine/input/MouseButtonCallback.java +++ b/engine-core/plutocore/src/main/java/org/plutoengine/input/MouseButtonCallback.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.input; +package org.plutoengine.input; import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFWMouseButtonCallback; diff --git a/plutocore/src/main/java/cz/tefek/pluto/engine/input/ScrollInputCallback.java b/engine-core/plutocore/src/main/java/org/plutoengine/input/ScrollInputCallback.java old mode 100644 new mode 100755 similarity index 93% rename from plutocore/src/main/java/cz/tefek/pluto/engine/input/ScrollInputCallback.java rename to engine-core/plutocore/src/main/java/org/plutoengine/input/ScrollInputCallback.java index cd8e0a5..5940392 --- a/plutocore/src/main/java/cz/tefek/pluto/engine/input/ScrollInputCallback.java +++ b/engine-core/plutocore/src/main/java/org/plutoengine/input/ScrollInputCallback.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.input; +package org.plutoengine.input; import org.lwjgl.glfw.GLFWScrollCallback; diff --git a/engine-core/plutodisplay/build.gradle.kts b/engine-core/plutodisplay/build.gradle.kts new file mode 100755 index 0000000..24f9b21 --- /dev/null +++ b/engine-core/plutodisplay/build.gradle.kts @@ -0,0 +1,24 @@ +import org.plutoengine.Versions + +plugins { + java + `java-library` +} + +description = "" + +dependencies { + api(project(":plutoengine:plutoruntime")) + + api("org.lwjgl", "lwjgl") + api("org.lwjgl", "lwjgl-glfw") + api("org.lwjgl", "lwjgl-opengl") + api("org.lwjgl", "lwjgl-stb") + runtimeOnly("org.lwjgl", "lwjgl", classifier = Versions.lwjglNatives) + runtimeOnly("org.lwjgl", "lwjgl-glfw", classifier = Versions.lwjglNatives) + runtimeOnly("org.lwjgl", "lwjgl-opengl", classifier = Versions.lwjglNatives) + runtimeOnly("org.lwjgl", "lwjgl-stb", classifier = Versions.lwjglNatives) + + api("com.code-disaster.steamworks4j", "steamworks4j", Versions.steamworks4jVersion) + api("com.code-disaster.steamworks4j", "steamworks4j-server", Versions.steamworks4jServerVersion) +} \ No newline at end of file diff --git a/engine-core/plutodisplay/src/main/java/org/plutoengine/ModGLFW.java b/engine-core/plutodisplay/src/main/java/org/plutoengine/ModGLFW.java new file mode 100755 index 0000000..275ed8d --- /dev/null +++ b/engine-core/plutodisplay/src/main/java/org/plutoengine/ModGLFW.java @@ -0,0 +1,11 @@ +package org.plutoengine; + +import org.lwjgl.glfw.GLFW; + +import org.plutoengine.mod.ModEntry; + +@ModEntry(modID = "glfw", version = ModGLFW.VERSION, dependencies = ModLWJGL.class) +public class ModGLFW +{ + public static final String VERSION = GLFW.GLFW_VERSION_MAJOR + "." + GLFW.GLFW_VERSION_MINOR + "." + GLFW.GLFW_VERSION_REVISION; +} diff --git a/engine-core/plutodisplay/src/main/java/org/plutoengine/ModLWJGL.java b/engine-core/plutodisplay/src/main/java/org/plutoengine/ModLWJGL.java new file mode 100755 index 0000000..d61eb8c --- /dev/null +++ b/engine-core/plutodisplay/src/main/java/org/plutoengine/ModLWJGL.java @@ -0,0 +1,12 @@ +package org.plutoengine; + +import org.lwjgl.Version; + +import org.plutoengine.mod.ModEntry; + +@ModEntry(modID = "lwjgl", + version = ModLWJGL.version) +public class ModLWJGL +{ + public static final String version = Version.VERSION_MAJOR + "." + Version.VERSION_MINOR + "." + Version.VERSION_REVISION; +} diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/buffer/BufferHelper.java b/engine-core/plutodisplay/src/main/java/org/plutoengine/buffer/BufferHelper.java old mode 100644 new mode 100755 similarity index 65% rename from plutostatic/src/main/java/cz/tefek/pluto/engine/buffer/BufferHelper.java rename to engine-core/plutodisplay/src/main/java/org/plutoengine/buffer/BufferHelper.java index e15c0dc..9fef20a --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/buffer/BufferHelper.java +++ b/engine-core/plutodisplay/src/main/java/org/plutoengine/buffer/BufferHelper.java @@ -1,6 +1,5 @@ -package cz.tefek.pluto.engine.buffer; +package org.plutoengine.buffer; -import org.apache.commons.io.IOUtils; import org.lwjgl.BufferUtils; import java.io.IOException; @@ -11,8 +10,6 @@ import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import cz.tefek.pluto.io.asl.resource.ResourceAddress; - /** * A utility class to handle primitive native buffers. * @@ -101,35 +98,6 @@ public final class BufferHelper return newBuffer; } - /** - * Loads a file denoted by the specified path and returns a - * {@link ByteBuffer} containing the read bytes. - * - * @param path The file's path. - * @return A {@link ByteBuffer} containing the file's contents. - * @throws IOException Upon standard I/O errors. - * - * @author 493msi - * @since 0.1 - */ - public static ByteBuffer readToFlippedByteBuffer(String path) throws IOException - { - try (var fc = FileChannel.open(Path.of(path))) - { - var size = fc.size(); - - if (size > Integer.MAX_VALUE) - { - throw new IOException("File ' + pah + ' is too big to be read into a ByteBuffer!"); - } - - ByteBuffer buf = BufferUtils.createByteBuffer((int) size); - fc.read(buf); - buf.flip(); - return buf; - } - } - /** * Loads a file denoted by the specified {@link Path} and fills the input * {@link ByteBuffer} with the read bytes. @@ -158,25 +126,7 @@ public final class BufferHelper } /** - * {@link ResourceAddress} version of - * {@link BufferHelper#readToByteBuffer(Path path, ByteBuffer buf)}. - * - * @param addr The file's {@link ResourceAddress}. - * @param buf The input buffer to be filled with data. - * - * @return The input {@link ByteBuffer}. - * @throws IOException Upon standard I/O errors. - * - * @author 493msi - * @since 0.3 - */ - public static ByteBuffer readToByteBuffer(ResourceAddress addr, ByteBuffer buf) throws IOException - { - return readToByteBuffer(addr.toNIOPath(), buf); - } - - /** - * Loads a file denoted by the specified {@link ResourceAddress} and returns + * Loads a file denoted by the specified {@link Path} and returns * a {@link ByteBuffer} containing the read bytes. * * @param path The file's path. @@ -186,12 +136,8 @@ public final class BufferHelper * @author 493msi * @since 0.3 */ - public static ByteBuffer readToFlippedByteBuffer(ResourceAddress path) throws IOException + public static ByteBuffer readToFlippedByteBuffer(Path path) throws IOException { - try (var is = Files.newInputStream(path.toNIOPath())) - { - var ba = IOUtils.toByteArray(is); - return flippedByteBuffer(ba); - } + return flippedByteBuffer(Files.readAllBytes(path)); } } diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/buffer/GLFWImageUtil.java b/engine-core/plutodisplay/src/main/java/org/plutoengine/buffer/GLFWImageUtil.java old mode 100644 new mode 100755 similarity index 86% rename from plutostatic/src/main/java/cz/tefek/pluto/engine/buffer/GLFWImageUtil.java rename to engine-core/plutodisplay/src/main/java/org/plutoengine/buffer/GLFWImageUtil.java index 6f1f825..6954fe2 --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/buffer/GLFWImageUtil.java +++ b/engine-core/plutodisplay/src/main/java/org/plutoengine/buffer/GLFWImageUtil.java @@ -1,12 +1,11 @@ -package cz.tefek.pluto.engine.buffer; +package org.plutoengine.buffer; import org.lwjgl.BufferUtils; import org.lwjgl.glfw.GLFWImage; +import org.plutoengine.tpl.ImageLoader; import java.nio.file.Path; -import cz.tefek.pluto.io.tpl.TPL; - /** * A utility class to load image files for use in GLFW. * @@ -26,13 +25,13 @@ public class GLFWImageUtil * @author 493msi * @since 0.2 */ - public static GLFWImage.Buffer loadIconSet(String... icons) + public static GLFWImage.Buffer loadIconSet(Path... icons) { var icon = GLFWImage.create(icons.length); - for (String iconPath : icons) + for (var iconPath : icons) { - var img = TPL.loadSpecial(Path.of(iconPath), false); + var img = ImageLoader.loadSpecial(iconPath, false); var imgData = img.getData(); int imgWidth = img.getWidth(); int imgHeight = img.getHeight(); diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/buffer/package-info.java b/engine-core/plutodisplay/src/main/java/org/plutoengine/buffer/package-info.java old mode 100644 new mode 100755 similarity index 68% rename from plutostatic/src/main/java/cz/tefek/pluto/engine/buffer/package-info.java rename to engine-core/plutodisplay/src/main/java/org/plutoengine/buffer/package-info.java index 74612f6..7c020ff --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/buffer/package-info.java +++ b/engine-core/plutodisplay/src/main/java/org/plutoengine/buffer/package-info.java @@ -4,4 +4,4 @@ * @author 493msi * */ -package cz.tefek.pluto.engine.buffer; +package org.plutoengine.buffer; diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/display/Display.java b/engine-core/plutodisplay/src/main/java/org/plutoengine/display/Display.java old mode 100644 new mode 100755 similarity index 60% rename from plutostatic/src/main/java/cz/tefek/pluto/engine/display/Display.java rename to engine-core/plutodisplay/src/main/java/org/plutoengine/display/Display.java index 465a506..e64243b --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/display/Display.java +++ b/engine-core/plutodisplay/src/main/java/org/plutoengine/display/Display.java @@ -1,16 +1,15 @@ -package cz.tefek.pluto.engine.display; +package org.plutoengine.display; import org.lwjgl.glfw.*; -import org.lwjgl.opengl.ARBDebugOutput; -import org.lwjgl.opengl.GL; -import org.lwjgl.opengl.GL33; -import org.lwjgl.opengl.GLDebugMessageARBCallback; +import org.lwjgl.opengl.*; import org.lwjgl.system.MemoryUtil; -import cz.tefek.pluto.annotation.ThreadSensitive; -import cz.tefek.pluto.engine.gl.GLDebugInfo; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; +import org.plutoengine.Pluto; +import org.plutoengine.address.ThreadSensitive; +import org.plutoengine.component.PlutoLocalComponent; +import org.plutoengine.gl.GLDebugInfo; +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; /** * A wrapper class to provide abstraction over GLFW windows. @@ -18,37 +17,45 @@ import cz.tefek.pluto.io.logger.SmartSeverity; * @author 493msi * @since 0.2 */ -@ThreadSensitive -public class Display +@ThreadSensitive(localContexts = true) +public class Display extends PlutoLocalComponent { int width; int height; boolean debugMode; boolean coreProfile = true; + private String name = Pluto.ENGINE_NAME; + private boolean wasResized; private boolean openGLContext; private long windowPointer; - private final GLFWErrorCallback glfwErrorCallback; + private final GLFWErrorCallbackI glfwErrorCallback; - private GLFWWindowSizeCallback resizeCallback; + private GLFWWindowSizeCallbackI resizeCallback; - private GLDebugMessageARBCallback glDebugCallback; + private GLDebugMessageARBCallbackI glDebugCallback; Display() { - this.glfwErrorCallback = new DisplayErrorCallback(); + this.windowPointer = MemoryUtil.NULL; + + this.glfwErrorCallback = (int error, long description) -> { + Logger.logf(SmartSeverity.ERROR, "GLFW Error code %d:\n", error); + Logger.logf(GLFWErrorCallback.getDescription(description)); + }; + GLFW.glfwSetErrorCallback(this.glfwErrorCallback); } - public void create(String name) + public void create() { GLFW.glfwWindowHint(GLFW.GLFW_STENCIL_BITS, 4); - this.windowPointer = GLFW.glfwCreateWindow(this.width, this.height, name, MemoryUtil.NULL, MemoryUtil.NULL); + this.windowPointer = GLFW.glfwCreateWindow(this.width, this.height, this.name, MemoryUtil.NULL, MemoryUtil.NULL); if (this.windowPointer == MemoryUtil.NULL) { @@ -68,25 +75,21 @@ public class Display GLFW.glfwMakeContextCurrent(this.windowPointer); - this.resizeCallback = new GLFWWindowSizeCallback() { - @Override - public void invoke(long window, int width, int height) + this.resizeCallback = (long window, int width, int height) -> { + if (width > 0 && height > 0) { - if (width > 0 && height > 0) + if (this.debugMode) { - if (Display.this.debugMode) - { - Logger.logf(SmartSeverity.INFO, "Resized to %dx%d.\n", width, height); - } + Logger.logf(SmartSeverity.INFO, "Resized to %dx%d.\n", width, height); + } - Display.this.width = width; - Display.this.height = height; - Display.this.wasResized = true; + this.width = width; + this.height = height; + this.wasResized = true; - if (Display.this.openGLContext) - { - GL33.glViewport(0, 0, Display.this.width, Display.this.height); - } + if (this.openGLContext) + { + GL33.glViewport(0, 0, this.width, this.height); } } }; @@ -96,7 +99,10 @@ public class Display public void setName(String newName) { - GLFW.glfwSetWindowTitle(this.windowPointer, newName); + this.name = newName; + + if (this.windowPointer != MemoryUtil.NULL) + GLFW.glfwSetWindowTitle(this.windowPointer, this.name); } public void show() @@ -152,19 +158,19 @@ public class Display public void destroy() { - if (this.glfwErrorCallback != null) + if (this.glfwErrorCallback instanceof GLFWErrorCallback glfwErrorCallback) { - this.glfwErrorCallback.free(); + glfwErrorCallback.free(); } - if (this.glDebugCallback != null) + if (this.glDebugCallback instanceof GLDebugMessageARBCallback glDebugMessageARBCallback) { - this.glDebugCallback.free(); + glDebugMessageARBCallback.free(); } - if (this.resizeCallback != null) + if (this.resizeCallback instanceof GLFWWindowSizeCallback windowSizeCallback) { - this.resizeCallback.free(); + windowSizeCallback.free(); } if (this.windowPointer != MemoryUtil.NULL) @@ -201,16 +207,12 @@ public class Display if (this.debugMode) { - this.glDebugCallback = new GLDebugMessageARBCallback() { - @Override - public void invoke(int source, int type, int id, int severity, int length, long messagePtr, long userParam) - { - var message = GLDebugMessageARBCallback.getMessage(length, messagePtr); - Logger.log(SmartSeverity.WARNING, message); - } + this.glDebugCallback = (int source, int type, int id, int severity, int length, long messagePtr, long userParam) -> { + var message = GLDebugMessageARBCallback.getMessage(length, messagePtr); + Logger.log(SmartSeverity.WARNING, message); }; - ARBDebugOutput.glDebugMessageCallbackARB(this.glDebugCallback, 0); + ARBDebugOutput.glDebugMessageCallbackARB(this.glDebugCallback, MemoryUtil.NULL); } GL33.glEnable(GL33.GL_CULL_FACE); @@ -218,4 +220,10 @@ public class Display this.openGLContext = true; } + + @Override + public boolean isUnique() + { + return true; + } } diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/display/DisplayBuilder.java b/engine-core/plutodisplay/src/main/java/org/plutoengine/display/DisplayBuilder.java old mode 100644 new mode 100755 similarity index 98% rename from plutostatic/src/main/java/cz/tefek/pluto/engine/display/DisplayBuilder.java rename to engine-core/plutodisplay/src/main/java/org/plutoengine/display/DisplayBuilder.java index c4fe319..32a1018 --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/display/DisplayBuilder.java +++ b/engine-core/plutodisplay/src/main/java/org/plutoengine/display/DisplayBuilder.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.display; +package org.plutoengine.display; import org.lwjgl.glfw.GLFW; diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/display/Framerate.java b/engine-core/plutodisplay/src/main/java/org/plutoengine/display/Framerate.java old mode 100644 new mode 100755 similarity index 92% rename from plutostatic/src/main/java/cz/tefek/pluto/engine/display/Framerate.java rename to engine-core/plutodisplay/src/main/java/org/plutoengine/display/Framerate.java index e183aeb..087ffe9 --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/display/Framerate.java +++ b/engine-core/plutodisplay/src/main/java/org/plutoengine/display/Framerate.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.display; +package org.plutoengine.display; import java.util.Deque; import java.util.concurrent.LinkedBlockingDeque; @@ -20,7 +20,7 @@ public class Framerate private static boolean firstRemoved = false; - private static Deque drawTimestamps = new LinkedBlockingDeque<>(); + private static final Deque drawTimestamps = new LinkedBlockingDeque<>(); public static double getFrameTime() { diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/display/package-info.java b/engine-core/plutodisplay/src/main/java/org/plutoengine/display/package-info.java old mode 100644 new mode 100755 similarity index 63% rename from plutostatic/src/main/java/cz/tefek/pluto/engine/display/package-info.java rename to engine-core/plutodisplay/src/main/java/org/plutoengine/display/package-info.java index f32cb0e..10831d5 --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/display/package-info.java +++ b/engine-core/plutodisplay/src/main/java/org/plutoengine/display/package-info.java @@ -4,4 +4,4 @@ * @author 493msi * */ -package cz.tefek.pluto.engine.display; +package org.plutoengine.display; diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/gl/GLDebugInfo.java b/engine-core/plutodisplay/src/main/java/org/plutoengine/gl/GLDebugInfo.java old mode 100644 new mode 100755 similarity index 94% rename from plutostatic/src/main/java/cz/tefek/pluto/engine/gl/GLDebugInfo.java rename to engine-core/plutodisplay/src/main/java/org/plutoengine/gl/GLDebugInfo.java index c90c6a1..0882af2 --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/gl/GLDebugInfo.java +++ b/engine-core/plutodisplay/src/main/java/org/plutoengine/gl/GLDebugInfo.java @@ -1,12 +1,12 @@ -package cz.tefek.pluto.engine.gl; +package org.plutoengine.gl; import org.lwjgl.opengl.ARBFramebufferObject; import org.lwjgl.opengl.ARBUniformBufferObject; import org.lwjgl.opengl.GL33; import org.lwjgl.opengl.GLCapabilities; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; public class GLDebugInfo { diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/gl/IOpenGLEnum.java b/engine-core/plutodisplay/src/main/java/org/plutoengine/gl/IOpenGLEnum.java old mode 100644 new mode 100755 similarity index 81% rename from plutostatic/src/main/java/cz/tefek/pluto/engine/gl/IOpenGLEnum.java rename to engine-core/plutodisplay/src/main/java/org/plutoengine/gl/IOpenGLEnum.java index bbf1e91..1162828 --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/gl/IOpenGLEnum.java +++ b/engine-core/plutodisplay/src/main/java/org/plutoengine/gl/IOpenGLEnum.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.gl; +package org.plutoengine.gl; /** * Denotes the implementing class is a set of OpenGL enums. diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/ProjectionMatrix.java b/engine-core/plutodisplay/src/main/java/org/plutoengine/math/ProjectionMatrix.java old mode 100644 new mode 100755 similarity index 97% rename from plutostatic/src/main/java/cz/tefek/pluto/engine/math/ProjectionMatrix.java rename to engine-core/plutodisplay/src/main/java/org/plutoengine/math/ProjectionMatrix.java index 3d02d73..24fc182 --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/ProjectionMatrix.java +++ b/engine-core/plutodisplay/src/main/java/org/plutoengine/math/ProjectionMatrix.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.math; +package org.plutoengine.math; import org.joml.Matrix4f; diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/TransformationMatrix.java b/engine-core/plutodisplay/src/main/java/org/plutoengine/math/TransformationMatrix.java old mode 100644 new mode 100755 similarity index 98% rename from plutostatic/src/main/java/cz/tefek/pluto/engine/math/TransformationMatrix.java rename to engine-core/plutodisplay/src/main/java/org/plutoengine/math/TransformationMatrix.java index 52d641b..a8bca07 --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/TransformationMatrix.java +++ b/engine-core/plutodisplay/src/main/java/org/plutoengine/math/TransformationMatrix.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.math; +package org.plutoengine.math; import org.joml.Matrix3x2f; import org.joml.Matrix4f; diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/ViewMatrix.java b/engine-core/plutodisplay/src/main/java/org/plutoengine/math/ViewMatrix.java old mode 100644 new mode 100755 similarity index 94% rename from plutostatic/src/main/java/cz/tefek/pluto/engine/math/ViewMatrix.java rename to engine-core/plutodisplay/src/main/java/org/plutoengine/math/ViewMatrix.java index 9fc3f44..f78ac42 --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/ViewMatrix.java +++ b/engine-core/plutodisplay/src/main/java/org/plutoengine/math/ViewMatrix.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.math; +package org.plutoengine.math; import org.joml.Matrix3x2f; diff --git a/plutolib/src/main/java/cz/tefek/pluto/math/package-info.java b/engine-core/plutodisplay/src/main/java/org/plutoengine/math/package-info.java old mode 100644 new mode 100755 similarity index 65% rename from plutolib/src/main/java/cz/tefek/pluto/math/package-info.java rename to engine-core/plutodisplay/src/main/java/org/plutoengine/math/package-info.java index b439110..cc1dc3b --- a/plutolib/src/main/java/cz/tefek/pluto/math/package-info.java +++ b/engine-core/plutodisplay/src/main/java/org/plutoengine/math/package-info.java @@ -4,4 +4,4 @@ * @author 493msi * */ -package cz.tefek.pluto.math; +package org.plutoengine.math; diff --git a/engine-core/plutoframebuffer/build.gradle.kts b/engine-core/plutoframebuffer/build.gradle.kts new file mode 100755 index 0000000..edd87af --- /dev/null +++ b/engine-core/plutoframebuffer/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + java + `java-library` +} + +description = "" + +dependencies { + api(project(":plutoengine:plutotexture")) +} \ No newline at end of file diff --git a/plutoframebuffer/src/main/java/cz/tefek/pluto/engine/graphics/gl/fbo/Framebuffer.java b/engine-core/plutoframebuffer/src/main/java/org/plutoengine/graphics/gl/fbo/Framebuffer.java old mode 100644 new mode 100755 similarity index 91% rename from plutoframebuffer/src/main/java/cz/tefek/pluto/engine/graphics/gl/fbo/Framebuffer.java rename to engine-core/plutoframebuffer/src/main/java/org/plutoengine/graphics/gl/fbo/Framebuffer.java index 4358371..2e0a375 --- a/plutoframebuffer/src/main/java/cz/tefek/pluto/engine/graphics/gl/fbo/Framebuffer.java +++ b/engine-core/plutoframebuffer/src/main/java/org/plutoengine/graphics/gl/fbo/Framebuffer.java @@ -1,18 +1,18 @@ -package cz.tefek.pluto.engine.graphics.gl.fbo; +package org.plutoengine.graphics.gl.fbo; + +import org.lwjgl.opengl.GL33; import java.util.ArrayList; import java.util.List; -import org.lwjgl.opengl.GL33; - -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; public class Framebuffer { private int id; - private List textures; + private final List textures; private FramebufferDepthTexture depthTexture; diff --git a/plutoframebuffer/src/main/java/cz/tefek/pluto/engine/graphics/gl/fbo/FramebufferDepthTexture.java b/engine-core/plutoframebuffer/src/main/java/org/plutoengine/graphics/gl/fbo/FramebufferDepthTexture.java old mode 100644 new mode 100755 similarity index 71% rename from plutoframebuffer/src/main/java/cz/tefek/pluto/engine/graphics/gl/fbo/FramebufferDepthTexture.java rename to engine-core/plutoframebuffer/src/main/java/org/plutoengine/graphics/gl/fbo/FramebufferDepthTexture.java index 3cad363..b7448f3 --- a/plutoframebuffer/src/main/java/cz/tefek/pluto/engine/graphics/gl/fbo/FramebufferDepthTexture.java +++ b/engine-core/plutoframebuffer/src/main/java/org/plutoengine/graphics/gl/fbo/FramebufferDepthTexture.java @@ -1,10 +1,10 @@ -package cz.tefek.pluto.engine.graphics.gl.fbo; +package org.plutoengine.graphics.gl.fbo; import org.lwjgl.opengl.GL33; -import cz.tefek.pluto.engine.graphics.texture.MagFilter; -import cz.tefek.pluto.engine.graphics.texture.MinFilter; -import cz.tefek.pluto.engine.graphics.texture.WrapMode; +import org.plutoengine.graphics.texture.MagFilter; +import org.plutoengine.graphics.texture.MinFilter; +import org.plutoengine.graphics.texture.WrapMode; public class FramebufferDepthTexture extends FramebufferTexture { diff --git a/plutoframebuffer/src/main/java/cz/tefek/pluto/engine/graphics/gl/fbo/FramebufferTexture.java b/engine-core/plutoframebuffer/src/main/java/org/plutoengine/graphics/gl/fbo/FramebufferTexture.java old mode 100644 new mode 100755 similarity index 72% rename from plutoframebuffer/src/main/java/cz/tefek/pluto/engine/graphics/gl/fbo/FramebufferTexture.java rename to engine-core/plutoframebuffer/src/main/java/org/plutoengine/graphics/gl/fbo/FramebufferTexture.java index 5dda373..b76552a --- a/plutoframebuffer/src/main/java/cz/tefek/pluto/engine/graphics/gl/fbo/FramebufferTexture.java +++ b/engine-core/plutoframebuffer/src/main/java/org/plutoengine/graphics/gl/fbo/FramebufferTexture.java @@ -1,11 +1,11 @@ -package cz.tefek.pluto.engine.graphics.gl.fbo; +package org.plutoengine.graphics.gl.fbo; import org.lwjgl.system.MemoryUtil; -import cz.tefek.pluto.engine.graphics.texture.MagFilter; -import cz.tefek.pluto.engine.graphics.texture.MinFilter; -import cz.tefek.pluto.engine.graphics.texture.WrapMode; -import cz.tefek.pluto.engine.graphics.texture.texture2d.RectangleTexture; +import org.plutoengine.graphics.texture.MagFilter; +import org.plutoengine.graphics.texture.MinFilter; +import org.plutoengine.graphics.texture.WrapMode; +import org.plutoengine.graphics.texture.texture2d.RectangleTexture; public class FramebufferTexture extends RectangleTexture { diff --git a/engine-core/plutogui/build.gradle.kts b/engine-core/plutogui/build.gradle.kts new file mode 100755 index 0000000..6c55bf2 --- /dev/null +++ b/engine-core/plutogui/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + java + `java-library` +} + +description = "" + +dependencies { + api(project(":plutoengine:plutospritesheet")) +} \ No newline at end of file diff --git a/engine-core/plutogui/src/main/java/org/plutoengine/graphics/PlutoGUIMod.java b/engine-core/plutogui/src/main/java/org/plutoengine/graphics/PlutoGUIMod.java new file mode 100755 index 0000000..cbac52d --- /dev/null +++ b/engine-core/plutogui/src/main/java/org/plutoengine/graphics/PlutoGUIMod.java @@ -0,0 +1,57 @@ +package org.plutoengine.graphics; + +import org.plutoengine.Pluto; +import org.plutoengine.graphics.font.FontManager; +import org.plutoengine.graphics.font.FontShader; +import org.plutoengine.graphics.texture.MagFilter; +import org.plutoengine.graphics.texture.MinFilter; +import org.plutoengine.graphics.texture.WrapMode; +import org.plutoengine.graphics.texture.texture2d.RectangleTexture; +import org.plutoengine.gui.font.FontRenderer; +import org.plutoengine.mod.IModEntryPoint; +import org.plutoengine.mod.Mod; +import org.plutoengine.mod.ModEntry; +import org.plutoengine.shader.RenderShaderBuilder; + +/** + * @author 493msi + * + */ +@ModEntry(modID = PlutoGUIMod.MOD_ID, + dependencies = { PlutoSpriteSheetMod.class }, + version = Pluto.VERSION) +public class PlutoGUIMod implements IModEntryPoint +{ + public static final String MOD_ID = "tefek.plutogui"; + + public static Mod instance; + + public static RectangleTexture uiElementsAtlas; + + private static FontShader fontShader; + + public void onLoad(Mod mod) + { + instance = mod; + + fontShader = new RenderShaderBuilder(mod.getResource("shaders.VertexFontShader#glsl"), mod.getResource("shaders.FragmentFontShader#glsl")).build(FontShader.class, false); + + // Load the default font + FontManager.loadFont(mod.getResource("font.default")); + + FontRenderer.load(fontShader); + + uiElementsAtlas = new RectangleTexture(); + uiElementsAtlas.load(mod.getResource("gui.elements#png"), MagFilter.NEAREST, MinFilter.NEAREST, WrapMode.CLAMP_TO_EDGE, WrapMode.CLAMP_TO_EDGE); + } + + public void onUnload() + { + uiElementsAtlas.delete(); + + FontManager.unloadAll(); + + FontRenderer.unload(); + fontShader.dispose(); + } +} diff --git a/engine-core/plutogui/src/main/java/org/plutoengine/graphics/font/FontManager.java b/engine-core/plutogui/src/main/java/org/plutoengine/graphics/font/FontManager.java new file mode 100755 index 0000000..be6eed3 --- /dev/null +++ b/engine-core/plutogui/src/main/java/org/plutoengine/graphics/font/FontManager.java @@ -0,0 +1,96 @@ +package org.plutoengine.graphics.font; + +import org.plutoengine.graphics.texture.MagFilter; +import org.plutoengine.graphics.texture.MinFilter; +import org.plutoengine.graphics.texture.Texture; +import org.plutoengine.graphics.texture.WrapMode; +import org.plutoengine.graphics.texture.texture2d.RectangleTexture; +import org.plutoengine.gui.font.CharacterInfo; +import org.plutoengine.gui.font.Font; +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; + +public class FontManager +{ + private static final Map fonts = new HashMap<>(); + + public static void loadFont(Path address) + { + String fontname = null; + int width = 0; + int height = 0; + var def = new HashMap(); + + int row = 0; + + try + { + var lines = Files.readAllLines(address.resolve("definitions#txt")); + + for (var line : lines) + { + if (line.startsWith("//")) + continue; + + if (row == 0) + { + String[] fontinfo = line.split(","); + + fontname = fontinfo[0]; + + String[] dim = fontinfo[1].split("x"); + + width = Integer.parseInt(dim[0]); + height = Integer.parseInt(dim[1]); + } + else + { + String[] offs = line.split(" ")[1].split(";"); + + def.put(line.charAt(0), new CharacterInfo(row - 1, Integer.parseInt(offs[0]), Integer.parseInt(offs[1]))); + } + + row++; + } + } + catch (Exception e) + { + Logger.log(SmartSeverity.ERROR, "Could not load font: " + address.toString()); + e.printStackTrace(); + } + + Font font = new Font(fontname, width, height, def); + RectangleTexture texture = new RectangleTexture(); + texture.load(address.resolve("tex#png"), MagFilter.NEAREST, MinFilter.LINEAR, WrapMode.CLAMP_TO_EDGE, WrapMode.CLAMP_TO_EDGE); + font.setTexture(texture); + + fonts.put(fontname, font); + } + + public static void unloadAll() + { + fonts.values() + .stream() + .map(Font::getTexture) + .forEach(Texture::delete); + fonts.clear(); + } + + public static Font getFontByName(String fontname) + { + var font = fonts.get(fontname); + + if (font == null) + { + // Logger.log(SmartSeverity.WARNING, "Font with name " + fontname + " could not be found, using the default one instead (if there is one)."); + return fonts.get("default"); + } + + return font; + } +} diff --git a/engine-core/plutogui/src/main/java/org/plutoengine/graphics/font/FontShader.java b/engine-core/plutogui/src/main/java/org/plutoengine/graphics/font/FontShader.java new file mode 100755 index 0000000..5df97fe --- /dev/null +++ b/engine-core/plutogui/src/main/java/org/plutoengine/graphics/font/FontShader.java @@ -0,0 +1,37 @@ +package org.plutoengine.graphics.font; + +import org.plutoengine.graphics.gl.vao.attrib.ReservedAttributes; +import org.plutoengine.shader.ShaderBase; +import org.plutoengine.shader.ShaderProgram; +import org.plutoengine.shader.VertexArrayAttribute; +import org.plutoengine.shader.uniform.*; +import org.plutoengine.shader.uniform.auto.AutoViewportProjection; + +@ShaderProgram +public final class FontShader extends ShaderBase +{ + @AutoViewportProjection + @Uniform(name = "projection") + public UniformMat4 projectionMatrix; + + @Uniform(name = "transformation") + public UniformMat4 transformationMatrix; + + @Uniform + public UniformVec2 uvBase; + + @Uniform + public UniformVec2 uvDelta; + + @Uniform + public UniformRGBA recolor; + + @Uniform + public UniformBoolean italic; + + @VertexArrayAttribute(ReservedAttributes.POSITION) + public int position; + + @VertexArrayAttribute(ReservedAttributes.UV) + public int uvCoords; +} diff --git a/engine-core/plutogui/src/main/java/org/plutoengine/gui/font/CharacterInfo.java b/engine-core/plutogui/src/main/java/org/plutoengine/gui/font/CharacterInfo.java new file mode 100755 index 0000000..f0187cb --- /dev/null +++ b/engine-core/plutogui/src/main/java/org/plutoengine/gui/font/CharacterInfo.java @@ -0,0 +1,6 @@ +package org.plutoengine.gui.font; + +public record CharacterInfo(int number, int leftOffset, int rightOffset) +{ + +} diff --git a/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/Font.java b/engine-core/plutogui/src/main/java/org/plutoengine/gui/font/Font.java old mode 100644 new mode 100755 similarity index 85% rename from plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/Font.java rename to engine-core/plutogui/src/main/java/org/plutoengine/gui/font/Font.java index b678a72..e9d23c0 --- a/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/Font.java +++ b/engine-core/plutogui/src/main/java/org/plutoengine/gui/font/Font.java @@ -1,15 +1,15 @@ -package cz.tefek.pluto.engine.gui.font; +package org.plutoengine.gui.font; + +import org.plutoengine.graphics.texture.texture2d.RectangleTexture; import java.util.HashMap; -import cz.tefek.pluto.engine.graphics.texture.texture2d.RectangleTexture; - public class Font { private String name; private int width; private int height; - private HashMap definitions; + private final HashMap definitions; private RectangleTexture texture; public Font(String name, int width, int height, HashMap def) diff --git a/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/FontHelper.java b/engine-core/plutogui/src/main/java/org/plutoengine/gui/font/FontHelper.java old mode 100644 new mode 100755 similarity index 88% rename from plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/FontHelper.java rename to engine-core/plutogui/src/main/java/org/plutoengine/gui/font/FontHelper.java index 0e65eab..e8cd2cd --- a/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/FontHelper.java +++ b/engine-core/plutogui/src/main/java/org/plutoengine/gui/font/FontHelper.java @@ -1,6 +1,6 @@ -package cz.tefek.pluto.engine.gui.font; +package org.plutoengine.gui.font; -import cz.tefek.pluto.engine.graphics.font.FontManager; +import org.plutoengine.graphics.font.FontManager; public class FontHelper { @@ -100,9 +100,9 @@ public class FontHelper charInf = font.getDefinitions().get('?'); } - totalSpacing -= charInf.getLeftOffset() * relativeSize - relativeSize; + totalSpacing -= charInf.leftOffset() * relativeSize - relativeSize; totalSpacing += absoluteCharWidth * relativeSize; - totalSpacing -= charInf.getRightOffset() * relativeSize - 1; + totalSpacing -= charInf.rightOffset() * relativeSize - 1; } maxW = Math.max(maxW, totalSpacing); @@ -113,9 +113,8 @@ public class FontHelper public static int calcStringHeight(Object string, float relSize) { - int size = (int) (30f * relSize * string.toString().split("\n").length); - return size; + return (int) (30f * relSize * string.toString().split("\n").length); } } diff --git a/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/FontRenderer.java b/engine-core/plutogui/src/main/java/org/plutoengine/gui/font/FontRenderer.java old mode 100644 new mode 100755 similarity index 81% rename from plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/FontRenderer.java rename to engine-core/plutogui/src/main/java/org/plutoengine/gui/font/FontRenderer.java index a9eba36..ac59c7d --- a/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/FontRenderer.java +++ b/engine-core/plutogui/src/main/java/org/plutoengine/gui/font/FontRenderer.java @@ -1,15 +1,15 @@ -package cz.tefek.pluto.engine.gui.font; +package org.plutoengine.gui.font; import org.joml.Matrix4f; import org.joml.Vector3f; import org.lwjgl.opengl.GL11; -import cz.tefek.pluto.engine.graphics.font.FontManager; -import cz.tefek.pluto.engine.graphics.font.FontShader; -import cz.tefek.pluto.engine.graphics.gl.DrawMode; -import cz.tefek.pluto.engine.graphics.gl.vao.QuadPresets; -import cz.tefek.pluto.engine.graphics.gl.vao.VertexArray; -import cz.tefek.pluto.engine.math.TransformationMatrix; +import org.plutoengine.graphics.font.FontManager; +import org.plutoengine.graphics.font.FontShader; +import org.plutoengine.graphics.gl.DrawMode; +import org.plutoengine.graphics.gl.vao.QuadPresets; +import org.plutoengine.graphics.gl.vao.VertexArray; +import org.plutoengine.math.TransformationMatrix; public class FontRenderer { @@ -72,8 +72,8 @@ public class FontRenderer for (int characterIndex = 0; characterIndex < text.length(); characterIndex++) { - int column = 0; - int row = 0; + int column; + int row; var currentChar = text.charAt(characterIndex); @@ -104,17 +104,11 @@ public class FontRenderer cBef = text.charAt(characterIndex - 2); } - if (c != '\\' || cBef == '\\' && c == '\\') + if (c != '\\' || cBef == '\\') { if (!isShadow) { - char[] col = new char[10]; - - text.getChars(characterIndex + 3, characterIndex + 13, col, 0); - - String clr = String.valueOf(col); - - color(clr); + color(text.substring(characterIndex + 3, characterIndex + 13)); } characterIndex += 13; @@ -141,16 +135,9 @@ public class FontRenderer cBef = text.charAt(characterIndex - 2); } - if (c != '\\' || cBef == '\\' && c == '\\') + if (c != '\\' || cBef == '\\') { - if (text.charAt(characterIndex + 2) == '1') - { - italic(true); - } - else - { - italic(false); - } + italic(text.charAt(characterIndex + 2) == '1'); characterIndex += 2; @@ -164,26 +151,19 @@ public class FontRenderer switch (currentChar) { - case '\n': + case '\n' -> { color(color); drawX = xPos; drawY += lineHeight; continue; - - case ' ': + } + case ' ' -> { drawX += spaceWidth; continue; - - case 'g': - case 'y': - case 'p': - case 'j': - shift = 6 * relativeSize; - - break; - - default: - break; + } + case 'g', 'y', 'p', 'j' -> shift = 6 * relativeSize; + default -> { + } } var fontDefs = font.getDefinitions(); @@ -194,7 +174,7 @@ public class FontRenderer charInf = fontDefs.get('?'); } - var atlasIndex = charInf.getNumber(); + var atlasIndex = charInf.number(); row = atlasIndex / CHAR_WIDTH; column = atlasIndex % CHAR_WIDTH; @@ -204,7 +184,7 @@ public class FontRenderer float v = row * CHAR_HEIGHT; // Offset from the left - drawX -= charInf.getLeftOffset() * relativeSize; + drawX -= charInf.leftOffset() * relativeSize; float posY = shift + drawY; @@ -218,7 +198,7 @@ public class FontRenderer charVAO.draw(DrawMode.TRIANGLES); drawX += charWidth; - drawX -= charInf.getRightOffset() * relativeSize; + drawX -= charInf.rightOffset() * relativeSize; drawX += relativeSize; } @@ -239,10 +219,8 @@ public class FontRenderer dark = 0.35f; } - if (color instanceof float[]) + if (color instanceof float[] c) { - float[] c = (float[]) color; - if (c.length == 4) { recolor(c[0] - dark, c[1] - dark, c[2] - dark, c[3]); @@ -253,10 +231,8 @@ public class FontRenderer } } - if (color instanceof String) + if (color instanceof String col) { - String col = (String) color; - if (col.length() == 7) { recolor(Integer.valueOf(col.substring(1, 3), 16) / 256f - dark, Integer.valueOf(col.substring(3, 5), 16) / 256f - dark, Integer.valueOf(col.substring(5, 7), 16) / 256f - dark, 1); diff --git a/engine-core/plutolib/build.gradle.kts b/engine-core/plutolib/build.gradle.kts new file mode 100755 index 0000000..d767333 --- /dev/null +++ b/engine-core/plutolib/build.gradle.kts @@ -0,0 +1,27 @@ +import org.plutoengine.Versions + +plugins { + java + `java-library` +} + +description = "Multi-purpose utility library that can be used in basically any project." + +dependencies { + api(project(":plutoengine:plutouss2")) + + api("org.jetbrains", "annotations", "23.0.0") + + api("org.yaml", "snakeyaml", "1.28") + + api("com.fasterxml.jackson.core", "jackson-core", "2.13.2") + api("com.fasterxml.jackson.core", "jackson-databind", "2.13.2") + + api("com.google.guava", "guava", "28.0-jre") + + api("org.joml", "joml", Versions.jomlVersion) + api("commons-io", "commons-io", "2.6") + + api("org.apache.commons", "commons-lang3", "3.12.0") + +} \ No newline at end of file diff --git a/engine-core/plutolib/src/main/java/org/plutoengine/address/VirtualAddress.java b/engine-core/plutolib/src/main/java/org/plutoengine/address/VirtualAddress.java new file mode 100755 index 0000000..a2f884c --- /dev/null +++ b/engine-core/plutolib/src/main/java/org/plutoengine/address/VirtualAddress.java @@ -0,0 +1,295 @@ +package org.plutoengine.address; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.KeyDeserializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Range; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; + +@JsonDeserialize(using = VirtualAddress.Deserializer.class, keyUsing = VirtualAddress.KeyAddrDeserializer.class) +@JsonSerialize(using = VirtualAddress.Serializer.class, keyUsing = VirtualAddress.Serializer.class) +public final class VirtualAddress implements Comparable +{ + public static final int MAX_LENGTH = 128; + public static final int MAX_KEYS = 32; + public static final int MAX_KEY_LENGTH = 32; + + public static final int TOKEN_PATH_SEPARATOR = '.'; + public static final int TOKEN_HIERARCHY_UP = '~'; + + private static final VirtualAddress ROOT_ADDRESS = new VirtualAddress("", List.of(), false, 0); + + private final String fullPath; + + private final List components; + + private final boolean relative; + + @Range(from = 0, to = Integer.MAX_VALUE) + private final int rootOffset; + + VirtualAddress(String fullPath, List components, boolean relative, int rootOffset) + { + this.fullPath = fullPath; + this.components = Collections.unmodifiableList(components); + this.relative = relative; + this.rootOffset = rootOffset; + } + + public static VirtualAddress ofRoot() + { + return ROOT_ADDRESS; + } + + public List getComponents() + { + return this.components; + } + + public boolean isRelative() + { + return this.relative; + } + + public boolean isEmpty() + { + return this.fullPath.isEmpty(); + } + + @Range(from = 0, to = Integer.MAX_VALUE) + public int getRootOffset() + { + return this.rootOffset; + } + + @Override + public String toString() + { + return this.fullPath; + } + + @Override + public boolean equals(Object o) + { + if (this == o) + return true; + + if (o == null || this.getClass() != o.getClass()) + return false; + + VirtualAddress that = (VirtualAddress) o; + return this.fullPath.equals(that.fullPath); + } + + @Override + public int hashCode() + { + return Objects.hash(this.fullPath); + } + + public static VirtualAddress parse(String inputStr) + { + return parse(inputStr, false); + } + + public static VirtualAddress parse(String inputStr, boolean permitRelative) + { + var parser = new VirtualAddressParser(permitRelative); + inputStr.codePoints() + .sequential() + .forEachOrdered(parser::accept); + return parser.build(); + } + + public static VirtualAddressParser createParser(boolean permitRelative) + { + return new VirtualAddressParser(permitRelative); + } + + @Override + public int compareTo(VirtualAddress o) + { + if (this.rootOffset != o.rootOffset) + return this.rootOffset - o.rootOffset; + + return this.fullPath.compareToIgnoreCase(o.fullPath); + } + + public int getNameCount() + { + return this.components.size() + this.rootOffset; + } + + public @NotNull VirtualAddress getName(int index) + { + if (index < this.rootOffset) + return new VirtualAddress("", List.of(), true, 1); + + var component = this.components.get(index - this.rootOffset); + + return new VirtualAddress(component, List.of(component), true, 0); + } + + public @NotNull VirtualAddress getParent() + { + if (this.components.isEmpty()) + { + if (!this.relative) + { + throw new IllegalStateException("Cannot get a parent of a root absolute address!"); + } + else + { + var offset = this.rootOffset - 1; + var components = List.of(); + return new VirtualAddress(VirtualAddressParser.getNormalizedString(offset, components), components, true, offset); + } + } + + var componentsNew = this.components.subList(0, this.components.size() - 1); + return new VirtualAddress(VirtualAddressParser.getNormalizedString(this.rootOffset, componentsNew), componentsNew, this.relative, this.rootOffset); + } + + public @NotNull VirtualAddress relativize(@NotNull VirtualAddress other) + { + if (this.relative != other.relative) + throw new IllegalArgumentException("Cannot relativize an address when only one of the inputs is absolute!"); + + if (this.relative && this.rootOffset > other.rootOffset) + throw new IllegalArgumentException("Cannot relativize against a relative address with a root offset higher than the target one!"); + + if (this.isEmpty()) + return other; + + int newOffset = other.rootOffset - this.rootOffset; + + var thIt = this.components.iterator(); + var oIt = other.components.iterator(); + + var newPath = new ArrayList(); + + if (newOffset == 0) + { + while (thIt.hasNext() && oIt.hasNext()) + { + var thComp = thIt.next(); + var oComp = oIt.next(); + + if (!thComp.equals(oComp)) + { + newOffset++; + newPath.add(oComp); + break; + } + } + } + + while (thIt.hasNext()) + { + newOffset++; + thIt.next(); + } + + while (oIt.hasNext()) + newPath.add(oIt.next()); + + return new VirtualAddress(VirtualAddressParser.getNormalizedString(newOffset, newPath), newPath, true, newOffset); + } + + public @NotNull VirtualAddress resolve(@NotNull VirtualAddress other) + { + if (!other.relative) + return other; + + int removedComponentsSigned = other.rootOffset - this.components.size(); + int newOffset = this.rootOffset + Math.max(removedComponentsSigned, 0); + + if (!this.relative && newOffset > 0) + throw new IllegalArgumentException("Cannot resolve a relative address against an absolute address that would make it higher than the root!"); + + int compListEnd = Math.max(-removedComponentsSigned, 0); + var componentsNew = Stream.concat(this.components.stream().limit(compListEnd), other.components.stream()).toList(); + + return new VirtualAddress(VirtualAddressParser.getNormalizedString(newOffset, componentsNew), componentsNew, this.relative, newOffset); + } + + public @NotNull VirtualAddress subAddress(int startIndex, int endIndex) + { + var componentCount = this.components.size(); + var maxComponents = this.rootOffset + componentCount; + + if (startIndex == 0 && endIndex == 0) + { + var components = List.of(); + return new VirtualAddress(VirtualAddressParser.getNormalizedString(0, components), components, this.relative, 0); + } + + if (startIndex < 0 || endIndex > maxComponents || endIndex > startIndex) + throw new IndexOutOfBoundsException(); + + var newComponents = this.components.subList(startIndex - this.rootOffset, endIndex - this.rootOffset); + var newOffset = Math.max(0, this.rootOffset - startIndex); + + return new VirtualAddress(VirtualAddressParser.getNormalizedString(newOffset, newComponents), newComponents, this.relative, newOffset); + } + + public boolean startsWith(@NotNull VirtualAddress other) + { + return this.fullPath.startsWith(other.fullPath); + } + + public boolean endsWith(@NotNull VirtualAddress other) + { + return this.fullPath.endsWith(other.fullPath); + } + + public static class KeyAddrDeserializer extends KeyDeserializer + { + @Override + public VirtualAddress deserializeKey(String key, DeserializationContext ctxt) + { + return VirtualAddress.parse(key, false); + } + } + + public static class Serializer extends StdSerializer + { + public Serializer() + { + super(VirtualAddress.class); + } + + @Override + public void serialize(VirtualAddress value, JsonGenerator gen, SerializerProvider provider) throws IOException + { + gen.writeString(value.fullPath); + } + } + + public static class Deserializer extends StdDeserializer + { + public Deserializer() + { + super(VirtualAddress.class); + } + + @Override + public VirtualAddress deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + { + return VirtualAddress.parse(p.getValueAsString(), false); + } + } +} diff --git a/engine-core/plutolib/src/main/java/org/plutoengine/address/VirtualAddressParseException.java b/engine-core/plutolib/src/main/java/org/plutoengine/address/VirtualAddressParseException.java new file mode 100755 index 0000000..3eeb387 --- /dev/null +++ b/engine-core/plutolib/src/main/java/org/plutoengine/address/VirtualAddressParseException.java @@ -0,0 +1,9 @@ +package org.plutoengine.address; + +public class VirtualAddressParseException extends RuntimeException +{ + public VirtualAddressParseException(String message) + { + super(message); + } +} diff --git a/engine-core/plutolib/src/main/java/org/plutoengine/address/VirtualAddressParser.java b/engine-core/plutolib/src/main/java/org/plutoengine/address/VirtualAddressParser.java new file mode 100755 index 0000000..a834dd9 --- /dev/null +++ b/engine-core/plutolib/src/main/java/org/plutoengine/address/VirtualAddressParser.java @@ -0,0 +1,155 @@ +package org.plutoengine.address; + +import org.jetbrains.annotations.Range; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.List; + +public class VirtualAddressParser +{ + private enum State + { + HIERARCHY_UP, + KEY, + PATH_SEPARATOR + } + + private int position; + + private int depth; + + private State state; + + private final Deque components; + + private StringBuilder tokenBuilder; + + private final boolean permitRelative; + + // Decrement for every root-level ~ + @Range(from = 0, to = Integer.MAX_VALUE) + private int rootOffset = 0; + + VirtualAddressParser(boolean permitRelative) + { + this.depth = 0; + this.position = 0; + this.state = State.PATH_SEPARATOR; + this.components = new ArrayDeque<>(VirtualAddress.MAX_KEYS); + this.permitRelative = permitRelative; + } + + public void accept(int codepoint) + { + switch (this.state) + { + case PATH_SEPARATOR -> { + if (Character.isLetter(codepoint)) + { + if (this.components.size() >= VirtualAddress.MAX_KEYS) + throw new VirtualAddressParseException("Max amount of keys (%d) exceeded!".formatted(VirtualAddress.MAX_KEYS)); + + this.state = State.KEY; + this.tokenBuilder = new StringBuilder(VirtualAddress.MAX_KEY_LENGTH); + this.tokenBuilder.appendCodePoint(codepoint); + } + else if (codepoint == VirtualAddress.TOKEN_HIERARCHY_UP) + { + if (!this.permitRelative) + throw new VirtualAddressParseException("Cannot use the hierarchy-up token (%s) in a non-relative context.".formatted(VirtualAddress.TOKEN_HIERARCHY_UP)); + + this.state = State.HIERARCHY_UP; + this.tokenBuilder = null; + + if (!this.components.isEmpty()) + this.components.removeLast(); + else + this.rootOffset++; + } + else + { + throw new VirtualAddressParseException("Unexpected character at position %d: %s".formatted(this.position, Character.toString(codepoint))); + } + } + case KEY -> { + if (Character.isLetterOrDigit(codepoint) || codepoint == '_' || codepoint == '-') + { + if (this.tokenBuilder.length() >= VirtualAddress.MAX_KEY_LENGTH) + throw new VirtualAddressParseException("Single key length (%d) exceeded!".formatted(VirtualAddress.MAX_KEY_LENGTH)); + + this.state = State.KEY; + this.tokenBuilder.appendCodePoint(codepoint); + } + else if (codepoint == VirtualAddress.TOKEN_PATH_SEPARATOR) + { + if (this.depth >= VirtualAddress.MAX_KEYS) + throw new VirtualAddressParseException("Max amount of keys (%d) exceeded!".formatted(VirtualAddress.MAX_KEYS)); + + if (this.state != State.HIERARCHY_UP) + this.components.addLast(this.tokenBuilder.toString()); + + this.state = State.PATH_SEPARATOR; + this.depth++; + } + else + { + throw new VirtualAddressParseException("Unexpected character at position %d: %s".formatted(this.position, Character.toString(codepoint))); + } + } + case HIERARCHY_UP -> { + if (codepoint == VirtualAddress.TOKEN_HIERARCHY_UP) + { + if (!this.components.isEmpty()) + this.components.removeLast(); + else + this.rootOffset++; + } + else if (codepoint == VirtualAddress.TOKEN_PATH_SEPARATOR) + { + if (this.depth >= VirtualAddress.MAX_KEYS) + throw new VirtualAddressParseException("Max amount of keys (%d) exceeded!".formatted(VirtualAddress.MAX_KEYS)); + + this.state = State.PATH_SEPARATOR; + this.depth++; + } + else + { + throw new VirtualAddressParseException("Unexpected character at position %d: %s".formatted(this.position, Character.toString(codepoint))); + } + } + } + + this.position++; + + if (this.rootOffset >= VirtualAddress.MAX_KEYS) + throw new VirtualAddressParseException("Cannot move than %d levels up in the hierarchy!".formatted(VirtualAddress.MAX_KEYS)); + } + + public VirtualAddress build() + { + if (this.state == State.KEY && this.tokenBuilder != null) + this.components.addLast(this.tokenBuilder.toString()); + + var normalizedAddress = getNormalizedString(this.rootOffset, this.components); + + return new VirtualAddress(normalizedAddress, List.copyOf(this.components), this.permitRelative, this.rootOffset); + } + + static String getNormalizedString(int rootOffset, Iterable components) + { + var separator = Character.toString(VirtualAddress.TOKEN_PATH_SEPARATOR); + var componentsJoined = String.join(separator, components); + + var sb = new StringBuilder(); + + sb.append(Character.toString(VirtualAddress.TOKEN_HIERARCHY_UP).repeat(rootOffset)); + + if (rootOffset > 0 && !componentsJoined.isEmpty()) + sb.append(separator); + + sb.append(componentsJoined); + + return sb.toString(); + } +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/chrono/MiniTime.java b/engine-core/plutolib/src/main/java/org/plutoengine/chrono/MiniTime.java old mode 100644 new mode 100755 similarity index 85% rename from plutolib/src/main/java/cz/tefek/pluto/chrono/MiniTime.java rename to engine-core/plutolib/src/main/java/org/plutoengine/chrono/MiniTime.java index d8251ac..ca75756 --- a/plutolib/src/main/java/cz/tefek/pluto/chrono/MiniTime.java +++ b/engine-core/plutolib/src/main/java/org/plutoengine/chrono/MiniTime.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.chrono; +package org.plutoengine.chrono; import java.util.concurrent.TimeUnit; @@ -45,19 +45,8 @@ public class MiniTime { private static class MiniTimeParseException extends RuntimeException { - /** - * - */ - private static final long serialVersionUID = -5403949842120041373L; - - public MiniTimeParseException() - { - super("Time period could not be parsed. Correct format: \\_w\\_d\\_h\\_m\\_s **without spaces** between the units. You can skip a time unit. Example: 1h15m"); - } } - private static final TimeUnit miliseconds = TimeUnit.MILLISECONDS; - private static final int DAYS_IN_WEEK = 7; private static final int HOURS_IN_DAY = 24; private static final int MINUTES_IN_HOUR = 60; @@ -79,9 +68,7 @@ public class MiniTime public static long parse(String input) { if (input == null) - { - throw new IllegalArgumentException("MiniTime string cannot be null!"); - } + throw new NullPointerException(); // Nothing to parse if (input.isEmpty()) @@ -148,33 +135,28 @@ public class MiniTime switch (type.toLowerCase()) { - case "w": - case "W": + case "w" -> { allow = Integer.MAX_VALUE; multiplier = SECONDS_IN_MINUTE * MINUTES_IN_HOUR * HOURS_IN_DAY * DAYS_IN_WEEK * MILLIS_IN_MINUTE; - break; - case "d": - case "D": + } + case "d" -> { allow = DAYS_IN_WEEK; multiplier = SECONDS_IN_MINUTE * MINUTES_IN_HOUR * HOURS_IN_DAY * MILLIS_IN_MINUTE; - break; - case "h": - case "H": + } + case "h" -> { allow = HOURS_IN_DAY; multiplier = SECONDS_IN_MINUTE * MINUTES_IN_HOUR * MILLIS_IN_MINUTE; - break; - case "m": - case "M": + } + case "m" -> { allow = MINUTES_IN_HOUR; multiplier = SECONDS_IN_MINUTE * MILLIS_IN_MINUTE; - break; - case "s": - case "S": + } + case "s" -> { allow = SECONDS_IN_MINUTE; multiplier = MILLIS_IN_MINUTE; - break; - default: - break; + } + default -> { + } } // The top one can be more than it normally could have, for example you can @@ -189,7 +171,7 @@ public class MiniTime throw new MiniTimeParseException(); } - time += multiplier * number; + time += (long) multiplier * number; } return System.currentTimeMillis() + time; @@ -269,17 +251,17 @@ public class MiniTime throw new IllegalArgumentException("Negative time span cannot be converted to MiniTime."); } - var xweeks = miliseconds.toDays(diff) / DAYS_IN_WEEK; + var xweeks = TimeUnit.MILLISECONDS.toDays(diff) / DAYS_IN_WEEK; if (xweeks > Integer.MAX_VALUE) { return "forever"; } - var xdays = miliseconds.toDays(diff) % DAYS_IN_WEEK; - var xhours = miliseconds.toHours(diff) % HOURS_IN_DAY; - var xminutes = miliseconds.toMinutes(diff) % MINUTES_IN_HOUR; - var xseconds = miliseconds.toSeconds(diff) % SECONDS_IN_MINUTE; + var xdays = TimeUnit.MILLISECONDS.toDays(diff) % DAYS_IN_WEEK; + var xhours = TimeUnit.MILLISECONDS.toHours(diff) % HOURS_IN_DAY; + var xminutes = TimeUnit.MILLISECONDS.toMinutes(diff) % MINUTES_IN_HOUR; + var xseconds = TimeUnit.MILLISECONDS.toSeconds(diff) % SECONDS_IN_MINUTE; return formatTime(xweeks, xdays, xhours, xminutes, xseconds); } diff --git a/plutolib/src/main/java/cz/tefek/pluto/chrono/package-info.java b/engine-core/plutolib/src/main/java/org/plutoengine/chrono/package-info.java old mode 100644 new mode 100755 similarity index 72% rename from plutolib/src/main/java/cz/tefek/pluto/chrono/package-info.java rename to engine-core/plutolib/src/main/java/org/plutoengine/chrono/package-info.java index b2e2f9c..24eae26 --- a/plutolib/src/main/java/cz/tefek/pluto/chrono/package-info.java +++ b/engine-core/plutolib/src/main/java/org/plutoengine/chrono/package-info.java @@ -4,4 +4,4 @@ * @author 493msi * */ -package cz.tefek.pluto.chrono; +package org.plutoengine.chrono; diff --git a/plutolib/src/main/java/cz/tefek/pluto/event/lambda/LambdaEventFactory.java b/engine-core/plutolib/src/main/java/org/plutoengine/event/lambda/LambdaEventFactory.java old mode 100644 new mode 100755 similarity index 98% rename from plutolib/src/main/java/cz/tefek/pluto/event/lambda/LambdaEventFactory.java rename to engine-core/plutolib/src/main/java/org/plutoengine/event/lambda/LambdaEventFactory.java index c792464..566bc31 --- a/plutolib/src/main/java/cz/tefek/pluto/event/lambda/LambdaEventFactory.java +++ b/engine-core/plutolib/src/main/java/org/plutoengine/event/lambda/LambdaEventFactory.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.event.lambda; +package org.plutoengine.event.lambda; import java.util.ArrayList; import java.util.List; diff --git a/engine-core/plutolib/src/main/java/org/plutoengine/io/property/YAMLPropertiesReader.java b/engine-core/plutolib/src/main/java/org/plutoengine/io/property/YAMLPropertiesReader.java new file mode 100755 index 0000000..eddfe34 --- /dev/null +++ b/engine-core/plutolib/src/main/java/org/plutoengine/io/property/YAMLPropertiesReader.java @@ -0,0 +1,66 @@ +package org.plutoengine.io.property; + +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.error.YAMLException; +import org.yaml.snakeyaml.nodes.MappingNode; +import org.yaml.snakeyaml.nodes.Node; +import org.yaml.snakeyaml.nodes.ScalarNode; +import org.yaml.snakeyaml.nodes.Tag; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.LinkedHashMap; +import java.util.Map; + +public class YAMLPropertiesReader +{ + public static Map loadFromFile(Path file) throws IOException, YAMLException + { + try (var br = Files.newBufferedReader(file)) + { + var yaml = new Yaml(new Constructor() { + private void recursivelyBuildTree(Map map, String accessor, Node node) + { + if (node instanceof MappingNode mappingNode) + { + var kvps = mappingNode.getValue(); + + for (var kvp : kvps) + { + var key = kvp.getKeyNode(); + + if (key.getTag() != Tag.STR || !(key instanceof ScalarNode)) + throw new YAMLException("All keys in property trees must be strings!"); + + var valueNode = kvp.getValueNode(); + + var newAccessorFormat = valueNode instanceof MappingNode ? "%s%s." : "%s%s"; + + this.recursivelyBuildTree(map, newAccessorFormat.formatted(accessor, super.constructScalar((ScalarNode) key)), valueNode); + } + } + else if (node instanceof ScalarNode scalarNode) + { + map.put(accessor, super.constructScalar(scalarNode)); + } + else + { + throw new YAMLException("Invalid node tag: %s".formatted(node.getTag())); + } + } + + @Override + protected Object constructObject(Node node) + { + var propertyMap = new LinkedHashMap(); + this.recursivelyBuildTree(propertyMap, "", node); + return propertyMap; + } + }); + + return yaml.load(br); + } + } +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/math/ClampedSineWave.java b/engine-core/plutolib/src/main/java/org/plutoengine/math/ClampedSineWave.java old mode 100644 new mode 100755 similarity index 98% rename from plutolib/src/main/java/cz/tefek/pluto/math/ClampedSineWave.java rename to engine-core/plutolib/src/main/java/org/plutoengine/math/ClampedSineWave.java index c4b0d34..944e9cf --- a/plutolib/src/main/java/cz/tefek/pluto/math/ClampedSineWave.java +++ b/engine-core/plutolib/src/main/java/org/plutoengine/math/ClampedSineWave.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.math; +package org.plutoengine.math; /** * A clamped sine wave generator, for animations, mostly. diff --git a/plutolib/src/main/java/cz/tefek/pluto/math/CubicBezier.java b/engine-core/plutolib/src/main/java/org/plutoengine/math/CubicBezier.java old mode 100644 new mode 100755 similarity index 83% rename from plutolib/src/main/java/cz/tefek/pluto/math/CubicBezier.java rename to engine-core/plutolib/src/main/java/org/plutoengine/math/CubicBezier.java index b4f4379..67acdba --- a/plutolib/src/main/java/cz/tefek/pluto/math/CubicBezier.java +++ b/engine-core/plutolib/src/main/java/org/plutoengine/math/CubicBezier.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.math; +package org.plutoengine.math; /** * A class to generate a cubic bezier interpolation function. Not very @@ -10,7 +10,7 @@ package cz.tefek.pluto.math; public class CubicBezier { private static final int iterations = 16; - private double a, b, c, d; + private final double a, b, c, d; /** * Creates a new {@code CubicBezier} from the given parameters. @@ -62,23 +62,22 @@ public class CubicBezier double t = 0.5; double x; - double y = 3 * (1 - t) * (1 - t) * t * this.b + 3 * (1 - t) * t * t * this.d + t * t * t; + + // Resulting Y value + double result = 3 * (1 - t) * (1 - t) * t * this.b + 3 * (1 - t) * t * t * this.d + t * t * t; double delta = 0.25; - boolean uh; for (int i = 0; i < iterations; i++) { x = 3 * (1 - t) * (1 - t) * t * this.a + 3 * (1 - t) * t * t * this.c + t * t * t; - y = 3 * (1 - t) * (1 - t) * t * this.b + 3 * (1 - t) * t * t * this.d + t * t * t; + result = 3 * (1 - t) * (1 - t) * t * this.b + 3 * (1 - t) * t * t * this.d + t * t * t; - uh = x > xIn; - - t += uh ? -delta : delta; + t += x > xIn ? -delta : delta; delta /= 2; } - return y; + return result; } } diff --git a/plutolib/src/main/java/cz/tefek/pluto/math/CubicBezierLT.java b/engine-core/plutolib/src/main/java/org/plutoengine/math/CubicBezierLT.java old mode 100644 new mode 100755 similarity index 89% rename from plutolib/src/main/java/cz/tefek/pluto/math/CubicBezierLT.java rename to engine-core/plutolib/src/main/java/org/plutoengine/math/CubicBezierLT.java index a7b44e2..966e7c3 --- a/plutolib/src/main/java/cz/tefek/pluto/math/CubicBezierLT.java +++ b/engine-core/plutolib/src/main/java/org/plutoengine/math/CubicBezierLT.java @@ -1,8 +1,9 @@ -package cz.tefek.pluto.math; +package org.plutoengine.math; /** * A class to generate a cubic bezier interpolation function. This - * implementation creates a lookup table. + * implementation creates a lookup table, so create it one and then + * use it with basically no speed penalty. * * @since 0.2 * @author 493msi @@ -42,7 +43,7 @@ public class CubicBezierLT } /** - * Retrives the approximate value for the given x and the supplied + * Retrieves the approximate value for the given x and the supplied * parameters in the constructor. * * @param xIn the input X position diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/package-info.java b/engine-core/plutolib/src/main/java/org/plutoengine/math/package-info.java old mode 100644 new mode 100755 similarity index 61% rename from plutostatic/src/main/java/cz/tefek/pluto/engine/math/package-info.java rename to engine-core/plutolib/src/main/java/org/plutoengine/math/package-info.java index dc3367b..cc1dc3b --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/package-info.java +++ b/engine-core/plutolib/src/main/java/org/plutoengine/math/package-info.java @@ -4,4 +4,4 @@ * @author 493msi * */ -package cz.tefek.pluto.engine.math; +package org.plutoengine.math; diff --git a/engine-core/plutolib/src/main/java/org/plutoengine/util/color/Color.java b/engine-core/plutolib/src/main/java/org/plutoengine/util/color/Color.java new file mode 100755 index 0000000..63c2569 --- /dev/null +++ b/engine-core/plutolib/src/main/java/org/plutoengine/util/color/Color.java @@ -0,0 +1,468 @@ +package org.plutoengine.util.color; + +import javax.annotation.Nonnull; + +/** + * A simple 8-bit RGBA color container. + * + *

+ * Some methods mutate the object to avoid new object creation. + * These methods are prefixed with "store". + *

+ * + * @implNote Each of the color components is stored separately as a 32-bit integer + * to avoid unnecessary type conversion at the cost of some memory. + * + *

+ * This however should not be a problem as this class is not designed + * for large-scale or performance-sensitive color operations. + *

+ * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ +public final class Color +{ + // Black and white + public static Color WHITE = new Color(255, 255, 255); + public static Color BLACK = new Color(0, 0, 0); + + // Shades of gray + public static Color VERY_DARK_GRAY = new Color(40, 40, 40); + public static Color DARK_GRAY = new Color(85, 85, 85); + public static Color GRAY = new Color(128, 128, 128); + public static Color SILVER = new Color(192, 192, 192); + public static Color LIGHT_GRAY = new Color(212, 212, 212); + + // Basic colors + public static Color RED = new Color(255, 0, 0); + public static Color GREEN = new Color(0, 255, 0); + public static Color BLUE = new Color(0, 0, 255); + public static Color YELLOW = new Color(255, 255, 0); + + + public static Color TRANSPARENT = new Color(0, 0, 0, 0); + public static Color TRANSPARENT_WHITE = new Color(255, 255, 255, 0); + + public static Color AMBER = new Color(255, 190, 0); + public static Color AMETHYST = new Color(153, 102, 204); + public static Color APRICOT = new Color(235, 147, 115); + public static Color AZURE = new Color(0, 57, 169); + public static Color BROWN = new Color(150, 75, 0); + public static Color COBALT = new Color(0, 71, 171); + public static Color COPPER = new Color(184, 115, 51); + public static Color CORAL_RED = new Color(255, 50, 60); + public static Color CORNFLOWER_BLUE = new Color(112, 112, 255); + public static Color CRIMSON = new Color(220, 26, 64); + public static Color CYAN = new Color(0, 188, 212); + public static Color DARK_BROWN = new Color(66, 33, 0); + public static Color DARK_GREEN = new Color(0, 150, 0); + public static Color LIGHT_AZURE = new Color(0, 128, 255); + public static Color LIME = new Color(191, 255, 0); + public static Color MAGENTA = new Color(255, 0, 255); + public static Color MALACHITE = new Color(11, 218, 81); + public static Color NAVY_BLUE = new Color(0, 0, 128); + public static Color OBSIDIAN = new Color(16, 18, 29); + public static Color ORANGE = new Color(255, 102, 0); + public static Color ORANGE_RED = new Color(255, 55, 0); + public static Color PEAR = new Color(209, 226, 49); + public static Color PINK = new Color(253, 109, 134); + public static Color PUMPKIN_ORANGE = new Color(255, 117, 0); + public static Color SAPPHIRE = new Color(47, 81, 158); + public static Color TEAL = new Color(0, 140, 140); + public static Color TURQUOISE = new Color(10, 255, 141); + + + public static Color PASTEL_PINK = new Color(255, 192, 203); + public static Color PASTEL_LIME = new Color(221, 255, 192); + public static Color PASTEL_YELLOW = new Color(252, 255, 192); + public static Color PASTEL_CYAN = new Color(192, 255, 234); + public static Color PASTEL_BLUE = new Color(192, 199, 255); + public static Color PASTEL_VIOLET = new Color(192, 199, 255); + + public int red; + public int green; + public int blue; + public int alpha = 255; + + /** + * Creates a new Color object from the supplied RGBA color components. + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public Color(int red, int green, int blue, int alpha) + { + this.red = red; + this.green = green; + this.blue = blue; + this.alpha = alpha; + } + + /** + * Creates a new Color object from the supplied RGBA color components. + * + * Alpha is set to 255 by default. + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public Color(int red, int green, int blue) + { + this.red = red; + this.green = green; + this.blue = blue; + } + + /** + * Converts the supplied float-based {@link IRGBA} color object to a new {@link Color} object and returns it. + * + * @return A new {@link Color} object + * + * @param colorComponents An {@link IRGBA} color object + * + * @implNote Color values are rounded to the nearest integer. + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public static Color from(@Nonnull IRGBA colorComponents) + { + return new Color(Math.round(colorComponents.red() * 255), + Math.round(colorComponents.green() * 255), + Math.round(colorComponents.blue() * 255), + Math.round(colorComponents.alpha() * 255)); + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public static Color from(@Nonnull IRGB colorComponents) + { + return new Color(Math.round(colorComponents.red() * 255), + Math.round(colorComponents.green() * 255), + Math.round(colorComponents.blue() * 255)); + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public static Color from(int color, @Nonnull EnumColorFormat colorFormat) + { + return switch (colorFormat) + { + case CF_INT_BGR -> new Color(color & 0xff, (color >> 8) & 0xff, (color >> 16) & 0xff); + case CF_INT_RGB -> new Color((color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); + case CF_INT_ABGR -> new Color(color & 0xff, (color >> 8) & 0xff, (color >> 16) & 0xff, (color >> 24) & 0xff); + case CF_INT_ARGB -> new Color((color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff, (color >> 24) & 0xff); + case CF_INT_BGRA -> new Color((color >> 8) & 0xff, (color >> 16) & 0xff, (color >> 24) & 0xff, color & 0xff); + case CF_INT_RGBA -> new Color((color >> 24) & 0xff, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); + default -> throw new UnsupportedOperationException("Use the from(byte[], int, ColorFormat) for byte color formats!"); + }; + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public static Color fromAWT(@Nonnull java.awt.Color color) + { + return new Color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()); + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public static Color from(@Nonnull byte[] color, @Nonnull EnumColorFormat colorFormat) + { + return from(color, 0, colorFormat); + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public static Color from(@Nonnull byte[] color, int offset, @Nonnull EnumColorFormat colorFormat) + { + return switch (colorFormat) + { + case CF_3BYTE_BGR -> new Color(color[offset + 2] & 0xFF, color[offset + 1] & 0xFF, color[offset] & 0xFF); + case CF_3BYTE_RGB -> new Color(color[offset] & 0xFF, color[offset + 1] & 0xFF, color[offset + 2] & 0xFF); + case CF_4BYTE_ABGR -> new Color(color[offset + 3] & 0xFF, color[offset + 2] & 0xFF, color[offset + 1] & 0xFF, color[offset] & 0xFF); + case CF_4BYTE_ARGB -> new Color(color[offset + 1] & 0xFF, color[offset + 2] & 0xFF, color[offset + 3] & 0xFF, color[offset] & 0xFF); + case CF_4BYTE_BGRA -> new Color(color[offset + 2] & 0xFF, color[offset + 1] & 0xFF, color[offset] & 0xFF, color[offset + 3] & 0xFF); + case CF_4BYTE_RGBA -> new Color(color[offset] & 0xFF, color[offset + 1] & 0xFF, color[offset + 2] & 0xFF, color[offset + 3] & 0xFF); + default -> throw new UnsupportedOperationException("Use the from(int, ColorFormat) for int color formats!"); + }; + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public int getIntRGBA() + { + return get(EnumColorFormat.CF_INT_RGBA); + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public int getIntARGB() + { + return get(EnumColorFormat.CF_INT_ARGB); + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public int get(@Nonnull EnumColorFormat colorFormat) + { + return switch (colorFormat) + { + case CF_INT_BGR -> (this.blue << 16) | (this.green << 8) | this.red; + case CF_INT_RGB -> (this.red << 16) | (this.green << 8) | this.blue; + case CF_INT_ABGR -> (this.alpha << 24) | (this.blue << 16) | (this.green << 8) | this.red; + case CF_INT_ARGB -> (this.alpha << 24) | (this.red << 16) | (this.green << 8) | this.blue; + case CF_INT_BGRA -> (this.blue << 24) | (this.green << 16) | (this.red << 8) | this.alpha; + case CF_INT_RGBA -> (this.red << 24) | (this.green << 16) | (this.blue << 8) | this.alpha; + default -> throw new UnsupportedOperationException("Use the get(ColorFormat, byte[], int) for byte color formats!"); + }; + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public void get(@Nonnull EnumColorFormat colorFormat, @Nonnull byte[] dataOut) + { + get(colorFormat, dataOut, 0); + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public void get(@Nonnull EnumColorFormat colorFormat, @Nonnull byte[] dataOut, int offset) + { + switch (colorFormat) + { + case CF_3BYTE_BGR -> { + dataOut[offset++] = (byte) this.blue; + dataOut[offset++] = (byte) this.green; + dataOut[offset] = (byte) this.red; + } + case CF_3BYTE_RGB -> { + dataOut[offset++] = (byte) this.red; + dataOut[offset++] = (byte) this.green; + dataOut[offset] = (byte) this.blue; + } + case CF_4BYTE_ABGR -> { + dataOut[offset++] = (byte) this.alpha; + dataOut[offset++] = (byte) this.blue; + dataOut[offset++] = (byte) this.green; + dataOut[offset] = (byte) this.red; + } + case CF_4BYTE_ARGB -> { + dataOut[offset++] = (byte) this.alpha; + dataOut[offset++] = (byte) this.red; + dataOut[offset++] = (byte) this.green; + dataOut[offset] = (byte) this.blue; + } + case CF_4BYTE_BGRA -> { + dataOut[offset++] = (byte) this.blue; + dataOut[offset++] = (byte) this.green; + dataOut[offset++] = (byte) this.red; + dataOut[offset] = (byte) this.alpha; + } + case CF_4BYTE_RGBA -> { + dataOut[offset++] = (byte) this.red; + dataOut[offset++] = (byte) this.green; + dataOut[offset++] = (byte) this.blue; + dataOut[offset] = (byte) this.alpha; + } + default -> throw new UnsupportedOperationException("Use the get(ColorFormat) for int color formats!"); + } + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public int getRed() + { + return this.red; + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public int getGreen() + { + return this.green; + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public int getBlue() + { + return this.blue; + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public int getAlpha() + { + return this.alpha; + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public RGB getFloatComponentsRGB() + { + return new RGB(this.red / 255.0f, this.green / 255.0f, this.blue / 255.0f); + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public RGBA getFloatComponentsRGBA() + { + return new RGBA(this.red / 255.0f, this.green / 255.0f, this.blue / 255.0f, this.alpha / 255.0f); + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public HSB getFloatComponentsHSB() + { + return this.getFloatComponentsRGB().toHSB(); + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public HSBA getFloatComponentsHSBA() + { + return this.getFloatComponentsRGBA().toHSBA(); + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public void storeFloatComponentsRGBA(@Nonnull RGBA target) + { + storeFloatComponentsRGB(target); + target.a = this.alpha / 255.0f; + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public void storeFloatComponentsRGB(@Nonnull RGB target) + { + target.r = this.red / 255.0f; + target.g = this.green / 255.0f; + target.b = this.blue / 255.0f; + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public void storeFloatComponentsHSBA(@Nonnull HSBA target) + { + var hsb = this.getFloatComponentsHSBA(); + + target.h = hsb.h; + target.s = hsb.s; + target.b = hsb.b; + target.a = hsb.a; + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public void storeFloatComponentsHSB(@Nonnull HSB target) + { + var hsb = this.getFloatComponentsHSB(); + + target.h = hsb.h; + target.s = hsb.s; + target.b = hsb.b; + } + + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public java.awt.Color toAWT() + { + return new java.awt.Color(this.red, this.green, this.blue, this.alpha); + } +} diff --git a/engine-core/plutolib/src/main/java/org/plutoengine/util/color/EnumColorFormat.java b/engine-core/plutolib/src/main/java/org/plutoengine/util/color/EnumColorFormat.java new file mode 100755 index 0000000..a7350bf --- /dev/null +++ b/engine-core/plutolib/src/main/java/org/plutoengine/util/color/EnumColorFormat.java @@ -0,0 +1,81 @@ +package org.plutoengine.util.color; + +/** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ +public enum EnumColorFormat +{ + /** + * 8-bit RGBA stored in a big-endian 32-bit integer + */ + CF_INT_RGBA(4), + /** + * 8-bit BGRA stored in a big-endian 32-bit integer + */ + CF_INT_BGRA(4), + + /** + * 8-bit ARGB stored in a big-endian 32-bit integer + */ + CF_INT_ARGB(4), + /** + * 8-bit ABGR stored in a big-endian 32-bit integer + */ + CF_INT_ABGR(4), + + /** + * 8-bit RGB stored in a big-endian 32-bit integer, the highest 8-bits are unused + */ + CF_INT_RGB(4), + /** + * 8-bit RGB stored in a big-endian 32-bit integer, the highest 8-bits are unused + */ + CF_INT_BGR(4), + + /** + * 8-bit RGBA, one byte per color component + */ + CF_4BYTE_RGBA(4), + /** + * 8-bit BGRA, one byte per color component + */ + CF_4BYTE_BGRA(4), + + /** + * 8-bit ARGB, one byte per color component + */ + CF_4BYTE_ARGB(4), + /** + * 8-bit ABGR, one byte per color component + */ + CF_4BYTE_ABGR(4), + + /** + * 8-bit RGB, one byte per color component + */ + CF_3BYTE_RGB(3), + /** + * 8-bit BGR, one byte per color component + */ + CF_3BYTE_BGR(3); + + private final int size; + + EnumColorFormat(int size) + { + this.size = size; + } + + /** + * Returns the size in bytes. + * + * @return The size of the color format, in bytes + */ + public int getSize() + { + return this.size; + } +} diff --git a/engine-core/plutolib/src/main/java/org/plutoengine/util/color/HSB.java b/engine-core/plutolib/src/main/java/org/plutoengine/util/color/HSB.java new file mode 100755 index 0000000..f6a0f4a --- /dev/null +++ b/engine-core/plutolib/src/main/java/org/plutoengine/util/color/HSB.java @@ -0,0 +1,139 @@ +package org.plutoengine.util.color; + +import org.apache.commons.lang3.Range; + +/** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ +public class HSB +{ + /** + * Hue [0°..360°] + * */ + protected float h; + + /** + * Saturation [0..1] + * */ + protected float s; + private final static Range SATURATION_RANGE = Range.between(0.0f, 1.0f); + + /** + * Value/Brightness [0..1] + * */ + protected float b; + private final static Range BRIGHTNESS_RANGE = Range.between(0.0f, 1.0f); + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public HSB(float hue, float saturation, float brightness) + { + this.h = (360.0f + hue % 360.0f) % 360.0f; + this.s = SATURATION_RANGE.fit(saturation); + this.b = BRIGHTNESS_RANGE.fit(brightness); + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public RGB toRGB() + { + return this.toRGBA(1.0f); + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public RGB toRGBA() + { + return this.toRGBA(1.0f); + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public RGBA toRGBA(float alpha) + { + float h6 = this.h / 60.0f; + + int hueSide = (int) h6; + + // The color component furthest on the hue wheel + float p = this.b * (1 - this.s); + + float hueFractCCW = h6 - hueSide; + // The second-nearest color component on the hue wheel - counter-clockwise + float q = this.b * (1 - hueFractCCW * this.s); + + float hueFractCW = 1 - hueFractCCW; + // The second-nearest color component on the hue wheel - clockwise + float t = this.b * (1 - hueFractCW * this.s); + + return switch (hueSide % 6) + { + // Hues 60°-119° -- Green is the brightest color, no blue is present at max saturation + case 1 -> new RGBA(q, this.b, p, alpha); + // Hues 120°-179° -- Green is the brightest color, no red is present at max saturation + case 2 -> new RGBA(p, this.b, t, alpha); + // Hues 180°-239° -- Blue is the brightest color, no red is present at max saturation + case 3 -> new RGBA(p, q, this.b, alpha); + // Hues 240°-299° -- Blue is the brightest color, no green is present at max saturation + case 4 -> new RGBA(t, p, this.b, alpha); + // Hues 300°-359° -- Red is the brightest color, no green is present at max saturation + case 5 -> new RGBA(this.b, p, q, alpha); + // Hues 0°-59° -- Red is the brightest color, no blue is present at max saturation + case 0 -> new RGBA(this.b, t, p, alpha); + + default -> throw new IllegalStateException("This HSB object's hue is negative - this is not legal."); + }; + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public float hue() + { + return this.h; + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public float saturation() + { + return this.s; + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public float brightness() + { + return this.b; + } +} diff --git a/engine-core/plutolib/src/main/java/org/plutoengine/util/color/HSBA.java b/engine-core/plutolib/src/main/java/org/plutoengine/util/color/HSBA.java new file mode 100755 index 0000000..d5be1d5 --- /dev/null +++ b/engine-core/plutolib/src/main/java/org/plutoengine/util/color/HSBA.java @@ -0,0 +1,51 @@ +package org.plutoengine.util.color; + +import org.apache.commons.lang3.Range; + +/** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ +public class HSBA extends HSB +{ + protected float a; + private final static Range ALPHA_RANGE = Range.between(0.0f, 1.0f); + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public HSBA(float hue, float saturation, float lightness, float alpha) + { + super(hue, saturation, lightness); + + this.a = ALPHA_RANGE.fit(alpha); + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public float alpha() + { + return this.a; + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + @Override + public RGB toRGBA() + { + return this.toRGBA(this.a); + } +} diff --git a/engine-core/plutolib/src/main/java/org/plutoengine/util/color/IRGB.java b/engine-core/plutolib/src/main/java/org/plutoengine/util/color/IRGB.java new file mode 100755 index 0000000..05b9357 --- /dev/null +++ b/engine-core/plutolib/src/main/java/org/plutoengine/util/color/IRGB.java @@ -0,0 +1,31 @@ +package org.plutoengine.util.color; + +/** + * An interface for single precision RGB color objects. + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ +public interface IRGB +{ + /** + * Returns the red color component. + * + * @return The red component, in range 0..1 + */ + float red(); + + /** + * Returns the green color component. + * + * @return The green component, in range 0..1 + */ + float green(); + + /** + * Returns the blue color component. + * + * @return The blue color component, in range 0..1 + */ + float blue(); +} diff --git a/engine-core/plutolib/src/main/java/org/plutoengine/util/color/IRGBA.java b/engine-core/plutolib/src/main/java/org/plutoengine/util/color/IRGBA.java new file mode 100755 index 0000000..e48a690 --- /dev/null +++ b/engine-core/plutolib/src/main/java/org/plutoengine/util/color/IRGBA.java @@ -0,0 +1,17 @@ +package org.plutoengine.util.color; + +/** + * An interface for single precision RGBA color objects. + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ +public interface IRGBA extends IRGB +{ + /** + * Returns the alpha color component. + * + * @return The alpha component, in range 0..1 + */ + float alpha(); +} diff --git a/engine-core/plutolib/src/main/java/org/plutoengine/util/color/RGB.java b/engine-core/plutolib/src/main/java/org/plutoengine/util/color/RGB.java new file mode 100755 index 0000000..503e774 --- /dev/null +++ b/engine-core/plutolib/src/main/java/org/plutoengine/util/color/RGB.java @@ -0,0 +1,121 @@ +package org.plutoengine.util.color; + +import org.apache.commons.lang3.math.NumberUtils; + +/** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ +public class RGB implements IRGB +{ + protected float r; + protected float g; + protected float b; + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public RGB(float r, float g, float b) + { + this.r = r; + this.g = g; + this.b = b; + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public HSBA toHSB() + { + return this.toHSBA(1.0f); + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public HSBA toHSBA() + { + return this.toHSBA(1.0f); + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public HSBA toHSBA(float alpha) + { + float brightness = NumberUtils.max(this.r, this.g, this.b); + float min = NumberUtils.min(this.r, this.g, this.b); + + if (brightness == 0) + return new HSBA(0, 0, 0, alpha); + + float chroma = brightness - min; + + if (chroma == 0) + return new HSBA(0, 0, brightness, alpha); + + float saturation = chroma / brightness; + float hue; + + if (brightness == this.r) + if (this.g < this.b) + hue = (this.g - this.b) / chroma + 6; + else + hue = (this.g - this.b) / chroma; + else if (brightness == this.g) + hue = (this.b - this.r) / chroma + 2; + else + hue = (this.r - this.g) / chroma + 4; + + hue *= 60; + + return new HSBA(hue, saturation, brightness, alpha); + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public float red() + { + return this.r; + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public float green() + { + return this.g; + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public float blue() + { + return this.b; + } +} diff --git a/engine-core/plutolib/src/main/java/org/plutoengine/util/color/RGBA.java b/engine-core/plutolib/src/main/java/org/plutoengine/util/color/RGBA.java new file mode 100755 index 0000000..f82fb9f --- /dev/null +++ b/engine-core/plutolib/src/main/java/org/plutoengine/util/color/RGBA.java @@ -0,0 +1,47 @@ +package org.plutoengine.util.color; + +/** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ +public class RGBA extends RGB implements IRGBA +{ + protected float a; + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public RGBA(float r, float g, float b, float a) + { + super(r, g, b); + this.a = a; + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public float alpha() + { + return this.a; + } + + /** + * TODO + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + @Override + public HSBA toHSBA() + { + return this.toHSBA(this.a); + } +} diff --git a/engine-core/plutolib/src/test/java/org/plutoengine/address/VirtualAddressTest.java b/engine-core/plutolib/src/test/java/org/plutoengine/address/VirtualAddressTest.java new file mode 100755 index 0000000..eaab9f3 --- /dev/null +++ b/engine-core/plutolib/src/test/java/org/plutoengine/address/VirtualAddressTest.java @@ -0,0 +1,44 @@ +package org.plutoengine.address; + +import org.apache.commons.lang3.tuple.Pair; + +import java.util.List; + +public class VirtualAddressTest +{ + public static void main(String[] args) + { + System.out.println(); + + var basic = VirtualAddress.parse("cz.tefek.pluto"); + System.out.println(basic); + + // ~.~ - ~.~.~.a => ~.a + // a.b.c.d - ~.a.b.c.d => ~.~.~.~.~.a.b.c.d + // a.b.c.d - d => ~.~.~.~.d + // a.b.x.d - a.b.c.d => ~.~.c.d + // a.b.c - d => ~.~.~.d + // a.b.c - a.b => ~ + // a.b.c - a.e.g => ~.~.e.g + + var testCases = List.of( + Pair.of("~.~", "~.~.~.a"), + Pair.of("a.b.c.d", "~.a.b.c.d"), + Pair.of("a.b.c.d", "d"), + Pair.of("a.b.x.d", "a.b.c.d"), + Pair.of("a.b.c", "d"), + Pair.of("a.b.c", "a.b"), + Pair.of("a.b.c", "a.e.g"), + Pair.of("a.b.c", "a.b.c"), + Pair.of("a.b.c", "a.b.c.d") + ); + + testCases.forEach(pair -> { + var a = VirtualAddress.parse(pair.getLeft(), true); + var b = VirtualAddress.parse(pair.getRight(), true); + System.out.printf("\"%s\" - \"%s\" => \"%s\"%n", a, b, a.relativize(b)); + }); + + System.out.println(); + } +} diff --git a/engine-core/plutomesher/build.gradle.kts b/engine-core/plutomesher/build.gradle.kts new file mode 100755 index 0000000..aafb58e --- /dev/null +++ b/engine-core/plutomesher/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + java + `java-library` +} + +description = "" + +dependencies { + api(project(":plutoengine:plutodisplay")) +} \ No newline at end of file diff --git a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/DrawMode.java b/engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/DrawMode.java old mode 100644 new mode 100755 similarity index 86% rename from plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/DrawMode.java rename to engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/DrawMode.java index 5492999..dd9152e --- a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/DrawMode.java +++ b/engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/DrawMode.java @@ -1,9 +1,9 @@ -package cz.tefek.pluto.engine.graphics.gl; +package org.plutoengine.graphics.gl; import org.lwjgl.opengl.GL33; import org.lwjgl.opengl.GL40; -import cz.tefek.pluto.engine.gl.IOpenGLEnum; +import org.plutoengine.gl.IOpenGLEnum; public enum DrawMode implements IOpenGLEnum { @@ -26,9 +26,9 @@ public enum DrawMode implements IOpenGLEnum TRIANGLE_STRIP_ADJACENCY(GL33.GL_TRIANGLE_STRIP_ADJACENCY), PATCHES(GL40.GL_PATCHES); - private int glID; + private final int glID; - private DrawMode(int id) + DrawMode(int id) { this.glID = id; } diff --git a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/QuadPresets.java b/engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vao/QuadPresets.java old mode 100644 new mode 100755 similarity index 92% rename from plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/QuadPresets.java rename to engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vao/QuadPresets.java index 999aaa8..7a5c42e --- a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/QuadPresets.java +++ b/engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vao/QuadPresets.java @@ -1,6 +1,6 @@ -package cz.tefek.pluto.engine.graphics.gl.vao; +package org.plutoengine.graphics.gl.vao; -import cz.tefek.pluto.engine.graphics.gl.vao.attrib.data.VecArray; +import org.plutoengine.graphics.gl.vao.attrib.data.VecArray; /** * @author 493msi diff --git a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/VertexArray.java b/engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vao/VertexArray.java old mode 100644 new mode 100755 similarity index 91% rename from plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/VertexArray.java rename to engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vao/VertexArray.java index d61887a..e3e1331 --- a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/VertexArray.java +++ b/engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vao/VertexArray.java @@ -1,18 +1,18 @@ -package cz.tefek.pluto.engine.graphics.gl.vao; +package org.plutoengine.graphics.gl.vao; + +import org.lwjgl.opengl.GL33; +import org.lwjgl.system.MemoryUtil; +import org.plutoengine.graphics.gl.DrawMode; +import org.plutoengine.graphics.gl.vbo.ArrayBuffer; +import org.plutoengine.graphics.gl.vbo.IndexArrayBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Vector; -import org.lwjgl.opengl.GL33; -import org.lwjgl.system.MemoryUtil; - -import cz.tefek.pluto.engine.graphics.gl.DrawMode; -import cz.tefek.pluto.engine.graphics.gl.vbo.ArrayBuffer; -import cz.tefek.pluto.engine.graphics.gl.vbo.IndexArrayBuffer; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; public class VertexArray { diff --git a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/VertexArrayBuilder.java b/engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vao/VertexArrayBuilder.java old mode 100644 new mode 100755 similarity index 70% rename from plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/VertexArrayBuilder.java rename to engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vao/VertexArrayBuilder.java index 0003e1b..bc0916c --- a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/VertexArrayBuilder.java +++ b/engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vao/VertexArrayBuilder.java @@ -1,9 +1,9 @@ -package cz.tefek.pluto.engine.graphics.gl.vao; +package org.plutoengine.graphics.gl.vao; -import cz.tefek.pluto.engine.graphics.gl.vao.attrib.ReservedAttributes; -import cz.tefek.pluto.engine.graphics.gl.vao.attrib.data.VecArray; -import cz.tefek.pluto.engine.graphics.gl.vbo.FloatArrayBuffer; -import cz.tefek.pluto.engine.graphics.gl.vbo.IndexArrayBuffer; +import org.plutoengine.graphics.gl.vao.attrib.ReservedAttributes; +import org.plutoengine.graphics.gl.vao.attrib.data.VecArray; +import org.plutoengine.graphics.gl.vbo.FloatArrayBuffer; +import org.plutoengine.graphics.gl.vbo.IndexArrayBuffer; public class VertexArrayBuilder { diff --git a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/attrib/ReservedAttributes.java b/engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vao/attrib/ReservedAttributes.java old mode 100644 new mode 100755 similarity index 78% rename from plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/attrib/ReservedAttributes.java rename to engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vao/attrib/ReservedAttributes.java index 354222a..147efc3 --- a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/attrib/ReservedAttributes.java +++ b/engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vao/attrib/ReservedAttributes.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.graphics.gl.vao.attrib; +package org.plutoengine.graphics.gl.vao.attrib; public class ReservedAttributes { diff --git a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/attrib/data/VecArray.java b/engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vao/attrib/data/VecArray.java old mode 100644 new mode 100755 similarity index 94% rename from plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/attrib/data/VecArray.java rename to engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vao/attrib/data/VecArray.java index fbdff1b..987f71f --- a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/attrib/data/VecArray.java +++ b/engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vao/attrib/data/VecArray.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.graphics.gl.vao.attrib.data; +package org.plutoengine.graphics.gl.vao.attrib.data; import java.lang.reflect.Array; diff --git a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/ArrayBuffer.java b/engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vbo/ArrayBuffer.java old mode 100644 new mode 100755 similarity index 73% rename from plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/ArrayBuffer.java rename to engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vbo/ArrayBuffer.java index c1d9316..f472889 --- a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/ArrayBuffer.java +++ b/engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vbo/ArrayBuffer.java @@ -1,15 +1,12 @@ -package cz.tefek.pluto.engine.graphics.gl.vbo; +package org.plutoengine.graphics.gl.vbo; -import static org.lwjgl.opengl.GL15.GL_ARRAY_BUFFER; -import static org.lwjgl.opengl.GL15.glBindBuffer; -import static org.lwjgl.opengl.GL15.glDeleteBuffers; -import static org.lwjgl.opengl.GL15.glGenBuffers; +import org.plutoengine.graphics.gl.vao.attrib.data.VecArray; -import cz.tefek.pluto.engine.graphics.gl.vao.attrib.data.VecArray; +import static org.lwjgl.opengl.GL15.*; public abstract class ArrayBuffer> { - protected int glID = 0; + protected int glID; private final int vertexDimensions; private final int vertexCount; diff --git a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/EnumArrayBufferType.java b/engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vbo/EnumArrayBufferType.java old mode 100644 new mode 100755 similarity index 65% rename from plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/EnumArrayBufferType.java rename to engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vbo/EnumArrayBufferType.java index 6e53745..0fc1e0e --- a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/EnumArrayBufferType.java +++ b/engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vbo/EnumArrayBufferType.java @@ -1,8 +1,8 @@ -package cz.tefek.pluto.engine.graphics.gl.vbo; +package org.plutoengine.graphics.gl.vbo; import org.lwjgl.opengl.GL33; -import cz.tefek.pluto.engine.gl.IOpenGLEnum; +import org.plutoengine.gl.IOpenGLEnum; public enum EnumArrayBufferType implements IOpenGLEnum { @@ -10,9 +10,9 @@ public enum EnumArrayBufferType implements IOpenGLEnum INT(GL33.GL_INT), UNSIGNED_INT(GL33.GL_UNSIGNED_INT); - private int glID; + private final int glID; - private EnumArrayBufferType(int glEnum) + EnumArrayBufferType(int glEnum) { this.glID = glEnum; } diff --git a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/FloatArrayBuffer.java b/engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vbo/FloatArrayBuffer.java old mode 100644 new mode 100755 similarity index 80% rename from plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/FloatArrayBuffer.java rename to engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vbo/FloatArrayBuffer.java index e3b089e..3f7a959 --- a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/FloatArrayBuffer.java +++ b/engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vbo/FloatArrayBuffer.java @@ -1,8 +1,7 @@ -package cz.tefek.pluto.engine.graphics.gl.vbo; +package org.plutoengine.graphics.gl.vbo; import org.lwjgl.opengl.GL33; - -import cz.tefek.pluto.engine.graphics.gl.vao.attrib.data.VecArray; +import org.plutoengine.graphics.gl.vao.attrib.data.VecArray; public class FloatArrayBuffer extends ArrayBuffer> { diff --git a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/IndexArrayBuffer.java b/engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vbo/IndexArrayBuffer.java old mode 100644 new mode 100755 similarity index 90% rename from plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/IndexArrayBuffer.java rename to engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vbo/IndexArrayBuffer.java index ca652b9..3a57dcd --- a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/IndexArrayBuffer.java +++ b/engine-core/plutomesher/src/main/java/org/plutoengine/graphics/gl/vbo/IndexArrayBuffer.java @@ -1,11 +1,11 @@ -package cz.tefek.pluto.engine.graphics.gl.vbo; +package org.plutoengine.graphics.gl.vbo; import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER; import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW; import static org.lwjgl.opengl.GL15.glBindBuffer; import static org.lwjgl.opengl.GL15.glBufferData; -import cz.tefek.pluto.engine.graphics.gl.vao.attrib.data.VecArray; +import org.plutoengine.graphics.gl.vao.attrib.data.VecArray; public class IndexArrayBuffer extends ArrayBuffer> { diff --git a/engine-core/plutoruntime/build.gradle.kts b/engine-core/plutoruntime/build.gradle.kts new file mode 100755 index 0000000..4265f44 --- /dev/null +++ b/engine-core/plutoruntime/build.gradle.kts @@ -0,0 +1,44 @@ +import org.plutoengine.Versions + +sourceSets { + java { + create("config") { + } + + main { + java.srcDirs("$buildDir/generated/java") + } + } +} + +tasks { + val generateConfigs = register("generateConfigs", Copy::class) { + val projectVariables = mapOf("plutoVersion" to Versions.versionFull) + + inputs.properties(projectVariables) + + from("src/config/java") + + into("$buildDir/generated/java") + + expand(projectVariables) + } + + compileJava { + dependsOn(generateConfigs) + } +} + +dependencies { + api(project(":plutoengine:plutolib")) + api(project(":plutoengine:plutocomponent")) + + api(platform("org.lwjgl:lwjgl-bom:${Versions.lwjglVersion}")) + + implementation("org.lwjgl:lwjgl") + implementation("org.lwjgl:lwjgl-xxhash") + implementation("org.lwjgl:lwjgl-zstd") + runtimeOnly("org.lwjgl", "lwjgl", classifier = Versions.lwjglNatives) + runtimeOnly("org.lwjgl", "lwjgl-xxhash", classifier = Versions.lwjglNatives) + runtimeOnly("org.lwjgl", "lwjgl-zstd", classifier = Versions.lwjglNatives) +} \ No newline at end of file diff --git a/engine-core/plutoruntime/src/config/java/org/plutoengine/PlutoVersionConfig.java b/engine-core/plutoruntime/src/config/java/org/plutoengine/PlutoVersionConfig.java new file mode 100755 index 0000000..4ed6fd6 --- /dev/null +++ b/engine-core/plutoruntime/src/config/java/org/plutoengine/PlutoVersionConfig.java @@ -0,0 +1,11 @@ +package org.plutoengine; + +class PlutoVersionConfig +{ + /** + * The combined version string. + * + * @since 20.2.0.0-alpha.3 + * */ + protected static final String VERSION = "$plutoVersion"; +} diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/IVersion.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/IVersion.java new file mode 100755 index 0000000..983680f --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/IVersion.java @@ -0,0 +1,13 @@ +package org.plutoengine; + +public interface IVersion extends Comparable +{ + @Override + boolean equals(Object o); + + @Override + int hashCode(); + + @Override + String toString(); +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/Pluto.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/Pluto.java old mode 100644 new mode 100755 similarity index 54% rename from plutolib/src/main/java/cz/tefek/pluto/Pluto.java rename to engine-core/plutoruntime/src/main/java/org/plutoengine/Pluto.java index c206d5f..6254cf7 --- a/plutolib/src/main/java/cz/tefek/pluto/Pluto.java +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/Pluto.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto; +package org.plutoengine; /** * Constants shared by all Pluto libraries. @@ -7,65 +7,77 @@ package cz.tefek.pluto; * * @author 493msi */ -public class Pluto +public class Pluto extends PlutoVersionConfig { + /** + * The name of this engine. + * + * @since 20.2.0.0-alpha.3 + * */ + public static final String ENGINE_NAME = "PlutoEngine"; + public static final boolean DEBUG_MODE = Boolean.parseBoolean(System.getProperty("cz.tefek.pluto.debug")); - // TODO: Automate setting the version numbers with gradle - - /** - * The year version number, changes with breaking API changes. - * - * @since pre-alpha - * */ - public static final int VERSION_YEAR = 20; - - /** - * The major version number, changes with breaking API changes. - * - * @since pre-alpha - * */ - public static final int VERSION_MAJOR = 2; - - /** - * The minor version number, changes with backwards-compatible API changes. - * - * @since pre-alpha - * */ - public static final int VERSION_MINOR = 0; - - /** - * The patch number, changes with backwards-compatible fixes and patches. - * - * @since pre-alpha - * */ - public static final int VERSION_PATCH = 0; - - /** - * Denotes whether this build is a pre-release build. - * - * @since pre-alpha - * */ - public static final boolean PRERELEASE = true; - - /** - * The name of this pre-release, e.g. alpha, beta, RC and similar. - * - * @since pre-alpha - * */ - public static final String PRERELEASE_NAME = "alpha"; - - /** - * The pre-release patch number, incremented by 1 with *any* pre-release update. - * - * @since pre-alpha - * */ - public static final int PRERELEASE_PATCH = 2; - /** * The combined version string. * * @since pre-alpha * */ - public static final String VERSION = VERSION_YEAR + "." + VERSION_MAJOR + "." + VERSION_MINOR + "." + VERSION_PATCH + (PRERELEASE ? "-" + PRERELEASE_NAME + "." + PRERELEASE_PATCH : ""); + public static final String VERSION = PlutoVersionConfig.VERSION; + + /** + * The version object. + * + * @since 20.2.0.0-alpha.3 + * */ + public static final PlutoVersion VERSION_OBJ = PlutoVersion.of(VERSION); + + /** + * The year version number, changes with breaking API changes. + * + * @since pre-alpha + * */ + public static final int VERSION_YEAR = VERSION_OBJ.getYear(); + + /** + * The major version number, changes with breaking API changes. + * + * @since pre-alpha + * */ + public static final int VERSION_MAJOR = VERSION_OBJ.getMajor(); + + /** + * The minor version number, changes with backwards-compatible API changes. + * + * @since pre-alpha + * */ + public static final int VERSION_MINOR = VERSION_OBJ.getMinor(); + + /** + * The patch number, changes with backwards-compatible fixes and patches. + * + * @since pre-alpha + * */ + public static final int VERSION_PATCH = VERSION_OBJ.getPatch(); + + /** + * Denotes whether this build is a pre-release build. + * + * @since pre-alpha + * */ + public static final boolean PRERELEASE = VERSION_OBJ.isPrerelease(); + + /** + * The name of this pre-release, e.g. alpha, beta, RC and similar. + * + * @since pre-alpha + * */ + public static final String PRERELEASE_NAME = VERSION_OBJ.getPrereleaseName(); + + /** + * The pre-release patch number, incremented by 1 with *any* pre-release update. + * + * @since pre-alpha + * */ + public static final int PRERELEASE_PATCH = VERSION_OBJ.getPrereleaseNumber(); } diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/PlutoGlobal.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/PlutoGlobal.java new file mode 100755 index 0000000..7349dd9 --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/PlutoGlobal.java @@ -0,0 +1,9 @@ +package org.plutoengine; + +import org.plutoengine.component.ComponentManager; +import org.plutoengine.component.PlutoGlobalComponent; + +public class PlutoGlobal +{ + public static final ComponentManager COMPONENTS = new ComponentManager<>(PlutoGlobalComponent.class); +} diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/PlutoLocal.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/PlutoLocal.java new file mode 100755 index 0000000..28cbfa4 --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/PlutoLocal.java @@ -0,0 +1,33 @@ +package org.plutoengine; + +import org.plutoengine.address.ThreadSensitive; +import org.plutoengine.component.PlutoLocalComponent; +import org.plutoengine.component.ComponentManager; + +/** + * @since 20.2.0.0-alpha.3 + * + * @author 493msi + */ +@ThreadSensitive(localContexts = true) +public class PlutoLocal +{ + private static final ThreadLocal local = ThreadLocal.withInitial(PlutoLocal::new); + + public final ComponentManager COMPONENTS; + + private PlutoLocal() + { + this.COMPONENTS = new ComponentManager<>(PlutoLocalComponent.class); + } + + public static PlutoLocal instance() + { + return local.get(); + } + + public static ComponentManager components() + { + return instance().COMPONENTS; + } +} diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/PlutoVersion.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/PlutoVersion.java new file mode 100755 index 0000000..ebe996b --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/PlutoVersion.java @@ -0,0 +1,215 @@ +package org.plutoengine; + +import org.plutoengine.address.ConstantExpression; + +import javax.annotation.Nonnull; +import java.util.Objects; + +public final class PlutoVersion implements IVersion +{ + private final int year; + private final int major; + private final int minor; + private final int patch; + + private final boolean prerelease; + private final String prereleaseName; + private final int prereleaseNumber; + + public PlutoVersion(int year, int major, int minor, int patch) + { + this.year = year; + this.major = major; + this.minor = minor; + this.patch = patch; + + this.prerelease = false; + this.prereleaseName = null; + this.prereleaseNumber = 0; + } + + public PlutoVersion(int year, int major, int minor, int patch, String prereleaseName, int prereleaseNumber) + { + this.year = year; + this.major = major; + this.minor = minor; + this.patch = patch; + + this.prerelease = true; + this.prereleaseName = prereleaseName; + this.prereleaseNumber = prereleaseNumber; + } + + public int getYear() + { + return this.year; + } + + public int getMajor() + { + return this.major; + } + + public int getMinor() + { + return this.minor; + } + + public int getPatch() + { + return this.patch; + } + + public boolean isPrerelease() + { + return this.prerelease; + } + + public String getPrereleaseName() + { + return this.prereleaseName; + } + + public int getPrereleaseNumber() + { + return this.prereleaseNumber; + } + + @ConstantExpression + public static PlutoVersion of(@Nonnull String str) + { + assert !str.isBlank(); + + assert str.matches("[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+([+\\-][a-zA-Z0-9]+(\\.[0-9]+)?)?"); + + var parts = str.split("[+\\-]"); + + assert parts.length == 1 || parts.length == 2; + + var versionParts = parts[0].split("\\."); + + assert versionParts.length == 4; + + if (parts.length == 2) + { + var prereleaseParts = parts[1].split("\\."); + + assert prereleaseParts.length == 1 || prereleaseParts.length == 2; + + int prereleaseNumber; + + if (prereleaseParts.length == 2) + prereleaseNumber = Integer.parseInt(prereleaseParts[1]); + else + prereleaseNumber = 0; + + String prereleaseName = prereleaseParts[0]; + + return new PlutoVersion( + Integer.parseInt(versionParts[0]), + Integer.parseInt(versionParts[1]), + Integer.parseInt(versionParts[2]), + Integer.parseInt(versionParts[3]), + prereleaseName, + prereleaseNumber); + } + + return new PlutoVersion( + Integer.parseInt(versionParts[0]), + Integer.parseInt(versionParts[1]), + Integer.parseInt(versionParts[2]), + Integer.parseInt(versionParts[3])); + } + + @Override + public String toString() + { + if (prerelease) + return String.format("%d.%d.%d.%d-%s.%d", + this.year, + this.major, + this.minor, + this.patch, + this.prereleaseName, + this.prereleaseNumber); + else + return String.format("%d.%d.%d.%d", + this.year, + this.major, + this.minor, + this.patch); + } + + @Override + public int compareTo(@Nonnull PlutoVersion o) + { + int yearDiff = this.year - o.year; + if (yearDiff != 0) + return yearDiff; + + int majorDiff = this.major - o.major; + if (majorDiff != 0) + return majorDiff; + + int minorDiff = this.minor - o.minor; + if (minorDiff != 0) + return minorDiff; + + int patchDiff = this.patch - o.patch; + if (patchDiff != 0) + return patchDiff; + + if (!this.prerelease && !o.prerelease) + return 0; + + if (!this.prerelease) + return 1; + + if (!o.prerelease) + return -1; + + assert this.prereleaseName != null; + assert o.prereleaseName != null; + + int prereleaseNameDiff = this.prereleaseName.compareTo(o.prereleaseName); + + if (prereleaseNameDiff != 0) + return prereleaseNameDiff; + + return this.prereleaseNumber - o.prereleaseNumber; + } + + @Override + public boolean equals(Object o) + { + if (this == o) + return true; + + if (o == null || this.getClass() != o.getClass()) + return false; + + PlutoVersion that = (PlutoVersion) o; + + if (this.prerelease) + return this.year == that.year && + this.major == that.major && + this.minor == that.minor && + this.patch == that.patch && + this.prereleaseNumber == that.prereleaseNumber && + Objects.equals(this.prereleaseName, that.prereleaseName); + else + return this.year == that.year && + this.major == that.major && + this.minor == that.minor && + this.patch == that.patch; + } + + @Override + public int hashCode() + { + if (this.prerelease) + return Objects.hash(this.year, this.major, this.minor, this.patch); + else + return Objects.hash(this.year, this.major, this.minor, this.patch, this.prereleaseName, this.prereleaseNumber); + } +} diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/address/ConstantExpression.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/address/ConstantExpression.java new file mode 100755 index 0000000..9d2c8ca --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/address/ConstantExpression.java @@ -0,0 +1,22 @@ +package org.plutoengine.address; + +import javax.annotation.meta.TypeQualifier; +import java.lang.annotation.*; + +/** + * Denotes that the target field or method should be a constant expression - it is final, has no state + * and always yields the same deterministic result for given input. Generally, annotated methods + * should be thread-safe, however this is not required. + * + * @author 493msi + * + * @since 20.2.0.0-alpha.3 + * */ +@TypeQualifier +@Documented +@Retention(RetentionPolicy.CLASS) +@Target({ ElementType.METHOD, ElementType.FIELD }) +public @interface ConstantExpression +{ + +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/annotation/ThreadSensitive.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/address/ThreadSensitive.java old mode 100644 new mode 100755 similarity index 96% rename from plutolib/src/main/java/cz/tefek/pluto/annotation/ThreadSensitive.java rename to engine-core/plutoruntime/src/main/java/org/plutoengine/address/ThreadSensitive.java index 756d020..81e8298 --- a/plutolib/src/main/java/cz/tefek/pluto/annotation/ThreadSensitive.java +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/address/ThreadSensitive.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.annotation; +package org.plutoengine.address; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionClass.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/collision/CollisionClass.java old mode 100644 new mode 100755 similarity index 77% rename from plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionClass.java rename to engine-core/plutoruntime/src/main/java/org/plutoengine/collision/CollisionClass.java index b2ece82..b5ffd6b --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionClass.java +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/collision/CollisionClass.java @@ -1,11 +1,11 @@ -package cz.tefek.pluto.engine.math.collision; +package org.plutoengine.collision; public class CollisionClass { private static int idSource = 0; - private int id = idSource++; - private boolean selfCollision; + private final int id = idSource++; + private final boolean selfCollision; public CollisionClass(boolean selfCollision) { diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionSurface.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/collision/CollisionSurface.java old mode 100644 new mode 100755 similarity index 98% rename from plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionSurface.java rename to engine-core/plutoruntime/src/main/java/org/plutoengine/collision/CollisionSurface.java index 20f0d33..b659460 --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionSurface.java +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/collision/CollisionSurface.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.math.collision; +package org.plutoengine.collision; public class CollisionSurface { diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/component/PlutoGlobalComponent.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/component/PlutoGlobalComponent.java new file mode 100755 index 0000000..d80c432 --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/component/PlutoGlobalComponent.java @@ -0,0 +1,5 @@ +package org.plutoengine.component; + +public abstract class PlutoGlobalComponent extends AbstractComponent +{ +} diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/component/PlutoLocalComponent.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/component/PlutoLocalComponent.java new file mode 100755 index 0000000..e83f5b6 --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/component/PlutoLocalComponent.java @@ -0,0 +1,6 @@ +package org.plutoengine.component; + +public abstract class PlutoLocalComponent extends AbstractComponent +{ + +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/l10n/PlutoL10n.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/l10n/PlutoL10n.java old mode 100644 new mode 100755 similarity index 78% rename from plutolib/src/main/java/cz/tefek/pluto/l10n/PlutoL10n.java rename to engine-core/plutoruntime/src/main/java/org/plutoengine/l10n/PlutoL10n.java index 61b29ac..7c056f5 --- a/plutolib/src/main/java/cz/tefek/pluto/l10n/PlutoL10n.java +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/l10n/PlutoL10n.java @@ -1,17 +1,20 @@ -package cz.tefek.pluto.l10n; +package org.plutoengine.l10n; import java.util.HashMap; import java.util.Locale; import java.util.Map; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; +/** + * A localization manager. Extremely tentative. + * */ public class PlutoL10n { private static Locale defaultLocale; - private static Map> localizations = new HashMap<>(); + private static final Map> localizations = new HashMap<>(); public static void init(Locale locale) { @@ -25,12 +28,7 @@ public class PlutoL10n public static void registerLocale(Locale locale) { - if (localizations.containsKey(locale)) - { - return; - } - - localizations.put(locale, new HashMap<>()); + localizations.computeIfAbsent(locale, key -> new HashMap<>()); } public static void setLocale(Locale locale) diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/logger/ISeverity.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/logger/ISeverity.java old mode 100644 new mode 100755 similarity index 71% rename from plutolib/src/main/java/cz/tefek/pluto/io/logger/ISeverity.java rename to engine-core/plutoruntime/src/main/java/org/plutoengine/logger/ISeverity.java index 1bfd725..19a46e8 --- a/plutolib/src/main/java/cz/tefek/pluto/io/logger/ISeverity.java +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/logger/ISeverity.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.io.logger; +package org.plutoengine.logger; public interface ISeverity { diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/logger/Logger.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/logger/Logger.java old mode 100644 new mode 100755 similarity index 87% rename from plutolib/src/main/java/cz/tefek/pluto/io/logger/Logger.java rename to engine-core/plutoruntime/src/main/java/org/plutoengine/logger/Logger.java index 98ee8fb..7266036 --- a/plutolib/src/main/java/cz/tefek/pluto/io/logger/Logger.java +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/logger/Logger.java @@ -1,13 +1,13 @@ -package cz.tefek.pluto.io.logger; +package org.plutoengine.logger; -import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.nio.file.Files; -import java.nio.file.Path; import java.time.LocalDateTime; -import cz.tefek.pluto.io.asl.resource.ResourceHelper; +import org.plutoengine.component.ComponentToken; +import org.plutoengine.component.PlutoGlobalComponent; +import org.plutoengine.resource.filesystem.ResourceManager; /** *

@@ -15,20 +15,27 @@ import cz.tefek.pluto.io.asl.resource.ResourceHelper; *

* *

- * The log file is currently hardcoded to {@link ResourceHelper#GLOBAL_ROOT}/log--YYYY-MM-DD--HH-MM-SS.txt + * The log file is currently hardcoded to {@link ResourceManager#GLOBAL_ROOT}/logs/log--YYYY-MM-DD--HH-MM-SS.txt *

* * @author 493msi * * @since pre-alpha */ -public class Logger +public class Logger extends PlutoGlobalComponent { + public static final ComponentToken TOKEN = ComponentToken.create(Logger::new); + private static PrintStream stdout = null; private static PrintStream stderr = null; private static OutputStream fileLog = null; + private Logger() + { + + } + /** * Initializes the logger and replaces the standard output and standard error output with the logger methods. * @@ -40,22 +47,23 @@ public class Logger * * @since pre-alpha * */ - public static void setup() throws IOException + @Override + public void onMount() throws Exception { - close(); + this.onUnmount(); stdout = new PrintStream(System.out); stderr = new PrintStream(System.err); // TODO: Unhardcode the log directory - var logsDir = Path.of(ResourceHelper.GLOBAL_ROOT, "logs"); + var logsDir = ResourceManager.GLOBAL_ROOT.resolve("logs"); if (!Files.isDirectory(logsDir)) { if (Files.exists(logsDir)) { - System.err.printf("[#] Failed to initialize the logger, the path `%s` is obstructed by a non-directory!%n", logsDir.toAbsolutePath().toString()); + System.err.printf("[#] Failed to initialize the logger, the path `%s` is obstructed by a non-directory!%n", logsDir.toAbsolutePath()); return; } @@ -84,7 +92,8 @@ public class Logger * * @since pre-alpha * */ - public static void close() throws IOException + @Override + public void onUnmount() throws Exception { if (fileLog != null) fileLog.close(); @@ -223,4 +232,10 @@ public class Logger { (s.isStdErr() ? System.err : System.out).print(s.getDisplayName() + string); } + + @Override + public boolean isUnique() + { + return true; + } } diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/logger/OutputSplitStream.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/logger/OutputSplitStream.java old mode 100644 new mode 100755 similarity index 84% rename from plutolib/src/main/java/cz/tefek/pluto/io/logger/OutputSplitStream.java rename to engine-core/plutoruntime/src/main/java/org/plutoengine/logger/OutputSplitStream.java index 4738f29..a615555 --- a/plutolib/src/main/java/cz/tefek/pluto/io/logger/OutputSplitStream.java +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/logger/OutputSplitStream.java @@ -1,5 +1,6 @@ -package cz.tefek.pluto.io.logger; +package org.plutoengine.logger; +import javax.annotation.Nonnull; import java.io.IOException; import java.io.OutputStream; @@ -10,7 +11,7 @@ import java.io.OutputStream; * @author 493msi * */ -class OutputSplitStream extends OutputStream +public class OutputSplitStream extends OutputStream { private final OutputStream outputStreamA; private final boolean shouldAClose; @@ -36,14 +37,14 @@ class OutputSplitStream extends OutputStream } @Override - public void write(byte[] b) throws IOException + public void write(@Nonnull byte[] b) throws IOException { this.outputStreamA.write(b); this.outputStreamB.write(b); } @Override - public void write(byte[] b, int off, int len) throws IOException + public void write(@Nonnull byte[] b, int off, int len) throws IOException { this.outputStreamA.write(b, off, len); this.outputStreamB.write(b, off, len); diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/logger/SmartSeverity.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/logger/SmartSeverity.java old mode 100644 new mode 100755 similarity index 85% rename from plutolib/src/main/java/cz/tefek/pluto/io/logger/SmartSeverity.java rename to engine-core/plutoruntime/src/main/java/org/plutoengine/logger/SmartSeverity.java index c2fa4b3..7043adb --- a/plutolib/src/main/java/cz/tefek/pluto/io/logger/SmartSeverity.java +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/logger/SmartSeverity.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.io.logger; +package org.plutoengine.logger; /** * A more visual way to denote what's actually happening. @@ -11,6 +11,7 @@ public enum SmartSeverity implements ISeverity { ADDED("[+] ", false), REMOVED("[-] ", false), + CHECK("[✓] ", false), ZERO("[0] ", false), @@ -23,18 +24,21 @@ public enum SmartSeverity implements ISeverity AUDIO_MINUS("[-] [♪] ", false), AUDIO_WARNING("[!] [♪] ", true), AUDIO_ERROR("[X] [♪] ", true), + AUDIO_CHECK("[✓] [♪] ", true), MODULE("[i] [M] ", false), MODULE_PLUS("[+] [M] ", false), MODULE_MINUS("[-] [M] ", false), MODULE_WARNING("[!] [M] ", true), MODULE_ERROR("[X] [M] ", true), + MODULE_CHECK("[✓] [M] ", true), EVENT("[i] [E] ", false), EVENT_PLUS("[+] [E] ", false), EVENT_MINUS("[-] [E] ", false), EVENT_WARNING("[!] [E] ", true), - EVENT_ERROR("[X] [E] ", true); + EVENT_ERROR("[X] [E] ", true), + EVENT_CHECK("[✓] [E] ", true); private final String displayName; private final boolean usesStdErr; diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/EnumModLoadingPhase.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/EnumModLoadingPhase.java new file mode 100755 index 0000000..624c42a --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/EnumModLoadingPhase.java @@ -0,0 +1,16 @@ +package org.plutoengine.mod; + +public enum EnumModLoadingPhase +{ + INITIAL, + + SCANNING_EXTERNAL, + + CLASSLOADING_EXTERNAL, + + LOADING, + + DONE, + + UNLOADING +} diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/IModEntryPoint.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/IModEntryPoint.java new file mode 100755 index 0000000..f1fbb2c --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/IModEntryPoint.java @@ -0,0 +1,33 @@ +package org.plutoengine.mod; + +/** + * The main entry point for non-virtual mods. + * + * @author 493msi + * @since 20.2.0.0-alpha.3 + * */ +public interface IModEntryPoint +{ + /** + * Called when the mod loader loads this mod. + * + * @author 493msi + * @since 20.2.0.0-alpha.3 + * @param mod The {@link Mod} object + * */ + default void onLoad(Mod mod) + { + + } + + /** + * Called when the mod loader unloads this mod. + * + * @author 493msi + * @since 20.2.0.0-alpha.3 + * */ + default void onUnload() + { + + } +} diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/Mod.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/Mod.java new file mode 100755 index 0000000..b1ae4a3 --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/Mod.java @@ -0,0 +1,147 @@ +package org.plutoengine.mod; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.plutoengine.address.VirtualAddress; +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; +import org.plutoengine.resource.filesystem.ResourceManager; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.Objects; + +/** + * Mod object. + * + *

+ * {@link ModLoader} automatically creates a Mod object for each class + * annotated by {@link ModEntry @ModEntry} (assuming it is registered or class + * loaded by ModClassLoader, which auto-detects and registers {@link ModEntry + * ModEntries}). + *

+ * + * @see PlutoModLoader tutorial for + * more information. + * + * @since 20.2.0.0-alpha.3 + * + * @author 493msi + */ +public class Mod implements Comparable +{ + private final VirtualAddress id; + + private Class mainClass; + + private Class[] dependencies; + + private ModManifest manifest; + + private ResourceManager resourceManager; + + private Mod(VirtualAddress id) + { + this.id = id; + } + + static Mod from(VirtualAddress modID, Class[] dependencies, Class mainClass) + { + var mod = new Mod(modID); + mod.mainClass = mainClass; + mod.dependencies = dependencies; + + var modIDStr = mod.id.toString(); + + var modDir = ResourceManager.MOD_DIRECTORY.resolve(modIDStr); + var modManifestFile = modDir.resolve(ResourceManager.MOD_MANIFEST_PATH); + var modManifest = (ModManifest) null; + + if (!Files.isDirectory(modDir) || !Files.isRegularFile(modManifestFile)) + { + Logger.logf(SmartSeverity.MODULE_WARNING, "Mod ID '%s' has a missing manifest (%s file located in the %s directory)," + + " it will not have any metadata attached to it.%n", modIDStr, ResourceManager.MOD_MANIFEST_PATH, modDir); + } + else + { + try (var br = Files.newBufferedReader(modManifestFile)) + { + var om = new ObjectMapper(); + modManifest = om.readValue(br, ModManifest.class); + } + catch (Exception e) + { + Logger.logf(SmartSeverity.MODULE_ERROR, "Mod ID '%s' has a broken manifest (%s file located in the %s directory)," + + " it will not have any metadata attached to it.%n", modIDStr, ResourceManager.MOD_MANIFEST_PATH, modDir); + + Logger.log(e); + } + } + + mod.manifest = Objects.requireNonNullElseGet(modManifest, () -> new ModManifest(modID.toString(), "", "", Map.of())); + + mod.resourceManager = new ResourceManager(mod); + + mod.manifest.resourceRoots().forEach((rfsID, rfsData) -> { + try + { + mod.resourceManager.addFileSystem(rfsID, rfsData.type()); + } + catch (IOException e) + { + Logger.logf(SmartSeverity.MODULE_ERROR, "Could not open the file system '%s+%s'!", modIDStr, rfsID); + + Logger.log(e); + } + }); + + return mod; + } + + public VirtualAddress getID() + { + return this.id; + } + + public Class getMainClass() + { + return this.mainClass; + } + + public Class[] getDependencies() + { + return this.dependencies; + } + + public ModManifest getManifest() + { + return this.manifest; + } + + public ResourceManager getResourceManager() + { + return this.resourceManager; + } + + public Path getResource(String path) + { + if (!path.contains("$")) + path = "default$" + path; + + return this.resourceManager.getPath(path); + } + + @Override + public int compareTo(Mod o) + { + return this.id.compareTo(o.id); + } + + @Override + public String toString() + { + return this.id.toString(); + } +} + diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/ModClassLoader.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/ModClassLoader.java new file mode 100755 index 0000000..d173a32 --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/ModClassLoader.java @@ -0,0 +1,170 @@ +package org.plutoengine.mod; + +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; +import org.plutoengine.resource.filesystem.ResourceManager; + +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * @deprecated Reserved for future use + * @author 493msi + */ +@Deprecated +public class ModClassLoader +{ + public static void loadJars() throws IOException + { + Logger.log(SmartSeverity.MODULE, "Looking for installed mods."); + + var modContainingDir = ResourceManager.MOD_DIRECTORY; + + if (!Files.isDirectory(modContainingDir)) + { + Files.createDirectories(modContainingDir); + + Logger.logf(SmartSeverity.MODULE, "No mods found in '%s'.%n", modContainingDir.toAbsolutePath()); + + return; + } + + int loadedJars = 0; + + try (var modDirs = Files.list(modContainingDir)) + { + for (var it = modDirs.filter(Files::isDirectory).iterator(); it.hasNext();) + { + var modDir = it.next(); + + var modJar = modDir.resolve("mod.jar"); + + if (!Files.isRegularFile(modJar)) + { + Logger.logf(SmartSeverity.MODULE_WARNING, "Failed to load mod jar file: '%s'.%n", modJar.toAbsolutePath()); + Logger.log(SmartSeverity.MODULE_WARNING, "Reason: Missing mod jar file."); + + return; + } + + var dataDir = modDir.resolve("data"); + + if (!Files.isDirectory(dataDir)) + Files.createDirectories(dataDir); + + classLoadJar(modJar); + + Logger.logf(SmartSeverity.MODULE_PLUS, "Loaded mod jar file: %s%n", modJar.toAbsolutePath()); + + loadedJars++; + } + } + + + Logger.logf(SmartSeverity.MODULE, "Mod loading complete, loaded %d mod/s from %d jar file/s.%n", loadedJars); + } + + private static PlutoClassLoader classLoadJar(Path jarPath) throws IOException + { + try (var jarFile = new JarFile(jarPath.toFile())) + { + Enumeration e = jarFile.entries(); + + var classLoader = new PlutoClassLoader(); + + while (e.hasMoreElements()) + { + JarEntry je = e.nextElement(); + + if (je.isDirectory()) + { + continue; + } + + var entryName = je.getName(); + + var resourcePath = String.format("file:%s!/%s", jarPath.toAbsolutePath(), entryName); + var resourceURL = new URL("jar", null, resourcePath); + + classLoader.createResource(entryName, resourceURL); + + if (!entryName.endsWith(".class")) + { + continue; + } + + var is = jarFile.getInputStream(je); + var classBytes = is.readAllBytes(); + is.close(); + + var className = entryName + .replaceAll("\\.class$", "") + .replace('/', '.'); + + classLoader.createClass(className, classBytes); + } + + return classLoader; + } + } + + public static class PlutoClassLoader extends ClassLoader + { + private final Map> classMap; + private final Map resourceMap; + + private PlutoClassLoader() + { + this.classMap = new HashMap<>(); + this.resourceMap = new HashMap<>(); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException + { + var clazz = this.classMap.get(name); + + if (clazz == null) + { + throw new ClassNotFoundException(String.format("Undefined class: '%s'", name)); + } + + return clazz; + } + + public Collection> getClasses() + { + return Collections.unmodifiableCollection(classMap.values()); + } + + @Override + protected URL findResource(String name) + { + return this.resourceMap.get(name); + } + + @Override + protected Enumeration findResources(String name) + { + var resource = this.findResource(name); + return resource == null ? Collections.emptyEnumeration() : Collections.enumeration(List.of(resource)); + } + + private void createClass(String name, byte[] data) + { + var clazz = this.defineClass(name, data, 0, data.length); + this.resolveClass(clazz); + this.classMap.put(name, clazz); + } + + private void createResource(String name, URL url) + { + this.resourceMap.put(name, url); + } + } +} diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/ModEntry.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/ModEntry.java new file mode 100755 index 0000000..635fa3c --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/ModEntry.java @@ -0,0 +1,32 @@ +package org.plutoengine.mod; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * The heart of any Pluto mod. Annotate your class with this annotation + * so the PlutoModLoader can load it. The class must be directly registered + * or processed by {@link ModClassLoader} (as an external mod). + * + *

+ * Mods can optionally have an entry point (e.g. {@link IModEntryPoint}. + *

+ * + *

What is a virtual mod?

+ * + *

+ * Virtual mods don't have any functionality by themselves except + * being a structural point of the mod tree. They don't have an entry point. + *

+ * + * @author 493msi + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface ModEntry +{ + String modID(); + + String version(); + + Class[] dependencies() default {}; +} diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/ModLoader.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/ModLoader.java new file mode 100755 index 0000000..e03b7d8 --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/ModLoader.java @@ -0,0 +1,266 @@ +package org.plutoengine.mod; + +import org.plutoengine.address.VirtualAddress; +import org.plutoengine.address.VirtualAddressParseException; +import org.plutoengine.component.ComponentManager; +import org.plutoengine.component.ComponentToken; +import org.plutoengine.component.PlutoLocalComponent; +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; + +import java.util.*; + +/** + * + * + * @author 493msi + * @since 20.2.0.0-alpha.3 + */ +public final class ModLoader extends PlutoLocalComponent +{ + public static final ComponentToken TOKEN = ComponentToken.create(ModLoader::new); + + private EnumModLoadingPhase loadingPhase; + private final Map modNameLookup; + private final Map, Mod> modLookup; + private final Deque loadedModStack; + private final Set loadList; + private final Map entryPoints; + + /** + * + * + * @author 493msi + * @since 20.2.0.0-alpha.3 + */ + private ModLoader() + { + this.loadingPhase = EnumModLoadingPhase.INITIAL; + this.modNameLookup = new HashMap<>(); + this.modLookup = new HashMap<>(); + this.loadedModStack = new LinkedList<>(); + this.loadList = new LinkedHashSet<>(); + this.entryPoints = new HashMap<>(); + } + + /** + * Denotes whether this component should be unique. + * Unique components can only exist once per instance + * in a given {@link ComponentManager}. + * + * @return whether this component should be unique + * + * @author 493msi + * @since 20.2.0.0-alpha.3 + */ + @Override + public boolean isUnique() + { + return true; + } + + /** + * + *

+ * Note: This method can only be called before the initialization process begins. + *

+ * + * @author 493msi + * @since 20.2.0.0-alpha.3 + */ + public void registerMod(Class modClass) + { + if (loadingPhase != EnumModLoadingPhase.INITIAL && loadingPhase != EnumModLoadingPhase.CLASSLOADING_EXTERNAL) + { + Logger.logf(SmartSeverity.MODULE_ERROR, "Cannot register a mod during loading phase %s!%n", loadingPhase.name()); + return; + } + + if (modLookup.containsKey(modClass)) + { + Logger.logf(SmartSeverity.MODULE_CHECK, "Mod class '%s' is already registered, skipping it.%n", modClass.getCanonicalName()); + return; + } + + var modInterface = modClass.getDeclaredAnnotation(ModEntry.class); + + if (modInterface == null) + { + Logger.logf(SmartSeverity.MODULE_WARNING, "Cannot create a mod from the class '%s', it is not annotated with @%s%n".formatted(modClass.getCanonicalName(), ModEntry.class.getName())); + return; + } + + VirtualAddress modID; + + try + { + modID = VirtualAddress.parse(modInterface.modID(), false); + } + catch (VirtualAddressParseException e) + { + Logger.logf(SmartSeverity.MODULE_WARNING, "The ID of mod '%s' is empty or invalid, it will not be loaded.%n", modClass); + return; + } + + var mod = Mod.from(modID, modInterface.dependencies(), modClass); + + this.modNameLookup.put(modID, mod); + this.modLookup.put(modClass, mod); + this.loadList.add(mod); + + var dependencies = mod.getDependencies(); + + for (var dependency : dependencies) + { + if (modLookup.containsKey(dependency)) + continue; + + Logger.logf(SmartSeverity.MODULE_PLUS, "Adding '%s' as a dependency of '%s'.%n", dependency.getCanonicalName(), mod.getID()); + + this.registerMod(dependency); + } + } + + /** + * Returns all loaded mods in no particular order. + * + * @return A collection of all loaded mods + * + * @author 493msi + * @since 20.2.0.0-alpha.3 + */ + public Collection getAllMods() + { + return Collections.unmodifiableCollection(this.loadedModStack); + } + + /** + * Attempts to find a {@link Mod} with the corresponding mod ID and returns it, + * otherwise returns an empty {@link Optional}. + * + * @param modID A mod ID string + * + * @return A {@link Mod} with the corresponding ID; or an empty optional + * + * @author 493msi + * @since 20.2.0.0-alpha.3 + */ + public Optional getModByID(VirtualAddress modID) + { + return Optional.ofNullable(this.modNameLookup.get(modID)); + } + + /** + * + * + * @author 493msi + * @since 20.2.0.0-alpha.3 + */ + public void load() + { + Logger.log(SmartSeverity.MODULE, "Number of pre-added mod/s: " + this.loadList.size()); + + this.loadingPhase = EnumModLoadingPhase.SCANNING_EXTERNAL; + + // TODO: Load external mods + + this.loadingPhase = EnumModLoadingPhase.CLASSLOADING_EXTERNAL; + + // TODO: Classload external mods + + this.loadingPhase = EnumModLoadingPhase.LOADING; + + Logger.log(SmartSeverity.MODULE, "Mod initialization started."); + + try + { + int modCount = this.loadList.size(); + int i = 0; + + for (var mod : this.loadList) + { + i++; + + Logger.logf(SmartSeverity.MODULE, "[%d / %d] Initializing '%s'...%n", i, modCount, mod.getID()); + + var modClass = mod.getMainClass(); + + if (IModEntryPoint.class.isAssignableFrom(modClass)) + { + var entryPoint = modClass.asSubclass(IModEntryPoint.class); + Logger.logf(SmartSeverity.MODULE, "[%d / %d] Mod '%s' has an entry point, constructing...%n", i, modCount, mod.getID()); + + try + { + var constructor = entryPoint.getConstructor(); + IModEntryPoint entryPointInstance = constructor.newInstance(); + Logger.logf(SmartSeverity.MODULE, "[%d / %d] Running onLoad for mod '%s'...%n", i, modCount, mod.getID()); + entryPointInstance.onLoad(mod); + this.entryPoints.put(mod, entryPointInstance); + } + catch (NoSuchMethodException e) + { + Logger.logf(SmartSeverity.MODULE_ERROR, "[%d / %d] Error: Mod '%s' has an entry point but does not have an accessible default constructor!" + + " It will not be initialized...%n", i, modCount, mod.getID()); + } + } + + this.loadedModStack.push(mod); + } + } + catch (Exception e) + { + Logger.log(SmartSeverity.MODULE_ERROR, "Caught the following exception while initializing mods:"); + Logger.log(e); + + Logger.log(SmartSeverity.MODULE_ERROR, "Mod initialization was canceled due to an error."); + } + + if (this.loadingPhase == EnumModLoadingPhase.DONE) + { + Logger.log(SmartSeverity.MODULE_CHECK, "Mod initialization finished."); + } + else + { + Logger.log(SmartSeverity.MODULE_ERROR, "Mod initialization incomplete."); + } + } + + /** + * + * + * @author 493msi + * @since 20.2.0.0-alpha.3 + */ + public void unload() + { + this.loadingPhase = EnumModLoadingPhase.UNLOADING; + Logger.log(SmartSeverity.MODULE_MINUS, "Unloading all mods."); + + int modCount = this.loadedModStack.size(); + int i = 0; + + while (!this.loadedModStack.isEmpty()) + { + i++; + + var mod = this.loadedModStack.pop(); + + Logger.logf(SmartSeverity.MODULE, "[%d / %d] Deinitializing '%s'...%n", i, modCount, mod.getID()); + + var entryPointInstance = this.entryPoints.get(mod); + + if (entryPointInstance != null) + { + Logger.logf(SmartSeverity.MODULE, "[%d / %d] Running onUnload for mod '%s'...%n", i, modCount, mod.getID()); + entryPointInstance.onUnload(); + } + } + + this.modNameLookup.clear(); + this.loadList.clear(); + this.loadedModStack.clear(); + this.modLookup.clear(); + this.entryPoints.clear(); + } +} diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/ModManifest.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/ModManifest.java new file mode 100755 index 0000000..ff416c3 --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/mod/ModManifest.java @@ -0,0 +1,23 @@ +package org.plutoengine.mod; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.ext.NioPathDeserializer; +import org.plutoengine.address.VirtualAddress; +import org.plutoengine.resource.filesystem.EnumBackingFileSystem; + +import java.nio.file.Path; +import java.util.Map; + +public record ModManifest(String displayName, + String description, + String author, + @JsonDeserialize(keyAs = VirtualAddress.class, contentAs = ResourceRootInfo.class) Map resourceRoots) +{ + public record ResourceRootInfo( + EnumBackingFileSystem type, + @JsonDeserialize(using = NioPathDeserializer.class) Path path + ) + { + + } +} diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/EnumBackingFileSystem.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/EnumBackingFileSystem.java new file mode 100755 index 0000000..504e237 --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/EnumBackingFileSystem.java @@ -0,0 +1,122 @@ +package org.plutoengine.resource.filesystem; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.util.StdConverter; +import org.apache.commons.lang3.function.TriFunction; +import org.plutoengine.address.VirtualAddress; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.BiFunction; + +@JsonSerialize(converter = EnumBackingFileSystem.Serializer.class) +@JsonDeserialize(converter = EnumBackingFileSystem.Deserializer.class) +public enum EnumBackingFileSystem +{ + FS_DIRECTORY("open", + uri -> FileSystems.getDefault(), + (uri, fileSystem) -> fileSystem.provider().getPath(uri), + (fs, address, ext) -> { + var sep = fs.getSeparator(); + var rootOffs = address.getRootOffset(); + + var path = (".." + sep).repeat(rootOffs) + String.join(sep, address.getComponents()); + + if (ext != null) + return fs.getPath(path + "." + ext); + + return fs.getPath(path); + }), + FS_ZIP("zip", + uri -> FileSystems.newFileSystem(uri, Map.of()), + (uri, fileSystem) -> fileSystem.getPath("/"), + (fs, address, ext) -> { + var sep = fs.getSeparator(); + var rootOffs = address.getRootOffset(); + + var path = (".." + sep).repeat(rootOffs) + String.join(sep, address.getComponents()); + + if (ext != null) + return fs.getPath(path + "." + ext); + + return fs.getPath(path); + }); + + @FunctionalInterface + public interface FileSystemCreateFunc + { + FileSystem create(URI uri) throws IOException; + } + + private static final Map LOOKUP = new HashMap<>(); + + static + { + for (var item : values()) + LOOKUP.put(item.id, item); + } + + private final String id; + private final FileSystemCreateFunc createFunction; + private final BiFunction getRootFunction; + // (input filesystem, path address, extension) -> backing path + private final TriFunction backingPathConverter; + + EnumBackingFileSystem(String id, FileSystemCreateFunc createFunc, BiFunction getRootFunction, TriFunction backingPathConverter) + { + this.id = id; + this.createFunction = createFunc; + this.getRootFunction = getRootFunction; + this.backingPathConverter = backingPathConverter; + } + + public FileSystem create(URI uri) throws IOException + { + return this.createFunction.create(uri); + } + + public Path createRootPath(URI uri, FileSystem fs) + { + return this.getRootFunction.apply(uri, fs); + } + + public String getID() + { + return this.id; + } + + public Path createBackingPath(FileSystem fs, UnresolvedResourcePath urp) + { + return this.backingPathConverter.apply(fs, urp.pathAddress(), urp.extension()); + } + + public static EnumBackingFileSystem getByID(String id) + { + return LOOKUP.get(id); + } + + public static class Serializer extends StdConverter + { + @Override + public String convert(EnumBackingFileSystem value) + { + return value.id; + } + } + + public static class Deserializer extends StdConverter + { + @Override + public EnumBackingFileSystem convert(String value) + { + return Objects.requireNonNull(LOOKUP.get(value)); + } + } +} diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/ResourceFileSystem.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/ResourceFileSystem.java new file mode 100755 index 0000000..2705c33 --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/ResourceFileSystem.java @@ -0,0 +1,182 @@ +package org.plutoengine.resource.filesystem; + +import org.jetbrains.annotations.NotNull; +import org.plutoengine.address.VirtualAddress; +import org.plutoengine.mod.Mod; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.*; +import java.nio.file.attribute.UserPrincipalLookupService; +import java.util.List; +import java.util.Set; + +final class ResourceFileSystem extends FileSystem implements Comparable +{ + private final Mod mod; + private final VirtualAddress address; + + private final URI uri; + private final FileSystem fileSystemImpl; + + private final ResourceFileSystemProvider provider; + + private final ResourcePath root; + + private final EnumBackingFileSystem backingFileSystemType; + private final Path backingRootPath; + + ResourceFileSystem(ResourceFileSystemProvider provider, + Mod mod, + VirtualAddress address, + EnumBackingFileSystem backingFileSystemType, + URI uri, + FileSystem backingFileSystem, + Path rootPath) + { + this.provider = provider; + this.backingFileSystemType = backingFileSystemType; + this.uri = uri; + + this.fileSystemImpl = backingFileSystem; + this.backingRootPath = rootPath; + + this.mod = mod; + this.address = address; + + var urp = new UnresolvedResourcePath(this.mod.getID(), + this.getAddress(), + false, + VirtualAddress.ofRoot(), + null); + + this.root = this.getPath(urp); + } + + public Mod getMod() + { + return this.mod; + } + + public VirtualAddress getAddress() + { + return this.address; + } + + @Override + public ResourceFileSystemProvider provider() + { + return this.provider; + } + + @Override + public void close() throws IOException + { + this.provider.destroyFileSystem(this.uri); + } + + @Override + public boolean isOpen() + { + return this.fileSystemImpl.isOpen(); + } + + @Override + public boolean isReadOnly() + { + return this.fileSystemImpl.isReadOnly(); + } + + @Override + public String getSeparator() + { + return Character.toString(VirtualAddress.TOKEN_PATH_SEPARATOR); + } + + public ResourcePath getRoot() + { + return this.root; + } + + @Override + public Iterable getRootDirectories() + { + return List.of(this.getRoot()); + } + + @Override + public Iterable getFileStores() + { + return this.fileSystemImpl.getFileStores(); + } + + @Override + public Set supportedFileAttributeViews() + { + return this.fileSystemImpl.supportedFileAttributeViews(); + } + + @Override + public @NotNull ResourcePath getPath(@NotNull String first, String @NotNull... more) + { + var separator = this.getSeparator(); + + var pathBld = new StringBuilder(first); + + if (more.length > 0) + { + pathBld.append(VirtualAddress.TOKEN_PATH_SEPARATOR); + pathBld.append(String.join(separator, more)); + } + + var pathStr = pathBld.toString(); + var raw = pathStr.replace("..", "."); + + var urp = ResourcePathParser.parse(raw); + + return this.getPath(urp); + } + + public ResourcePath getPath(UnresolvedResourcePath urp) + { + return new ResourcePath(this, + urp.pathAddress(), + urp.extension(), + this.backingRootPath.resolve(this.backingFileSystemType.createBackingPath(this.fileSystemImpl, urp))); + } + + @Override + public PathMatcher getPathMatcher(String syntaxAndPattern) + { + return this.fileSystemImpl.getPathMatcher(syntaxAndPattern); + } + + @Override + public UserPrincipalLookupService getUserPrincipalLookupService() + { + return this.fileSystemImpl.getUserPrincipalLookupService(); + } + + @Override + public WatchService newWatchService() throws IOException + { + return this.fileSystemImpl.newWatchService(); + } + + @Override + public int compareTo(ResourceFileSystem o) + { + var modDiff = this.mod.compareTo(o.mod); + + if (modDiff != 0) + return modDiff; + + return this.address.compareTo(o.address); + } + + @Override + public String toString() + { + return this.mod + "+" + this.address; + } +} diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/ResourceFileSystemProvider.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/ResourceFileSystemProvider.java new file mode 100755 index 0000000..60e4d05 --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/ResourceFileSystemProvider.java @@ -0,0 +1,323 @@ +package org.plutoengine.resource.filesystem; + +import org.jetbrains.annotations.NotNull; +import org.plutoengine.PlutoLocal; +import org.plutoengine.address.VirtualAddress; +import org.plutoengine.mod.Mod; +import org.plutoengine.mod.ModLoader; + +import java.io.IOException; +import java.net.URI; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.FileAttributeView; +import java.nio.file.spi.FileSystemProvider; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class ResourceFileSystemProvider extends FileSystemProvider +{ + record ResourceFileSystemID( + Mod mod, + VirtualAddress rootAddress + ) + { + + } + + static final String ENV_PLUTO_MOD = "pluto-mod"; + static final String ENV_PLUTO_ADDRESS = "pluto-address"; + static final String ENV_PLUTO_FS_TYPE = "pluto-rfs-type"; + + private final Map fsURIs = new HashMap<>(); + private final Map refCount = new HashMap<>(); + private final Map fileSystems = new HashMap<>(); + + /** + * @deprecated Do not call directly! + */ + @Deprecated + public ResourceFileSystemProvider() + { + + } + + @Override + public String getScheme() + { + return ResourceManager.URI_SCHEME; + } + + @Override + public synchronized ResourceFileSystem newFileSystem(URI uri, Map env) throws IOException + { + var type = (EnumBackingFileSystem) env.get(ENV_PLUTO_FS_TYPE); + + var fs = type.create(uri); + + var mod = (Mod) env.get(ENV_PLUTO_MOD); + var address = (VirtualAddress) env.get(ENV_PLUTO_ADDRESS); + + var rootPath = type.createRootPath(uri, fs); + + var rfs = new ResourceFileSystem(this, mod, address, type, uri, fs, rootPath); + + this.fileSystems.put(uri, rfs); + this.refCount.compute(uri, (k, v) -> v != null ? v + 1 : 1); + + var rfsID = new ResourceFileSystemID(mod, address); + fsURIs.put(rfsID, uri); + + return rfs; + } + + synchronized void destroyFileSystem(URI uri) throws IOException + { + if (this.refCount.computeIfPresent(uri, (k, v) -> v > 1 ? v - 1 : null) != null) + return; + + var fs = this.fileSystems.get(uri); + + if (fs != null) + { + this.fsURIs.remove(new ResourceFileSystemID(fs.getMod(), fs.getAddress())); + + if (fs != FileSystems.getDefault()) + fs.close(); + } + + this.fileSystems.remove(uri); + } + + @Override + public synchronized ResourceFileSystem getFileSystem(URI uri) + { + return this.fileSystems.get(uri); + } + + @Override + public synchronized ResourcePath getPath(@NotNull URI uri) + { + var urp = ResourcePathParser.parse(uri); + + var modLoader = PlutoLocal.components().getComponent(ModLoader.class); + + var mod = modLoader.getModByID(urp.modID()); + + if (mod.isEmpty()) + throw new IllegalArgumentException("No such mod exists!"); + + var rfsID = new ResourceFileSystemID(mod.get(), urp.containerID()); + + var rfsURI = this.fsURIs.get(rfsID); + + if (rfsURI == null) + throw new IllegalArgumentException("No such mod ID or resource file system registered!"); + + var rfs = this.fileSystems.get(rfsURI); + + assert rfs != null; + + return rfs.getPath(urp); + } + + @Override + public synchronized SeekableByteChannel newByteChannel(Path path, Set options, FileAttribute... attrs) throws IOException + { + if (!(path instanceof ResourcePath rp)) + throw new IllegalArgumentException("Expected a path of type %s!".formatted(ResourcePath.class)); + + var backingPath = rp.getBackingPath(); + var backingFileSystem = backingPath.getFileSystem(); + var backingProvider = backingFileSystem.provider(); + + return backingProvider.newByteChannel(backingPath, options, attrs); + } + + @Override + public synchronized DirectoryStream newDirectoryStream(Path dir, DirectoryStream.Filter filter) throws IOException + { + if (!(dir instanceof ResourcePath rp)) + throw new IllegalArgumentException("Expected a path of type %s!".formatted(ResourcePath.class)); + + var backingPath = rp.getBackingPath(); + var backingFileSystem = backingPath.getFileSystem(); + var backingProvider = backingFileSystem.provider(); + + return backingProvider.newDirectoryStream(backingPath, filter); + } + + @Override + public synchronized void createDirectory(Path dir, FileAttribute... attrs) throws IOException + { + if (!(dir instanceof ResourcePath rp)) + throw new IllegalArgumentException("Expected a path of type %s!".formatted(ResourcePath.class)); + + var backingPath = rp.getBackingPath(); + var backingFileSystem = backingPath.getFileSystem(); + var backingProvider = backingFileSystem.provider(); + + backingProvider.createDirectory(backingPath, attrs); + } + + @Override + public synchronized void delete(Path path) throws IOException + { + if (!(path instanceof ResourcePath rp)) + throw new IllegalArgumentException("Expected a path of type %s!".formatted(ResourcePath.class)); + + var backingPath = rp.getBackingPath(); + var backingFileSystem = backingPath.getFileSystem(); + var backingProvider = backingFileSystem.provider(); + + backingProvider.delete(backingPath); + } + + @Override + public synchronized void copy(Path source, Path target, CopyOption... options) throws IOException + { + if (!(source instanceof ResourcePath rpSrc)) + throw new IllegalArgumentException("Expected a path of type %s!".formatted(ResourcePath.class)); + + if (!(target instanceof ResourcePath rpTgt)) + throw new IllegalArgumentException("Expected a path of type %s!".formatted(ResourcePath.class)); + + var backingSourcePath = rpSrc.getBackingPath(); + var backingSourceFileSystem = backingSourcePath.getFileSystem(); + var backingSourceProvider = backingSourceFileSystem.provider(); + + var backingTargetPath = rpTgt.getBackingPath(); + + backingSourceProvider.copy(backingSourcePath, backingTargetPath, options); + } + + @Override + public synchronized void move(Path source, Path target, CopyOption... options) throws IOException + { + if (!(source instanceof ResourcePath rpSrc)) + throw new IllegalArgumentException("Expected a path of type %s!".formatted(ResourcePath.class)); + + if (!(target instanceof ResourcePath rpTgt)) + throw new IllegalArgumentException("Expected a path of type %s!".formatted(ResourcePath.class)); + + var backingSourcePath = rpSrc.getBackingPath(); + var backingSourceFileSystem = backingSourcePath.getFileSystem(); + var backingSourceProvider = backingSourceFileSystem.provider(); + + var backingTargetPath = rpTgt.getBackingPath(); + + backingSourceProvider.move(backingSourcePath, backingTargetPath, options); + } + + @Override + public synchronized boolean isSameFile(Path path, Path path2) throws IOException + { + if (!(path instanceof ResourcePath rp)) + throw new IllegalArgumentException("Expected a path of type %s!".formatted(ResourcePath.class)); + + var backingPath = rp.getBackingPath(); + var backingFileSystem = backingPath.getFileSystem(); + var backingProvider = backingFileSystem.provider(); + + if (!(path2 instanceof ResourcePath rp2)) + throw new IllegalArgumentException("Expected a path of type %s!".formatted(ResourcePath.class)); + + var backingPath2 = rp2.getBackingPath(); + + return backingProvider.isSameFile(backingPath, backingPath2); + } + + @Override + public synchronized boolean isHidden(Path path) throws IOException + { + if (!(path instanceof ResourcePath rp)) + throw new IllegalArgumentException("Expected a path of type %s!".formatted(ResourcePath.class)); + + var backingPath = rp.getBackingPath(); + var backingFileSystem = backingPath.getFileSystem(); + var backingProvider = backingFileSystem.provider(); + + return backingProvider.isHidden(backingPath); + } + + @Override + public synchronized FileStore getFileStore(Path path) throws IOException + { + if (!(path instanceof ResourcePath rp)) + throw new IllegalArgumentException("Expected a path of type %s!".formatted(ResourcePath.class)); + + var backingPath = rp.getBackingPath(); + var backingFileSystem = backingPath.getFileSystem(); + var backingProvider = backingFileSystem.provider(); + + return backingProvider.getFileStore(backingPath); + } + + @Override + public synchronized void checkAccess(Path path, AccessMode... modes) throws IOException + { + if (!(path instanceof ResourcePath rp)) + throw new IllegalArgumentException("Expected a path of type %s!".formatted(ResourcePath.class)); + + var backingPath = rp.getBackingPath(); + var backingFileSystem = backingPath.getFileSystem(); + var backingProvider = backingFileSystem.provider(); + + backingProvider.checkAccess(backingPath, modes); + } + + @Override + public synchronized V getFileAttributeView(Path path, Class type, LinkOption... options) + { + if (!(path instanceof ResourcePath rp)) + throw new IllegalArgumentException("Expected a path of type %s!".formatted(ResourcePath.class)); + + var backingPath = rp.getBackingPath(); + var backingFileSystem = backingPath.getFileSystem(); + var backingProvider = backingFileSystem.provider(); + + return backingProvider.getFileAttributeView(backingPath, type, options); + } + + @Override + public synchronized A readAttributes(Path path, Class < A > type, LinkOption... options) throws IOException + { + if (!(path instanceof ResourcePath rp)) + throw new IllegalArgumentException("Expected a path of type %s!".formatted(ResourcePath.class)); + + var backingPath = rp.getBackingPath(); + var backingFileSystem = backingPath.getFileSystem(); + var backingProvider = backingFileSystem.provider(); + + return backingProvider.readAttributes(backingPath, type, options); + } + + @Override + public synchronized Map readAttributes(Path path, String attributes, LinkOption... options) throws IOException + { + if (!(path instanceof ResourcePath rp)) + throw new IllegalArgumentException("Expected a path of type %s!".formatted(ResourcePath.class)); + + var backingPath = rp.getBackingPath(); + var backingFileSystem = backingPath.getFileSystem(); + var backingProvider = backingFileSystem.provider(); + + return backingProvider.readAttributes(backingPath, attributes, options); + } + + @Override + public synchronized void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException + { + if (!(path instanceof ResourcePath rp)) + throw new IllegalArgumentException("Expected a path of type %s!".formatted(ResourcePath.class)); + + var backingPath = rp.getBackingPath(); + var backingFileSystem = backingPath.getFileSystem(); + var backingProvider = backingFileSystem.provider(); + + backingProvider.setAttribute(backingPath, attribute, value, options); + } +} diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/ResourceManager.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/ResourceManager.java new file mode 100755 index 0000000..33a8079 --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/ResourceManager.java @@ -0,0 +1,118 @@ +package org.plutoengine.resource.filesystem; + +import org.plutoengine.address.VirtualAddress; +import org.plutoengine.mod.Mod; + +import javax.annotation.concurrent.ThreadSafe; +import java.io.Closeable; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.ProviderNotFoundException; +import java.nio.file.spi.FileSystemProvider; +import java.util.HashMap; +import java.util.Map; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; + +@ThreadSafe +public class ResourceManager implements Closeable +{ + public static final Path GLOBAL_ROOT = Path.of(""); + public static final Path MOD_DIRECTORY = GLOBAL_ROOT.resolve("mods"); + public static final String URI_SCHEME = "pluto+asl"; + public static final Path MOD_MANIFEST_PATH = Path.of("info.json"); + + private static ResourceFileSystemProvider RESOURCE_FILE_SYSTEM_PROVIDER = null; + + public static ResourceFileSystemProvider provider() + { + if (RESOURCE_FILE_SYSTEM_PROVIDER == null) + { + try + { + var loader = ServiceLoader.load(FileSystemProvider.class, ResourceFileSystemProvider.class.getClassLoader()); + + for (FileSystemProvider provider : loader) + { + if (provider.getScheme().equals(URI_SCHEME)) + { + RESOURCE_FILE_SYSTEM_PROVIDER = (ResourceFileSystemProvider) provider; + break; + } + } + } + catch (ProviderNotFoundException | ServiceConfigurationError e) + { + throw new RuntimeException(e); + } + } + + return RESOURCE_FILE_SYSTEM_PROVIDER; + } + + private final Mod mod; + private final Path modDirectory; + private final Map fileSystems; + + public ResourceManager(Mod mod) + { + this.mod = mod; + var modID = this.mod.getID(); + this.modDirectory = MOD_DIRECTORY.resolve(modID.toString()); + this.fileSystems = new HashMap<>(); + } + + public void addFileSystem(VirtualAddress fileSystemAddress, EnumBackingFileSystem type) throws IOException + { + var manifest = mod.getManifest(); + var resourceRoots = manifest.resourceRoots(); + var info = resourceRoots.get(fileSystemAddress); + + var envMap = new HashMap(); + envMap.put(ResourceFileSystemProvider.ENV_PLUTO_MOD, mod); + envMap.put(ResourceFileSystemProvider.ENV_PLUTO_ADDRESS, fileSystemAddress); + envMap.put(ResourceFileSystemProvider.ENV_PLUTO_FS_TYPE, type); + + var fsPath = this.modDirectory.resolve(info.path()); + var rfs = provider().newFileSystem(fsPath.toUri(), envMap); + + this.fileSystems.put(fileSystemAddress, rfs); + } + + public Path getModDirectory() + { + return this.modDirectory; + } + + public ResourceFileSystem getFileSystem(VirtualAddress fileSystemAddress) + { + return this.fileSystems.get(fileSystemAddress); + } + + public Path getPath(VirtualAddress fileSystemID, VirtualAddress address, String ext) + { + var fs = this.fileSystems.get(fileSystemID); + return fs.getPath(new UnresolvedResourcePath(this.mod.getID(), fs.getAddress(), address.isRelative(), address, ext)); + } + + public Path getPath(String path) + { + var urp = ResourcePathParser.parse(this.mod.getID() + "+" + path); + return this.getPath(urp); + } + + private ResourcePath getPath(UnresolvedResourcePath urp) + { + var fs = this.fileSystems.get(urp.containerID()); + return fs.getPath(urp); + } + + @Override + public void close() throws IOException + { + for (var fs : this.fileSystems.values()) + fs.close(); + + this.fileSystems.clear(); + } +} diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/ResourcePath.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/ResourcePath.java new file mode 100755 index 0000000..58a1123 --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/ResourcePath.java @@ -0,0 +1,314 @@ +package org.plutoengine.resource.filesystem; + +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.plutoengine.address.VirtualAddress; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.*; +import java.util.Comparator; +import java.util.Objects; + +/** + * pluto+asl://modID+resource.root$absolute.path.to.item[#extension] + * pluto+asl://!relative.path.to.item[#extension] + * pluto+asl://!^.path.to.item[#extension] + * + * modID unique mod identifier + * assetContainerID asset container (optional, default asset pack if unspecified) + * + * $ absolute path + * ! relative path + * + * . path separator + * ^ go up one directory + * + * #extension extension separator (optional) + * + * @author 493msi + * + */ +final class ResourcePath implements Path +{ + public static final int MAX_EXTENSION_CHARACTERS = 16; + + private static final Comparator FILE_SYSTEM_COMPARATOR = Comparator.nullsLast(Comparator.naturalOrder()); + + private final VirtualAddress address; + private final String extension; + private final ResourceFileSystem fileSystem; + private final Path backingPath; + + private String strRepresentation; + + ResourcePath(ResourceFileSystem fileSystem, VirtualAddress address, String extension, Path backingPath) + { + this.fileSystem = fileSystem; + this.address = address; + this.extension = extension; + this.backingPath = backingPath; + } + + public Path getBackingPath() + { + return this.backingPath; + } + + @Override + public ResourceFileSystem getFileSystem() + { + if (this.fileSystem == null) + throw new IllegalStateException("This relative ResourcePath does not have an assigned file system, try using ofFileSystem(ResourceFileSystem) first."); + + return this.fileSystem; + } + + @Override + public boolean isAbsolute() + { + return !this.address.isRelative(); + } + + @Override + public ResourcePath getRoot() + { + return this.getFileSystem().getRoot(); + } + + @Override + public ResourcePath getFileName() + { + if (this.address.isEmpty()) + return null; + + return new ResourcePath(this.fileSystem, this.address.getName(this.address.getNameCount() - 1), this.extension, this.backingPath.getFileName()); + } + + @Override + public ResourcePath getParent() + { + if (this.getNameCount() == 0 && this.isAbsolute()) + return null; + + return new ResourcePath(this.fileSystem, this.address.getParent(), null, this.backingPath.getParent()); + } + + @Override + public int getNameCount() + { + return this.address.getNameCount(); + } + + @Override + public @NotNull ResourcePath getName(int index) + { + if (index == 0 && this.address.isEmpty()) + return this; + + return new ResourcePath( + this.fileSystem, + this.address.getName(index), + this.address.getNameCount() == index ? this.extension : null, + this.backingPath.getName(index)); + } + + @Override + public @NotNull ResourcePath subpath(int beginIndex, int endIndex) + { + return new ResourcePath( + this.fileSystem, + this.address.subAddress(beginIndex, endIndex), + this.address.getNameCount() == endIndex ? this.extension : null, + this.backingPath.subpath(beginIndex, endIndex)); + } + + @Override + public boolean startsWith(@NotNull Path other) + { + if (!(other instanceof ResourcePath rp)) + throw new IllegalArgumentException("other must be of type %s!".formatted(ResourcePath.class)); + + return this.toString() + .startsWith(rp.toString()); + } + + @Override + public boolean endsWith(@NotNull Path other) + { + if (!(other instanceof ResourcePath rp)) + throw new IllegalArgumentException("other must be of type %s!".formatted(ResourcePath.class)); + + return this.toString() + .endsWith(rp.toString()); + } + + @Override + public ResourcePath normalize() + { + // Resource paths are normalized by design + return this; + } + + @Override + public ResourcePath resolve(@NotNull Path other) + { + if (!(other instanceof ResourcePath rp)) + throw new IllegalArgumentException("other must be of type %s!".formatted(ResourcePath.class)); + + if (rp.isAbsolute()) + return rp; + + var addr = this.address.resolve(rp.address); + + var urp = new UnresolvedResourcePath( + this.fileSystem.getMod().getID(), + this.fileSystem.getAddress(), + addr.isRelative(), + addr, + rp.extension + ); + + return this.fileSystem.getPath(urp); + } + + @Override + public @NotNull ResourcePath resolve(@NotNull String other) + { + // Unless we are SURE "other" is absolute, we consider it to be relative + + if (!other.contains(Character.toString(ResourcePathParser.TOKEN_ABSOLUTE_SEPARATOR))) + other = StringUtils.prependIfMissing(other, Character.toString(ResourcePathParser.TOKEN_RELATIVE_SEPARATOR)); + + return this.resolve(this.getFileSystem().getPath(other)); + } + + @Override + public @NotNull ResourcePath relativize(@NotNull Path other) + { + if (!(other instanceof ResourcePath rp)) + throw new IllegalArgumentException("other must be of type %s!".formatted(ResourcePath.class)); + + + if (this.isAbsolute() != rp.isAbsolute()) + throw new IllegalArgumentException("Cannot relativize a %s when only one of the inputs is absolute!".formatted(ResourcePath.class)); + + if (this.isAbsolute() && !this.fileSystem.equals(rp.fileSystem)) + throw new IllegalArgumentException("Cannot relativize towards an absolute %s from a different file system!".formatted(ResourcePath.class)); + + var addr = this.address.relativize(address); + + var urp = new UnresolvedResourcePath( + this.fileSystem.getMod().getID(), + this.fileSystem.getAddress(), + addr.isRelative(), + addr, + rp.extension + ); + + return this.fileSystem.getPath(urp); + } + + @Override + public @NotNull URI toUri() + { + try + { + return new URI(ResourceManager.URI_SCHEME, this.toStringNoFragment(), this.extension); + } + catch (URISyntaxException e) + { + throw new IllegalStateException(e); + } + } + + @Override + public ResourcePath toAbsolutePath() + { + if (this.isAbsolute()) + return this; + + // Convert to an absolute path, basically resolving against the file system's root + return this.getFileSystem() + .getRoot() + .resolve(this); + } + + @Override + public @NotNull ResourcePath toRealPath(LinkOption @NotNull... options) + { + return this.toAbsolutePath(); + } + + @Override + public @NotNull WatchKey register(@NotNull WatchService watcher, WatchEvent.Kind@NotNull[] events, WatchEvent.Modifier... modifiers) throws IOException + { + return this.backingPath.register(watcher, events, modifiers); + } + + @Override + public int compareTo(@NotNull Path other) + { + if (!(other instanceof ResourcePath rp)) + throw new UnsupportedOperationException(); + + var containerDiff = Objects.compare(this.fileSystem, rp.fileSystem, FILE_SYSTEM_COMPARATOR); + + if (containerDiff != 0) + return containerDiff; + + var addressDif = this.address.compareTo(rp.address); + + if (addressDif != 0) + return addressDif; + + return this.extension.compareTo(rp.extension); + } + + @Override + public boolean equals(Object other) + { + if (!(other instanceof ResourcePath rp)) + return false; + + return Objects.equals(this.address, rp.address) && + Objects.equals(this.fileSystem, rp.fileSystem) && + Objects.equals(this.extension, rp.extension); + } + + @Override + public int hashCode() + { + return Objects.hash(this.address, this.fileSystem, this.extension); + } + + private String toStringNoFragment() + { + if (this.isAbsolute()) + return this.fileSystem.toString() + + Character.toString(ResourcePathParser.TOKEN_ABSOLUTE_SEPARATOR) + + this.address.toString(); + else + return Character.toString(ResourcePathParser.TOKEN_RELATIVE_SEPARATOR) + + this.address.toString(); + + } + + @Override + public @NotNull String toString() + { + if (this.strRepresentation == null) + { + if (this.extension != null) + this.strRepresentation = this.toStringNoFragment() + + Character.toString(ResourcePathParser.TOKEN_FILE_EXTENSION_SEPARATOR) + + this.extension; + else + this.strRepresentation = this.toStringNoFragment(); + + } + + return this.strRepresentation; + } +} diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/ResourcePathParseException.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/ResourcePathParseException.java new file mode 100755 index 0000000..e454eaa --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/ResourcePathParseException.java @@ -0,0 +1,14 @@ +package org.plutoengine.resource.filesystem; + +public class ResourcePathParseException extends RuntimeException +{ + public ResourcePathParseException(String message) + { + super(message); + } + + public ResourcePathParseException(Throwable cause, String message) + { + super(message, cause); + } +} diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/ResourcePathParser.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/ResourcePathParser.java new file mode 100755 index 0000000..5eb6226 --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/ResourcePathParser.java @@ -0,0 +1,228 @@ +package org.plutoengine.resource.filesystem; + +import org.plutoengine.address.VirtualAddress; +import org.plutoengine.address.VirtualAddressParseException; +import org.plutoengine.address.VirtualAddressParser; + +import java.net.URI; +import java.util.function.IntConsumer; +import java.util.stream.IntStream; + +public class ResourcePathParser +{ + private enum State + { + RS_BEGIN, + RS_MOD_ID, + RS_FILE_SYSTEM, + RS_ADDRESS, + RS_EXTENSION + } + + public static final int TOKEN_FILE_EXTENSION_SEPARATOR = '#'; + public static final int TOKEN_FILE_SYSTEM_SEPARATOR = '+'; + public static final int TOKEN_ABSOLUTE_SEPARATOR = '$'; + public static final int TOKEN_RELATIVE_SEPARATOR = '!'; + + private VirtualAddress modID; + private VirtualAddress containerID; + private VirtualAddress pathAddress; + private String extension; + + private VirtualAddressParser modIDParser; + private VirtualAddressParser containerIDParser; + private VirtualAddressParser pathAddressParser; + private StringBuilder extensionBuilder; + + private boolean isRelative; + private State state; + + private ResourcePathParser() + { + this.state = State.RS_BEGIN; + } + + private void accept(int codepoint) + { + switch (this.state) + { + case RS_BEGIN -> { + if (codepoint == TOKEN_RELATIVE_SEPARATOR) + { + this.isRelative = true; + this.pathAddressParser = VirtualAddress.createParser(true); + this.state = State.RS_ADDRESS; + } + else + { + this.modIDParser = VirtualAddress.createParser(false); + this.modIDParser.accept(codepoint); + this.state = State.RS_MOD_ID; + } + } + + case RS_MOD_ID -> { + switch (codepoint) + { + case TOKEN_RELATIVE_SEPARATOR, TOKEN_ABSOLUTE_SEPARATOR, TOKEN_FILE_SYSTEM_SEPARATOR -> { + this.modID = this.modIDParser.build(); + + switch (codepoint) + { + case TOKEN_FILE_SYSTEM_SEPARATOR -> { + this.containerIDParser = VirtualAddress.createParser(false); + this.state = State.RS_FILE_SYSTEM; + } + + case TOKEN_ABSOLUTE_SEPARATOR -> { + this.pathAddressParser = VirtualAddress.createParser(false); + this.state = State.RS_ADDRESS; + } + } + + + this.isRelative = false; + } + + default -> this.modIDParser.accept(codepoint); + } + } + + case RS_FILE_SYSTEM -> { + if (codepoint == TOKEN_ABSOLUTE_SEPARATOR) + { + this.pathAddressParser = VirtualAddress.createParser(this.isRelative); + this.state = State.RS_ADDRESS; + this.containerID = this.containerIDParser.build(); + } + else + { + this.containerIDParser.accept(codepoint); + } + } + + case RS_ADDRESS -> { + if (codepoint == TOKEN_FILE_EXTENSION_SEPARATOR) + { + this.pathAddress = this.pathAddressParser.build(); + this.extensionBuilder = new StringBuilder(); + this.state = State.RS_EXTENSION; + } + else + { + this.pathAddressParser.accept(codepoint); + } + } + + case RS_EXTENSION -> { + if (!Character.isLetterOrDigit(codepoint)) + throw new ResourcePathParseException("Unexpected character in the extension!"); + + this.extensionBuilder.appendCodePoint(codepoint); + } + } + } + + private UnresolvedResourcePath build() + { + switch (this.state) + { + case RS_ADDRESS -> this.pathAddress = this.pathAddressParser.build(); + case RS_EXTENSION -> this.extension = this.extensionBuilder.toString(); + default -> throw new ResourcePathParseException("Unexpected end of ResourcePath!"); + } + + if (this.extension != null && (this.extension.isEmpty() || this.extension.length() > ResourcePath.MAX_EXTENSION_CHARACTERS)) + throw new ResourcePathParseException("The ResourcePath extension must not be blank (when set) or longer than " + ResourcePath.MAX_EXTENSION_CHARACTERS + "!"); + + return new UnresolvedResourcePath(this.modID, this.containerID, this.isRelative, this.pathAddress, this.extension); + } + + + /** + * modID+resource.root$absolute.path.to.item[#extension] + * !relative.path.to.item[#extension] + * !~.path.to.item[#extension] + * + * modID unique mod identifier + * assetContainerID asset container (optional, default asset pack if unspecified) + * $ absolute path + * ! relative path + * + * . path separator + * ~ go up one directory + * + * #extension extension separator (optional) + * + * @author 493msi + * + */ + + private static UnresolvedResourcePath parse(IntStream it) + { + var parser = new ResourcePathParser(); + IntConsumer parserConsumer = parser::accept; + it.forEachOrdered(parserConsumer); + return parser.build(); + } + + // +---------------------------------------------+------------+---------+----------------------------+--------------------------+--------------------------+----------+ + // | input | scheme | opaque | SSP | authority | path | fragment | + // +---------------------------------------------+------------+---------+----------------------------+--------------------------+--------------------------+----------+ + // | pluto+asl:mod.id$path.to.resource#ext | pluto+asl | true | mod.id$path.to.resource | | | ext | + // | pluto+asl://mod.id$path.to.resource#ext | pluto+asl | false | //mod.id$path.to.resource | mod.id$path.to.resource | | ext | + // +---------------------------------------------+------------+---------+----------------------------+--------------------------+--------------------------+----------+ + + /** + * + * @param uri The input URI + * @return An {@link UnresolvedResourcePath} + */ + public static UnresolvedResourcePath parse(URI uri) + { + if (uri == null) + return null; + + if (!ResourceManager.URI_SCHEME.equals(uri.getScheme())) + throw new ResourcePathParseException("Invalid URI scheme!"); + + var normalizedAddress = uri.isOpaque() ? uri.getSchemeSpecificPart() : uri.getAuthority(); + + if (normalizedAddress == null) + throw new ResourcePathParseException("Could not find a valid resource path in the supplied URI!"); + + var codepoints = normalizedAddress.codePoints(); + + var extension = uri.getFragment(); + + if (extension != null) + { + var extensionCodepoints = IntStream.concat(IntStream.of(TOKEN_FILE_EXTENSION_SEPARATOR), extension.codePoints()); + codepoints = IntStream.concat(codepoints, extensionCodepoints); + } + + try + { + return parse(codepoints); + } + catch (VirtualAddressParseException | ResourcePathParseException e) + { + throw new ResourcePathParseException(e, "Failed to parse %s: %s".formatted(ResourcePath.class.getSimpleName(), uri)); + } + } + + public static UnresolvedResourcePath parse(String pathStr) + { + if (pathStr == null) + return null; + + try + { + return parse(pathStr.codePoints()); + } + catch (VirtualAddressParseException e) + { + throw new ResourcePathParseException(e, "Failed to parse %s: %s".formatted(ResourcePath.class.getSimpleName(), pathStr)); + } + } +} diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/UnresolvedResourcePath.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/UnresolvedResourcePath.java new file mode 100755 index 0000000..4d5f06c --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/resource/filesystem/UnresolvedResourcePath.java @@ -0,0 +1,13 @@ +package org.plutoengine.resource.filesystem; + +import org.plutoengine.address.VirtualAddress; + +record UnresolvedResourcePath( + VirtualAddress modID, + VirtualAddress containerID, + boolean relative, + VirtualAddress pathAddress, + String extension +) +{ +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/tpl/TPNImage.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/tpl/ImageABGR.java old mode 100644 new mode 100755 similarity index 71% rename from plutolib/src/main/java/cz/tefek/pluto/io/tpl/TPNImage.java rename to engine-core/plutoruntime/src/main/java/org/plutoengine/tpl/ImageABGR.java index e10c4a4..e5839ad --- a/plutolib/src/main/java/cz/tefek/pluto/io/tpl/TPNImage.java +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/tpl/ImageABGR.java @@ -1,26 +1,27 @@ -package cz.tefek.pluto.io.tpl; +package org.plutoengine.tpl; +import java.awt.image.BufferedImage; import java.nio.ByteBuffer; /** * A wrapper around a native color buffer for easier handling * by various APIs, such as OpenGL and GLFW. * - * @implNote TPNImage is always ABGR due to image format - * limitations of {@link java.awt.image.BufferedImage} + * @implNote TPNImage is always assumed to be ABGR due to image format + * limitations of {@link BufferedImage}. * * @author 493msi * * @since pre-alpha */ -public class TPNImage +public class ImageABGR { private final ByteBuffer data; private final int width; private final int height; /** - * Creates a new {@link TPNImage} from the specified buffer, width and height. + * Creates a new {@link ImageABGR} from the specified buffer, width and height. * * @param bfr The input {@link ByteBuffer} * @param width This image's width @@ -28,7 +29,7 @@ public class TPNImage * * @since pre-alpha * */ - public TPNImage(ByteBuffer bfr, int width, int height) + public ImageABGR(ByteBuffer bfr, int width, int height) { this.data = bfr; this.width = width; @@ -38,7 +39,7 @@ public class TPNImage /** * Returns the width of the color buffer. * - * @return The width of this {@link TPNImage} + * @return The width of this {@link ImageABGR} * * @since pre-alpha * */ @@ -50,7 +51,7 @@ public class TPNImage /** * Returns the height of the color buffer. * - * @return The height of this {@link TPNImage} + * @return The height of this {@link ImageABGR} * * @since pre-alpha * */ diff --git a/engine-core/plutoruntime/src/main/java/org/plutoengine/tpl/ImageLoader.java b/engine-core/plutoruntime/src/main/java/org/plutoengine/tpl/ImageLoader.java new file mode 100755 index 0000000..13a0daa --- /dev/null +++ b/engine-core/plutoruntime/src/main/java/org/plutoengine/tpl/ImageLoader.java @@ -0,0 +1,175 @@ +package org.plutoengine.tpl; + +import javax.annotation.Nullable; +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.*; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; + +/** + * Quick ABGR (8-bit per channel, 32 bits per pixel) image loader for OpenGL textures. + * Color component swizzling may be needed. + * + * @author 493msi + * + * @see ImageABGR + * + * @since pre-alpha + */ +public class ImageLoader +{ + private static final int PLACEHOLDER_SIZE = 16; + + private static final int PLACEHOLDER_CHECKEDBOARD = 8; + private static final int PLACEHOLDER_CHECKEDBOARD_SQUARE_SIZE = PLACEHOLDER_SIZE / PLACEHOLDER_CHECKEDBOARD; + + private static final BufferedImage placeholder; + + static + { + placeholder = new BufferedImage(PLACEHOLDER_SIZE, PLACEHOLDER_SIZE, BufferedImage.TYPE_INT_ARGB); + var data = placeholder.getData(); + var dataBuffer = (DataBufferInt) data.getDataBuffer(); + + for (int i = 0; i < PLACEHOLDER_SIZE * PLACEHOLDER_SIZE; i++) + { + int x = i % PLACEHOLDER_SIZE; + int y = i / PLACEHOLDER_SIZE; + boolean checker = x / PLACEHOLDER_CHECKEDBOARD_SQUARE_SIZE % 2 == y / PLACEHOLDER_CHECKEDBOARD_SQUARE_SIZE % 2; + + dataBuffer.setElem(i, checker ? 0xFFFF0000 : 0xFF000000); + } + } + + private static BufferedImage loadBufferedImage(@Nullable Path path) + { + if (path == null) + return placeholder; + + try (var is = Files.newInputStream(path)) + { + return ImageIO.read(is); + } + catch (Exception e) + { + Logger.logf(SmartSeverity.ERROR, "[TPL] Image could not be loaded: %s%n", path); + Logger.log(e); + } + + return placeholder; + } + + /** + * Loads an image from a file the denoted by the input {@link Path} into a {@link ImageABGR} buffer. + * + * If the input {@link Path} is null, a placeholder will be generated. + * + * @param path The source {@link Path}, from which the image will be loaded + * + * @return The output {@link ImageABGR}, never null + * + * @see ImageABGR + * + * @since 20.2.0.0-alpha.1 + * */ + public static ImageABGR load(@Nullable Path path) + { + return loadImage(loadBufferedImage(path)); + } + + /** + * Loads an image from a file the denoted by the input {@link Path} into a {@link ImageABGR} buffer. + * + * If the input {@link Path} is null, a placeholder will be generated. + * + * @param path The source {@link Path}, from which the image will be loaded + * @param flipY Whether the image should be flipped vertically (for OpenGL uses) + * + * @return The output {@link ImageABGR}, never null + * + * @see ImageABGR + * + * @since 20.2.0.0-alpha.1 + * */ + public static ImageABGR loadSpecial(@Nullable Path path, boolean flipY) + { + return loadImageSpecial(loadBufferedImage(path), flipY); + } + + /** + * Writes a {@link BufferedImage} into a {@link ImageABGR} buffer. + * + * If the input {@link Path} is null, a placeholder will be generated. + * + * @param image The source {@link BufferedImage} + * @param flipY Whether the image should be flipped vertically (for OpenGL uses) + * + * @return The output {@link ImageABGR}, never null + * + * @see ImageABGR + * + * @since 20.2.0.0-alpha.1 + * */ + public static ImageABGR loadImageSpecial(@Nullable BufferedImage image, boolean flipY) + { + if (image == null) + { + Logger.log(SmartSeverity.WARNING, "[TPL] Null BufferedImage supplied, generating a placeholder."); + + return loadImageSpecial(placeholder, flipY); + } + + int width = image.getWidth(); + int height = image.getHeight(); + + if (width > 16384 || height > 16384 || width < 1 || height < 1) + { + Logger.log(SmartSeverity.ERROR, "[TPL] BufferedImage size is invalid (< 1 or > 16384), generating a placeholder."); + + return loadImageSpecial(placeholder, flipY); + } + + BufferedImage copy = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_4BYTE_ABGR); + Graphics2D imgGraphics = copy.createGraphics(); + imgGraphics.drawImage(image, + 0, flipY ? copy.getHeight() : 0, copy.getWidth(), flipY ? 0 : copy.getHeight(), + 0, 0, image.getWidth(), image.getHeight(), + null); // I wonder if this is pixel-perfect + imgGraphics.dispose(); + + Raster data = copy.getRaster(); + DataBuffer dataBuffer = data.getDataBuffer(); + DataBufferByte byteBuffer = (DataBufferByte) dataBuffer; + byte[] byteData = byteBuffer.getData(); + ByteBuffer buffer = ByteBuffer.allocateDirect(width * height * 4).order(ByteOrder.nativeOrder()); + buffer.put(byteData); + buffer.flip(); + + return new ImageABGR(buffer, width, height); + } + + + /** + * Writes a {@link BufferedImage} into a {@link ImageABGR} buffer. + * + * If the input {@link Path} is null, a placeholder will be generated. + * + * @param image The source {@link BufferedImage} + * + * @return The output {@link ImageABGR}, never null + * + * @see ImageABGR + * + * @since pre-alpha + * */ + public static ImageABGR loadImage(@Nullable BufferedImage image) + { + return loadImageSpecial(image, true); + } +} diff --git a/engine-core/plutoruntime/src/main/resources/META-INF/services/java.nio.file.spi.FileSystemProvider b/engine-core/plutoruntime/src/main/resources/META-INF/services/java.nio.file.spi.FileSystemProvider new file mode 100755 index 0000000..2aa315d --- /dev/null +++ b/engine-core/plutoruntime/src/main/resources/META-INF/services/java.nio.file.spi.FileSystemProvider @@ -0,0 +1 @@ +org.plutoengine.resource.filesystem.ResourceFileSystemProvider \ No newline at end of file diff --git a/engine-core/plutoshader/build.gradle.kts b/engine-core/plutoshader/build.gradle.kts new file mode 100755 index 0000000..5d90be3 --- /dev/null +++ b/engine-core/plutoshader/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + java + `java-library` +} + +description = "Automated shader loader and manager." + +dependencies { + api(project(":plutoengine:plutotexture")) + api(project(":plutoengine:plutomesher")) +} \ No newline at end of file diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/IShaderProgram.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/IShaderProgram.java old mode 100644 new mode 100755 similarity index 84% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/IShaderProgram.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/IShaderProgram.java index cd9eb1b..9562c2f --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/IShaderProgram.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/IShaderProgram.java @@ -1,8 +1,7 @@ -package cz.tefek.pluto.engine.shader; +package org.plutoengine.shader; import org.lwjgl.opengl.GL33; - -import cz.tefek.pluto.engine.shader.type.IShader; +import org.plutoengine.shader.type.IShader; public interface IShaderProgram { diff --git a/engine-core/plutoshader/src/main/java/org/plutoengine/shader/PlutoShaderMod.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/PlutoShaderMod.java new file mode 100755 index 0000000..edd8807 --- /dev/null +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/PlutoShaderMod.java @@ -0,0 +1,14 @@ +package org.plutoengine.shader; + +import org.plutoengine.ModLWJGL; +import org.plutoengine.Pluto; +import org.plutoengine.mod.ModEntry; + +@ModEntry(modID = PlutoShaderMod.MOD_ID, + dependencies = { ModLWJGL.class }, + version = Pluto.VERSION) +public class PlutoShaderMod +{ + public static final String MOD_ID = "tefek.plutoshader"; + +} diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/RenderShaderBuilder.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/RenderShaderBuilder.java old mode 100644 new mode 100755 similarity index 87% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/RenderShaderBuilder.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/RenderShaderBuilder.java index 1662354..b455934 --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/RenderShaderBuilder.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/RenderShaderBuilder.java @@ -1,21 +1,20 @@ -package cz.tefek.pluto.engine.shader; +package org.plutoengine.shader; import org.lwjgl.opengl.GL33; +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; +import org.plutoengine.shader.type.FragmentShader; +import org.plutoengine.shader.type.VertexShader; +import org.plutoengine.shader.uniform.Uniform; +import org.plutoengine.shader.uniform.UniformBase; +import org.plutoengine.shader.uniform.UniformMat4; +import org.plutoengine.shader.uniform.auto.AutoViewportProjection; +import org.plutoengine.shader.uniform.auto.AutomaticUniforms; +import java.io.UncheckedIOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; - -import cz.tefek.pluto.engine.shader.type.FragmentShader; -import cz.tefek.pluto.engine.shader.type.VertexShader; -import cz.tefek.pluto.engine.shader.uniform.Uniform; -import cz.tefek.pluto.engine.shader.uniform.UniformBase; -import cz.tefek.pluto.engine.shader.uniform.UniformMat4; -import cz.tefek.pluto.engine.shader.uniform.auto.AutoViewportProjection; -import cz.tefek.pluto.engine.shader.uniform.auto.AutomaticUniforms; -import cz.tefek.pluto.io.asl.resource.ResourceAddress; -import cz.tefek.pluto.io.asl.resource.ResourceSubscriber; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; +import java.nio.file.Path; public class RenderShaderBuilder { @@ -24,17 +23,19 @@ public class RenderShaderBuilder private VertexShader vertexShader; private FragmentShader fragmentShader; - public RenderShaderBuilder(ResourceSubscriber subscriber, String vtx, String frg) - { - this(new ResourceAddress(subscriber, vtx), new ResourceAddress(subscriber, frg)); - } - - public RenderShaderBuilder(ResourceAddress vtx, ResourceAddress frg) + public RenderShaderBuilder(Path vtx, Path frg) { this.tempShaders = true; - this.vertexShader = new VertexShader(vtx); - this.fragmentShader = new FragmentShader(frg); + try + { + this.vertexShader = new VertexShader(vtx); + this.fragmentShader = new FragmentShader(frg); + } + catch (UncheckedIOException e) + { + Logger.log(e); + } } public RenderShaderBuilder(VertexShader vtx, FragmentShader frg) @@ -49,6 +50,9 @@ public class RenderShaderBuilder public T build(Class shaderClass, boolean manualAttributeLayout) { + if (this.vertexShader == null || this.fragmentShader == null) + return null; + try { var programAnnotation = shaderClass.getDeclaredAnnotation(ShaderProgram.class); @@ -206,9 +210,8 @@ public class RenderShaderBuilder if (field.getAnnotation(AutoViewportProjection.class) != null) { - if (uniform instanceof UniformMat4) + if (uniform instanceof UniformMat4 umat4) { - UniformMat4 umat4 = (UniformMat4) uniform; AutomaticUniforms.VIEWPORT_PROJECTION.addListener(mat4 -> { program.start(); diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/ShaderBase.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/ShaderBase.java old mode 100644 new mode 100755 similarity index 85% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/ShaderBase.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/ShaderBase.java index 979b5fe..47965b1 --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/ShaderBase.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/ShaderBase.java @@ -1,9 +1,9 @@ -package cz.tefek.pluto.engine.shader; +package org.plutoengine.shader; import org.lwjgl.opengl.GL33; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; public abstract class ShaderBase implements IShaderProgram { diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/ShaderCompiler.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/ShaderCompiler.java old mode 100644 new mode 100755 similarity index 53% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/ShaderCompiler.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/ShaderCompiler.java index fa30544..79a27aa --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/ShaderCompiler.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/ShaderCompiler.java @@ -1,12 +1,15 @@ -package cz.tefek.pluto.engine.shader; +package org.plutoengine.shader; import org.lwjgl.opengl.GL33; -import cz.tefek.pluto.engine.shader.type.EnumShaderType; -import cz.tefek.pluto.io.asl.resource.ResourceAddress; -import cz.tefek.pluto.io.asl.textio.TextIn; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.plutoengine.shader.type.EnumShaderType; +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; public class ShaderCompiler { @@ -16,10 +19,18 @@ public class ShaderCompiler return code.trim(); } - public static int load(ResourceAddress address, EnumShaderType type) + public static int load(Path path, EnumShaderType type) { - var sourceString = preprocessCode(TextIn.fromAddress(address)); - return load(address.toString(), sourceString, type); + try + { + var sourceString = preprocessCode(Files.readString(path)); + return load(path.toString(), sourceString, type); + } + catch (IOException e) + { + Logger.logf(SmartSeverity.ERROR, "Failed to load shader: %s%n", path.toString()); + throw new UncheckedIOException(e); + } } private static int load(String name, String code, EnumShaderType type) diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/ShaderProgram.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/ShaderProgram.java old mode 100644 new mode 100755 similarity index 87% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/ShaderProgram.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/ShaderProgram.java index bf9705d..f3c6a09 --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/ShaderProgram.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/ShaderProgram.java @@ -1,11 +1,11 @@ -package cz.tefek.pluto.engine.shader; - -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +package org.plutoengine.shader; import java.lang.annotation.Retention; import java.lang.annotation.Target; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + @Retention(RUNTIME) @Target(TYPE) public @interface ShaderProgram diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/VertexArrayAttribute.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/VertexArrayAttribute.java old mode 100644 new mode 100755 similarity index 92% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/VertexArrayAttribute.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/VertexArrayAttribute.java index a49f0a5..9389885 --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/VertexArrayAttribute.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/VertexArrayAttribute.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.shader; +package org.plutoengine.shader; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/type/EnumShaderType.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/type/EnumShaderType.java old mode 100644 new mode 100755 similarity index 71% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/type/EnumShaderType.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/type/EnumShaderType.java index 1ae6e77..531b00b --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/type/EnumShaderType.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/type/EnumShaderType.java @@ -1,10 +1,12 @@ -package cz.tefek.pluto.engine.shader.type; +package org.plutoengine.shader.type; import org.lwjgl.opengl.GL33; import org.lwjgl.opengl.GL40; import org.lwjgl.opengl.GL43; -public enum EnumShaderType +import org.plutoengine.gl.IOpenGLEnum; + +public enum EnumShaderType implements IOpenGLEnum { VERTEX(GL33.GL_VERTEX_SHADER), GEOMETRY(GL33.GL_GEOMETRY_SHADER), @@ -13,9 +15,9 @@ public enum EnumShaderType TESSELATION_CONTROL(GL40.GL_TESS_CONTROL_SHADER), TESSELATION_EVALUATION(GL40.GL_TESS_EVALUATION_SHADER); - private int id; + private final int id; - private EnumShaderType(int id) + EnumShaderType(int id) { this.id = id; } diff --git a/engine-core/plutoshader/src/main/java/org/plutoengine/shader/type/FragmentShader.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/type/FragmentShader.java new file mode 100755 index 0000000..038a624 --- /dev/null +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/type/FragmentShader.java @@ -0,0 +1,21 @@ +package org.plutoengine.shader.type; + +import org.plutoengine.shader.ShaderCompiler; + +import java.nio.file.Path; + +public final class FragmentShader implements IShader +{ + private final int id; + + public FragmentShader(Path path) + { + this.id = ShaderCompiler.load(path, EnumShaderType.FRAGMENT); + } + + @Override + public int getID() + { + return this.id; + } +} diff --git a/engine-core/plutoshader/src/main/java/org/plutoengine/shader/type/GeometryShader.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/type/GeometryShader.java new file mode 100755 index 0000000..233de9d --- /dev/null +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/type/GeometryShader.java @@ -0,0 +1,21 @@ +package org.plutoengine.shader.type; + +import java.nio.file.Path; + +import org.plutoengine.shader.ShaderCompiler; + +public final class GeometryShader implements IShader +{ + private final int id; + + public GeometryShader(Path path) + { + this.id = ShaderCompiler.load(path, EnumShaderType.GEOMETRY); + } + + @Override + public int getID() + { + return this.id; + } +} diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/type/IShader.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/type/IShader.java old mode 100644 new mode 100755 similarity index 78% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/type/IShader.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/type/IShader.java index 8086938..8c66b0f --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/type/IShader.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/type/IShader.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.shader.type; +package org.plutoengine.shader.type; import org.lwjgl.opengl.GL33; diff --git a/engine-core/plutoshader/src/main/java/org/plutoengine/shader/type/VertexShader.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/type/VertexShader.java new file mode 100755 index 0000000..b461fc4 --- /dev/null +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/type/VertexShader.java @@ -0,0 +1,21 @@ +package org.plutoengine.shader.type; + +import java.nio.file.Path; + +import org.plutoengine.shader.ShaderCompiler; + +public final class VertexShader implements IShader +{ + private final int id; + + public VertexShader(Path path) + { + this.id = ShaderCompiler.load(path, EnumShaderType.VERTEX); + } + + @Override + public int getID() + { + return this.id; + } +} diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/ubo/UniformBufferBindingPoint.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/ubo/UniformBufferBindingPoint.java old mode 100644 new mode 100755 similarity index 87% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/ubo/UniformBufferBindingPoint.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/ubo/UniformBufferBindingPoint.java index d52edf4..4dfcd67 --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/ubo/UniformBufferBindingPoint.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/ubo/UniformBufferBindingPoint.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.shader.ubo; +package org.plutoengine.shader.ubo; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/ubo/UniformBufferObject.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/ubo/UniformBufferObject.java old mode 100644 new mode 100755 similarity index 86% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/ubo/UniformBufferObject.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/ubo/UniformBufferObject.java index f15d88d..a222d20 --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/ubo/UniformBufferObject.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/ubo/UniformBufferObject.java @@ -1,16 +1,16 @@ -package cz.tefek.pluto.engine.shader.ubo; +package org.plutoengine.shader.ubo; import org.lwjgl.opengl.GL33; -import cz.tefek.pluto.engine.shader.ShaderBase; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; +import org.plutoengine.shader.ShaderBase; +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; public final class UniformBufferObject { - private int id; - private long size; - private String name; + private final int id; + private final long size; + private final String name; public UniformBufferObject(long size, String name) { @@ -69,6 +69,11 @@ public final class UniformBufferObject this.unbind(); } + public long getSize() + { + return this.size; + } + public void bind() { GL33.glBindBuffer(GL33.GL_UNIFORM_BUFFER, this.id); diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/Uniform.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/Uniform.java old mode 100644 new mode 100755 similarity index 89% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/Uniform.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/Uniform.java index 4e68531..cadca09 --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/Uniform.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/Uniform.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.shader.uniform; +package org.plutoengine.shader.uniform; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformArrayInt.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformArrayInt.java old mode 100644 new mode 100755 similarity index 84% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformArrayInt.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformArrayInt.java index 51befac..75c60aa --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformArrayInt.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformArrayInt.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.shader.uniform; +package org.plutoengine.shader.uniform; import org.lwjgl.opengl.GL33; diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformArrayMat3x2.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformArrayMat3x2.java old mode 100644 new mode 100755 similarity index 94% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformArrayMat3x2.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformArrayMat3x2.java index 57ad758..6226229 --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformArrayMat3x2.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformArrayMat3x2.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.shader.uniform; +package org.plutoengine.shader.uniform; import org.joml.Matrix3x2fc; import org.lwjgl.opengl.GL33; diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformBase.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformBase.java old mode 100644 new mode 100755 similarity index 83% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformBase.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformBase.java index 38f652f..fb5d4c6 --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformBase.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformBase.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.shader.uniform; +package org.plutoengine.shader.uniform; public abstract class UniformBase { diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformBoolean.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformBoolean.java old mode 100644 new mode 100755 similarity index 85% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformBoolean.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformBoolean.java index 04839a2..dd1b4e5 --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformBoolean.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformBoolean.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.shader.uniform; +package org.plutoengine.shader.uniform; import org.lwjgl.opengl.GL33; diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformFloat.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformFloat.java old mode 100644 new mode 100755 similarity index 84% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformFloat.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformFloat.java index 43af75b..b99916c --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformFloat.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformFloat.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.shader.uniform; +package org.plutoengine.shader.uniform; import org.lwjgl.opengl.GL33; diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformInt.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformInt.java old mode 100644 new mode 100755 similarity index 84% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformInt.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformInt.java index b60d149..1bd4bca --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformInt.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformInt.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.shader.uniform; +package org.plutoengine.shader.uniform; import org.lwjgl.opengl.GL33; diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformMat3.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformMat3.java old mode 100644 new mode 100755 similarity index 91% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformMat3.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformMat3.java index 85e7a1b..0792c1a --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformMat3.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformMat3.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.shader.uniform; +package org.plutoengine.shader.uniform; import org.joml.Matrix3fc; import org.lwjgl.opengl.GL33; diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformMat3x2.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformMat3x2.java old mode 100644 new mode 100755 similarity index 91% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformMat3x2.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformMat3x2.java index df9e783..3303e76 --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformMat3x2.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformMat3x2.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.shader.uniform; +package org.plutoengine.shader.uniform; import org.joml.Matrix3x2fc; import org.lwjgl.opengl.GL33; diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformMat4.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformMat4.java old mode 100644 new mode 100755 similarity index 91% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformMat4.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformMat4.java index 6a63ee0..3d547b7 --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformMat4.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformMat4.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.shader.uniform; +package org.plutoengine.shader.uniform; import org.joml.Matrix4fc; import org.lwjgl.opengl.GL33; diff --git a/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformRGB.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformRGB.java new file mode 100755 index 0000000..a8558f7 --- /dev/null +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformRGB.java @@ -0,0 +1,57 @@ +package org.plutoengine.shader.uniform; + +import org.lwjgl.opengl.GL33; + +import org.plutoengine.util.color.IRGB; + +/** + * A uniform allowing loading RGBA color data into shader uniforms. + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ +public class UniformRGB extends UniformVec3 +{ + /** + * Creates a new instance of the {@link UniformRGB} uniform + * with the specified shader location. + * + * @param location The location within the shader + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public UniformRGB(int location) + { + super(location); + } + + /** + /** + * Loads the {@link IRGB} color components into the shader uniform. + * + * @param value The {@link IRGB} color object + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public void load(IRGB value) + { + GL33.glUniform3f(this.location, value.red(), value.green(), value.blue()); + } + + /** + * Loads the RGB color components into the shader uniform. + * + * @param r The red color component, in range [0..1] + * @param g The green color component, in range [0..1] + * @param b The blue color component, in range [0..1] + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public void load(float r, float g, float b) + { + GL33.glUniform3f(this.location, r, g, b); + } +} diff --git a/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformRGBA.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformRGBA.java new file mode 100755 index 0000000..e1d2a97 --- /dev/null +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformRGBA.java @@ -0,0 +1,57 @@ +package org.plutoengine.shader.uniform; + +import org.lwjgl.opengl.GL33; + +import org.plutoengine.util.color.IRGBA; + +/** + * A uniform allowing loading RGBA color data into shader uniforms. + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ +public class UniformRGBA extends UniformVec4 +{ + /** + * Creates a new instance of the {@link UniformRGBA} uniform + * with the specified shader location. + * + * @param location The location within the shader + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public UniformRGBA(int location) + { + super(location); + } + + /** + * Loads the {@link IRGBA} color components into the shader uniform. + * + * @param value The {@link IRGBA} color object + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public void load(IRGBA value) + { + GL33.glUniform4f(this.location, value.red(), value.green(), value.blue(), value.alpha()); + } + + /** + * Loads the RGBA color components into the shader uniform. + * + * @param r The red color component, in range [0..1] + * @param g The green color component, in range [0..1] + * @param b The blue color component, in range [0..1] + * @param a The alpha component, in range [0..1] + * + * @since 20.2.0.0-alpha.3 + * @author 493msi + */ + public void load(float r, float g, float b, float a) + { + GL33.glUniform4f(this.location, r, g, b, a); + } +} diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformVec2.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformVec2.java old mode 100644 new mode 100755 similarity index 89% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformVec2.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformVec2.java index 3ba419f..3a57585 --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformVec2.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformVec2.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.shader.uniform; +package org.plutoengine.shader.uniform; import org.joml.Vector2fc; import org.lwjgl.opengl.GL33; diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformVec3.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformVec3.java old mode 100644 new mode 100755 similarity index 90% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformVec3.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformVec3.java index c84f6d1..26e24fe --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformVec3.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformVec3.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.shader.uniform; +package org.plutoengine.shader.uniform; import org.joml.Vector3fc; import org.lwjgl.opengl.GL33; diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformVec4.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformVec4.java old mode 100644 new mode 100755 similarity index 90% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformVec4.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformVec4.java index 53f856a..97f2eb3 --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/UniformVec4.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/UniformVec4.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.shader.uniform; +package org.plutoengine.shader.uniform; import org.joml.Vector4fc; import org.lwjgl.opengl.GL33; diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/auto/AutoViewportProjection.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/auto/AutoViewportProjection.java old mode 100644 new mode 100755 similarity index 88% rename from plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/auto/AutoViewportProjection.java rename to engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/auto/AutoViewportProjection.java index a299a97..8cee37d --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/auto/AutoViewportProjection.java +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/auto/AutoViewportProjection.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.shader.uniform.auto; +package org.plutoengine.shader.uniform.auto; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/auto/AutomaticUniforms.java b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/auto/AutomaticUniforms.java new file mode 100755 index 0000000..64a66cf --- /dev/null +++ b/engine-core/plutoshader/src/main/java/org/plutoengine/shader/uniform/auto/AutomaticUniforms.java @@ -0,0 +1,11 @@ +package org.plutoengine.shader.uniform.auto; + +import org.joml.Matrix4fc; + +import org.plutoengine.event.lambda.LambdaEventFactory; +import org.plutoengine.event.lambda.LambdaEventFactory.LambdaEvent; + +public class AutomaticUniforms +{ + public static final LambdaEvent VIEWPORT_PROJECTION = LambdaEventFactory.createEvent(); +} diff --git a/engine-core/plutospritesheet/build.gradle.kts b/engine-core/plutospritesheet/build.gradle.kts new file mode 100755 index 0000000..19ca890 --- /dev/null +++ b/engine-core/plutospritesheet/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + java + `java-library` +} + +description = "A library to manage, store and draw sprites." + +dependencies { + api(project(":plutoengine:plutoframebuffer")) + api(project(":plutoengine:plutoshader")) +} \ No newline at end of file diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/IRectangleShader2D.java b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/IRectangleShader2D.java old mode 100644 new mode 100755 similarity index 80% rename from plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/IRectangleShader2D.java rename to engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/IRectangleShader2D.java index 1c8b0f2..140d240 --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/IRectangleShader2D.java +++ b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/IRectangleShader2D.java @@ -1,11 +1,11 @@ -package cz.tefek.pluto.engine.graphics; +package org.plutoengine.graphics; import org.joml.Matrix3x2fc; import org.joml.Matrix4fc; import org.joml.Vector2fc; import org.joml.Vector4fc; -import cz.tefek.pluto.engine.shader.IShaderProgram; +import org.plutoengine.shader.IShaderProgram; public interface IRectangleShader2D extends IShaderProgram { @@ -22,7 +22,7 @@ public interface IRectangleShader2D extends IShaderProgram default void loadRecolor(Vector4fc col) { - this.loadUV(col.x(), col.y(), col.z(), col.w()); + this.loadRecolor(col.x(), col.y(), col.z(), col.w()); } void loadRecolor(float r, float g, float b, float a); diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/IShader2D.java b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/IShader2D.java old mode 100644 new mode 100755 similarity index 80% rename from plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/IShader2D.java rename to engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/IShader2D.java index f345f08..1c177d2 --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/IShader2D.java +++ b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/IShader2D.java @@ -1,11 +1,11 @@ -package cz.tefek.pluto.engine.graphics; +package org.plutoengine.graphics; import org.joml.Matrix3x2fc; import org.joml.Matrix4fc; import org.joml.Vector2fc; import org.joml.Vector4fc; -import cz.tefek.pluto.engine.shader.IShaderProgram; +import org.plutoengine.shader.IShaderProgram; public interface IShader2D extends IShaderProgram { @@ -22,7 +22,7 @@ public interface IShader2D extends IShaderProgram default void loadRecolor(Vector4fc col) { - this.loadUV(col.x(), col.y(), col.z(), col.w()); + this.loadRecolor(col.x(), col.y(), col.z(), col.w()); } void loadRecolor(float r, float g, float b, float a); diff --git a/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/PlutoSpriteSheetMod.java b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/PlutoSpriteSheetMod.java new file mode 100755 index 0000000..da3b35d --- /dev/null +++ b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/PlutoSpriteSheetMod.java @@ -0,0 +1,63 @@ +package org.plutoengine.graphics; + +import org.plutoengine.ModLWJGL; +import org.plutoengine.Pluto; +import org.plutoengine.graphics.spritesheet.FramebufferTiledSpriteSheet; +import org.plutoengine.mod.IModEntryPoint; +import org.plutoengine.mod.Mod; +import org.plutoengine.mod.ModEntry; +import org.plutoengine.shader.PlutoShaderMod; +import org.plutoengine.shader.RenderShaderBuilder; + +@ModEntry(modID = PlutoSpriteSheetMod.MOD_ID, + version = Pluto.VERSION, + dependencies = { ModLWJGL.class, PlutoShaderMod.class }) +public class PlutoSpriteSheetMod implements IModEntryPoint +{ + public static final String MOD_ID = "tefek.plutospritesheet"; + + public static Mod instance; + + /** + * Strictly internal use only, do NOT use this outside of plutospritesheet + */ + private static Shader2D shader2D; + + /** + * Strictly internal use only, do NOT use this outside of plutospritesheet + */ + private static ShaderRectangle2D shaderRectangle2D; + + /** + * Strictly internal use only, do NOT use this outside of plutospritesheet + */ + private static ShaderRectangle2D spriteSheetShader; + + @Override + public void onLoad(Mod mod) + { + instance = mod; + + shader2D = new RenderShaderBuilder(mod.getResource("shaders.v2D#glsl"), mod.getResource("shaders.f2D#glsl")).build(Shader2D.class, false); + shaderRectangle2D = new RenderShaderBuilder(mod.getResource("shaders.VertexRectangle2D#glsl"), mod.getResource("shaders.FragmentRectangle2D#glsl")).build(ShaderRectangle2D.class, false); + spriteSheetShader = new RenderShaderBuilder(mod.getResource("shaders.VertexSpriteSheet#glsl"), mod.getResource("shaders.FragmentSpriteSheet#glsl")).build(ShaderRectangle2D.class, false); + + Renderer2D.load(shader2D); + RectangleRenderer2D.load(shaderRectangle2D); + + FramebufferTiledSpriteSheet.setSpriteShader(spriteSheetShader); + } + + @Override + public void onUnload() + { + FramebufferTiledSpriteSheet.setSpriteShader(null); + + spriteSheetShader.dispose(); + shaderRectangle2D.dispose(); + shader2D.dispose(); + + RectangleRenderer2D.unload(); + Renderer2D.unload(); + } +} diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/RectangleRenderer2D.java b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/RectangleRenderer2D.java old mode 100644 new mode 100755 similarity index 90% rename from plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/RectangleRenderer2D.java rename to engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/RectangleRenderer2D.java index 2c061f5..5394af5 --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/RectangleRenderer2D.java +++ b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/RectangleRenderer2D.java @@ -1,17 +1,16 @@ -package cz.tefek.pluto.engine.graphics; - -import java.util.Stack; +package org.plutoengine.graphics; import org.joml.Matrix3x2f; import org.joml.Matrix3x2fc; import org.joml.Vector4f; import org.lwjgl.opengl.GL33; +import org.plutoengine.graphics.gl.DrawMode; +import org.plutoengine.graphics.gl.vao.QuadPresets; +import org.plutoengine.graphics.gl.vao.VertexArray; +import org.plutoengine.graphics.sprite.Sprite; +import org.plutoengine.graphics.texture.texture2d.RectangleTexture; -import cz.tefek.pluto.engine.graphics.gl.DrawMode; -import cz.tefek.pluto.engine.graphics.gl.vao.QuadPresets; -import cz.tefek.pluto.engine.graphics.gl.vao.VertexArray; -import cz.tefek.pluto.engine.graphics.sprite.Sprite; -import cz.tefek.pluto.engine.graphics.texture.texture2d.RectangleTexture; +import java.util.Stack; /** * A builder-like renderer for 2D rectangles. Note that the internal state is @@ -45,7 +44,7 @@ public class RectangleRenderer2D protected boolean modifiedTransformation = false; - private static Stack customShaderStack = new Stack<>(); + private static final Stack customShaderStack = new Stack<>(); public static void load(ShaderRectangle2D defaultShaderIn) { @@ -137,12 +136,7 @@ public class RectangleRenderer2D public RectangleRenderer2D identity() { - this.transformation.m00 = 1; - this.transformation.m01 = 0; - this.transformation.m10 = 0; - this.transformation.m11 = 1; - this.transformation.m20 = 0; - this.transformation.m21 = 0; + this.transformation.identity(); return this; } diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/Renderer2D.java b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/Renderer2D.java old mode 100644 new mode 100755 similarity index 90% rename from plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/Renderer2D.java rename to engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/Renderer2D.java index 455a55f..c7b8d12 --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/Renderer2D.java +++ b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/Renderer2D.java @@ -1,16 +1,15 @@ -package cz.tefek.pluto.engine.graphics; - -import java.util.Stack; +package org.plutoengine.graphics; import org.joml.Matrix3x2f; import org.joml.Matrix3x2fc; import org.joml.Vector4f; import org.lwjgl.opengl.GL33; +import org.plutoengine.graphics.gl.DrawMode; +import org.plutoengine.graphics.gl.vao.QuadPresets; +import org.plutoengine.graphics.gl.vao.VertexArray; +import org.plutoengine.graphics.texture.texture2d.Texture2D; -import cz.tefek.pluto.engine.graphics.gl.DrawMode; -import cz.tefek.pluto.engine.graphics.gl.vao.QuadPresets; -import cz.tefek.pluto.engine.graphics.gl.vao.VertexArray; -import cz.tefek.pluto.engine.graphics.texture.texture2d.Texture2D; +import java.util.Stack; /** * A builder-like renderer for 2D rectangles. Note that the internal state is @@ -39,7 +38,7 @@ public class Renderer2D protected boolean modifiedTransformation = false; - private static Stack customShaderStack = new Stack<>(); + private static final Stack customShaderStack = new Stack<>(); public static void load(IShader2D defaultShaderIn) { @@ -119,12 +118,7 @@ public class Renderer2D public Renderer2D identity() { - this.transformation.m00 = 1; - this.transformation.m01 = 0; - this.transformation.m10 = 0; - this.transformation.m11 = 1; - this.transformation.m20 = 0; - this.transformation.m21 = 0; + this.transformation.identity(); return this; } diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/Shader2D.java b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/Shader2D.java old mode 100644 new mode 100755 similarity index 66% rename from plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/Shader2D.java rename to engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/Shader2D.java index 17e2099..85cd7db --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/Shader2D.java +++ b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/Shader2D.java @@ -1,18 +1,18 @@ -package cz.tefek.pluto.engine.graphics; +package org.plutoengine.graphics; import org.joml.Matrix3x2fc; import org.joml.Matrix4fc; -import cz.tefek.pluto.engine.graphics.gl.vao.attrib.ReservedAttributes; -import cz.tefek.pluto.engine.shader.ShaderBase; -import cz.tefek.pluto.engine.shader.ShaderProgram; -import cz.tefek.pluto.engine.shader.VertexArrayAttribute; -import cz.tefek.pluto.engine.shader.uniform.Uniform; -import cz.tefek.pluto.engine.shader.uniform.UniformMat3x2; -import cz.tefek.pluto.engine.shader.uniform.UniformMat4; -import cz.tefek.pluto.engine.shader.uniform.UniformVec2; -import cz.tefek.pluto.engine.shader.uniform.UniformVec4; -import cz.tefek.pluto.engine.shader.uniform.auto.AutoViewportProjection; +import org.plutoengine.graphics.gl.vao.attrib.ReservedAttributes; +import org.plutoengine.shader.ShaderBase; +import org.plutoengine.shader.ShaderProgram; +import org.plutoengine.shader.VertexArrayAttribute; +import org.plutoengine.shader.uniform.Uniform; +import org.plutoengine.shader.uniform.UniformMat3x2; +import org.plutoengine.shader.uniform.UniformMat4; +import org.plutoengine.shader.uniform.UniformVec2; +import org.plutoengine.shader.uniform.UniformVec4; +import org.plutoengine.shader.uniform.auto.AutoViewportProjection; /** * @author 493msi diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/ShaderRectangle2D.java b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/ShaderRectangle2D.java old mode 100644 new mode 100755 similarity index 66% rename from plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/ShaderRectangle2D.java rename to engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/ShaderRectangle2D.java index ed6622b..388355d --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/ShaderRectangle2D.java +++ b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/ShaderRectangle2D.java @@ -1,18 +1,18 @@ -package cz.tefek.pluto.engine.graphics; +package org.plutoengine.graphics; import org.joml.Matrix3x2fc; import org.joml.Matrix4fc; -import cz.tefek.pluto.engine.graphics.gl.vao.attrib.ReservedAttributes; -import cz.tefek.pluto.engine.shader.ShaderBase; -import cz.tefek.pluto.engine.shader.ShaderProgram; -import cz.tefek.pluto.engine.shader.VertexArrayAttribute; -import cz.tefek.pluto.engine.shader.uniform.Uniform; -import cz.tefek.pluto.engine.shader.uniform.UniformMat3x2; -import cz.tefek.pluto.engine.shader.uniform.UniformMat4; -import cz.tefek.pluto.engine.shader.uniform.UniformVec2; -import cz.tefek.pluto.engine.shader.uniform.UniformVec4; -import cz.tefek.pluto.engine.shader.uniform.auto.AutoViewportProjection; +import org.plutoengine.graphics.gl.vao.attrib.ReservedAttributes; +import org.plutoengine.shader.ShaderBase; +import org.plutoengine.shader.ShaderProgram; +import org.plutoengine.shader.VertexArrayAttribute; +import org.plutoengine.shader.uniform.Uniform; +import org.plutoengine.shader.uniform.UniformMat3x2; +import org.plutoengine.shader.uniform.UniformMat4; +import org.plutoengine.shader.uniform.UniformVec2; +import org.plutoengine.shader.uniform.UniformVec4; +import org.plutoengine.shader.uniform.auto.AutoViewportProjection; /** * @author 493msi diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/skeleton/SpriteSkeleton.java b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/skeleton/SpriteSkeleton.java old mode 100644 new mode 100755 similarity index 92% rename from plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/skeleton/SpriteSkeleton.java rename to engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/skeleton/SpriteSkeleton.java index a08a16d..21c04ce --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/skeleton/SpriteSkeleton.java +++ b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/skeleton/SpriteSkeleton.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.graphics.skeleton; +package org.plutoengine.graphics.skeleton; import org.joml.Vector2f; import org.joml.Vector2fc; diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/skeleton/SpriteSkeletonLimb.java b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/skeleton/SpriteSkeletonLimb.java old mode 100644 new mode 100755 similarity index 86% rename from plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/skeleton/SpriteSkeletonLimb.java rename to engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/skeleton/SpriteSkeletonLimb.java index e2aae9b..81579fd --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/skeleton/SpriteSkeletonLimb.java +++ b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/skeleton/SpriteSkeletonLimb.java @@ -1,17 +1,16 @@ -package cz.tefek.pluto.engine.graphics.skeleton; - -import java.util.ArrayList; -import java.util.List; +package org.plutoengine.graphics.skeleton; import org.apache.commons.lang3.tuple.Pair; import org.joml.Matrix3x2f; import org.joml.Vector2f; import org.joml.Vector2fc; import org.lwjgl.opengl.GL33; +import org.plutoengine.graphics.RectangleRenderer2D; +import org.plutoengine.graphics.Renderer2D; +import org.plutoengine.graphics.sprite.PartialTextureSprite; -import cz.tefek.pluto.engine.graphics.RectangleRenderer2D; -import cz.tefek.pluto.engine.graphics.Renderer2D; -import cz.tefek.pluto.engine.graphics.sprite.PartialTextureSprite; +import java.util.ArrayList; +import java.util.List; public class SpriteSkeletonLimb { @@ -31,8 +30,8 @@ public class SpriteSkeletonLimb // The reason I don't use an IdentityHashMap here is because I need to retain the insertion order perfectly, // so the skeleton nicely draws back to front and not in a seemingly random order. - private List> backChildren; // Children connected to this limb from behind at the position - private List> frontChildren; // Children connected to this limb from front at position + private final List> backChildren; // Children connected to this limb from behind at the position + private final List> frontChildren; // Children connected to this limb from front at position public static final int MAX_DEPTH = 8; @@ -83,7 +82,13 @@ public class SpriteSkeletonLimb protected void renderInternal(RectangleRenderer2D renderer, Vector2fc position, Vector2fc scale) { - renderer.identity().translate(position.x(), position.y()).rotate(this.rotation).translate(-this.pivotPoint.x * scale.x(), -this.pivotPoint.y * scale.y()).scale(this.width * scale.x(), this.height * scale.y()).sprite(this.sprite).flush(); + renderer.identity() + .translate(position.x(), position.y()) + .rotate(this.rotation) + .translate(-this.pivotPoint.x * scale.x(), -this.pivotPoint.y * scale.y()) + .scale(this.width * scale.x(), this.height * scale.y()) + .sprite(this.sprite) + .flush(); } protected void renderChildren(RectangleRenderer2D renderer, Vector2fc position, Vector2fc scale) diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/DisposablePlaceholderSprite.java b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/sprite/DisposablePlaceholderSprite.java old mode 100644 new mode 100755 similarity index 61% rename from plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/DisposablePlaceholderSprite.java rename to engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/sprite/DisposablePlaceholderSprite.java index 2e6cea1..54659ad --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/DisposablePlaceholderSprite.java +++ b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/sprite/DisposablePlaceholderSprite.java @@ -1,11 +1,11 @@ -package cz.tefek.pluto.engine.graphics.sprite; +package org.plutoengine.graphics.sprite; import java.awt.image.BufferedImage; -import cz.tefek.pluto.engine.graphics.texture.MagFilter; -import cz.tefek.pluto.engine.graphics.texture.MinFilter; -import cz.tefek.pluto.engine.graphics.texture.WrapMode; -import cz.tefek.pluto.engine.graphics.texture.texture2d.RectangleTexture; +import org.plutoengine.graphics.texture.MagFilter; +import org.plutoengine.graphics.texture.MinFilter; +import org.plutoengine.graphics.texture.WrapMode; +import org.plutoengine.graphics.texture.texture2d.RectangleTexture; public class DisposablePlaceholderSprite extends DisposableTextureSprite { diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/DisposableTextureSprite.java b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/sprite/DisposableTextureSprite.java old mode 100644 new mode 100755 similarity index 76% rename from plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/DisposableTextureSprite.java rename to engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/sprite/DisposableTextureSprite.java index e33a9f8..12a2585 --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/DisposableTextureSprite.java +++ b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/sprite/DisposableTextureSprite.java @@ -1,6 +1,6 @@ -package cz.tefek.pluto.engine.graphics.sprite; +package org.plutoengine.graphics.sprite; -import cz.tefek.pluto.engine.graphics.texture.texture2d.RectangleTexture; +import org.plutoengine.graphics.texture.texture2d.RectangleTexture; public class DisposableTextureSprite extends PartialTextureSprite implements SpriteDisposable { diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/PartialTextureSprite.java b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/sprite/PartialTextureSprite.java old mode 100644 new mode 100755 similarity index 89% rename from plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/PartialTextureSprite.java rename to engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/sprite/PartialTextureSprite.java index 4eaa849..23ee34a --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/PartialTextureSprite.java +++ b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/sprite/PartialTextureSprite.java @@ -1,6 +1,6 @@ -package cz.tefek.pluto.engine.graphics.sprite; +package org.plutoengine.graphics.sprite; -import cz.tefek.pluto.engine.graphics.texture.texture2d.RectangleTexture; +import org.plutoengine.graphics.texture.texture2d.RectangleTexture; public class PartialTextureSprite implements Sprite { diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/Sprite.java b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/sprite/Sprite.java old mode 100644 new mode 100755 similarity index 72% rename from plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/Sprite.java rename to engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/sprite/Sprite.java index a814881..ae946d3 --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/Sprite.java +++ b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/sprite/Sprite.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.graphics.sprite; +package org.plutoengine.graphics.sprite; public interface Sprite { diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/SpriteDisposable.java b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/sprite/SpriteDisposable.java old mode 100644 new mode 100755 similarity index 62% rename from plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/SpriteDisposable.java rename to engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/sprite/SpriteDisposable.java index 64ba764..9d23b87 --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/SpriteDisposable.java +++ b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/sprite/SpriteDisposable.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.graphics.sprite; +package org.plutoengine.graphics.sprite; public interface SpriteDisposable extends Sprite { diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/TileSprite.java b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/sprite/TileSprite.java old mode 100644 new mode 100755 similarity index 93% rename from plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/TileSprite.java rename to engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/sprite/TileSprite.java index 5d763bf..b96bfcb --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/TileSprite.java +++ b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/sprite/TileSprite.java @@ -1,6 +1,6 @@ -package cz.tefek.pluto.engine.graphics.sprite; +package org.plutoengine.graphics.sprite; -import cz.tefek.pluto.engine.graphics.spritesheet.TiledSpriteSheet; +import org.plutoengine.graphics.spritesheet.TiledSpriteSheet; public class TileSprite> implements Sprite { diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/spritesheet/BufferedImageTiledSpriteSheet.java b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/spritesheet/BufferedImageTiledSpriteSheet.java old mode 100644 new mode 100755 similarity index 95% rename from plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/spritesheet/BufferedImageTiledSpriteSheet.java rename to engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/spritesheet/BufferedImageTiledSpriteSheet.java index 9942593..38c98c8 --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/spritesheet/BufferedImageTiledSpriteSheet.java +++ b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/spritesheet/BufferedImageTiledSpriteSheet.java @@ -1,12 +1,12 @@ -package cz.tefek.pluto.engine.graphics.spritesheet; +package org.plutoengine.graphics.spritesheet; + +import org.plutoengine.graphics.sprite.Sprite; +import org.plutoengine.graphics.sprite.TileSprite; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; -import cz.tefek.pluto.engine.graphics.sprite.Sprite; -import cz.tefek.pluto.engine.graphics.sprite.TileSprite; - // FIXME /** * A sprite atlas for {@link BufferedImage}. diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/spritesheet/FramebufferTiledSpriteSheet.java b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/spritesheet/FramebufferTiledSpriteSheet.java old mode 100644 new mode 100755 similarity index 84% rename from plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/spritesheet/FramebufferTiledSpriteSheet.java rename to engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/spritesheet/FramebufferTiledSpriteSheet.java index 4e45d2d..fa29f1a --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/spritesheet/FramebufferTiledSpriteSheet.java +++ b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/spritesheet/FramebufferTiledSpriteSheet.java @@ -1,20 +1,20 @@ -package cz.tefek.pluto.engine.graphics.spritesheet; +package org.plutoengine.graphics.spritesheet; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL33; -import cz.tefek.pluto.engine.graphics.IRectangleShader2D; -import cz.tefek.pluto.engine.graphics.RectangleRenderer2D; -import cz.tefek.pluto.engine.graphics.gl.fbo.Framebuffer; -import cz.tefek.pluto.engine.graphics.gl.fbo.FramebufferTexture; -import cz.tefek.pluto.engine.graphics.sprite.Sprite; -import cz.tefek.pluto.engine.graphics.sprite.TileSprite; -import cz.tefek.pluto.engine.graphics.texture.MagFilter; -import cz.tefek.pluto.engine.graphics.texture.MinFilter; -import cz.tefek.pluto.engine.graphics.texture.WrapMode; -import cz.tefek.pluto.engine.graphics.texture.sampler.Sampler2D; -import cz.tefek.pluto.engine.graphics.texture.texture2d.RectangleTexture; -import cz.tefek.pluto.engine.math.ProjectionMatrix; +import org.plutoengine.graphics.IRectangleShader2D; +import org.plutoengine.graphics.RectangleRenderer2D; +import org.plutoengine.graphics.gl.fbo.Framebuffer; +import org.plutoengine.graphics.gl.fbo.FramebufferTexture; +import org.plutoengine.graphics.sprite.Sprite; +import org.plutoengine.graphics.sprite.TileSprite; +import org.plutoengine.graphics.texture.MagFilter; +import org.plutoengine.graphics.texture.MinFilter; +import org.plutoengine.graphics.texture.WrapMode; +import org.plutoengine.graphics.texture.sampler.Sampler2D; +import org.plutoengine.graphics.texture.texture2d.RectangleTexture; +import org.plutoengine.math.ProjectionMatrix; public class FramebufferTiledSpriteSheet extends TiledSpriteSheet { diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/spritesheet/SpriteSheet.java b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/spritesheet/SpriteSheet.java old mode 100644 new mode 100755 similarity index 90% rename from plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/spritesheet/SpriteSheet.java rename to engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/spritesheet/SpriteSheet.java index 0869127..037c3f3 --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/spritesheet/SpriteSheet.java +++ b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/spritesheet/SpriteSheet.java @@ -1,4 +1,4 @@ -package cz.tefek.pluto.engine.graphics.spritesheet; +package org.plutoengine.graphics.spritesheet; public abstract class SpriteSheet { diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/spritesheet/TiledSpriteSheet.java b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/spritesheet/TiledSpriteSheet.java old mode 100644 new mode 100755 similarity index 93% rename from plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/spritesheet/TiledSpriteSheet.java rename to engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/spritesheet/TiledSpriteSheet.java index 15e5724..91a48bd --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/spritesheet/TiledSpriteSheet.java +++ b/engine-core/plutospritesheet/src/main/java/org/plutoengine/graphics/spritesheet/TiledSpriteSheet.java @@ -1,13 +1,13 @@ -package cz.tefek.pluto.engine.graphics.spritesheet; +package org.plutoengine.graphics.spritesheet; + +import org.plutoengine.graphics.sprite.Sprite; +import org.plutoengine.graphics.sprite.SpriteDisposable; +import org.plutoengine.graphics.sprite.TileSprite; +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; import java.util.Vector; -import cz.tefek.pluto.engine.graphics.sprite.Sprite; -import cz.tefek.pluto.engine.graphics.sprite.SpriteDisposable; -import cz.tefek.pluto.engine.graphics.sprite.TileSprite; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; - public abstract class TiledSpriteSheet extends SpriteSheet { protected Vector>> sprites; @@ -29,7 +29,6 @@ public abstract class TiledSpriteSheet extends SpriteSheet public TiledSpriteSheet(int tileWidth, int tileHeight) { - this.tileWidth = tileWidth; this.tileHeight = tileHeight; @@ -132,11 +131,8 @@ public abstract class TiledSpriteSheet extends SpriteSheet this.drawSprite(sprite, newX, newY, newWidth, newHeight); - if (sprite instanceof SpriteDisposable) - { - var disposableSprite = (SpriteDisposable) sprite; + if (sprite instanceof SpriteDisposable disposableSprite) disposableSprite.delete(); - } var copySprite = new TileSprite>(newX, newY, newWidth, newHeight); copySprite.setSpriteSheet(this); diff --git a/engine-core/plutotexture/build.gradle.kts b/engine-core/plutotexture/build.gradle.kts new file mode 100755 index 0000000..aafb58e --- /dev/null +++ b/engine-core/plutotexture/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + java + `java-library` +} + +description = "" + +dependencies { + api(project(":plutoengine:plutodisplay")) +} \ No newline at end of file diff --git a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/MagFilter.java b/engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/MagFilter.java old mode 100644 new mode 100755 similarity index 70% rename from plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/MagFilter.java rename to engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/MagFilter.java index 4696920..94dc803 --- a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/MagFilter.java +++ b/engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/MagFilter.java @@ -1,8 +1,8 @@ -package cz.tefek.pluto.engine.graphics.texture; +package org.plutoengine.graphics.texture; import org.lwjgl.opengl.GL33; -import cz.tefek.pluto.engine.gl.IOpenGLEnum; +import org.plutoengine.gl.IOpenGLEnum; public enum MagFilter implements IOpenGLEnum { @@ -14,7 +14,7 @@ public enum MagFilter implements IOpenGLEnum this.id = id; } - private int id; + private final int id; @Override public int getGLID() diff --git a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/MinFilter.java b/engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/MinFilter.java old mode 100644 new mode 100755 similarity index 82% rename from plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/MinFilter.java rename to engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/MinFilter.java index c8cd9ea..5e7dd87 --- a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/MinFilter.java +++ b/engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/MinFilter.java @@ -1,8 +1,8 @@ -package cz.tefek.pluto.engine.graphics.texture; +package org.plutoengine.graphics.texture; import org.lwjgl.opengl.GL33; -import cz.tefek.pluto.engine.gl.IOpenGLEnum; +import org.plutoengine.gl.IOpenGLEnum; public enum MinFilter implements IOpenGLEnum { @@ -19,8 +19,8 @@ public enum MinFilter implements IOpenGLEnum this.mipMapped = isMipMapped; } - private int id; - private boolean mipMapped; + private final int id; + private final boolean mipMapped; @Override public int getGLID() diff --git a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/Texture.java b/engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/Texture.java old mode 100644 new mode 100755 similarity index 78% rename from plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/Texture.java rename to engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/Texture.java index 10c2adf..3cc595d --- a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/Texture.java +++ b/engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/Texture.java @@ -1,17 +1,17 @@ -package cz.tefek.pluto.engine.graphics.texture; +package org.plutoengine.graphics.texture; import org.lwjgl.opengl.GL33; import org.lwjgl.system.MemoryUtil; import java.awt.image.BufferedImage; import java.nio.ByteBuffer; +import java.nio.file.Path; import java.util.Arrays; -import cz.tefek.pluto.io.asl.resource.ResourceAddress; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; -import cz.tefek.pluto.io.tpl.TPL; -import cz.tefek.pluto.io.tpl.TPNImage; +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; +import org.plutoengine.tpl.ImageABGR; +import org.plutoengine.tpl.ImageLoader; public abstract class Texture { @@ -177,46 +177,24 @@ public abstract class Texture return WrapMode.REPEAT; } - /** - * @deprecated Loading assets via {@link String} filenames is discouraged. - * */ - @Deprecated - public void load(String file) + public void load(Path path) { var wrap = new WrapMode[this.dimensions]; Arrays.fill(wrap, this.getDefaultWrapMode()); - this.load(file, MagFilter.LINEAR, MinFilter.LINEAR, wrap); + this.load(path, MagFilter.LINEAR, MinFilter.LINEAR, wrap); } - public void load(ResourceAddress file) + public void load(Path path, MagFilter magFilter, MinFilter minFilter, WrapMode... wrap) { - var wrap = new WrapMode[this.dimensions]; - Arrays.fill(wrap, this.getDefaultWrapMode()); - this.load(file.toPath(), MagFilter.LINEAR, MinFilter.LINEAR, wrap); - } - - public void load(ResourceAddress file, MagFilter magFilter, MinFilter minFilter, WrapMode... wrap) - { - this.load(file.toPath(), magFilter, minFilter, wrap); - } - - /** - * @deprecated Loading assets via {@link String} filenames is discouraged. - * */ - @Deprecated - public void load(String file, MagFilter magFilter, MinFilter minFilter, WrapMode... wrap) - { - TPNImage image = TPL.load(file); - this.load(image, magFilter, minFilter, wrap); + this.load(ImageLoader.load(path), magFilter, minFilter, wrap); } public void load(BufferedImage imageIn, MagFilter magFilter, MinFilter minFilter, WrapMode... wrap) { - TPNImage image = TPL.loadImage(imageIn); - this.load(image, magFilter, minFilter, wrap); + this.load(ImageLoader.loadImage(imageIn), magFilter, minFilter, wrap); } - public void load(TPNImage image, MagFilter magFilter, MinFilter minFilter, WrapMode... wrap) + public void load(ImageABGR image, MagFilter magFilter, MinFilter minFilter, WrapMode... wrap) { this.load(image.getData(), image.getWidth(), image.getHeight(), magFilter, minFilter, wrap); } diff --git a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/WrapMode.java b/engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/WrapMode.java old mode 100644 new mode 100755 similarity index 88% rename from plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/WrapMode.java rename to engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/WrapMode.java index 7433fd1..d45bfb2 --- a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/WrapMode.java +++ b/engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/WrapMode.java @@ -1,11 +1,10 @@ -package cz.tefek.pluto.engine.graphics.texture; - -import java.util.EnumSet; +package org.plutoengine.graphics.texture; import org.lwjgl.opengl.GL33; import org.lwjgl.opengl.GL44; +import org.plutoengine.gl.IOpenGLEnum; -import cz.tefek.pluto.engine.gl.IOpenGLEnum; +import java.util.EnumSet; public enum WrapMode implements IOpenGLEnum { @@ -24,7 +23,7 @@ public enum WrapMode implements IOpenGLEnum public static final EnumSet clampModes = EnumSet.of(WrapMode.CLAMP_TO_EDGE, WrapMode.CLAMP_TO_BORDER, MIRROR_CLAMP_TO_EDGE); public static final EnumSet mirrorModes = EnumSet.of(WrapMode.MIRROR_CLAMP_TO_EDGE, WrapMode.MIRRORED_REPEAT); - private int id; + private final int id; @Override public int getGLID() diff --git a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/sampler/Sampler2D.java b/engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/sampler/Sampler2D.java old mode 100644 new mode 100755 similarity index 80% rename from plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/sampler/Sampler2D.java rename to engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/sampler/Sampler2D.java index 145fdb8..8ac03fd --- a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/sampler/Sampler2D.java +++ b/engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/sampler/Sampler2D.java @@ -1,18 +1,17 @@ -package cz.tefek.pluto.engine.graphics.texture.sampler; +package org.plutoengine.graphics.texture.sampler; + +import org.lwjgl.opengl.GL33; +import org.plutoengine.graphics.texture.MagFilter; +import org.plutoengine.graphics.texture.MinFilter; +import org.plutoengine.graphics.texture.WrapMode; import java.util.HashSet; -import org.lwjgl.opengl.GL33; - -import cz.tefek.pluto.engine.graphics.texture.MagFilter; -import cz.tefek.pluto.engine.graphics.texture.MinFilter; -import cz.tefek.pluto.engine.graphics.texture.WrapMode; - public class Sampler2D { - private int id; + private final int id; - private HashSet usedUnits; + private final HashSet usedUnits; public Sampler2D() { diff --git a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/sampler/Sampler3D.java b/engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/sampler/Sampler3D.java old mode 100644 new mode 100755 similarity index 82% rename from plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/sampler/Sampler3D.java rename to engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/sampler/Sampler3D.java index c632859..ae98ca6 --- a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/sampler/Sampler3D.java +++ b/engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/sampler/Sampler3D.java @@ -1,18 +1,17 @@ -package cz.tefek.pluto.engine.graphics.texture.sampler; +package org.plutoengine.graphics.texture.sampler; + +import org.lwjgl.opengl.GL33; +import org.plutoengine.graphics.texture.MagFilter; +import org.plutoengine.graphics.texture.MinFilter; +import org.plutoengine.graphics.texture.WrapMode; import java.util.HashSet; -import org.lwjgl.opengl.GL33; - -import cz.tefek.pluto.engine.graphics.texture.MagFilter; -import cz.tefek.pluto.engine.graphics.texture.MinFilter; -import cz.tefek.pluto.engine.graphics.texture.WrapMode; - public class Sampler3D { - private int id; + private final int id; - private HashSet usedUnits; + private final HashSet usedUnits; public Sampler3D() { diff --git a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/texture2d/RectangleTexture.java b/engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/texture2d/RectangleTexture.java old mode 100644 new mode 100755 similarity index 79% rename from plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/texture2d/RectangleTexture.java rename to engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/texture2d/RectangleTexture.java index c1d7149..8fa50ff --- a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/texture2d/RectangleTexture.java +++ b/engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/texture2d/RectangleTexture.java @@ -1,13 +1,13 @@ -package cz.tefek.pluto.engine.graphics.texture.texture2d; +package org.plutoengine.graphics.texture.texture2d; import org.lwjgl.opengl.GL33; +import org.plutoengine.graphics.texture.Texture; +import org.plutoengine.graphics.texture.WrapMode; import java.util.Arrays; -import cz.tefek.pluto.engine.graphics.texture.Texture; -import cz.tefek.pluto.engine.graphics.texture.WrapMode; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; +import org.plutoengine.logger.Logger; +import org.plutoengine.logger.SmartSeverity; public class RectangleTexture extends Texture { diff --git a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/texture2d/Texture2D.java b/engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/texture2d/Texture2D.java old mode 100644 new mode 100755 similarity index 80% rename from plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/texture2d/Texture2D.java rename to engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/texture2d/Texture2D.java index f2ae05f..22325b9 --- a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/texture2d/Texture2D.java +++ b/engine-core/plutotexture/src/main/java/org/plutoengine/graphics/texture/texture2d/Texture2D.java @@ -1,8 +1,8 @@ -package cz.tefek.pluto.engine.graphics.texture.texture2d; +package org.plutoengine.graphics.texture.texture2d; import org.lwjgl.opengl.GL33; -import cz.tefek.pluto.engine.graphics.texture.Texture; +import org.plutoengine.graphics.texture.Texture; /** * @author 493msi diff --git a/engine-core/plutouss2/build.gradle.kts b/engine-core/plutouss2/build.gradle.kts new file mode 100755 index 0000000..221870c --- /dev/null +++ b/engine-core/plutouss2/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + java + `java-library` +} + +description = "UniversalSerializationSystem 2 (formerly UserStorageSystem) is a simple library for " + + "in-memory serialization of basic data types to ByteBuffers with versioned schema support." + +dependencies { + +} diff --git a/engine-core/plutouss2/src/main/java/org/plutoengine/uss2/properties/EnumUSS2PropertyType.java b/engine-core/plutouss2/src/main/java/org/plutoengine/uss2/properties/EnumUSS2PropertyType.java new file mode 100755 index 0000000..160a8c6 --- /dev/null +++ b/engine-core/plutouss2/src/main/java/org/plutoengine/uss2/properties/EnumUSS2PropertyType.java @@ -0,0 +1,10 @@ +package org.plutoengine.uss2.properties; + +public enum EnumUSS2PropertyType +{ + BYTE, + INT, + LONG, + STRING, + DOUBLE +} diff --git a/engine-core/plutouss2/src/main/java/org/plutoengine/uss2/properties/USS2PropertyObject.java b/engine-core/plutouss2/src/main/java/org/plutoengine/uss2/properties/USS2PropertyObject.java new file mode 100755 index 0000000..2321b34 --- /dev/null +++ b/engine-core/plutouss2/src/main/java/org/plutoengine/uss2/properties/USS2PropertyObject.java @@ -0,0 +1,99 @@ +package org.plutoengine.uss2.properties; + +import java.nio.ByteBuffer; + +public final class USS2PropertyObject +{ + final USS2PropertySchema schema; + + final int currentVersion; + final int latestVersion; + boolean dirty; + + final ByteBuffer data; + + private USS2PropertyObject(ByteBuffer buf, USS2PropertySchema schema) + { + this.schema = schema; + + if (buf.limit() != schema.getCapacity()) + throw new IllegalArgumentException("The input ByteBuffer's limit and the USS2PropertySchema size must be the same value!"); + + this.currentVersion = 0xff & buf.get(0); + this.latestVersion = schema.getVersion(); + + if (this.currentVersion > this.latestVersion) + throw new RuntimeException(String.format("The file's version (%d) is newer than what USS2 can read (%d)!", this.currentVersion, this.latestVersion)); + + this.data = buf; + } + + private static USS2PropertyObject upgrade(USS2PropertyObject po) + { + var poNew = create(po.schema); + var props = po.schema.getProperties(); + props.forEach(property -> { + if (property instanceof USS2PropertySchema.USS2Int ussInt) + { + ussInt.write(poNew, ussInt.read(po)); + } + else if (property instanceof USS2PropertySchema.USS2Long ussLong) + { + ussLong.write(poNew, ussLong.read(po)); + } + else if (property instanceof USS2PropertySchema.USS2Double ussDouble) + { + ussDouble.write(poNew, ussDouble.read(po)); + } + else if (property instanceof USS2PropertySchema.USS2Byte ussByte) + { + ussByte.write(poNew, ussByte.read(po)); + } + else if (property instanceof USS2PropertySchema.USS2String ussString) + { + ussString.write(poNew, ussString.read(po)); + } + }); + + return poNew; + } + + public static USS2PropertyObject create(USS2PropertySchema schema) + { + var buf = ByteBuffer.wrap(new byte[schema.getCapacity()]); + buf.put(0, (byte) schema.getVersion()); + + var po = new USS2PropertyObject(buf, schema); + po.dirty = true; + + return po; + } + + public static USS2PropertyObject from(ByteBuffer buf, USS2PropertySchema schema) + { + var po = new USS2PropertyObject(buf, schema); + + if (po.latestVersion != po.currentVersion) + return upgrade(po); + + return po; + } + + public ByteBuffer getData() + { + return this.data; + } + + public byte[] getDataByteArray() + { + if (!this.data.hasArray()) + throw new RuntimeException("Data does not have a backing array!"); + + return this.data.array(); + } + + public boolean isDirty() + { + return this.dirty; + } +} diff --git a/engine-core/plutouss2/src/main/java/org/plutoengine/uss2/properties/USS2PropertySchema.java b/engine-core/plutouss2/src/main/java/org/plutoengine/uss2/properties/USS2PropertySchema.java new file mode 100755 index 0000000..3a8b979 --- /dev/null +++ b/engine-core/plutouss2/src/main/java/org/plutoengine/uss2/properties/USS2PropertySchema.java @@ -0,0 +1,263 @@ +package org.plutoengine.uss2.properties; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public abstract class USS2PropertySchema +{ + public static final int DOES_NOT_EXIST_IN_VERSION = -1; + public static final int MAX_VERSION = 0xff; + + private final List properties; + + private final int capacity; + + private int version; + private final int[] offsets; + + protected USS2PropertySchema(int capacity) + { + if (capacity < 16) + throw new IllegalArgumentException("Please use a starting capacity of at least 16 bytes."); + + this.properties = new ArrayList<>(); + this.offsets = new int[MAX_VERSION + 1]; + + // Version byte + Arrays.fill(this.offsets, Byte.BYTES); + + this.capacity = capacity; + this.version = 0; + } + + public final String getInfo() + { + float used = this.offsets[MAX_VERSION] / (float) this.capacity; + final int bars = 30; + int usedBars = Math.round(used * bars); + + return String.format("[%s%s] %.2f%% of schema used (%d/%d bytes).", "|".repeat(usedBars), " ".repeat(bars - usedBars), used * 100, this.offsets[MAX_VERSION], this.capacity); + } + + private void declareProperty(USS2Property property) + { + Arrays.fill(property.offsets, 0, property.version, DOES_NOT_EXIST_IN_VERSION); + System.arraycopy(this.offsets, property.version, property.offsets, property.version, property.offsets.length - property.version); + + // No need to check all versions, because the highest version is the largest by default (properties cannot be removed) + if (this.offsets[MAX_VERSION] + property.objectSize > this.capacity) + throw new IllegalStateException( + String.format("Error: Declaring another property (of size %d) " + + "would put the schema's total size to %d, which is over the capacity of %d.", + property.objectSize, + this.offsets[MAX_VERSION] + property.objectSize, + this.capacity)); + + this.properties.add(property); + + if (property.version > this.version) + this.version = property.version; + + for (int i = property.version; i <= MAX_VERSION; i++) + this.offsets[i] += property.objectSize; + } + + public final List getProperties() + { + return Collections.unmodifiableList(this.properties); + } + + public final int getVersion() + { + return this.version; + } + + public final int getCapacity() + { + return this.capacity; + } + + protected final USS2Int declareInt(byte version) + { + var ussInt = new USS2Int(0xff & version); + this.declareProperty(ussInt); + return ussInt; + } + + protected final USS2Long declareLong(byte version) + { + var ussLong = new USS2Long(0xff & version); + this.declareProperty(ussLong); + return ussLong; + } + + protected final USS2Double declareDouble(byte version) + { + var ussDouble = new USS2Double(0xff & version); + this.declareProperty(ussDouble); + return ussDouble; + } + + protected final USS2Byte declareByte(byte version) + { + var ussByte = new USS2Byte(0xff & version); + this.declareProperty(ussByte); + return ussByte; + } + + protected final USS2String declareString(byte version, byte length) + { + var ussByte = new USS2String(0xff & version, 0xff & length); + this.declareProperty(ussByte); + return ussByte; + } + + public static abstract class USS2Property + { + private final int version; + private final int objectSize; + protected final int[] offsets; + + protected USS2Property(int version, int objectSize) + { + this.version = version; + this.objectSize = objectSize; + this.offsets = new int[MAX_VERSION]; + } + } + + public static final class USS2Int extends USS2Property + { + private USS2Int(int version) + { + super(version, Integer.BYTES); + } + + public void write(USS2PropertyObject po, int value) + { + po.dirty = true; + po.data.putInt(this.offsets[po.currentVersion], value); + } + + public int read(USS2PropertyObject po) + { + if (this.offsets[po.currentVersion] == DOES_NOT_EXIST_IN_VERSION) + { + return 0; + } + + return po.data.getInt(this.offsets[po.currentVersion]); + } + } + + public static final class USS2Long extends USS2Property + { + private USS2Long(int version) + { + super(version, Long.BYTES); + } + + public void write(USS2PropertyObject po, long value) + { + po.dirty = true; + po.data.putLong(this.offsets[po.currentVersion], value); + } + + public long read(USS2PropertyObject po) + { + if (this.offsets[po.currentVersion] == DOES_NOT_EXIST_IN_VERSION) + { + return 0; + } + + return po.data.getLong(this.offsets[po.currentVersion]); + } + } + + public static final class USS2Double extends USS2Property + { + private USS2Double(int version) + { + super(version, Double.BYTES); + } + + public void write(USS2PropertyObject po, double value) + { + po.dirty = true; + po.data.putDouble(this.offsets[po.currentVersion], value); + } + + public double read(USS2PropertyObject po) + { + if (this.offsets[po.currentVersion] == DOES_NOT_EXIST_IN_VERSION) + { + return 0.0; + } + + return po.data.getDouble(this.offsets[po.currentVersion]); + } + } + + public static final class USS2Byte extends USS2Property + { + public USS2Byte(int version) + { + super(version, Byte.BYTES); + } + + public void write(USS2PropertyObject po, byte value) + { + po.dirty = true; + po.data.put(this.offsets[po.currentVersion], value); + } + + public byte read(USS2PropertyObject po) + { + if (this.offsets[po.currentVersion] == DOES_NOT_EXIST_IN_VERSION) + { + return 0; + } + + return po.data.get(this.offsets[po.currentVersion]); + } + } + + public static final class USS2String extends USS2Property + { + private USS2String(int version, int length) + { + super(version, Byte.BYTES + length); + } + + public void write(USS2PropertyObject po, String value) + { + po.dirty = true; + var offset = this.offsets[po.currentVersion]; + var data = value.getBytes(StandardCharsets.UTF_8); + po.data.position(offset); + po.data.put((byte) data.length); + po.data.put(data); + po.data.rewind(); + } + + public String read(USS2PropertyObject po) + { + if (this.offsets[po.currentVersion] == DOES_NOT_EXIST_IN_VERSION) + { + return ""; + } + + var offset = this.offsets[po.currentVersion]; + po.data.position(offset); + var length = po.data.get(); + var bytes = new byte[0xff & length]; + po.data.get(bytes); + po.data.rewind(); + + return new String(bytes, StandardCharsets.UTF_8); + } + } +} diff --git a/engine-demo/.gitignore b/engine-demo/.gitignore new file mode 100755 index 0000000..6aca3e9 --- /dev/null +++ b/engine-demo/.gitignore @@ -0,0 +1,30 @@ +/*/.settings + +/.vscode + +# Ignore Eclipse project files +/*/.project +/*/.classpath +/.project +/.settings + +# Ignore IDEA project files +/.idea +/*/.idea +.idea + +*.log + +/.gradle/ + +# Ignore Gradle project-specific cache directory +.gradle + +# Ignore Gradle build output directory +/build +/*/build + +/bin +/*/bin + +/*/logs \ No newline at end of file diff --git a/engine-demo/basic-application/build.gradle.kts b/engine-demo/basic-application/build.gradle.kts new file mode 100755 index 0000000..7a87c44 --- /dev/null +++ b/engine-demo/basic-application/build.gradle.kts @@ -0,0 +1,59 @@ +import java.time.ZoneId +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter + +import org.plutoengine.Versions + + +val buildTime: String = ZonedDateTime.now() + .withZoneSameInstant(ZoneId.of("Europe/Prague")) + .toLocalDateTime() + .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + +sourceSets { + java { + create("config") { + } + + main { + java.srcDirs("$buildDir/generated/java") + } + } +} + +tasks { + val generateConfigs = register("generateConfigs", Copy::class) { + val projectVariables = mapOf( + "gameVersion" to Versions.versionFull, + "gameBuild" to buildTime + ) + + inputs.properties(projectVariables) + + from("src/config/java") + + into("$buildDir/generated/java") + + expand(projectVariables) + } + + compileJava { + dependsOn(generateConfigs) + } +} + +application { + mainClass.set("cz.tefek.plutodemo.Main") +} + + +tasks.withType { + jvmArgs = listOf( + "-Dcz.tefek.pluto.debug=true", + "-Dorg.lwjgl.util.Debug=true" + ) +} + +dependencies { + implementation(project(":plutoengine:plutocore")) +} \ No newline at end of file diff --git a/engine-demo/basic-application/mods/glfw/info.json b/engine-demo/basic-application/mods/glfw/info.json new file mode 100755 index 0000000..3bb8187 --- /dev/null +++ b/engine-demo/basic-application/mods/glfw/info.json @@ -0,0 +1,8 @@ +{ + "displayName": "GLFW", + "author": "The GLFW team", + "description": "The GLFW library, used for native window creation.", + "resourceRoots": { + + } +} diff --git a/engine-demo/basic-application/mods/lwjgl/info.json b/engine-demo/basic-application/mods/lwjgl/info.json new file mode 100755 index 0000000..e4a2c52 --- /dev/null +++ b/engine-demo/basic-application/mods/lwjgl/info.json @@ -0,0 +1,8 @@ +{ + "displayName": "LWJGL", + "description": "Lightweight Java Game Library", + "author": "The LWJGL team", + "resourceRoots": { + + } +} diff --git a/engine-demo/basic-application/mods/tefek.demo.basicapplication/icons/icon128.png b/engine-demo/basic-application/mods/tefek.demo.basicapplication/icons/icon128.png new file mode 100755 index 0000000..97c0d85 Binary files /dev/null and b/engine-demo/basic-application/mods/tefek.demo.basicapplication/icons/icon128.png differ diff --git a/engine-demo/basic-application/mods/tefek.demo.basicapplication/icons/icon16.png b/engine-demo/basic-application/mods/tefek.demo.basicapplication/icons/icon16.png new file mode 100755 index 0000000..9a03fab Binary files /dev/null and b/engine-demo/basic-application/mods/tefek.demo.basicapplication/icons/icon16.png differ diff --git a/engine-demo/basic-application/mods/tefek.demo.basicapplication/icons/icon32.png b/engine-demo/basic-application/mods/tefek.demo.basicapplication/icons/icon32.png new file mode 100755 index 0000000..63d4f55 Binary files /dev/null and b/engine-demo/basic-application/mods/tefek.demo.basicapplication/icons/icon32.png differ diff --git a/engine-demo/basic-application/mods/tefek.demo.basicapplication/icons/icon64.png b/engine-demo/basic-application/mods/tefek.demo.basicapplication/icons/icon64.png new file mode 100755 index 0000000..ddf061f Binary files /dev/null and b/engine-demo/basic-application/mods/tefek.demo.basicapplication/icons/icon64.png differ diff --git a/engine-demo/basic-application/mods/tefek.demo.basicapplication/icons/splash.png b/engine-demo/basic-application/mods/tefek.demo.basicapplication/icons/splash.png new file mode 100755 index 0000000..fca5ad9 Binary files /dev/null and b/engine-demo/basic-application/mods/tefek.demo.basicapplication/icons/splash.png differ diff --git a/engine-demo/basic-application/mods/tefek.demo.basicapplication/info.json b/engine-demo/basic-application/mods/tefek.demo.basicapplication/info.json new file mode 100755 index 0000000..329c142 --- /dev/null +++ b/engine-demo/basic-application/mods/tefek.demo.basicapplication/info.json @@ -0,0 +1,11 @@ +{ + "displayName": "Basic App Demo", + "description": "Basic application demo for PlutoEngine.", + "author": "Tefek", + "resourceRoots": { + "icons": { + "path": "icons", + "type": "open" + } + } +} \ No newline at end of file diff --git a/engine-demo/basic-application/mods/tefek.demo.basicapplication/info.schema.json b/engine-demo/basic-application/mods/tefek.demo.basicapplication/info.schema.json new file mode 100755 index 0000000..da36159 --- /dev/null +++ b/engine-demo/basic-application/mods/tefek.demo.basicapplication/info.schema.json @@ -0,0 +1,57 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://plutoengine.org/developer/mod.schema.json", + + "title": "PlutoEngine mod schema", + + "type": "object", + + "$defs": { + "nonEmptyString": { + "type": "string", + "pattern": "^.+$" + }, + "virtualAddress": { + "$ref": "#/$defs/nonEmptyString", + "pattern": "^([a-zA-Z0-9_-]+\\.)*[a-zA-Z0-9_-]+$" + } + }, + + "properties": { + "displayName": { + "description": "This name will be visible in applications when browsing the mod list. It does not necessarily need to be unique, however should be representative of the mod.", + "$ref": "#/$defs/nonEmptyString" + }, + "author": { + "description": "Name(s) of the author(s) of this mod.", + "$ref": "#/$defs/nonEmptyString" + }, + "description": { + "description": "Short description string.", + "type": "string" + }, + "resourceRoots": { + "type": "object", + "properties": { + + }, + "propertyNames": { + "$ref": "#/$defs/virtualAddress" + }, + "additionalProperties": { + "type": "object", + "properties": { + "type": { + "enum": ["open", "zip"] + }, + "path": { + "type": "string" + } + }, + "required": ["type", "path"] + } + } + }, + + "required": ["displayName", "author", "resourceRoots"] +} \ No newline at end of file diff --git a/engine-demo/basic-application/mods/tefek.plutogui/default/font/default/definitions.txt b/engine-demo/basic-application/mods/tefek.plutogui/default/font/default/definitions.txt new file mode 100755 index 0000000..4e1f3f7 --- /dev/null +++ b/engine-demo/basic-application/mods/tefek.plutogui/default/font/default/definitions.txt @@ -0,0 +1,100 @@ +//FONT DEFINITION, DO NOT CHANGE UNLESS YOU KNOW WHAT ARE YOU DOING. +//Use double slash to comment one row. +//First uncommented row must contain name of the font texture and its size. +//Width of one character is 16px, height 24px. Different resolutions are WIP. +default,256x144 +A 0;0 +B 0;0 +C 0;0 +D 0;0 +E 0;0 +F 0;0 +G 0;0 +H 0;0 +I 4;5 +J 1;0 +K 1;0 +L 0;0 +M 0;0 +N 0;0 +O 0;0 +P 0;2 +Q 0;0 +R 0;1 +S 0;0 +T 0;0 +U 0;0 +V 0;1 +W 0;0 +X 0;1 +Y 1;0 +Z 0;0 +a 3;3 +b 2;2 +c 2;3 +d 2;2 +e 2;3 +f 3;4 +g 2;3 +h 2;2 +i 4;7 +j 4;4 +k 2;3 +l 4;8 +m 1;0 +n 2;3 +o 3;2 +p 3;2 +q 3;4 +r 3;2 +s 3;4 +t 1;5 +u 3;3 +v 2;4 +w 0;0 +x 3;4 +y 3;2 +z 2;2 +1 5;1 +2 1;1 +3 2;3 +4 2;2 +5 1;3 +6 1;1 +7 1;1 +8 1;1 +9 1;1 +0 1;1 +- 3;3 ++ 2;3 +/ 1;2 +* 6;2 +) 7;4 +( 6;5 +[ 6;5 +] 6;5 +\ 1;3 +< 3;5 +> 3;6 +ˇ 3;2 +´ 5;6 += 2;2 +^ 3;2 +. 5;7 +! 5;8 +? 1;2 +: 5;7 +@ 1;0 +# 0;2 +~ 1;3 +& 2;0 +, 6;5 +; 6;4 +% 0;0 +| 5;8 +° 4;7 +" 4;5 +} 0;9 +{ 0;9 +' 5;6 +_ 2;2 diff --git a/engine-demo/basic-application/mods/tefek.plutogui/default/font/default/readme.txt b/engine-demo/basic-application/mods/tefek.plutogui/default/font/default/readme.txt new file mode 100755 index 0000000..312b4d8 --- /dev/null +++ b/engine-demo/basic-application/mods/tefek.plutogui/default/font/default/readme.txt @@ -0,0 +1,2 @@ +Feel free to create and send me a better font, I'll be glad. +Everything you need to know is in the definitions.txt file. diff --git a/engine-demo/basic-application/mods/tefek.plutogui/default/font/default/tex.png b/engine-demo/basic-application/mods/tefek.plutogui/default/font/default/tex.png new file mode 100755 index 0000000..e2715c4 Binary files /dev/null and b/engine-demo/basic-application/mods/tefek.plutogui/default/font/default/tex.png differ diff --git a/engine-demo/basic-application/mods/tefek.plutogui/default/gui/elements.png b/engine-demo/basic-application/mods/tefek.plutogui/default/gui/elements.png new file mode 100755 index 0000000..17a3698 Binary files /dev/null and b/engine-demo/basic-application/mods/tefek.plutogui/default/gui/elements.png differ diff --git a/engine-demo/basic-application/mods/tefek.plutogui/default/shaders/FragmentFontShader.glsl b/engine-demo/basic-application/mods/tefek.plutogui/default/shaders/FragmentFontShader.glsl new file mode 100755 index 0000000..a8940ba --- /dev/null +++ b/engine-demo/basic-application/mods/tefek.plutogui/default/shaders/FragmentFontShader.glsl @@ -0,0 +1,15 @@ +#version 330 core + +in vec2 uvCoordinates; + +uniform sampler2DRect textureSampler; +uniform vec4 recolor; + +out vec4 out_Color; + +void main(void) +{ + vec4 color = texture(textureSampler, uvCoordinates); + + out_Color = color * recolor; +} \ No newline at end of file diff --git a/engine-demo/basic-application/mods/tefek.plutogui/default/shaders/VertexFontShader.glsl b/engine-demo/basic-application/mods/tefek.plutogui/default/shaders/VertexFontShader.glsl new file mode 100755 index 0000000..25f45d6 --- /dev/null +++ b/engine-demo/basic-application/mods/tefek.plutogui/default/shaders/VertexFontShader.glsl @@ -0,0 +1,27 @@ +#version 330 core + +in vec2 position; +in vec2 uvCoords; + +out vec2 uvCoordinates; + +uniform mat4 projection; +uniform mat4 transformation; + +uniform vec2 uvBase; +uniform vec2 uvDelta; + +uniform int italic; + +void main(void) +{ + vec2 pos = vec2(position.x, position.y); + + if(italic == 1) + { + pos = vec2(position.x - position.y / 4.0, position.y); + } + + uvCoordinates = uvBase + uvCoords * uvDelta; + gl_Position = projection * transformation * vec4(pos, 0.0, 1.0); +} \ No newline at end of file diff --git a/engine-demo/basic-application/mods/tefek.plutogui/info.json b/engine-demo/basic-application/mods/tefek.plutogui/info.json new file mode 100755 index 0000000..ec2d942 --- /dev/null +++ b/engine-demo/basic-application/mods/tefek.plutogui/info.json @@ -0,0 +1,11 @@ +{ + "displayName": "Pluto Engine GUI Renderer", + "description": "", + "author": "Tefek", + "resourceRoots": { + "default": { + "path": "default", + "type": "open" + } + } +} diff --git a/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/FragmentColor2D.glsl b/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/FragmentColor2D.glsl new file mode 100755 index 0000000..1294253 --- /dev/null +++ b/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/FragmentColor2D.glsl @@ -0,0 +1,10 @@ +#version 330 core + +uniform vec4 color; + +out vec4 out_Color; + +void main(void) +{ + out_Color = color; +} \ No newline at end of file diff --git a/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/FragmentRectangle2D.glsl b/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/FragmentRectangle2D.glsl new file mode 100755 index 0000000..bc16e2a --- /dev/null +++ b/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/FragmentRectangle2D.glsl @@ -0,0 +1,16 @@ +#version 330 core + +in vec2 uvCoordinates; + +uniform sampler2DRect textureSampler; + +uniform vec4 recolor; + +out vec4 out_Color; + +void main(void) +{ + vec4 color = texture(textureSampler, uvCoordinates) * recolor; + + out_Color = color; +} \ No newline at end of file diff --git a/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/FragmentRectangleWorld2D.glsl b/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/FragmentRectangleWorld2D.glsl new file mode 100755 index 0000000..bc16e2a --- /dev/null +++ b/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/FragmentRectangleWorld2D.glsl @@ -0,0 +1,16 @@ +#version 330 core + +in vec2 uvCoordinates; + +uniform sampler2DRect textureSampler; + +uniform vec4 recolor; + +out vec4 out_Color; + +void main(void) +{ + vec4 color = texture(textureSampler, uvCoordinates) * recolor; + + out_Color = color; +} \ No newline at end of file diff --git a/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/FragmentSpriteSheet.glsl b/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/FragmentSpriteSheet.glsl new file mode 100755 index 0000000..bc16e2a --- /dev/null +++ b/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/FragmentSpriteSheet.glsl @@ -0,0 +1,16 @@ +#version 330 core + +in vec2 uvCoordinates; + +uniform sampler2DRect textureSampler; + +uniform vec4 recolor; + +out vec4 out_Color; + +void main(void) +{ + vec4 color = texture(textureSampler, uvCoordinates) * recolor; + + out_Color = color; +} \ No newline at end of file diff --git a/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/VertexColor2D.glsl b/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/VertexColor2D.glsl new file mode 100755 index 0000000..e1f61ea --- /dev/null +++ b/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/VertexColor2D.glsl @@ -0,0 +1,11 @@ +#version 330 core + +in vec2 position; + +uniform mat4 projection; +uniform mat3x2 transformation; + +void main(void) +{ + gl_Position = projection * vec4(transformation * vec3(position.x, position.y, 1.0), 0.0, 1.0); +} \ No newline at end of file diff --git a/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/VertexRectangle2D.glsl b/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/VertexRectangle2D.glsl new file mode 100755 index 0000000..2a4fe41 --- /dev/null +++ b/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/VertexRectangle2D.glsl @@ -0,0 +1,19 @@ +#version 330 core + +in vec2 position; +in vec2 uvCoords; + +out vec2 uvCoordinates; + +uniform mat4 projection; +uniform mat3x2 transformation; + +uniform vec2 uvBase; +uniform vec2 uvDelta; + +void main(void) +{ + gl_Position = projection * vec4(transformation * vec3(position.x, position.y, 1.0), 0.0, 1.0); + + uvCoordinates = uvBase + uvCoords * uvDelta; +} \ No newline at end of file diff --git a/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/VertexRectangleWorld2D.glsl b/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/VertexRectangleWorld2D.glsl new file mode 100755 index 0000000..2a4fe41 --- /dev/null +++ b/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/VertexRectangleWorld2D.glsl @@ -0,0 +1,19 @@ +#version 330 core + +in vec2 position; +in vec2 uvCoords; + +out vec2 uvCoordinates; + +uniform mat4 projection; +uniform mat3x2 transformation; + +uniform vec2 uvBase; +uniform vec2 uvDelta; + +void main(void) +{ + gl_Position = projection * vec4(transformation * vec3(position.x, position.y, 1.0), 0.0, 1.0); + + uvCoordinates = uvBase + uvCoords * uvDelta; +} \ No newline at end of file diff --git a/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/VertexSpriteSheet.glsl b/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/VertexSpriteSheet.glsl new file mode 100755 index 0000000..2a4fe41 --- /dev/null +++ b/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/VertexSpriteSheet.glsl @@ -0,0 +1,19 @@ +#version 330 core + +in vec2 position; +in vec2 uvCoords; + +out vec2 uvCoordinates; + +uniform mat4 projection; +uniform mat3x2 transformation; + +uniform vec2 uvBase; +uniform vec2 uvDelta; + +void main(void) +{ + gl_Position = projection * vec4(transformation * vec3(position.x, position.y, 1.0), 0.0, 1.0); + + uvCoordinates = uvBase + uvCoords * uvDelta; +} \ No newline at end of file diff --git a/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/f2D.glsl b/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/f2D.glsl new file mode 100755 index 0000000..69af927 --- /dev/null +++ b/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/f2D.glsl @@ -0,0 +1,16 @@ +#version 330 core + +in vec2 uvCoordinates; + +uniform sampler2D textureSampler; + +uniform vec4 recolor; + +out vec4 out_Color; + +void main(void) +{ + vec4 color = texture(textureSampler, uvCoordinates) * recolor; + + out_Color = color; +} \ No newline at end of file diff --git a/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/v2D.glsl b/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/v2D.glsl new file mode 100755 index 0000000..59fe8d1 --- /dev/null +++ b/engine-demo/basic-application/mods/tefek.plutospritesheet/default/shaders/v2D.glsl @@ -0,0 +1,19 @@ +#version 330 core + +in vec2 position; +in vec2 uvCoords; + +out vec2 uvCoordinates; + +uniform mat4 projection; +uniform mat3x2 transformation; + +uniform vec2 uvBase; +uniform vec2 uvDelta; + +void main(void) +{ + gl_Position = projection * vec4(transformation * vec3(position.x, position.y, 1.0), 0.0, 1.0); + + uvCoordinates = uvBase + uvCoords * uvDelta; +} \ No newline at end of file diff --git a/engine-demo/basic-application/mods/tefek.plutospritesheet/info.json b/engine-demo/basic-application/mods/tefek.plutospritesheet/info.json new file mode 100755 index 0000000..f330c93 --- /dev/null +++ b/engine-demo/basic-application/mods/tefek.plutospritesheet/info.json @@ -0,0 +1,11 @@ +{ + "displayName": "Pluto SpriteSheet", + "description": "A library to manage, store and draw sprites.", + "author": "Tefek", + "resourceRoots": { + "default": { + "path": "default", + "type": "open" + } + } +} diff --git a/engine-demo/basic-application/src/config/java/org/plutoengine/demo/VersionInfo.java b/engine-demo/basic-application/src/config/java/org/plutoengine/demo/VersionInfo.java new file mode 100755 index 0000000..a7cbe35 --- /dev/null +++ b/engine-demo/basic-application/src/config/java/org/plutoengine/demo/VersionInfo.java @@ -0,0 +1,8 @@ +package org.plutoengine.demo; + +public class VersionInfo +{ + public static final String GAME_VERSION = "${gameVersion}"; + + public static final String GAME_BUILD = "${gameBuild}"; +} diff --git a/engine-demo/basic-application/src/main/java/org/plutoengine/demo/BasicApplicationDemoMod.java b/engine-demo/basic-application/src/main/java/org/plutoengine/demo/BasicApplicationDemoMod.java new file mode 100755 index 0000000..96ac7ce --- /dev/null +++ b/engine-demo/basic-application/src/main/java/org/plutoengine/demo/BasicApplicationDemoMod.java @@ -0,0 +1,13 @@ +package org.plutoengine.demo; + +import org.plutoengine.graphics.PlutoGUIMod; +import org.plutoengine.mod.ModEntry; + +@ModEntry( + modID = "tefek.demo.basicapplication", + version = VersionInfo.GAME_VERSION, + dependencies = { PlutoGUIMod.class } +) +public class BasicApplicationDemoMod +{ +} diff --git a/engine-demo/basic-application/src/main/java/org/plutoengine/demo/Main.java b/engine-demo/basic-application/src/main/java/org/plutoengine/demo/Main.java new file mode 100755 index 0000000..9db1941 --- /dev/null +++ b/engine-demo/basic-application/src/main/java/org/plutoengine/demo/Main.java @@ -0,0 +1,56 @@ +package org.plutoengine.demo; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL33; + +import org.plutoengine.PlutoApplication; +import org.plutoengine.display.Display; +import org.plutoengine.display.Framerate; +import org.plutoengine.gui.font.FontHelper; +import org.plutoengine.gui.font.FontRenderer; +import org.plutoengine.math.ProjectionMatrix; +import org.plutoengine.shader.uniform.auto.AutomaticUniforms; + +public class Main extends PlutoApplication +{ + public static Main INSTANCE; + + public static void main(String[] args) throws Exception + { + var config = new PlutoApplication.StartupConfig(); + config.windowName("JSRClone " + VersionInfo.GAME_VERSION); + + INSTANCE = new Main(); + INSTANCE.run(args, config); + } + + @Override + public void loop() + { + GL33.glEnable(GL11.GL_BLEND); + GL33.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + var projection = ProjectionMatrix.createOrtho2D(this.display.getWidth(), this.display.getHeight()); + AutomaticUniforms.VIEWPORT_PROJECTION.fire(projection); + + var buildStr = String.format("Build %s", VersionInfo.GAME_BUILD); + var strWidth = FontHelper.calcStringWidth(buildStr, "default", 0.75f); + FontRenderer.drawString(this.display.getWidth() - strWidth + 1, 3, buildStr, 0, 0, 0, 1, 0.75f, true); + FontRenderer.drawString(this.display.getWidth() - strWidth, 2, buildStr, 0.7f, 0.7f, 0.7f, 1, 0.75f, false); + + var fpsStr = String.format("%d FPS", Framerate.getInterpolatedFPS()); + FontRenderer.drawString(3, 3, fpsStr, 0, 0, 0, 1, 0.75f, true); + FontRenderer.drawString(2, 2, fpsStr, 0.13f, 0.75f, 0.62f, 1, 0.75f, false); + } + + public static Display getDisplay() + { + return INSTANCE.getDisplayInstance(); + } + + @Override + protected Class getMainModule() + { + return BasicApplicationDemoMod.class; + } +} diff --git a/engine-demo/build.gradle.kts b/engine-demo/build.gradle.kts new file mode 100755 index 0000000..a1f6be5 --- /dev/null +++ b/engine-demo/build.gradle.kts @@ -0,0 +1,15 @@ +import org.plutoengine.Versions + +subprojects { + apply(plugin = "java") + apply(plugin = "application") + + repositories { + mavenCentral() + } + + configure { + sourceCompatibility = Versions.javaTargetVersion + targetCompatibility = Versions.javaTargetVersion + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar old mode 100644 new mode 100755 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties old mode 100644 new mode 100755 index ac33e99..a0f7639 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew.bat b/gradlew.bat old mode 100644 new mode 100755 diff --git a/plutoaudio/build.gradle b/plutoaudio/build.gradle deleted file mode 100644 index ba9d446..0000000 --- a/plutoaudio/build.gradle +++ /dev/null @@ -1,10 +0,0 @@ -apply plugin: 'java-library' - -description = "PlutoEngine's sound subsystem." - -dependencies { - api project(":plutostatic") - - api "org.lwjgl:lwjgl-openal" - runtimeOnly "org.lwjgl:lwjgl-openal::$lwjglNatives" -} \ No newline at end of file diff --git a/plutocore/build.gradle b/plutocore/build.gradle deleted file mode 100644 index c27e2a8..0000000 --- a/plutocore/build.gradle +++ /dev/null @@ -1,8 +0,0 @@ -apply plugin: 'java-library' - -description = "The foundation module for games and apps built on top of PlutoEngine." - -dependencies { - api project(":plutogui") - api project(":plutoaudio") -} \ No newline at end of file diff --git a/plutocore/src/main/java/cz/tefek/pluto/engine/input/InputBus.java b/plutocore/src/main/java/cz/tefek/pluto/engine/input/InputBus.java deleted file mode 100644 index 3b4da24..0000000 --- a/plutocore/src/main/java/cz/tefek/pluto/engine/input/InputBus.java +++ /dev/null @@ -1,160 +0,0 @@ -package cz.tefek.pluto.engine.input; - -import org.lwjgl.glfw.GLFW; - -import cz.tefek.pluto.annotation.ThreadSensitive; -import cz.tefek.pluto.engine.display.Display; - -@ThreadSensitive(localContexts = true) -public class InputBus -{ - private static final ThreadLocal INSTANCE = new ThreadLocal<>(); - - private final KeyboardInputCallback keyboard = new KeyboardInputCallback(); - private final MouseButtonCallback mouseButton = new MouseButtonCallback(); - private final CursorPositionCallback cursorPosition = new CursorPositionCallback(); - private final ScrollInputCallback scroll = new ScrollInputCallback(); - private final KeyboardCharInput charInput = new KeyboardCharInput(); - - private InputBus() - { - } - - public static void init(Display display) - { - var instance = new InputBus(); - - GLFW.glfwSetKeyCallback(display.getWindowPointer(), instance.keyboard); - GLFW.glfwSetMouseButtonCallback(display.getWindowPointer(), instance.mouseButton); - GLFW.glfwSetCursorPosCallback(display.getWindowPointer(), instance.cursorPosition); - GLFW.glfwSetScrollCallback(display.getWindowPointer(), instance.scroll); - GLFW.glfwSetCharCallback(display.getWindowPointer(), instance.charInput); - - INSTANCE.set(instance); - } - - public static void destroy() - { - var instance = INSTANCE.get(); - - instance.scroll.free(); - instance.mouseButton.free(); - instance.keyboard.free(); - instance.cursorPosition.free(); - instance.charInput.free(); - } - - public static KeyboardInputCallback keyboard() - { - return INSTANCE.get().keyboard; - } - - public static MouseButtonCallback mouseButtons() - { - return INSTANCE.get().mouseButton; - } - - public static ScrollInputCallback scroll() - { - return INSTANCE.get().scroll; - } - - public static CursorPositionCallback cursorPosition() - { - return INSTANCE.get().cursorPosition; - } - - public static KeyboardCharInput charInput() - { - return INSTANCE.get().charInput; - } - - public static void resetStates() - { - var instance = INSTANCE.get(); - - instance.keyboard.resetPressed(); - instance.mouseButton.reset(); - instance.scroll.reset(); - instance.cursorPosition.reset(); - instance.charInput.reset(); - } - - @ThreadSensitive(localContexts = true) - public static class Mouse - { - public static boolean clicked(int button) - { - return INSTANCE.get().mouseButton.buttonClicked[button]; - } - - public static boolean released(int button) - { - return INSTANCE.get().mouseButton.buttonReleased[button]; - } - - public static boolean isButtonDown(int button) - { - return INSTANCE.get().mouseButton.buttonDown[button]; - } - - public static double getX() - { - return INSTANCE.get().cursorPosition.getX(); - } - - public static double getY() - { - return INSTANCE.get().cursorPosition.getY(); - } - - public static boolean isInside(int x1, int y1, int x2, int y2) - { - return INSTANCE.get().cursorPosition.isInside(x1, y1, x2, y2); - } - - public static double getDX() - { - return INSTANCE.get().cursorPosition.getDeltaX(); - } - - public static double getDY() - { - return INSTANCE.get().cursorPosition.getDeltaY(); - } - - public static double getScrollX() - { - return INSTANCE.get().scroll.getXScroll(); - } - - public static double getScrollY() - { - return INSTANCE.get().scroll.getYScroll(); - } - } - - @ThreadSensitive(localContexts = true) - public static class Keyboard - { - public static boolean pressed(int key) - { - return INSTANCE.get().keyboard.hasBeenPressed(key); - } - - public static boolean released(int key) - { - return INSTANCE.get().keyboard.hasBeenReleased(key); - } - - public static boolean isKeyDown(int key) - { - return INSTANCE.get().keyboard.isKeyDown(key); - } - - public static String getTypedText() - { - return INSTANCE.get().charInput.getTypedText(); - } - } -} diff --git a/plutoframebuffer/build.gradle b/plutoframebuffer/build.gradle deleted file mode 100644 index 09b3592..0000000 --- a/plutoframebuffer/build.gradle +++ /dev/null @@ -1,7 +0,0 @@ -apply plugin: 'java-library' - -description = "" - -dependencies { - api project(":plutotexturing") -} \ No newline at end of file diff --git a/plutogui/build.gradle b/plutogui/build.gradle deleted file mode 100644 index 2c1e7ca..0000000 --- a/plutogui/build.gradle +++ /dev/null @@ -1,7 +0,0 @@ -apply plugin: 'java-library' - -description = "" - -dependencies { - api project(":plutospritesheet") -} \ No newline at end of file diff --git a/plutogui/src/main/java/cz/tefek/pluto/engine/graphics/PlutoGUIMod.java b/plutogui/src/main/java/cz/tefek/pluto/engine/graphics/PlutoGUIMod.java deleted file mode 100644 index c01310f..0000000 --- a/plutogui/src/main/java/cz/tefek/pluto/engine/graphics/PlutoGUIMod.java +++ /dev/null @@ -1,73 +0,0 @@ -package cz.tefek.pluto.engine.graphics; - -import cz.tefek.pluto.Pluto; -import cz.tefek.pluto.engine.graphics.font.FontManager; -import cz.tefek.pluto.engine.graphics.font.FontShader; -import cz.tefek.pluto.engine.graphics.texture.MagFilter; -import cz.tefek.pluto.engine.graphics.texture.MinFilter; -import cz.tefek.pluto.engine.graphics.texture.WrapMode; -import cz.tefek.pluto.engine.graphics.texture.texture2d.RectangleTexture; -import cz.tefek.pluto.engine.gui.font.FontRenderer; -import cz.tefek.pluto.engine.shader.RenderShaderBuilder; -import cz.tefek.pluto.io.asl.resource.ResourceAddress; -import cz.tefek.pluto.io.asl.resource.ResourceSubscriber; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.modloader.Mod; -import cz.tefek.pluto.modloader.ModEntry; -import cz.tefek.pluto.modloader.ModLoaderCore; -import cz.tefek.pluto.modloader.event.ModPreLoad; -import cz.tefek.pluto.modloader.event.ModPreLoadEvent; -import cz.tefek.pluto.modloader.event.ModUnload; -import cz.tefek.pluto.modloader.event.ModUnloadEvent; - -/** - * @author 493msi - * - */ -@ModEntry(modid = PlutoGUIMod.MOD_ID, - displayName = "Pluto Engine GUI Renderer", - author = "Tefek", - dependencies = { PlutoSpriteSheetMod.class }, - version = Pluto.VERSION, - description = "Everything GUI of PlutoEngine.") -public class PlutoGUIMod -{ - public static final String MOD_ID = "plutogui"; - - public static Mod instance; - public static ResourceSubscriber subscriber; - - public static RectangleTexture uiElementsAtlas; - - private static FontShader fontShader; - - @ModPreLoad - public static void preLoad(ModPreLoadEvent event) - { - instance = ModLoaderCore.getModByID(MOD_ID); - subscriber = instance.getDefaultResourceSubscriber(); - - Logger.log("Intializing " + MOD_ID + "..."); - - fontShader = new RenderShaderBuilder(subscriber, "shaders.VertexFontShader#glsl", "shaders.FragmentFontShader#glsl").build(FontShader.class, false); - - // Load the default font - FontManager.loadFont(new ResourceAddress(subscriber, "font.default")); - - FontRenderer.load(fontShader); - - uiElementsAtlas = new RectangleTexture(); - uiElementsAtlas.load(new ResourceAddress(subscriber, "gui.elements#png"), MagFilter.NEAREST, MinFilter.NEAREST, WrapMode.CLAMP_TO_EDGE, WrapMode.CLAMP_TO_EDGE); - } - - @ModUnload - public static void unload(ModUnloadEvent unloadEvent) - { - uiElementsAtlas.delete(); - - FontManager.unloadAll(); - - FontRenderer.unload(); - fontShader.dispose(); - } -} diff --git a/plutogui/src/main/java/cz/tefek/pluto/engine/graphics/font/FontManager.java b/plutogui/src/main/java/cz/tefek/pluto/engine/graphics/font/FontManager.java deleted file mode 100644 index e94315e..0000000 --- a/plutogui/src/main/java/cz/tefek/pluto/engine/graphics/font/FontManager.java +++ /dev/null @@ -1,102 +0,0 @@ -package cz.tefek.pluto.engine.graphics.font; - -import java.util.HashMap; -import java.util.Map; - -import java.io.BufferedReader; -import java.nio.file.Files; - -import cz.tefek.pluto.engine.graphics.texture.MagFilter; -import cz.tefek.pluto.engine.graphics.texture.MinFilter; -import cz.tefek.pluto.engine.graphics.texture.Texture; -import cz.tefek.pluto.engine.graphics.texture.WrapMode; -import cz.tefek.pluto.engine.graphics.texture.texture2d.RectangleTexture; -import cz.tefek.pluto.engine.gui.font.CharacterInfo; -import cz.tefek.pluto.engine.gui.font.Font; -import cz.tefek.pluto.io.asl.resource.ResourceAddress; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; - -public class FontManager -{ - private static Map fonts = new HashMap<>(); - - public static void loadFont(ResourceAddress address) - { - String fontname = null; - int width = 0; - int height = 0; - var def = new HashMap(); - - int row = 0; - - try (BufferedReader br = Files.newBufferedReader(address.copy().branch("definitions").fileExtension("txt").toNIOPath())) - { - String line; - while ((line = br.readLine()) != null) - { - if (line.startsWith("//")) - { - continue; - } - - if (row == 0) - { - String[] fontinfo; - fontinfo = line.split(","); - - fontname = fontinfo[0]; - - String[] dim = fontinfo[1].split("x"); - - width = Integer.parseInt(dim[0]); - height = Integer.parseInt(dim[1]); - } - - if (row > 0) - { - String[] offs = null; - - offs = line.split(" ")[1].split(";"); - - def.put(line.charAt(0), new CharacterInfo(row - 1, Integer.parseInt(offs[0]), Integer.parseInt(offs[1]))); - } - - row++; - } - - br.close(); - } - catch (Exception e) - { - Logger.log(SmartSeverity.ERROR, "Could not load font: " + address.toString()); - e.printStackTrace(); - } - - Font font = new Font(fontname, width, height, def); - RectangleTexture texture = new RectangleTexture(); - texture.load(address.copy().branch("tex").fileExtension("png"), MagFilter.NEAREST, MinFilter.LINEAR, WrapMode.CLAMP_TO_EDGE, WrapMode.CLAMP_TO_EDGE); - font.setTexture(texture); - - fonts.put(fontname, font); - } - - public static void unloadAll() - { - fonts.values().stream().map(Font::getTexture).forEach(Texture::delete); - fonts.clear(); - } - - public static Font getFontByName(String fontname) - { - var font = fonts.get(fontname); - - if (font == null) - { - Logger.log(SmartSeverity.WARNING, "Font with name " + fontname + " could not be found, using the default one instead (if there is one)."); - return fonts.get("default"); - } - - return font; - } -} diff --git a/plutogui/src/main/java/cz/tefek/pluto/engine/graphics/font/FontShader.java b/plutogui/src/main/java/cz/tefek/pluto/engine/graphics/font/FontShader.java deleted file mode 100644 index f4fc5a3..0000000 --- a/plutogui/src/main/java/cz/tefek/pluto/engine/graphics/font/FontShader.java +++ /dev/null @@ -1,41 +0,0 @@ -package cz.tefek.pluto.engine.graphics.font; - -import cz.tefek.pluto.engine.graphics.gl.vao.attrib.ReservedAttributes; -import cz.tefek.pluto.engine.shader.ShaderBase; -import cz.tefek.pluto.engine.shader.ShaderProgram; -import cz.tefek.pluto.engine.shader.VertexArrayAttribute; -import cz.tefek.pluto.engine.shader.uniform.Uniform; -import cz.tefek.pluto.engine.shader.uniform.UniformBoolean; -import cz.tefek.pluto.engine.shader.uniform.UniformMat4; -import cz.tefek.pluto.engine.shader.uniform.UniformVec2; -import cz.tefek.pluto.engine.shader.uniform.UniformVec4; -import cz.tefek.pluto.engine.shader.uniform.auto.AutoViewportProjection; - -@ShaderProgram -public final class FontShader extends ShaderBase -{ - @AutoViewportProjection - @Uniform(name = "projection") - public UniformMat4 projectionMatrix; - - @Uniform(name = "transformation") - public UniformMat4 transformationMatrix; - - @Uniform - public UniformVec2 uvBase; - - @Uniform - public UniformVec2 uvDelta; - - @Uniform - public UniformVec4 recolor; - - @Uniform - public UniformBoolean italic; - - @VertexArrayAttribute(ReservedAttributes.POSITION) - public int position; - - @VertexArrayAttribute(ReservedAttributes.UV) - public int uvCoords; -} diff --git a/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/CharacterInfo.java b/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/CharacterInfo.java deleted file mode 100644 index ddb262f..0000000 --- a/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/CharacterInfo.java +++ /dev/null @@ -1,30 +0,0 @@ -package cz.tefek.pluto.engine.gui.font; - -public class CharacterInfo -{ - int leftOffs; - private int rightOffs; - int number; - - public CharacterInfo(int number, int leftOffs, int rightOffs) - { - this.number = number; - this.leftOffs = leftOffs; - this.rightOffs = rightOffs; - } - - public int getLeftOffset() - { - return this.leftOffs; - } - - public int getNumber() - { - return this.number; - } - - public int getRightOffset() - { - return this.rightOffs; - } -} diff --git a/plutolib/build.gradle b/plutolib/build.gradle deleted file mode 100644 index 6354853..0000000 --- a/plutolib/build.gradle +++ /dev/null @@ -1,12 +0,0 @@ -apply plugin: 'java-library' - -description = "" - -dependencies { - api platform("org.lwjgl:lwjgl-bom:$lwjglVersion") - - api "commons-io:commons-io:2.6" - api "com.google.code.gson:gson:2.8.5" - api "com.google.guava:guava:28.0-jre" - api "org.apache.commons:commons-lang3:3.9" -} \ No newline at end of file diff --git a/plutolib/src/main/java/cz/tefek/pluto/event/EventData.java b/plutolib/src/main/java/cz/tefek/pluto/event/EventData.java deleted file mode 100644 index a37a41c..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/event/EventData.java +++ /dev/null @@ -1,10 +0,0 @@ -package cz.tefek.pluto.event; - -/** - * @author 493msi - * - */ -public class EventData -{ - -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/event/staticmode/StaticPlutoEvent.java b/plutolib/src/main/java/cz/tefek/pluto/event/staticmode/StaticPlutoEvent.java deleted file mode 100644 index f66ec28..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/event/staticmode/StaticPlutoEvent.java +++ /dev/null @@ -1,25 +0,0 @@ -package cz.tefek.pluto.event.staticmode; - -import static java.lang.annotation.ElementType.ANNOTATION_TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import cz.tefek.pluto.event.EventData; - -/** - * @author 493msi - * - */ -@Retention(RUNTIME) -@Target(ANNOTATION_TYPE) -public @interface StaticPlutoEvent -{ - /** - * This actually does nothing. ¯\_(ツ)_/¯ Well, you can use it for improved - * code readability. - * - */ - Class passingParamClass() default EventData.class; -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/event/staticmode/StaticPlutoEventManager.java b/plutolib/src/main/java/cz/tefek/pluto/event/staticmode/StaticPlutoEventManager.java deleted file mode 100644 index a26ab15..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/event/staticmode/StaticPlutoEventManager.java +++ /dev/null @@ -1,223 +0,0 @@ -package cz.tefek.pluto.event.staticmode; - -import java.util.*; -import java.util.Map.Entry; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; - -import cz.tefek.pluto.event.EventData; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; - -/** - * A universal event manager. Register an event {@link Annotation} of your - * choice (must be annotated with {@link StaticPlutoEvent @Event}), then - * annotate some public static method and you are done! Now you can trigger the - * callbacks with ease. Multiple per-method events are possible! Note that - * event callbacks require a data-passing parameter - * (extends {@link EventData}). The method will not be invoked - * otherwise! - * - * @author 493msi - * - */ -public class StaticPlutoEventManager -{ - private static Map, List> eventRegistry = new HashMap<>(); - private static List orphans = new ArrayList<>(); - - public static void registerEventHandler(Class clazz) - { - Method[] methods = clazz.getMethods(); - - int methodsFound = 0; - - for (Method method : methods) - { - // The callback method must be static and public! - if (!(Modifier.isStatic(method.getModifiers()) && Modifier.isPublic(method.getModifiers()))) - { - continue; - } - - for (Annotation annotation : method.getDeclaredAnnotations()) - { - if (annotation.annotationType().getAnnotation(StaticPlutoEvent.class) != null) - { - methodsFound++; - - var parentAnnotation = eventRegistry.get(annotation.annotationType()); - - // Find a parent annotation. - if (parentAnnotation != null) - { - parentAnnotation.add(method); - } - // No parent annotation and fix for methods with 2+ event - // annotations. - else if (!orphans.contains(method)) - { - orphans.add(method); - } - } - } - } - - Logger.log(SmartSeverity.EVENT_PLUS, "Event handler " + clazz.getCanonicalName() + " scan found " + methodsFound + " method callback(s)."); - } - - public static void registerEvent(Class annotation) - { - // @Event is necessary. - if (annotation.getAnnotation(StaticPlutoEvent.class) != null) - { - if (eventRegistry.containsKey(annotation)) - { - Logger.log(SmartSeverity.EVENT_ERROR, "Annotation " + annotation.getCanonicalName() + " is already registered!"); - return; - } - - eventRegistry.put(annotation, new ArrayList<>()); - - Logger.log(SmartSeverity.EVENT_PLUS, "Event " + annotation.getCanonicalName() + " successfully registered!"); - - short retroactivelyFound = 0; - - // Let's check all existing event Methods for this event. - for (Entry, List> entry : eventRegistry.entrySet()) - { - // Checking the Method list for this event would make no - // sense. - if (annotation.equals(entry.getKey())) - { - continue; - } - - for (Method method : entry.getValue()) - { - // Just in case. - if (method.isAnnotationPresent(annotation)) - { - eventRegistry.get(annotation).add(method); - retroactivelyFound++; - } - } - } - - Logger.log(SmartSeverity.EVENT_PLUS, "Retroactive method checking found " + retroactivelyFound + " item(s)."); - - // Let's check the Method orphanage for some potential - // candidates. - - short orphansFound = 0; - - int orphansBefore = orphans.size(); - - List foundParents = new ArrayList<>(); - - for (Method method : orphans) - { - if (method.isAnnotationPresent(annotation)) - { - foundParents.add(method); - - // No duplicates. - if (!eventRegistry.get(annotation).contains(method)) - { - eventRegistry.get(annotation).add(method); - orphansFound++; - } - } - } - - orphans.removeAll(foundParents); - - Logger.log(SmartSeverity.EVENT_PLUS, orphansFound + " orphan method(s) was/were bound and " + (orphansBefore - orphans.size()) + " removed from the storage!"); - } - else - { - Logger.log(SmartSeverity.EVENT_ERROR, "Annotation " + annotation.getCanonicalName() + " is not annotated with @Event, can't register it."); - } - } - - public static void fireEvent(Class event, EventData data) - { - if (event.getAnnotation(StaticPlutoEvent.class) != null) - { - List methodList = eventRegistry.get(event); - - if (methodList != null) - { - for (Method m : methodList) - { - // If a method contains more than one parameter, the most - // viable one will be chosen. Also, methods with no - // parameters are not valid. - - Class[] params = m.getParameterTypes(); - - Class mostSuitableParam = null; - EventData[] paramOut = new EventData[params.length]; - - if (params.length == 0) - { - Logger.log(SmartSeverity.EVENT_WARNING, "Method " + m.toGenericString() + " has no parameters, will not be invoked by event!"); - } - - for (int i = 0; i < params.length; i++) { - Class parameter = params[i]; - - if (!EventData.class.isAssignableFrom(parameter)) { - Logger.log(SmartSeverity.EVENT_ERROR, "Method " + m.toGenericString() + " contains invalid parameters. Only EventData instances are permitted."); - mostSuitableParam = null; - break; - } - - if (parameter.isInstance(data)) - { - if (mostSuitableParam == null) - { - mostSuitableParam = parameter; - paramOut[i] = data; - } - else if (mostSuitableParam.isAssignableFrom(parameter)) - { - mostSuitableParam = parameter; - Arrays.fill(paramOut, 0, i, null); - paramOut[i] = data; - } - } - } - - if (mostSuitableParam != null) - { - try - { - m.invoke(null, (Object[]) paramOut); - } - catch (Exception e) - { - Logger.log(e); - } - } - } - } - else - { - Logger.log(SmartSeverity.EVENT_ERROR, "There is no event like " + event.getCanonicalName() + " registered."); - } - } - else - { - Logger.log(SmartSeverity.EVENT_ERROR, event.getCanonicalName() + " is not an event!"); - } - } - - public static void unregisterAll() - { - eventRegistry.clear(); - orphans.clear(); - } -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/Resource.java b/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/Resource.java deleted file mode 100644 index a6fca89..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/Resource.java +++ /dev/null @@ -1,33 +0,0 @@ -package cz.tefek.pluto.io.asl.resource; - -/** - * Allows loading a resource from the file system location defined by the - * supplied {@link ResourceAddress}. - * - * For example Resource<String> means you have a - * Resource that will output a String when loaded. - * - * @author 493msi - * - * @param The type of the loaded Resource. - */ -public abstract class Resource -{ - protected ResourceAddress address; - protected boolean virtual; - - protected R value; - - public Resource(ResourceAddress raddress, boolean virtual) - { - this.address = raddress; - this.virtual = virtual; - } - - public R load() - { - return this.virtual ? this.value : this.loadFromFile(); - } - - protected abstract R loadFromFile(); -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/ResourceAddress.java b/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/ResourceAddress.java deleted file mode 100644 index 0a62edb..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/ResourceAddress.java +++ /dev/null @@ -1,304 +0,0 @@ -package cz.tefek.pluto.io.asl.resource; - -import javax.annotation.Nullable; -import java.nio.file.FileSystems; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Pattern; - -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; -import cz.tefek.pluto.modloader.ModLoaderCore; - -/** - * Resource address is a universal key for all resource and file loading. You - * just need a {@link ResourceSubscriber} (which holds the root folder location) - * and a {@link String} containing the address. The address itself works like a - * Java package. For example "sample.textures.test" formats as - * [root_folder]/sample/textures/test when converted using - * toPath(). To define a file extension for your address, use the - * fileExtension(String) method. To remove the file extension use - * fileExtension(null). - * - * @author 493msi - * - * @see ResourceSubscriber - */ -public class ResourceAddress -{ - public static final int LIMIT = 32; - public static final int MAX_BRANCH_STRING_LENGTH = 32; - - public static final String BRANCH_STRING_PATTERN = "[a-zA-Z0-9_]+"; - public static final String FILE_EXTENSION_PATTERN = "[a-zA-Z0-9_]+"; - - public static final String RESOURCE_ADDRESS_STRING_PATTERN = "^(" + ModLoaderCore.MOD_ID_STRING_PATTERN + ")\\$((?:" + BRANCH_STRING_PATTERN + "\\.)*?" + BRANCH_STRING_PATTERN + "?)(?:#([a-zA-Z0-9_]+))?$"; - public static final Pattern RESOURCE_ADDRESS_PATTERN = Pattern.compile(RESOURCE_ADDRESS_STRING_PATTERN, Pattern.CASE_INSENSITIVE); - - protected final List subAddress = new ArrayList<>(LIMIT); - protected ResourceSubscriber resSubscriber; - - protected String fileExtension = null; - - /** - * Copy constructor for internal use. - */ - protected ResourceAddress(ResourceSubscriber subscriber, List subAddr, String fileExtension) - { - this.resSubscriber = subscriber; - - this.subAddress.addAll(subAddr); - - this.fileExtension(fileExtension); - } - - public ResourceAddress(ResourceSubscriber sub, String address) - { - if (sub == null) - { - throw new IllegalArgumentException("Empty resource subscriber is not allowed."); - } - - this.resSubscriber = sub; - - var parts = address.split("#"); - - if (parts.length == 2) - { - address = parts[0]; - this.fileExtension(parts[1]); - } - else if (parts.length > 2) - { - throw new IllegalArgumentException("Illegal ResourceAddress component count!"); - } - - if (address == null) - { - throw new IllegalArgumentException("Empty address is not allowed."); - } - - String[] as = address.split("\\."); - - if (as.length == 0) - { - throw new IllegalArgumentException("Zero length address is not allowed."); - } - - if (as.length >= LIMIT) - { - throw new IllegalArgumentException("Address can't branch deeper more than " + (LIMIT - 1) + " times."); - } - - if (as.length == 1) - { - Logger.log(SmartSeverity.WARNING, "Please do not use tier 1 addresses, so it doesn't conflict with core assets."); - } - - for (String branch : as) - { - if (branch.length() < 1 || branch.length() > MAX_BRANCH_STRING_LENGTH) - { - throw new IllegalArgumentException("Length of branch must be higher than 0 and lower than 33, this is not an essay."); - } - - if (!branch.matches("^[a-zA-Z0-9_]+$")) - { - throw new IllegalArgumentException("Wrong address branch format: " + branch); - } - - this.subAddress.add(branch); - } - } - - public static ResourceAddress parse(String strVal) - { - if (strVal == null) - { - return null; - } - - if (!strVal.matches(RESOURCE_ADDRESS_STRING_PATTERN)) - { - throw new IllegalArgumentException("Malformed resource address: " + strVal); - } - - var addressComponents = strVal.split("[$#]"); - - if (addressComponents.length < 2 || addressComponents.length > 3) - { - throw new IllegalArgumentException("Illegal ResourceAddress component count!"); - } - - var mod = ModLoaderCore.getModByID(addressComponents[0]); - - if (mod == null) - { - throw new IllegalArgumentException(String.format("Mod with ID %s not found!", addressComponents[0])); - } - - var raddress = new ResourceAddress(mod.getDefaultResourceSubscriber(), addressComponents[1]); - - if (addressComponents.length == 3) - { - raddress.fileExtension(addressComponents[2]); - } - - return raddress; - } - - public ResourceAddress fileExtension(@Nullable String ext) - { - if (ext == null || "".equals(ext)) - { - this.fileExtension = null; - return this; - } - - if (!ext.matches(FILE_EXTENSION_PATTERN)) - { - throw new IllegalArgumentException("@ResourceAddress.fileExtension: Wrong file extension format. Only english alphabet, numbers and underscore are permitted."); - } - - this.fileExtension = ext; - return this; - } - - public boolean hasFileExtension() - { - if (this.fileExtension == null) - { - return false; - } - - return !this.fileExtension.isBlank(); - } - - public String getFileExtension() - { - return this.fileExtension; - } - - public ResourceAddress branch(String branch) - { - if (branch == null) - { - throw new NullPointerException("@ResourceAddress.branch: INPUT = NULL!"); - } - - if (branch.length() < 1 || branch.length() > MAX_BRANCH_STRING_LENGTH) - { - throw new IllegalArgumentException("@ResourceAddress.branch: Length of branch must be higher than 0 and lower than " + (MAX_BRANCH_STRING_LENGTH + 1) + "."); - } - - if (!branch.matches(BRANCH_STRING_PATTERN)) - { - throw new IllegalArgumentException("@ResourceAddress.branch: Wrong branch format. Only english alphabet, numbers and underscore are permitted."); - } - - if (this.subAddress.size() >= LIMIT) - { - throw new IllegalArgumentException("@ResourceAddress.branch: Address can't branch deeper more than " + (LIMIT - 1) + " times."); - } - - this.subAddress.add(branch); - - return this; - } - - @Override - public String toString() - { - return this.resSubscriber.getMod().getModID() + "$" + this.subAddressToString(); - } - - public ResourceAddress copy() - { - return new ResourceAddress(this.resSubscriber, this.subAddress, this.fileExtension); - } - - public String toPath() - { - StringBuilder sbPath = new StringBuilder(this.resSubscriber.getRootPath()); - - for (String branch : this.subAddress) - { - if (branch == null) - { - break; - } - - sbPath.append("/"); - sbPath.append(branch); - } - - if (this.hasFileExtension()) - { - sbPath.append(".").append(this.fileExtension); - } - - return sbPath.toString(); - } - - public String subAddressToString() - { - StringBuilder sbPath = new StringBuilder(); - - boolean firstLoop = true; - - for (String branch : this.subAddress) - { - if (branch == null) - { - break; - } - - if (!firstLoop) - { - sbPath.append("."); - } - else - { - firstLoop = false; - } - - sbPath.append(branch); - } - - if (this.hasFileExtension()) - { - sbPath.append("#"); - sbPath.append(this.fileExtension); - } - - return sbPath.toString(); - } - - public Path toNIOPath() - { - var pathBuilder = new StringBuilder(this.resSubscriber.getRootPath()); - final var separator = FileSystems.getDefault().getSeparator(); - pathBuilder.append(separator); - pathBuilder.append(String.join(separator, this.subAddress)); - - if (this.hasFileExtension()) - { - pathBuilder.append('.'); - pathBuilder.append(this.fileExtension); - } - - return Path.of(pathBuilder.toString()); - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + ((this.fileExtension == null) ? 0 : this.fileExtension.hashCode()); - result = prime * result + this.resSubscriber.hashCode(); - result = prime * result + this.subAddress.hashCode(); - return result; - } -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/ResourceAddressTypeAdapter.java b/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/ResourceAddressTypeAdapter.java deleted file mode 100644 index 2618ee1..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/ResourceAddressTypeAdapter.java +++ /dev/null @@ -1,24 +0,0 @@ -package cz.tefek.pluto.io.asl.resource; - -import java.io.IOException; - -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; - -public class ResourceAddressTypeAdapter extends TypeAdapter -{ - @Override - public void write(JsonWriter out, ResourceAddress value) throws IOException - { - out.value(String.valueOf(value)); - } - - @Override - public ResourceAddress read(JsonReader in) throws IOException - { - var strVal = in.nextString(); - return "null".equalsIgnoreCase(strVal) ? null : ResourceAddress.parse(strVal); - } - -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/ResourceHelper.java b/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/ResourceHelper.java deleted file mode 100644 index f7e9b07..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/ResourceHelper.java +++ /dev/null @@ -1,30 +0,0 @@ -package cz.tefek.pluto.io.asl.resource; - -import java.io.IOException; -import java.nio.file.Files; - -/** - * Helper functions for {@link ResourceAddress}es. - * - * @author 493msi - */ -public class ResourceHelper -{ - public static final String GLOBAL_ROOT = ""; - public static final String DEFAULT_RESOURCE_ROOT = GLOBAL_ROOT + "data"; - - /** - * Retrieves the size of the file denoted by {@link ResourceAddress} - * - * @param addr The input {@link ResourceAddress} - * - * @throws IOException On I/O errors. - * - * @return the file size - * @since 0.3 - */ - public static long fileSize(ResourceAddress addr) throws IOException - { - return Files.size(addr.toNIOPath()); - } -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/ResourceSubscriber.java b/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/ResourceSubscriber.java deleted file mode 100644 index 3fa1cbd..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/ResourceSubscriber.java +++ /dev/null @@ -1,47 +0,0 @@ -package cz.tefek.pluto.io.asl.resource; - -import cz.tefek.pluto.modloader.Mod; - -/** - * Allows access to resources using {@link ResourceAddress}. Requires a - * {@link Mod} instance to operate. - * - * @author 493msi - */ -public class ResourceSubscriber -{ - private Mod owner; - private String rootFolder; - - public ResourceSubscriber(Mod mod) - { - this(mod, ResourceHelper.DEFAULT_RESOURCE_ROOT); - } - - public ResourceSubscriber(Mod mod, String root) - { - if (mod == null) - { - throw new IllegalArgumentException("Mod can't be null!"); - } - - this.owner = mod; - this.rootFolder = root; - } - - public Mod getMod() - { - return this.owner; - } - - public String getRootPath() - { - return this.rootFolder; - } - - @Override - public int hashCode() - { - return this.owner.getModID().hashCode(); - } -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/raid/IIdentifiable.java b/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/raid/IIdentifiable.java deleted file mode 100644 index 5b31474..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/raid/IIdentifiable.java +++ /dev/null @@ -1,17 +0,0 @@ -package cz.tefek.pluto.io.asl.resource.raid; - -import cz.tefek.pluto.io.asl.resource.ResourceAddress; - -public interface IIdentifiable -{ - ResourceAddress getID(); - - default String getStringID() - { - return this.getID().toString(); - } - - int getRAID(); - - void onRegister(int raid); -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/raid/RAID.java b/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/raid/RAID.java deleted file mode 100644 index 74a871d..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/raid/RAID.java +++ /dev/null @@ -1,61 +0,0 @@ -package cz.tefek.pluto.io.asl.resource.raid; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -/** - * Runtime Assigned ID (or Resource Address ID) - */ -public class RAID implements Iterable -{ - private static final int INITIAL_SIZE = 512; - - private List raid; - - private Map reverseRaid; - - public RAID() - { - this.raid = new ArrayList<>(INITIAL_SIZE); - this.reverseRaid = new HashMap<>(); - } - - public void register(E item) - { - var address = item.getStringID(); - - if (this.reverseRaid.containsKey(address)) - { - throw new IllegalArgumentException("Cannot register two items with the same resource ID!"); - } - - var pos = this.raid.size(); - this.raid.add(item); - this.reverseRaid.put(address, pos); - item.onRegister(pos); - } - - public E getByIntID(int id) - { - if (id < 0 || id >= this.raid.size()) - { - return null; - } - - return this.raid.get(id); - } - - public int getIDOf(E item) - { - return this.reverseRaid.get(item.getStringID()); - } - - @Override - public Iterator iterator() - { - return this.raid.iterator(); - } -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/type/ResourceImage.java b/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/type/ResourceImage.java deleted file mode 100644 index 11e0c5c..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/type/ResourceImage.java +++ /dev/null @@ -1,58 +0,0 @@ -package cz.tefek.pluto.io.asl.resource.type; - -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; - -import cz.tefek.pluto.io.asl.resource.Resource; -import cz.tefek.pluto.io.asl.resource.ResourceAddress; -import cz.tefek.pluto.io.asl.resource.ResourceHelper; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; - -/** - * {@link ResourceAddress} in, {@link BufferedImage} out. - * - * @author 493msi - */ -public class ResourceImage extends Resource -{ - public ResourceImage(ResourceAddress raddress, boolean virtual) - { - super(raddress, virtual); - } - - public ResourceImage(ResourceAddress raddress) - { - super(raddress, false); - } - - @Override - public BufferedImage loadFromFile() - { - try - { - return ImageIO.read(new File(this.address.toPath())); - } - catch (IOException e) - { - Logger.log(SmartSeverity.ERROR, "Could not load BufferedImage: " + this.address.toString() + ", will load placeholder."); - Logger.log(e); - - try - { - return ImageIO.read(new File(ResourceHelper.GLOBAL_ROOT + "data/assets/err/missingTex.png")); - } - catch (IOException e1) - { - Logger.log(SmartSeverity.ERROR, "Placeholder BufferedImage not found: " + ResourceHelper.GLOBAL_ROOT + "data/assets/err/missingTex.png"); - Logger.log("This is not good! :C"); - - Logger.log(e1); - } - - return null; - } - } -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/type/ResourceInputStream.java b/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/type/ResourceInputStream.java deleted file mode 100644 index eeed8dc..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/type/ResourceInputStream.java +++ /dev/null @@ -1,40 +0,0 @@ -package cz.tefek.pluto.io.asl.resource.type; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; - -import cz.tefek.pluto.io.asl.resource.Resource; -import cz.tefek.pluto.io.asl.resource.ResourceAddress; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; - -/** - * {@link ResourceAddress} in, {@link InputStream} out. - * - * @author 493msi - */ -public class ResourceInputStream extends Resource -{ - public ResourceInputStream(ResourceAddress raddress) - { - super(raddress, false); - } - - @Override - protected InputStream loadFromFile() - { - try - { - return Files.newInputStream(this.address.toNIOPath()); - } - catch (IOException e) - { - Logger.log(SmartSeverity.ERROR, "Failed to open " + this.address + "!"); - Logger.log(e); - } - - return null; - } - -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/asl/textio/TextIn.java b/plutolib/src/main/java/cz/tefek/pluto/io/asl/textio/TextIn.java deleted file mode 100644 index 57f1a2c..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/io/asl/textio/TextIn.java +++ /dev/null @@ -1,78 +0,0 @@ -package cz.tefek.pluto.io.asl.textio; - -import java.io.File; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - -import cz.tefek.pluto.io.asl.resource.ResourceAddress; -import cz.tefek.pluto.io.logger.Logger; - -/** - * A simple text file reader. Apart from generic methods of loading, you can use - * a {@link ResourceAddress}. For writing use {@link TextOut}. - * - * @author 493msi - */ -public class TextIn -{ - public static String load(URL url) - { - try - { - load(url.toURI()); - } - catch (URISyntaxException e) - { - Logger.log(e); - } - - return null; - } - - public static String load(URI uri) - { - try - { - return Files.readString(Paths.get(uri)); - } - catch (Exception e) - { - Logger.log(e); - } - - return null; - } - - public static String load(Path path) - { - try - { - return Files.readString(path); - } - catch (Exception e) - { - Logger.log(e); - } - - return null; - } - - public static String loadInternal(String filename) - { - return load(TextIn.class.getResource("/" + filename)); - } - - public static String loadExternal(String filename) - { - return load(new File(filename).toURI()); - } - - public static String fromAddress(ResourceAddress address) - { - return load(address.toNIOPath()); - } -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/asl/textio/TextOut.java b/plutolib/src/main/java/cz/tefek/pluto/io/asl/textio/TextOut.java deleted file mode 100644 index 8f47577..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/io/asl/textio/TextOut.java +++ /dev/null @@ -1,28 +0,0 @@ -package cz.tefek.pluto.io.asl.textio; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintStream; - -/** - * Simplifies text writer creation. For reading use {@link TextIn}. - * - * @author 493msi - */ - -public class TextOut -{ - public static PrintStream createPrintStream(String filePath) throws IOException - { - PrintStream printstream = new PrintStream(createFOStream(filePath)); - - return printstream; - } - - public static FileOutputStream createFOStream(String filePath) throws IOException - { - FileOutputStream fos = new FileOutputStream(filePath); - - return fos; - } -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/logger/Severity.java b/plutolib/src/main/java/cz/tefek/pluto/io/logger/Severity.java deleted file mode 100644 index 9fd47ba..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/io/logger/Severity.java +++ /dev/null @@ -1,39 +0,0 @@ -package cz.tefek.pluto.io.logger; - -/** - * Message severity. - * - * @author 493msi - * - * @deprecated Use {@link SmartSeverity} instead. - */ -@Deprecated -public enum Severity implements ISeverity -{ - INFO("[INFO] ", false), - WARNING("[WARNING] ", true), - ERROR("[ERROR] ", true), - EXCEPTION("[EXCEPTION] ", true), - NONE("", false); - - private String displayName; - private boolean usesStdErr; - - Severity(String name, boolean usesStdErr) - { - this.displayName = name; - this.usesStdErr = usesStdErr; - } - - @Override - public String getDisplayName() - { - return this.displayName; - } - - @Override - public boolean isStdErr() - { - return this.usesStdErr; - } -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/pluto/pp/InvalidPlutoPackageException.java b/plutolib/src/main/java/cz/tefek/pluto/io/pluto/pp/InvalidPlutoPackageException.java deleted file mode 100644 index bd4d791..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/io/pluto/pp/InvalidPlutoPackageException.java +++ /dev/null @@ -1,16 +0,0 @@ -package cz.tefek.pluto.io.pluto.pp; - -/** - * Thrown when a provided package definition is for some reason not valid. - * - * @author 493msi - */ -public class InvalidPlutoPackageException extends Exception -{ - private static final long serialVersionUID = 7853852981742059946L; - - public InvalidPlutoPackageException(String message, Throwable e) - { - super(message, e); - } -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/pluto/pp/PlutoPackage.java b/plutolib/src/main/java/cz/tefek/pluto/io/pluto/pp/PlutoPackage.java deleted file mode 100644 index 89f7e3f..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/io/pluto/pp/PlutoPackage.java +++ /dev/null @@ -1,95 +0,0 @@ -package cz.tefek.pluto.io.pluto.pp; - -public class PlutoPackage -{ - public final String id; - - protected String name; - protected String version; - protected int build; - protected String description; - protected String iconURL; - protected String author; - protected Class[] dependencies; - protected int earliestCompatibleBuild; - - public PlutoPackage(String id) - { - this.id = id; - } - - public String getName() - { - return this.name; - } - - void setName(String name) - { - this.name = name; - } - - public String getVersion() - { - return this.version; - } - - void setVersion(String version) - { - this.version = version; - } - - public int getBuild() - { - return this.build; - } - - void setBuild(int build) - { - this.build = build; - } - - public String getDescription() - { - return this.description; - } - - void setDescription(String description) - { - this.description = description; - } - - public String getIconURL() - { - return this.iconURL; - } - - void setIconURL(String iconURL) - { - this.iconURL = iconURL; - } - - public String getAuthor() - { - return this.author; - } - - void setAuthor(String author) - { - this.author = author; - } - - public Class[] getDependencies() - { - return this.dependencies; - } - - public boolean isBackwardsCompatibleWithBuild(int build) - { - return build > this.earliestCompatibleBuild; - } - - public boolean isMod() - { - return false; - } -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/tpl/TPL.java b/plutolib/src/main/java/cz/tefek/pluto/io/tpl/TPL.java deleted file mode 100644 index 0c8ec69..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/io/tpl/TPL.java +++ /dev/null @@ -1,312 +0,0 @@ -package cz.tefek.pluto.io.tpl; - -import java.awt.Graphics2D; -import java.awt.image.*; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.file.Files; -import java.nio.file.Path; - -import javax.annotation.Nullable; -import javax.imageio.ImageIO; - -import cz.tefek.pluto.io.asl.resource.ResourceAddress; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; - -/** - * Quick ABGR (8-bit per channel, 32 bits per pixel) image loader for OpenGL textures. - * Color component swizzling may be needed. - * - * FIXME: Refactor {@link TPL#loadBufferedImage} - * - * @author 493msi - * - * @see TPNImage - * - * @since pre-alpha - */ -public class TPL -{ - private static final int PLACEHOLDER_SIZE = 16; - - private static final int PLACEHOLDER_CHECKEDBOARD = 8; - private static final int PLACEHOLDER_CHECKEDBOARD_SQUARE_SIZE = PLACEHOLDER_SIZE / PLACEHOLDER_CHECKEDBOARD; - - private static final BufferedImage placeholder; - - static - { - placeholder = new BufferedImage(PLACEHOLDER_SIZE, PLACEHOLDER_SIZE, BufferedImage.TYPE_INT_ARGB); - var data = placeholder.getData(); - var dataBuffer = (DataBufferInt) data.getDataBuffer(); - - for (int i = 0; i < PLACEHOLDER_SIZE * PLACEHOLDER_SIZE; i++) - { - int x = i % PLACEHOLDER_SIZE; - int y = i / PLACEHOLDER_SIZE; - boolean checker = x / PLACEHOLDER_CHECKEDBOARD_SQUARE_SIZE % 2 == y / PLACEHOLDER_CHECKEDBOARD_SQUARE_SIZE % 2; - - dataBuffer.setElem(i, checker ? 0xFFFF0000 : 0xFF000000); - } - } - - // TODO: Fix this mess - private static BufferedImage loadBufferedImage(@Nullable Object source) - { - var inputStream = (InputStream) null; - - try - { - if (source instanceof ResourceAddress) - { - inputStream = Files.newInputStream(((ResourceAddress) source).toNIOPath()); - } - else if (source instanceof String) - { - inputStream = new FileInputStream((String) source); - } - else if (source instanceof File) - { - inputStream = new FileInputStream((File) source); - } - else if (source instanceof Path) - { - inputStream = Files.newInputStream((Path) source); - } - - if (inputStream != null) - { - return ImageIO.read(inputStream); - } - } - catch (Exception e) - { - Logger.log(SmartSeverity.ERROR, "[TPL] Image could not be loaded: " + source); - Logger.log(e); - } - finally - { - try - { - if (inputStream != null) - inputStream.close(); - } - catch (IOException e) - { - Logger.log(SmartSeverity.ERROR, "[TPL] Failed to close: " + source); - Logger.log(e); - } - } - - return placeholder; - } - - /** - * Loads an image from a file the denoted by the input {@link ResourceAddress} into a {@link TPNImage} buffer. - * - * If the input {@link ResourceAddress} is null, a placeholder will be generated. - * - * @param address The source {@link ResourceAddress}, from which the image will be loaded - * - * @return The output {@link TPNImage}, never null - * - * @see TPNImage - * - * @since pre-alpha - * */ - public static TPNImage load(@Nullable ResourceAddress address) - { - return loadImage(loadBufferedImage(address)); - } - - /** - * Loads an image from the denoted filename to a {@link TPNImage} buffer. - * - * If the input filename is null, a placeholder will be generated. - * - * @deprecated Use the {@link TPL#load(ResourceAddress)} or {@link TPL#load(File)} variants. - * - * @param filename The source filename, from which the image will be loaded - * - * @return The output {@link TPNImage}, never null - * - * @see TPNImage - * - * @since pre-alpha - * */ - @Deprecated - public static TPNImage load(@Nullable String filename) - { - return loadImage(loadBufferedImage(filename)); - } - - /** - * Loads an image from the input {@link File} into a {@link TPNImage} buffer. - * - * If the input {@link File} is null, a placeholder will be generated. - * - * @param file The source {@link File}, from which the image will be loaded - * - * @return The output {@link TPNImage}, never null - * - * @see TPNImage - * - * @since 20.2.0.0-alpha.1 - * */ - public static TPNImage load(@Nullable File file) - { - return loadImage(loadBufferedImage(file)); - } - - /** - * Loads an image from a file the denoted by the input {@link Path} into a {@link TPNImage} buffer. - * - * If the input {@link Path} is null, a placeholder will be generated. - * - * @param path The source {@link Path}, from which the image will be loaded - * - * @return The output {@link TPNImage}, never null - * - * @see TPNImage - * - * @since 20.2.0.0-alpha.1 - * */ - public static TPNImage load(@Nullable Path path) - { - return loadImage(loadBufferedImage(path)); - } - - /** - * Loads an image from a file the denoted by the input {@link ResourceAddress} into a {@link TPNImage} buffer. - * - * If the input {@link ResourceAddress} is null, a placeholder will be generated. - * - * @param address The source {@link ResourceAddress}, from which the image will be loaded - * @param flipY Whether the image should flipped vertically (for OpenGL uses) - * - * @return The output {@link TPNImage}, never null - * - * @see TPNImage - * - * @since 20.2.0.0-alpha.1 - * */ - public static TPNImage loadSpecial(@Nullable ResourceAddress address, boolean flipY) - { - return loadImageSpecial(loadBufferedImage(address), flipY); - } - - - /** - * Loads an image from the input {@link File} into a {@link TPNImage} buffer. - * - * If the input {@link File} is null, a placeholder will be generated. - * - * @param file The source {@link File}, from which the image will be loaded - * @param flipY Whether the image should flipped vertically (for OpenGL uses) - * - * @return The output {@link TPNImage}, never null - * - * @see TPNImage - * - * @since 20.2.0.0-alpha.1 - * */ - public static TPNImage load(@Nullable File file, boolean flipY) - { - return loadImageSpecial(loadBufferedImage(file), flipY); - } - - /** - * Loads an image from a file the denoted by the input {@link Path} into a {@link TPNImage} buffer. - * - * If the input {@link Path} is null, a placeholder will be generated. - * - * @param path The source {@link Path}, from which the image will be loaded - * @param flipY Whether the image should flipped vertically (for OpenGL uses) - * - * @return The output {@link TPNImage}, never null - * - * @see TPNImage - * - * @since 20.2.0.0-alpha.1 - * */ - public static TPNImage loadSpecial(@Nullable Path path, boolean flipY) - { - return loadImageSpecial(loadBufferedImage(path), flipY); - } - - /** - * Writes a {@link BufferedImage} into a {@link TPNImage} buffer. - * - * If the input {@link Path} is null, a placeholder will be generated. - * - * @param image The source {@link BufferedImage} - * @param flipY Whether the image should flipped vertically (for OpenGL uses) - * - * @return The output {@link TPNImage}, never null - * - * @see TPNImage - * - * @since 20.2.0.0-alpha.1 - * */ - public static TPNImage loadImageSpecial(@Nullable BufferedImage image, boolean flipY) - { - if (image == null) - { - Logger.log(SmartSeverity.WARNING, "[TPL] Null BufferedImage supplied, generating a placeholder."); - - return loadImageSpecial(placeholder, flipY); - } - - int width = image.getWidth(); - int height = image.getHeight(); - - if (width > 16384 || height > 16384 || width < 1 || height < 1) - { - Logger.log(SmartSeverity.ERROR, "[TPL] BufferedImage size is invalid (< 1 or > 16384), generating a placeholder."); - - return loadImageSpecial(placeholder, flipY); - } - - BufferedImage copy = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_4BYTE_ABGR); - Graphics2D imgGraphics = copy.createGraphics(); - imgGraphics.drawImage(image, - 0, flipY ? copy.getHeight() : 0, copy.getWidth(), flipY ? 0 : copy.getHeight(), - 0, 0, image.getWidth(), image.getHeight(), - null); // I wonder if this is pixel-perfect - imgGraphics.dispose(); - - Raster data = copy.getRaster(); - DataBuffer dataBuffer = data.getDataBuffer(); - DataBufferByte byteBuffer = (DataBufferByte) dataBuffer; - byte[] byteData = byteBuffer.getData(); - ByteBuffer buffer = ByteBuffer.allocateDirect(width * height * 4).order(ByteOrder.nativeOrder()); - buffer.put(byteData); - buffer.flip(); - - return new TPNImage(buffer, width, height); - } - - - /** - * Writes a {@link BufferedImage} into a {@link TPNImage} buffer. - * - * If the input {@link Path} is null, a placeholder will be generated. - * - * @param image The source {@link BufferedImage} - * - * @return The output {@link TPNImage}, never null - * - * @see TPNImage - * - * @since pre-alpha - * */ - public static TPNImage loadImage(@Nullable BufferedImage image) - { - return loadImageSpecial(image, true); - } -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/Mod.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/Mod.java deleted file mode 100644 index 6686989..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/Mod.java +++ /dev/null @@ -1,141 +0,0 @@ -package cz.tefek.pluto.modloader; - -import cz.tefek.pluto.io.asl.resource.ResourceSubscriber; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.pluto.pp.PlutoPackage; - -/** - * Mod object. Can be used to create a {@link ResourceSubscriber}. - * {@link ModLoaderCore} automatically creates a Mod object for each class - * annotated by {@link ModEntry @ModEntry} (assuming it is registered or class - * loaded by ModClassLoader, which auto-detects and registers {@link ModEntry - * ModEntries}). - * - * @see PlutoModLoader tutorial for - * more information. - * - * @author 493msi - */ -public class Mod extends PlutoPackage -{ - private Class mainClass; - private Object instance; - - private boolean clientSupport; - private boolean serverSupport; - - private ResourceSubscriber defaultResourceSubscriber; - - private String rootDataFolder; - - Mod(Class mainClass, String rootDataFolder) - { - super(extractModID(mainClass)); - - if (mainClass != null) - { - ModEntry modInterface = mainClass.getDeclaredAnnotation(ModEntry.class); - - if (modInterface != null) - { - this.mainClass = mainClass; - this.name = modInterface.displayName().isBlank() ? modInterface.modid() : modInterface.displayName(); - this.author = modInterface.author(); - this.version = modInterface.version(); - this.build = modInterface.build(); - this.earliestCompatibleBuild = modInterface.earliestCompatibleBuild(); - this.dependencies = modInterface.dependencies(); - this.description = modInterface.description(); - this.iconURL = modInterface.iconURL(); - - this.rootDataFolder = rootDataFolder; - this.defaultResourceSubscriber = new ResourceSubscriber(this, rootDataFolder); - - this.clientSupport = modInterface.clientSupport(); - this.serverSupport = modInterface.serverSupport(); - } - } - } - - private static String extractModID(Class mainClass) - { - if (mainClass != null) - { - ModEntry modInterface = mainClass.getDeclaredAnnotation(ModEntry.class); - - if (modInterface != null) - { - return modInterface.modid(); - } - } - - return null; - } - - Class getMainClass() - { - return this.mainClass; - } - - Object getClassInstance() throws ReflectiveOperationException - { - if (this.instance == null) - { - Logger.log("|Pluto Mod Loader| Loading mod instance: " + this.name); - this.instance = this.mainClass.getDeclaredConstructor().newInstance(); - } - - return this.instance; - } - - public String getModID() - { - return this.id; - } - - public String getModName() - { - return this.name; - } - - public String getModAuthor() - { - return this.author; - } - - public String getModVersion() - { - return this.version; - } - - public int getModBuild() - { - return this.build; - } - - public boolean isClientSupported() - { - return this.clientSupport; - } - - public boolean isServerSupported() - { - return this.serverSupport; - } - - public ResourceSubscriber getDefaultResourceSubscriber() - { - return this.defaultResourceSubscriber; - } - - public void setRootDataFolder(String rootDataFolder) - { - this.rootDataFolder = rootDataFolder; - this.defaultResourceSubscriber = new ResourceSubscriber(this, rootDataFolder); - } - - public String getRootDataFolder() - { - return this.rootDataFolder; - } -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/ModClassLoader.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/ModClassLoader.java deleted file mode 100644 index efe52d3..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/ModClassLoader.java +++ /dev/null @@ -1,154 +0,0 @@ -package cz.tefek.pluto.modloader; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -import java.io.File; -import java.net.URL; -import java.net.URLClassLoader; - -import cz.tefek.pluto.io.asl.resource.ResourceHelper; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; - -/** - * Class-loads all valid mods. The only requirement for the mod is to have a - * mod.jar file inside the base folder (for example - * mods/spaghetti/mod.jar). - * - * @author 493msi - */ -public class ModClassLoader -{ - public static ArrayList mods; - - public static void loadJars() - { - Logger.log(SmartSeverity.MODULE, "Looking for installed mods."); - - File modDir = new File(ResourceHelper.GLOBAL_ROOT + "mods/"); - - if (!modDir.exists()) - { - modDir.mkdir(); - - Logger.log(SmartSeverity.MODULE, " No mod found."); - - return; - } - - mods = new ArrayList<>(Arrays.asList(modDir.list())); - - if (mods.size() == 0) - { - Logger.log(SmartSeverity.MODULE, "No mod found."); - } - else - { - Logger.log(SmartSeverity.MODULE, "Found " + mods.size() + " mod(s) to load."); - - try - { - loadAll(); - } - catch (Exception e) - { - Logger.log(e); - } - } - } - - private static void loadAll() throws Exception - { - int i = 0; - - if (mods.size() > 0) - { - if (mods.size() == 1) - { - Logger.log(SmartSeverity.MODULE, "There is one mod to load."); - } - else - { - Logger.log(SmartSeverity.MODULE, "There are " + mods.size() + " mods to load."); - } - - for (String modname : mods) - { - var modFolder = ResourceHelper.GLOBAL_ROOT + "mods/" + modname; - var dataDir = modFolder + "/data"; - - if (new File(modFolder + "/mod.jar").exists()) - { - var dataDirFile = new File(dataDir); - - if (!dataDirFile.isDirectory()) - { - dataDirFile.mkdirs(); - } - - String pathToJar = modFolder + "/mod.jar"; - - JarFile jarFile = new JarFile(pathToJar); - Enumeration e = jarFile.entries(); - - URL[] urls = { new URL("jar:file:" + pathToJar + "!/") }; - - URLClassLoader sysLoader = URLClassLoader.newInstance(urls, ClassLoader.getSystemClassLoader()); - - while (e.hasMoreElements()) - { - JarEntry je = e.nextElement(); - - if (je.isDirectory()) - { - continue; - } - - // Not sure what to do with non-java files. - // They are ignored (for now). - if (!je.getName().endsWith(".class")) - { - continue; - } - - String className = je.getName().replaceAll("\\.class$", "").replace('/', '.'); - - Class modClass = sysLoader.loadClass(className); - - if (modClass.getDeclaredAnnotation(ModEntry.class) != null) - { - ModLoaderCore.registerMod(modClass, dataDir); - } - } - - jarFile.close(); - - Logger.log(SmartSeverity.MODULE_PLUS, "Loaded mod jar file: " + modname + "/mod.jar"); - i++; - } - else - { - Logger.log(SmartSeverity.MODULE_WARNING, "Failed to load mod jar file: " + modname + "."); - Logger.log(SmartSeverity.MODULE_WARNING, "Reason: Missing mod.jar file."); - } - } - - if (i < 1 || i == 0) - { - Logger.log(SmartSeverity.MODULE, "Loading mods complete, loaded " + i + " mods."); - } - else - { - Logger.log(SmartSeverity.MODULE, "Loading mods complete, loaded " + i + " mod."); - } - } - else - { - Logger.log(SmartSeverity.MODULE, "There aren't any mods, skipping initialising phase."); - } - } -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/ModEntry.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/ModEntry.java deleted file mode 100644 index 0df0350..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/ModEntry.java +++ /dev/null @@ -1,39 +0,0 @@ -package cz.tefek.pluto.modloader; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * The heart of any Pluto mod. Annotate your class with this, so the - * PlutoModLoader can load it. The class must be directly registered or - * processed by {@link ModClassLoader} (as an external mod). - * - * @see PlutoModLoader tutorial for - * more information. - * - * @author 493msi - */ -@Retention(RetentionPolicy.RUNTIME) -public @interface ModEntry { - String modid(); - - String displayName() default ""; - - String author() default "anonymous author"; - - String version() default "unknown version"; - - Class[] dependencies() default {}; - - String iconURL() default ""; - - String description() default "No description available"; - - int build() default 0; - - int earliestCompatibleBuild() default 0; - - boolean clientSupport() default true; - - boolean serverSupport() default false; -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/ModInstaller.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/ModInstaller.java deleted file mode 100644 index 02a0e71..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/ModInstaller.java +++ /dev/null @@ -1,119 +0,0 @@ -package cz.tefek.pluto.modloader; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; - -import cz.tefek.pluto.io.asl.resource.ResourceHelper; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; - -/** - * Unzips mod packages from the packages folder into the mods folder. WIP - * - * 2020 Update: Still WIP - * - * @author 493msi - */ -public class ModInstaller -{ - public static void unpackNewMods() - { - Logger.log(SmartSeverity.MODULE, "Looking for new mod packages to install."); - - File f = new File(ResourceHelper.GLOBAL_ROOT + "packages/"); - if (!f.exists()) - { - f.mkdir(); - - Logger.log(SmartSeverity.MODULE, "Package folder does not exist, creating it and aborting unpacking."); - - return; - } - - ArrayList files = new ArrayList(Arrays.asList(f.list())); - - if (files.size() == 0) - { - Logger.log(SmartSeverity.MODULE, "No mod package found."); - } - else - { - Logger.log(SmartSeverity.MODULE_PLUS, "Found " + files.size() + " mod packages."); - - for (String file : files) - { - Logger.log(SmartSeverity.MODULE_PLUS, "Mod package found: " + file + ", installing it."); - - try - { - extract(file); - } - catch (IOException e) - { - Logger.log(SmartSeverity.MODULE_ERROR, "Unpacking of " + file + " failed!"); - Logger.log(e); - } - - new File(ResourceHelper.GLOBAL_ROOT + "packages/" + file).delete(); - } - } - } - - private static void extract(String filepath) throws IOException - { - byte[] buffer = new byte[2048]; - - InputStream fileInput = null; - fileInput = new FileInputStream(ResourceHelper.GLOBAL_ROOT + "packages/" + filepath); - - ZipInputStream stream = new ZipInputStream(fileInput); - String outdir = ResourceHelper.GLOBAL_ROOT + "mods/"; - - if (!new File(outdir).exists()) - { - new File(outdir).mkdir(); - } - - try - { - String filename = filepath.replaceAll("\\.zip$", ""); - - new File(outdir + filename).mkdir(); - - ZipEntry entry; - while ((entry = stream.getNextEntry()) != null) - { - String outpath = outdir + filename + "/" + entry.getName(); - FileOutputStream output = null; - try - { - output = new FileOutputStream(outpath); - int len = 0; - while ((len = stream.read(buffer)) > 0) - { - output.write(buffer, 0, len); - } - } - finally - { - if (output != null) - { - output.close(); - } - } - } - } - finally - { - stream.close(); - } - } -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/ModLoaderCore.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/ModLoaderCore.java deleted file mode 100644 index 3d97229..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/ModLoaderCore.java +++ /dev/null @@ -1,268 +0,0 @@ -package cz.tefek.pluto.modloader; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; - -import cz.tefek.pluto.event.staticmode.StaticPlutoEventManager; -import cz.tefek.pluto.io.asl.resource.ResourceHelper; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; -import cz.tefek.pluto.modloader.event.ModLoad; -import cz.tefek.pluto.modloader.event.ModLoadEvent; -import cz.tefek.pluto.modloader.event.ModPostLoad; -import cz.tefek.pluto.modloader.event.ModPostLoadEvent; -import cz.tefek.pluto.modloader.event.ModPreLoad; -import cz.tefek.pluto.modloader.event.ModPreLoadEvent; -import cz.tefek.pluto.modloader.event.ModUnload; -import cz.tefek.pluto.modloader.event.ModUnloadEvent; - -public class ModLoaderCore -{ - public static final String MOD_ID_STRING_PATTERN = "[a-z0-9]+[a-z0-9_]*"; - public static final String FULL_MOD_ID_STRING_PATTERN = "^" + MOD_ID_STRING_PATTERN + "$"; - - static ModLoadingPhase loadingPhase = ModLoadingPhase.WAITING; - - private static final List modArchive = new ArrayList<>(); - - private static final Queue loadBuffer = new LinkedList<>(); - - public static void registerMod(Class modClass, String modDataRoot) - { - if (loadingPhase != ModLoadingPhase.WAITING && loadingPhase != ModLoadingPhase.CLASSLOADING) - { - Logger.log(SmartSeverity.MODULE_ERROR, "Cannot register mod during loading phase " + loadingPhase.name() + "!"); - return; - } - - if (getModByMainClass(modClass) != null) - { - Logger.log(SmartSeverity.MODULE_WARNING, "Mod class " + modClass.getCanonicalName() + " is already registered, skipping it."); - return; - } - - if (modDataRoot == null) - { - modDataRoot = ResourceHelper.DEFAULT_RESOURCE_ROOT; - } - - var registeredMod = loadBuffer.stream().filter(presentMod -> presentMod.getMainClass().equals(modClass)).findAny(); - - if (registeredMod.isPresent()) - { - var mod = registeredMod.get(); - - mod.setRootDataFolder(modDataRoot); - - return; - } - - Mod mod = new Mod(modClass, modDataRoot); - - if (!mod.getModID().matches(FULL_MOD_ID_STRING_PATTERN)) - { - Logger.log(SmartSeverity.MODULE_WARNING, "Mod id " + mod.getModID() + " contains invalid characters (or none at all), mod will not be loaded."); - Logger.log(SmartSeverity.MODULE_WARNING, "Only lowercase letters (a to z) and numbers (0 to 9) are allowed characters."); - return; - } - - var deps = mod.getDependencies(); - - for (var dependency : deps) - { - var registeredDependency = loadBuffer.stream().filter(presentMod -> presentMod.getMainClass().equals(dependency)).findAny(); - - if (registeredDependency.isPresent()) - { - continue; - } - - registerMod(dependency); - } - - loadBuffer.add(mod); - } - - public static void registerMod(Class modClass) - { - registerMod(modClass, null); - } - - public static List getAllMods() - { - return Collections.unmodifiableList(modArchive); - } - - public static Mod getModByID(String id) - { - for (Mod mod : modArchive) - { - if (mod.getModID().equals(id)) - { - return mod; - } - } - - return null; - } - - private static Mod getModByMainClass(Class modClass) - { - for (Mod mod : modArchive) - { - if (modClass.equals(mod.getMainClass())) - { - return mod; - } - } - - return null; - } - - public static void loadProcedure() - { - loadingPhase = ModLoadingPhase.PREPARING; - - StaticPlutoEventManager.registerEvent(ModPreLoad.class); - StaticPlutoEventManager.registerEvent(ModLoad.class); - StaticPlutoEventManager.registerEvent(ModPostLoad.class); - StaticPlutoEventManager.registerEvent(ModUnload.class); - - Logger.log("[Pluto Mod Loader] Number of manually added mods: " + modArchive.size()); - loadingPhase = ModLoadingPhase.UPACKING; - ModInstaller.unpackNewMods(); - loadingPhase = ModLoadingPhase.CLASSLOADING; - ModClassLoader.loadJars(); - loadingPhase = ModLoadingPhase.INITIALIZING; - - while (loadBuffer.peek() != null) - { - var mod = loadBuffer.remove(); - StaticPlutoEventManager.registerEventHandler(mod.getMainClass()); - modArchive.add(mod); - } - - if (!modArchive.isEmpty()) - { - Logger.log(SmartSeverity.MODULE, "Initializing mod(s)..."); - initMods(); - - if (loadingPhase == ModLoadingPhase.FINISHING) - { - Logger.log(SmartSeverity.MODULE, "Initializing mod(s) finished."); - } - else - { - Logger.log(SmartSeverity.MODULE_ERROR, "Initializing mod(s) was canceled due to error(s)."); - } - } - } - - static void initMods() - { - while (loadingPhase != ModLoadingPhase.CANCELED && loadingPhase != ModLoadingPhase.FINISHING) - { - switch (loadingPhase) - { - case INITIALIZING: - preLoadMods(); - break; - case PRELOADING: - loadMods(); - break; - case LOADING: - postLoadMods(); - break; - case POSTLOADING: - complete(); - break; - default: - break; - } - } - } - - public static void unloadProcedure() - { - Logger.log(SmartSeverity.MODULE_MINUS, "Unloading all mods."); - StaticPlutoEventManager.fireEvent(ModUnload.class, new ModUnloadEvent()); - modArchive.clear(); - } - - private static void preLoadMods() - { - loadingPhase = ModLoadingPhase.PRELOADING; - - try - { - ModPreLoadEvent preLoadData = new ModPreLoadEvent(); - preLoadData.mods = modArchive; - - StaticPlutoEventManager.fireEvent(ModPreLoad.class, preLoadData); - } - catch (Exception e) - { - Logger.log(SmartSeverity.MODULE_ERROR, "Problem encountered while pre-loading mods."); - Logger.log(SmartSeverity.MODULE_ERROR, "Mod loading stopped."); - - Logger.log(e); - - cancelLoading(); - } - } - - private static void loadMods() - { - loadingPhase = ModLoadingPhase.LOADING; - - try - { - ModLoadEvent loadData = new ModLoadEvent(); - - StaticPlutoEventManager.fireEvent(ModLoad.class, loadData); - } - catch (Exception e) - { - Logger.log(SmartSeverity.MODULE_ERROR, "Problem encountered while loading mods."); - Logger.log(SmartSeverity.MODULE_ERROR, "Mod loading stopped."); - - Logger.log(e); - - cancelLoading(); - } - } - - private static void postLoadMods() - { - loadingPhase = ModLoadingPhase.POSTLOADING; - - try - { - ModPostLoadEvent postLoadData = new ModPostLoadEvent(); - - StaticPlutoEventManager.fireEvent(ModPostLoad.class, postLoadData); - } - catch (Exception e) - { - Logger.log(SmartSeverity.MODULE_ERROR, "Problem encountered while post-loading mods."); - Logger.log(SmartSeverity.MODULE_ERROR, "Mod loading stopped."); - - Logger.log(e); - - cancelLoading(); - } - } - - private static void complete() - { - loadingPhase = ModLoadingPhase.FINISHING; - } - - private static void cancelLoading() - { - loadingPhase = ModLoadingPhase.CANCELED; - } -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/ModLoadingPhase.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/ModLoadingPhase.java deleted file mode 100644 index 292d756..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/ModLoadingPhase.java +++ /dev/null @@ -1,17 +0,0 @@ -package cz.tefek.pluto.modloader; - -public enum ModLoadingPhase -{ - UPACKING, - PREPARING, - INITIALIZING, - WAITING, - PRELOADING, - LOADING, - POSTLOADING, - FINISHING, - CANCELED, - INSTANTIATING, - CLASSLOADING, - UNLOADING; -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModLoad.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModLoad.java deleted file mode 100644 index 873a2dc..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModLoad.java +++ /dev/null @@ -1,23 +0,0 @@ -package cz.tefek.pluto.modloader.event; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import cz.tefek.pluto.event.staticmode.StaticPlutoEvent; - -/** - * Marks a static method as an event handler for mod loading. - * - * @author 493msi - * - * @since pre-alpha - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -@StaticPlutoEvent(passingParamClass = ModLoadEvent.class) -public @interface ModLoad -{ - -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModLoadEvent.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModLoadEvent.java deleted file mode 100644 index 7ca34b9..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModLoadEvent.java +++ /dev/null @@ -1,15 +0,0 @@ -package cz.tefek.pluto.modloader.event; - -import cz.tefek.pluto.event.EventData; -import cz.tefek.pluto.modloader.ModEntry; - -/** - * Instances are passed to {@link ModEntry ModEntries} on load. Currently does - * nothing. - * - * @author 493msi - * - */ -public class ModLoadEvent extends EventData -{ -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPostLoad.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPostLoad.java deleted file mode 100644 index 3e453a0..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPostLoad.java +++ /dev/null @@ -1,23 +0,0 @@ -package cz.tefek.pluto.modloader.event; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import cz.tefek.pluto.event.staticmode.StaticPlutoEvent; - -/** - * Marks a static method as an event handler for mod post-loading. - * - * @author 493msi - * - * @since pre-alpha - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -@StaticPlutoEvent(passingParamClass = ModPostLoadEvent.class) -public @interface ModPostLoad -{ - -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPostLoadEvent.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPostLoadEvent.java deleted file mode 100644 index 63c5576..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPostLoadEvent.java +++ /dev/null @@ -1,18 +0,0 @@ -package cz.tefek.pluto.modloader.event; - -import java.util.ArrayList; -import java.util.List; - -import cz.tefek.pluto.event.EventData; -import cz.tefek.pluto.modloader.ModEntry; - -/** - * Instances are passed to {@link ModEntry ModEntries} on post-load. - * - * @author 493msi - */ -public class ModPostLoadEvent extends EventData -{ - // TODO Cross-mod messaging - public final List crossMessages = new ArrayList(); -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPreLoad.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPreLoad.java deleted file mode 100644 index 435844a..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPreLoad.java +++ /dev/null @@ -1,27 +0,0 @@ -package cz.tefek.pluto.modloader.event; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import cz.tefek.pluto.event.staticmode.StaticPlutoEvent; - - -/** - * Marks a static method as an event handler for mod pre-loading. - * - * @author 493msi - * - * @since pre-alpha - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -@StaticPlutoEvent(passingParamClass = ModPreLoadEvent.class) -public @interface ModPreLoad -{ - -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPreLoadEvent.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPreLoadEvent.java deleted file mode 100644 index 0308eeb..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPreLoadEvent.java +++ /dev/null @@ -1,19 +0,0 @@ -package cz.tefek.pluto.modloader.event; - -import java.util.List; - -import cz.tefek.pluto.event.EventData; -import cz.tefek.pluto.modloader.Mod; -import cz.tefek.pluto.modloader.ModEntry; - -/** - * Instances are passed to {@link ModEntry ModEntries} on load. Carries a list - * of all {@link Mod} objects. - * - * @author 493msi - * - */ -public class ModPreLoadEvent extends EventData -{ - public List mods; -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModUnload.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModUnload.java deleted file mode 100644 index 4ec8bf9..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModUnload.java +++ /dev/null @@ -1,20 +0,0 @@ -package cz.tefek.pluto.modloader.event; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import cz.tefek.pluto.event.staticmode.StaticPlutoEvent; - -@Retention(RUNTIME) -@Target(METHOD) -@StaticPlutoEvent(passingParamClass = ModUnloadEvent.class) -/** - * @author 493msi - * - */ -public @interface ModUnload { - -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModUnloadEvent.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModUnloadEvent.java deleted file mode 100644 index 440a95a..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModUnloadEvent.java +++ /dev/null @@ -1,15 +0,0 @@ -package cz.tefek.pluto.modloader.event; - -import cz.tefek.pluto.event.EventData; -import cz.tefek.pluto.modloader.ModEntry; - -/** - * Instances are passed to {@link ModEntry ModEntries} on mod unload. - * - * @author 493msi - * - */ -public class ModUnloadEvent extends EventData -{ - -} diff --git a/plutomesher/build.gradle b/plutomesher/build.gradle deleted file mode 100644 index 7f75b6c..0000000 --- a/plutomesher/build.gradle +++ /dev/null @@ -1,7 +0,0 @@ -apply plugin: 'java-library' - -description = "" - -dependencies { - api project(":plutostatic") -} \ No newline at end of file diff --git a/plutoshader/build.gradle b/plutoshader/build.gradle deleted file mode 100644 index 7b7649b..0000000 --- a/plutoshader/build.gradle +++ /dev/null @@ -1,8 +0,0 @@ -apply plugin: 'java-library' - -description = "Automated shader loader and manager." - -dependencies { - api project(":plutotexturing") - api project(":plutomesher") -} \ No newline at end of file diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/PlutoShaderMod.java b/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/PlutoShaderMod.java deleted file mode 100644 index 101abe8..0000000 --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/PlutoShaderMod.java +++ /dev/null @@ -1,16 +0,0 @@ -package cz.tefek.pluto.engine.shader; - -import cz.tefek.pluto.Pluto; -import cz.tefek.pluto.engine.ModLWJGL; -import cz.tefek.pluto.modloader.ModEntry; - -@ModEntry(modid = PlutoShaderMod.MOD_ID, - displayName = "PlutoShader", - dependencies = { ModLWJGL.class }, - version = Pluto.VERSION, - description = "Automated shader loader and manager.") -public class PlutoShaderMod -{ - public static final String MOD_ID = "plutoshader"; - -} diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/type/FragmentShader.java b/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/type/FragmentShader.java deleted file mode 100644 index 363908c..0000000 --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/type/FragmentShader.java +++ /dev/null @@ -1,20 +0,0 @@ -package cz.tefek.pluto.engine.shader.type; - -import cz.tefek.pluto.engine.shader.ShaderCompiler; -import cz.tefek.pluto.io.asl.resource.ResourceAddress; - -public final class FragmentShader implements IShader -{ - private int id; - - public FragmentShader(ResourceAddress address) - { - this.id = ShaderCompiler.load(address, EnumShaderType.FRAGMENT); - } - - @Override - public int getID() - { - return this.id; - } -} diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/type/GeometryShader.java b/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/type/GeometryShader.java deleted file mode 100644 index 27e8436..0000000 --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/type/GeometryShader.java +++ /dev/null @@ -1,20 +0,0 @@ -package cz.tefek.pluto.engine.shader.type; - -import cz.tefek.pluto.engine.shader.ShaderCompiler; -import cz.tefek.pluto.io.asl.resource.ResourceAddress; - -public final class GeometryShader implements IShader -{ - private int id; - - public GeometryShader(ResourceAddress address) - { - this.id = ShaderCompiler.load(address, EnumShaderType.GEOMETRY); - } - - @Override - public int getID() - { - return this.id; - } -} diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/type/VertexShader.java b/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/type/VertexShader.java deleted file mode 100644 index e21ced1..0000000 --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/type/VertexShader.java +++ /dev/null @@ -1,20 +0,0 @@ -package cz.tefek.pluto.engine.shader.type; - -import cz.tefek.pluto.engine.shader.ShaderCompiler; -import cz.tefek.pluto.io.asl.resource.ResourceAddress; - -public final class VertexShader implements IShader -{ - private int id; - - public VertexShader(ResourceAddress address) - { - this.id = ShaderCompiler.load(address, EnumShaderType.VERTEX); - } - - @Override - public int getID() - { - return this.id; - } -} diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/auto/AutomaticUniforms.java b/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/auto/AutomaticUniforms.java deleted file mode 100644 index 3bc84e4..0000000 --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/uniform/auto/AutomaticUniforms.java +++ /dev/null @@ -1,11 +0,0 @@ -package cz.tefek.pluto.engine.shader.uniform.auto; - -import org.joml.Matrix4fc; - -import cz.tefek.pluto.event.lambda.LambdaEventFactory; -import cz.tefek.pluto.event.lambda.LambdaEventFactory.LambdaEvent; - -public class AutomaticUniforms -{ - public static final LambdaEvent VIEWPORT_PROJECTION = LambdaEventFactory.createEvent(); -} diff --git a/plutospritesheet/build.gradle b/plutospritesheet/build.gradle deleted file mode 100644 index efc2de0..0000000 --- a/plutospritesheet/build.gradle +++ /dev/null @@ -1,8 +0,0 @@ -apply plugin: 'java-library' - -description = "A library to manage, store and draw sprites." - -dependencies { - api project(":plutoframebuffer") - api project(":plutoshader") -} \ No newline at end of file diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/PlutoSpriteSheetMod.java b/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/PlutoSpriteSheetMod.java deleted file mode 100644 index 484bcf2..0000000 --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/PlutoSpriteSheetMod.java +++ /dev/null @@ -1,76 +0,0 @@ -package cz.tefek.pluto.engine.graphics; - -import cz.tefek.pluto.Pluto; -import cz.tefek.pluto.engine.ModLWJGL; -import cz.tefek.pluto.engine.graphics.spritesheet.FramebufferTiledSpriteSheet; -import cz.tefek.pluto.engine.shader.PlutoShaderMod; -import cz.tefek.pluto.engine.shader.RenderShaderBuilder; -import cz.tefek.pluto.io.asl.resource.ResourceSubscriber; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.modloader.Mod; -import cz.tefek.pluto.modloader.ModEntry; -import cz.tefek.pluto.modloader.ModLoaderCore; -import cz.tefek.pluto.modloader.event.ModPreLoad; -import cz.tefek.pluto.modloader.event.ModPreLoadEvent; -import cz.tefek.pluto.modloader.event.ModUnload; -import cz.tefek.pluto.modloader.event.ModUnloadEvent; - -@ModEntry(modid = PlutoSpriteSheetMod.MOD_ID, - version = Pluto.VERSION, - dependencies = { ModLWJGL.class, PlutoShaderMod.class }, - author = "493msi", - displayName = "Pluto SpriteSheet", - description = "A library to manage, store and draw sprites.") -public class PlutoSpriteSheetMod -{ - public static final String MOD_ID = "plutospritesheet"; - - public static Mod instance; - public static ResourceSubscriber subscriber; - - /** - * Strictly internal use only, do NOT use this outside of plutospritesheet - */ - private static Shader2D shader2D; - - /** - * Strictly internal use only, do NOT use this outside of plutospritesheet - */ - private static ShaderRectangle2D shaderRectangle2D; - - /** - * Strictly internal use only, do NOT use this outside of plutospritesheet - */ - private static ShaderRectangle2D spriteSheetShader; - - @ModPreLoad - public static void preLoad(ModPreLoadEvent event) - { - instance = ModLoaderCore.getModByID(MOD_ID); - subscriber = instance.getDefaultResourceSubscriber(); - - Logger.log("Intializing " + MOD_ID + "..."); - - shader2D = new RenderShaderBuilder(subscriber, "shaders.v2D#glsl", "shaders.f2D#glsl").build(Shader2D.class, false); - shaderRectangle2D = new RenderShaderBuilder(subscriber, "shaders.VertexRectangle2D#glsl", "shaders.FragmentRectangle2D#glsl").build(ShaderRectangle2D.class, false); - spriteSheetShader = new RenderShaderBuilder(subscriber, "shaders.VertexSpriteSheet#glsl", "shaders.FragmentSpriteSheet#glsl").build(ShaderRectangle2D.class, false); - - Renderer2D.load(shader2D); - RectangleRenderer2D.load(shaderRectangle2D); - - FramebufferTiledSpriteSheet.setSpriteShader(spriteSheetShader); - } - - @ModUnload - public static void unload(ModUnloadEvent unloadEvent) - { - FramebufferTiledSpriteSheet.setSpriteShader(null); - - spriteSheetShader.dispose(); - shaderRectangle2D.dispose(); - shader2D.dispose(); - - RectangleRenderer2D.unload(); - Renderer2D.unload(); - } -} diff --git a/plutostatic/build.gradle b/plutostatic/build.gradle deleted file mode 100644 index 0b681cb..0000000 --- a/plutostatic/build.gradle +++ /dev/null @@ -1,20 +0,0 @@ -apply plugin: 'java-library' - -description = "" - -dependencies { - api project(":plutolib") - - api "org.lwjgl:lwjgl" - api "org.lwjgl:lwjgl-glfw" - api "org.lwjgl:lwjgl-opengl" - api "org.lwjgl:lwjgl-stb" - runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives" - - api "org.joml:joml:${jomlVersion}" - api "com.code-disaster.steamworks4j:steamworks4j:${steamworks4jVersion}" - api "com.code-disaster.steamworks4j:steamworks4j-server:${steamworks4jServerVersion}" -} \ No newline at end of file diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/ModLWJGL.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/ModLWJGL.java deleted file mode 100644 index da7e26a..0000000 --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/ModLWJGL.java +++ /dev/null @@ -1,15 +0,0 @@ -package cz.tefek.pluto.engine; - -import org.lwjgl.Version; - -import cz.tefek.pluto.modloader.ModEntry; - -@ModEntry(modid = "modlwjgl", - version = ModLWJGL.version, - author = "The LWJGL team", - displayName = "LWJGL", - description = "The LWJGL library, without which the Pluto Engine wouldn't exist.") -public class ModLWJGL -{ - public static final String version = Version.VERSION_MAJOR + "." + Version.VERSION_MINOR + "." + Version.VERSION_REVISION; -} diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/display/DisplayErrorCallback.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/display/DisplayErrorCallback.java deleted file mode 100644 index 2b3aad7..0000000 --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/display/DisplayErrorCallback.java +++ /dev/null @@ -1,17 +0,0 @@ -package cz.tefek.pluto.engine.display; - -import org.lwjgl.glfw.GLFWErrorCallback; - -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; - -public class DisplayErrorCallback extends GLFWErrorCallback -{ - @Override - public void invoke(int error, long description) - { - Logger.logf(SmartSeverity.ERROR, "GLFW Error code %d:\n", error); - Logger.logf(getDescription(description)); - } - -} diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionSurfaceCircle.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionSurfaceCircle.java deleted file mode 100644 index 1c400da..0000000 --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionSurfaceCircle.java +++ /dev/null @@ -1,13 +0,0 @@ -package cz.tefek.pluto.engine.math.collision; - -import org.joml.Circlef; - -public class CollisionSurfaceCircle -{ - private Circlef circle; - - public Circlef getCircle() - { - return this.circle; - } -} diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionSurfaceLine.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionSurfaceLine.java deleted file mode 100644 index ca4a2ce..0000000 --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionSurfaceLine.java +++ /dev/null @@ -1,18 +0,0 @@ -package cz.tefek.pluto.engine.math.collision; - -import org.joml.LineSegmentf; - -public class CollisionSurfaceLine extends CollisionSurface -{ - public CollisionSurfaceLine(float friction, float bounciness, float drag) - { - super(friction, bounciness, drag); - } - - protected LineSegmentf lineSegment; - - public LineSegmentf getLineSegment() - { - return this.lineSegment; - } -} diff --git a/plutotexturing/build.gradle b/plutotexturing/build.gradle deleted file mode 100644 index 7f75b6c..0000000 --- a/plutotexturing/build.gradle +++ /dev/null @@ -1,7 +0,0 @@ -apply plugin: 'java-library' - -description = "" - -dependencies { - api project(":plutostatic") -} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index da0336b..0000000 --- a/settings.gradle +++ /dev/null @@ -1,12 +0,0 @@ -include 'plutolib', - 'plutostatic', - 'plutotexturing', - 'plutomesher', - 'plutoshader', - 'plutoframebuffer', - 'plutospritesheet', - 'plutogui', - 'plutoaudio', - 'plutocore' - -rootProject.name = 'plutoengine' diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100755 index 0000000..7a2c840 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,23 @@ +rootProject.name = "plutoengine-sdk" + +include("plutoengine", + "plutoengine-demos") + +project(":plutoengine").projectDir = file("./engine-core") +project(":plutoengine-demos").projectDir = file("./engine-demo") + +include ("plutoengine:plutouss2", + "plutoengine:plutolib", + "plutoengine:plutocomponent", + "plutoengine:plutoruntime", + "plutoengine:plutodisplay", + "plutoengine:plutotexture", + "plutoengine:plutomesher", + "plutoengine:plutoshader", + "plutoengine:plutoframebuffer", + "plutoengine:plutospritesheet", + "plutoengine:plutogui", + "plutoengine:plutoaudio", + "plutoengine:plutocore") + +include("plutoengine-demos:basic-application")