Fast-forward master to working and delete working #1
|
@ -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 }}
|
|
@ -1,13 +1,17 @@
|
|||
/*/.settings
|
||||
|
||||
/.vscode
|
||||
|
||||
# Ignore Eclipse project files
|
||||
/*/.project
|
||||
/*/.classpath
|
||||
/.project
|
||||
/.settings
|
||||
|
||||
# Ignore IDEA project files
|
||||
/.idea
|
||||
/*/.idea
|
||||
.idea
|
||||
|
||||
*.log
|
||||
|
||||
|
@ -19,3 +23,7 @@
|
|||
# Ignore Gradle build output directory
|
||||
/build
|
||||
/*/build
|
||||
|
||||
/bin
|
||||
/*/bin
|
||||
/logs/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
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
|
||||
|
|
|
@ -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
|
||||
* **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
|
||||
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
|
|
@ -1,3 +1,59 @@
|
|||
## 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
|
||||
|
@ -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
|
||||
* `[Pluto*]` Replaced `CRLF` with `LF` in all Java source files
|
83
build.gradle
83
build.gradle
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
tasks.withType<Wrapper> {
|
||||
distributionType = Wrapper.DistributionType.ALL
|
||||
gradleVersion = "7.4.2"
|
||||
}
|
||||
|
||||
subprojects {
|
||||
group = "cz.tefek"
|
||||
|
||||
tasks.withType<JavaCompile> {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.jetbrains.kotlin", "kotlin-gradle-plugin", "1.6.20")
|
||||
|
||||
implementation(gradleApi())
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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<JavaPluginExtension> {
|
||||
sourceCompatibility = Versions.javaTargetVersion
|
||||
targetCompatibility = Versions.javaTargetVersion
|
||||
}
|
||||
|
||||
configure<SourceSetContainer> {
|
||||
named("main") {
|
||||
tasks.withType<Jar> {
|
||||
from(allJava)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configure<PublishingExtension> {
|
||||
publications {
|
||||
create<MavenPublication>("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<SigningExtension> {
|
||||
val signingKey: String? by project
|
||||
val signingPassword: String? by project
|
||||
useInMemoryPgpKeys(signingKey, signingPassword)
|
||||
sign(the<PublishingExtension>().publications["maven"])
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
|
@ -1,4 +1,4 @@
|
|||
package cz.tefek.pluto.engine.audio;
|
||||
package org.plutoengine.audio;
|
||||
|
||||
import java.nio.ShortBuffer;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package cz.tefek.pluto.engine.audio;
|
||||
package org.plutoengine.audio;
|
||||
|
||||
public interface ISeekableAudioTrack extends IAudioStream
|
||||
{
|
|
@ -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<Long> device = new ThreadLocal<>() {
|
||||
@Override
|
||||
protected Long initialValue()
|
||||
{
|
||||
return MemoryUtil.NULL;
|
||||
}
|
||||
};
|
||||
private static final ThreadLocal<Long> device = ThreadLocal.withInitial(() -> MemoryUtil.NULL);
|
||||
|
||||
private static ThreadLocal<Long> context = new ThreadLocal<>() {
|
||||
@Override
|
||||
protected Long initialValue()
|
||||
{
|
||||
return MemoryUtil.NULL;
|
||||
}
|
||||
};
|
||||
private static final ThreadLocal<Long> context = ThreadLocal.withInitial(() -> MemoryUtil.NULL);
|
||||
|
||||
private static ThreadLocal<ALCapabilities> capabilities = new ThreadLocal<>();
|
||||
private static final ThreadLocal<ALCapabilities> capabilities = new ThreadLocal<>();
|
||||
|
||||
public static void initialize()
|
||||
{
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
{
|
|
@ -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")
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<R extends AbstractComponent>
|
||||
{
|
||||
private final Class<R> base;
|
||||
|
||||
protected final MultiValuedMap<ComponentToken<? extends R>, R> components;
|
||||
protected final Map<R, ComponentToken<? extends R>> tokens;
|
||||
protected final MultiValuedMap<Class<?>, R> implementationProviders;
|
||||
protected final MultiValuedMap<R, Class<?>> implementationReceivers;
|
||||
|
||||
public ComponentManager(@Nonnull Class<R> base)
|
||||
{
|
||||
this.base = base;
|
||||
this.components = new HashSetValuedHashMap<>();
|
||||
this.tokens = new HashMap<>();
|
||||
this.implementationProviders = new HashSetValuedHashMap<>();
|
||||
this.implementationReceivers = new ArrayListValuedHashMap<>();
|
||||
}
|
||||
|
||||
public <T extends R> T addComponent(@Nonnull ComponentToken<T> 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<R> getComponentBase()
|
||||
{
|
||||
return this.base;
|
||||
}
|
||||
|
||||
public <T extends R> Stream<T> streamComponents(@Nonnull Class<T> componentClazz)
|
||||
{
|
||||
var providers = this.implementationProviders.get(componentClazz);
|
||||
|
||||
return providers.stream().map(componentClazz::cast);
|
||||
}
|
||||
|
||||
public <T extends R> T getComponent(@Nonnull Class<T> componentClazz) throws NoSuchElementException
|
||||
{
|
||||
return this.streamComponents(componentClazz)
|
||||
.findAny()
|
||||
.orElseThrow();
|
||||
}
|
||||
|
||||
public <T extends R> T getComponent(@Nonnull Class<T> componentClazz, @Nonnull Comparator<T> heuristic) throws NoSuchElementException
|
||||
{
|
||||
return this.streamComponents(componentClazz)
|
||||
.max(heuristic)
|
||||
.orElseThrow();
|
||||
}
|
||||
|
||||
public <T extends R> List<T> getComponents(@Nonnull Class<T> 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 <T extends R> void removeComponents(@Nonnull ComponentToken<T> 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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<T extends IComponent>
|
||||
{
|
||||
private static final AtomicLong ID_SOURCE = new AtomicLong();
|
||||
|
||||
private final long id;
|
||||
private final Supplier<T> supplier;
|
||||
|
||||
private ComponentToken(Supplier<T> valueSupplier)
|
||||
{
|
||||
this.id = ID_SOURCE.getAndIncrement();
|
||||
this.supplier = valueSupplier;
|
||||
}
|
||||
|
||||
public static <T extends IComponent> ComponentToken<T> create(@Nonnull Supplier<T> 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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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"))
|
||||
}
|
106
plutocore/src/main/java/cz/tefek/pluto/PlutoApplication.java → engine-core/plutocore/src/main/java/org/plutoengine/PlutoApplication.java
Normal file → Executable file
106
plutocore/src/main/java/cz/tefek/pluto/PlutoApplication.java → engine-core/plutocore/src/main/java/org/plutoengine/PlutoApplication.java
Normal file → Executable file
|
@ -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()
|
|
@ -1,4 +1,4 @@
|
|||
package cz.tefek.pluto.engine.input;
|
||||
package org.plutoengine.input;
|
||||
|
||||
import org.lwjgl.glfw.GLFWCursorPosCallback;
|
||||
|
|
@ -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<InputBus> 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package cz.tefek.pluto.engine.input;
|
||||
package org.plutoengine.input;
|
||||
|
||||
import org.lwjgl.glfw.GLFWCharCallback;
|
||||
|
|
@ -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<Integer> keyPressed = new HashSet<>();
|
||||
private Set<Integer> keyDown = new HashSet<>();
|
||||
private Set<Integer> keyReleased = new HashSet<>();
|
||||
private final Set<Integer> keyDown = new HashSet<>();
|
||||
private final Set<Integer> keyReleased = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public void invoke(long window, int key, int scancode, int action, int mods)
|
|
@ -1,4 +1,4 @@
|
|||
package cz.tefek.pluto.engine.input;
|
||||
package org.plutoengine.input;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.glfw.GLFWMouseButtonCallback;
|
|
@ -1,4 +1,4 @@
|
|||
package cz.tefek.pluto.engine.input;
|
||||
package org.plutoengine.input;
|
||||
|
||||
import org.lwjgl.glfw.GLFWScrollCallback;
|
||||
|
|
@ -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)
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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();
|
|
@ -4,4 +4,4 @@
|
|||
* @author 493msi
|
||||
*
|
||||
*/
|
||||
package cz.tefek.pluto.engine.buffer;
|
||||
package org.plutoengine.buffer;
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package cz.tefek.pluto.engine.display;
|
||||
package org.plutoengine.display;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
|
@ -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<Long> drawTimestamps = new LinkedBlockingDeque<>();
|
||||
private static final Deque<Long> drawTimestamps = new LinkedBlockingDeque<>();
|
||||
|
||||
public static double getFrameTime()
|
||||
{
|
|
@ -4,4 +4,4 @@
|
|||
* @author 493msi
|
||||
*
|
||||
*/
|
||||
package cz.tefek.pluto.engine.display;
|
||||
package org.plutoengine.display;
|
|
@ -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
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
package cz.tefek.pluto.engine.gl;
|
||||
package org.plutoengine.gl;
|
||||
|
||||
/**
|
||||
* Denotes the implementing class is a set of OpenGL enums.
|
|
@ -1,4 +1,4 @@
|
|||
package cz.tefek.pluto.engine.math;
|
||||
package org.plutoengine.math;
|
||||
|
||||
import org.joml.Matrix4f;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package cz.tefek.pluto.engine.math;
|
||||
package org.plutoengine.math;
|
||||
|
||||
import org.joml.Matrix3x2f;
|
||||
import org.joml.Matrix4f;
|
|
@ -1,4 +1,4 @@
|
|||
package cz.tefek.pluto.engine.math;
|
||||
package org.plutoengine.math;
|
||||
|
||||
import org.joml.Matrix3x2f;
|
||||
|
|
@ -4,4 +4,4 @@
|
|||
* @author 493msi
|
||||
*
|
||||
*/
|
||||
package cz.tefek.pluto.math;
|
||||
package org.plutoengine.math;
|
|
@ -0,0 +1,10 @@
|
|||
plugins {
|
||||
java
|
||||
`java-library`
|
||||
}
|
||||
|
||||
description = ""
|
||||
|
||||
dependencies {
|
||||
api(project(":plutoengine:plutotexture"))
|
||||
}
|
|
@ -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<FramebufferTexture> textures;
|
||||
private final List<FramebufferTexture> textures;
|
||||
|
||||
private FramebufferDepthTexture depthTexture;
|
||||
|
|
@ -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
|
||||
{
|
|
@ -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
|
||||
{
|
|
@ -0,0 +1,10 @@
|
|||
plugins {
|
||||
java
|
||||
`java-library`
|
||||
}
|
||||
|
||||
description = ""
|
||||
|
||||
dependencies {
|
||||
api(project(":plutoengine:plutospritesheet"))
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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<String, Font> fonts = new HashMap<>();
|
||||
|
||||
public static void loadFont(Path address)
|
||||
{
|
||||
String fontname = null;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
var def = new HashMap<Character, CharacterInfo>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package org.plutoengine.gui.font;
|
||||
|
||||
public record CharacterInfo(int number, int leftOffset, int rightOffset)
|
||||
{
|
||||
|
||||
}
|
|
@ -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<Character, CharacterInfo> definitions;
|
||||
private final HashMap<Character, CharacterInfo> definitions;
|
||||
private RectangleTexture texture;
|
||||
|
||||
public Font(String name, int width, int height, HashMap<Character, CharacterInfo> def)
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
|
@ -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")
|
||||
|
||||
}
|
|
@ -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<VirtualAddress>
|
||||
{
|
||||
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<String> components;
|
||||
|
||||
private final boolean relative;
|
||||
|
||||
@Range(from = 0, to = Integer.MAX_VALUE)
|
||||
private final int rootOffset;
|
||||
|
||||
VirtualAddress(String fullPath, List<String> 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<String> 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.<String>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<String>();
|
||||
|
||||
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.<String>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<VirtualAddress>
|
||||
{
|
||||
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<VirtualAddress>
|
||||
{
|
||||
public Deserializer()
|
||||
{
|
||||
super(VirtualAddress.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VirtualAddress deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
|
||||
{
|
||||
return VirtualAddress.parse(p.getValueAsString(), false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package org.plutoengine.address;
|
||||
|
||||
public class VirtualAddressParseException extends RuntimeException
|
||||
{
|
||||
public VirtualAddressParseException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -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<String> 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<String> 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();
|
||||
}
|
||||
}
|
58
plutolib/src/main/java/cz/tefek/pluto/chrono/MiniTime.java → engine-core/plutolib/src/main/java/org/plutoengine/chrono/MiniTime.java
Normal file → Executable file
58
plutolib/src/main/java/cz/tefek/pluto/chrono/MiniTime.java → engine-core/plutolib/src/main/java/org/plutoengine/chrono/MiniTime.java
Normal file → Executable file
|
@ -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);
|
||||
}
|
|
@ -4,4 +4,4 @@
|
|||
* @author 493msi
|
||||
*
|
||||
*/
|
||||
package cz.tefek.pluto.chrono;
|
||||
package org.plutoengine.chrono;
|
|
@ -1,4 +1,4 @@
|
|||
package cz.tefek.pluto.event.lambda;
|
||||
package org.plutoengine.event.lambda;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
|
@ -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<String, String> loadFromFile(Path file) throws IOException, YAMLException
|
||||
{
|
||||
try (var br = Files.newBufferedReader(file))
|
||||
{
|
||||
var yaml = new Yaml(new Constructor() {
|
||||
private void recursivelyBuildTree(Map<String, String> 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<String, String>();
|
||||
this.recursivelyBuildTree(propertyMap, "", node);
|
||||
return propertyMap;
|
||||
}
|
||||
});
|
||||
|
||||
return yaml.load(br);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package cz.tefek.pluto.math;
|
||||
package org.plutoengine.math;
|
||||
|
||||
/**
|
||||
* A clamped sine wave generator, for animations, mostly.
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -4,4 +4,4 @@
|
|||
* @author 493msi
|
||||
*
|
||||
*/
|
||||
package cz.tefek.pluto.engine.math;
|
||||
package org.plutoengine.math;
|
|
@ -0,0 +1,468 @@
|
|||
package org.plutoengine.util.color;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* A simple 8-bit RGBA color container.
|
||||
*
|
||||
* <p><em>
|
||||
* Some methods mutate the object to avoid new object creation.
|
||||
* These methods are prefixed with "store".
|
||||
* </em></p>
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* <p>
|
||||
* This however should not be a problem as <em>this class is not designed
|
||||
* for large-scale or performance-sensitive color operations</em>.
|
||||
* </p>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<Float> SATURATION_RANGE = Range.between(0.0f, 1.0f);
|
||||
|
||||
/**
|
||||
* Value/Brightness [0..1]
|
||||
* */
|
||||
protected float b;
|
||||
private final static Range<Float> 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;
|
||||
}
|
||||
}
|
|
@ -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<Float> 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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
plugins {
|
||||
java
|
||||
`java-library`
|
||||
}
|
||||
|
||||
description = ""
|
||||
|
||||
dependencies {
|
||||
api(project(":plutoengine:plutodisplay"))
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
{
|
|
@ -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
|
||||
{
|
|
@ -1,4 +1,4 @@
|
|||
package cz.tefek.pluto.engine.graphics.gl.vao.attrib;
|
||||
package org.plutoengine.graphics.gl.vao.attrib;
|
||||
|
||||
public class ReservedAttributes
|
||||
{
|
|
@ -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;
|
||||
|
|
@ -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<T extends VecArray<?>>
|
||||
{
|
||||
protected int glID = 0;
|
||||
protected int glID;
|
||||
|
||||
private final int vertexDimensions;
|
||||
private final int vertexCount;
|
|
@ -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;
|
||||
}
|
|
@ -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<VecArray<float[]>>
|
||||
{
|
|
@ -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<VecArray<int[]>>
|
||||
{
|
|
@ -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)
|
||||
}
|
|
@ -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";
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package org.plutoengine;
|
||||
|
||||
public interface IVersion<T> extends Comparable<T>
|
||||
{
|
||||
@Override
|
||||
boolean equals(Object o);
|
||||
|
||||
@Override
|
||||
int hashCode();
|
||||
|
||||
@Override
|
||||
String toString();
|
||||
}
|
120
plutolib/src/main/java/cz/tefek/pluto/Pluto.java → engine-core/plutoruntime/src/main/java/org/plutoengine/Pluto.java
Normal file → Executable file
120
plutolib/src/main/java/cz/tefek/pluto/Pluto.java → engine-core/plutoruntime/src/main/java/org/plutoengine/Pluto.java
Normal file → Executable file
|
@ -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();
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package org.plutoengine;
|
||||
|
||||
import org.plutoengine.component.ComponentManager;
|
||||
import org.plutoengine.component.PlutoGlobalComponent;
|
||||
|
||||
public class PlutoGlobal
|
||||
{
|
||||
public static final ComponentManager<PlutoGlobalComponent> COMPONENTS = new ComponentManager<>(PlutoGlobalComponent.class);
|
||||
}
|
|
@ -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<PlutoLocal> local = ThreadLocal.withInitial(PlutoLocal::new);
|
||||
|
||||
public final ComponentManager<PlutoLocalComponent> COMPONENTS;
|
||||
|
||||
private PlutoLocal()
|
||||
{
|
||||
this.COMPONENTS = new ComponentManager<>(PlutoLocalComponent.class);
|
||||
}
|
||||
|
||||
public static PlutoLocal instance()
|
||||
{
|
||||
return local.get();
|
||||
}
|
||||
|
||||
public static ComponentManager<PlutoLocalComponent> components()
|
||||
{
|
||||
return instance().COMPONENTS;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue