SDK restructure, package reorganization, new ModLoader, new resource system

This commit is contained in:
Natty 2022-04-05 19:10:08 +02:00 committed by Tefek
parent 4f141f0227
commit d9a583ed8b
280 changed files with 5165 additions and 3376 deletions

0
.gitattributes vendored Normal file → Executable file
View File

10
.gitignore vendored Normal file → Executable file
View File

@ -1,13 +1,17 @@
/*/.settings
/.vscode
# Ignore Eclipse project files
/*/.project
/*/.classpath
/.project
/.settings
# Ignore IDEA project files
/.idea
/*/.idea
.idea
*.log
@ -18,4 +22,8 @@
# Ignore Gradle build output directory
/build
/*/build
/*/build
/bin
/*/bin
/logs/

2
LICENSE Normal file → Executable file
View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2019-2021 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

15
NEXT_RELEASE_DRAFT.md Normal file → Executable file
View File

@ -1,11 +1,4 @@
## Features targeted for 21.0.1.0-alpha.0
* `[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 21.0.1.0-alpha.1
## Features targeted for 22.0.1.0-alpha.0
* The stage subsystem
* A "stage", in this context, is a set of assets bound together
by programming logic, not necessarily a game level.
@ -25,7 +18,7 @@
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
@ -45,5 +38,5 @@
* 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 21.0.1.0-alpha.4
* The initial minimal release of `[PlutoCommandParser]`
## Features targeted for 22.0.1.0-alpha.1
* TBD

19
README.md Normal file → Executable file
View File

@ -25,19 +25,18 @@ version numbers.*
* **PlutoCore** - Stable
* **PlutoFramebuffer** - Stable
* **PlutoGUI** - Stable, awaiting a rewrite
* **PlutoLib** - Mostly stable, the module API still has some quirks
* **PlutoMesher** - Stable
* **PlutoShader** - Stable
* **PlutoTexture** - Stable
* **PlutoSpriteSheet** - Stable, some features are unfinished
* **PlutoStatic** - Stable, collision API nowhere near completion
* **PlutoDisplay** - Stable, collision API nowhere near completion
* **PlutoUSS2** - Stable
* **PlutoLib** - Mostly stable
### Unstable submodules
### Unstable submodules
* **PlutoRuntime** - Somewhat tentative, the module API has been rewritten and might contain bugs
* **PlutoAudio** - Somewhat usable, unfinished
### Broken submodules, do NOT use
* **PlutoCommandParser** - Unfinished, broken, unusable
* **PlutoDB** - Broken, unusable
## Current priorities
@ -45,8 +44,8 @@ See `NEXT_RELEASE_DRAFT.md` for details.
### Very high priority
[ *Implemented in the current release.* ]
* Rewrite the ModLoader
* Streamline PlutoLib, remove bad APIs and improve code quality
* Improve image loading capabilities, possibly rewrite PlutoLib#TPL
* The stage system and automated asset loading
### High priority
@ -54,12 +53,10 @@ See `NEXT_RELEASE_DRAFT.md` for details.
* Rewrite PlutoGUI
* Finish PlutoAudio
* Depends on the stage system
* Finish PlutoCommandParser
### Normal priority
[ *Planned for an upcoming release.* ]
* The collision system for PlutoStatic
* Improve image loading capabilities, possibly rewrite PlutoLib#TPL
### Low priority
[ *Items not required immediately, planned to be implemented eventually.* ]
@ -67,6 +64,10 @@ See `NEXT_RELEASE_DRAFT.md` for details.
* Alternatively, if this deems too difficult to implement,
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

68
UPDATE_NOTES.md Normal file → Executable file
View File

@ -1,34 +1,58 @@
## 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]` *Removed* `Severity`, use `SmartSeverity` instead
* `[PlutoLib]` *Removed* `TextIn`, `TextOut`, `ResourceImage` and `ResourceInputStream`
* `[PlutoLib]` *Removed* `StaticPlutoEventManager` as the implementation was too obscure
* The module system now uses its own event management
* *Removed* the `EventData` class
* `[PlutoLib]` Made `OutputSplitStream` public as it is now reusable
* `[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 `RAID#getIDOf` method now returns `OptionalInt` to avoid NPEs
* `[PlutoLib]` Added an `equals` implementation to `ResourceAddress`
* `[PlutoStatic]` Added the `ModGLFW` virtual module
* `[PlutoLib]` The transitive dependency JOML is now provided by `PlutoLib` instead of `PlutoStatic`
* `[PlutoLib]` Created a simple Color API
* `[PlutoShader]` Added the 8-bit RGBA `Color` class as a counterpart to AWT's `Color` class
* `[PlutoShader]` Added the `RGBA` and `RGB` single precision float color objects
* `[PlutoShader]` Added the respective `IRGBA` and `IRGB` read-only interfaces
* `[PlutoShader]` Added the `HSBA` and `HSB` single precision float color objects
* `[PlutoShader]` Added methods to convert between HSBA, RGBA, HSB and RGB
* `[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
* `[PlutoLib]` Created 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
* `[PlutoCore]` Made `PlutoApplication`'s constructor private
* `[PlutoLib]` `MiniTimeParseException` no longer contains a hardcoded String message
Awaiting implementation:
* `[PlutoLib]` Moved `cz.tefek.pluto.io.pluto.pp` to `cz.tefek.pluto.io.plutopackage`
* `[PlutoLib]` Completely reworked the module system
* `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

View File

@ -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 = 3
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()
}
}

12
build.gradle.kts Executable file
View File

@ -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"
}
}

24
buildSrc/build.gradle.kts Executable file
View File

@ -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())
}

View File

@ -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.0"
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
}

28
engine-core/.gitignore vendored Executable file
View File

@ -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

32
engine-core/build.gradle.kts Executable file
View File

@ -0,0 +1,32 @@
import org.plutoengine.Versions
subprojects {
apply(plugin = "java")
apply(plugin = "java-library")
apply(plugin = "maven-publish")
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"])
}
}
}
}

View File

@ -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)
}

View File

@ -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);
@ -112,14 +109,14 @@ public class AudioLoader
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

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.audio;
package org.plutoengine.audio;
import java.nio.ShortBuffer;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.audio;
package org.plutoengine.audio;
public interface ISeekableAudioTrack extends IAudioStream
{

View File

@ -1,23 +1,16 @@
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 javax.annotation.concurrent.ThreadSafe;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
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 org.plutoengine.Pluto;
import org.plutoengine.logger.Logger;
import org.plutoengine.logger.SmartSeverity;
/**
* @author 493msi
@ -26,23 +19,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()
{

View File

@ -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;

View File

@ -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;

View File

@ -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
{

View File

@ -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")
}

View File

@ -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);
}
}

View File

@ -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);
}
});
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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"))
}

View 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.
@ -116,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)
{
@ -174,45 +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()
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)
.export();
.setInitialSize(config.windowInitialWidth, config.windowInitialHeight);
}
else
{
this.display = new DisplayBuilder()
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)
.export();
.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);
@ -220,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())
{
@ -252,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()

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.input;
package org.plutoengine.input;
import org.lwjgl.glfw.GLFWCursorPosCallback;

View File

@ -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();
}
}
}

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.input;
package org.plutoengine.input;
import org.lwjgl.glfw.GLFWCharCallback;

View File

@ -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)

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.input;
package org.plutoengine.input;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWMouseButtonCallback;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.input;
package org.plutoengine.input;
import org.lwjgl.glfw.GLFWScrollCallback;

View File

@ -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)
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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));
}
}

View File

@ -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();

View File

@ -4,4 +4,4 @@
* @author 493msi
*
*/
package cz.tefek.pluto.engine.buffer;
package org.plutoengine.buffer;

View File

@ -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;
}
}

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.display;
package org.plutoengine.display;
import org.lwjgl.glfw.GLFW;

View File

@ -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()
{

View File

@ -4,4 +4,4 @@
* @author 493msi
*
*/
package cz.tefek.pluto.engine.display;
package org.plutoengine.display;

View File

@ -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
{

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.gl;
package org.plutoengine.gl;
/**
* Denotes the implementing class is a set of OpenGL enums.

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.math;
package org.plutoengine.math;
import org.joml.Matrix4f;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.math;
package org.plutoengine.math;
import org.joml.Matrix3x2f;
import org.joml.Matrix4f;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.math;
package org.plutoengine.math;
import org.joml.Matrix3x2f;

View File

@ -4,4 +4,4 @@
* @author 493msi
*
*/
package cz.tefek.pluto.math;
package org.plutoengine.math;

View File

@ -0,0 +1,10 @@
plugins {
java
`java-library`
}
description = ""
dependencies {
api(project(":plutoengine:plutotexture"))
}

View File

@ -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;

View File

@ -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
{

View File

@ -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
{

View File

@ -0,0 +1,10 @@
plugins {
java
`java-library`
}
description = ""
dependencies {
api(project(":plutoengine:plutospritesheet"))
}

View File

@ -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();
}
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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")
}

View File

@ -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);
}
}
}

View File

@ -0,0 +1,9 @@
package org.plutoengine.address;
public class VirtualAddressParseException extends RuntimeException
{
public VirtualAddressParseException(String message)
{
super(message);
}
}

View File

@ -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();
}
}

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.chrono;
package org.plutoengine.chrono;
import java.util.concurrent.TimeUnit;
@ -45,10 +45,6 @@ public class MiniTime
{
private static class MiniTimeParseException extends RuntimeException
{
public MiniTimeParseException()
{
}
}
private static final int DAYS_IN_WEEK = 7;
@ -72,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())
@ -141,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

View File

@ -4,4 +4,4 @@
* @author 493msi
*
*/
package cz.tefek.pluto.chrono;
package org.plutoengine.chrono;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.event.lambda;
package org.plutoengine.event.lambda;
import java.util.ArrayList;
import java.util.List;

View File

@ -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);
}
}
}

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.math;
package org.plutoengine.math;
/**
* A clamped sine wave generator, for animations, mostly.

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.math;
package org.plutoengine.math;
/**
* A class to generate a cubic bezier interpolation function. Not very

View File

@ -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

View File

@ -4,4 +4,4 @@
* @author 493msi
*
*/
package cz.tefek.pluto.engine.math;
package org.plutoengine.math;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.util.color;
package org.plutoengine.util.color;
import javax.annotation.Nonnull;
@ -155,23 +155,16 @@ public final class Color
*/
public static Color from(int color, @Nonnull EnumColorFormat colorFormat)
{
switch (colorFormat)
return switch (colorFormat)
{
case CF_INT_BGR:
return new Color(color & 0xff, (color >> 8) & 0xff, (color >> 16) & 0xff);
case CF_INT_RGB:
return new Color((color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
case CF_INT_ABGR:
return new Color(color & 0xff, (color >> 8) & 0xff, (color >> 16) & 0xff, (color >> 24) & 0xff);
case CF_INT_ARGB:
return new Color((color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff, (color >> 24) & 0xff);
case CF_INT_BGRA:
return new Color((color >> 8) & 0xff, (color >> 16) & 0xff, (color >> 24) & 0xff, color & 0xff);
case CF_INT_RGBA:
return 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!");
}
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!");
};
}
/**
@ -204,23 +197,16 @@ public final class Color
*/
public static Color from(@Nonnull byte[] color, int offset, @Nonnull EnumColorFormat colorFormat)
{
switch (colorFormat)
return switch (colorFormat)
{
case CF_3BYTE_BGR:
return new Color(color[offset + 2] & 0xFF, color[offset + 1] & 0xFF, color[offset] & 0xFF);
case CF_3BYTE_RGB:
return new Color(color[offset] & 0xFF, color[offset + 1] & 0xFF, color[offset + 2] & 0xFF);
case CF_4BYTE_ABGR:
return new Color(color[offset + 3] & 0xFF, color[offset + 2] & 0xFF, color[offset + 1] & 0xFF, color[offset] & 0xFF);
case CF_4BYTE_ARGB:
return new Color(color[offset + 1] & 0xFF, color[offset + 2] & 0xFF, color[offset + 3] & 0xFF, color[offset] & 0xFF);
case CF_4BYTE_BGRA:
return new Color(color[offset + 2] & 0xFF, color[offset + 1] & 0xFF, color[offset] & 0xFF, color[offset + 3] & 0xFF);
case CF_4BYTE_RGBA:
return 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!");
}
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!");
};
}
/**
@ -253,23 +239,16 @@ public final class Color
*/
public int get(@Nonnull EnumColorFormat colorFormat)
{
switch (colorFormat)
return switch (colorFormat)
{
case CF_INT_BGR:
return (this.blue << 16) | (this.green << 8) | this.red;
case CF_INT_RGB:
return (this.red << 16) | (this.green << 8) | this.blue;
case CF_INT_ABGR:
return (this.alpha << 24) | (this.blue << 16) | (this.green << 8) | this.red;
case CF_INT_ARGB:
return (this.alpha << 24) | (this.red << 16) | (this.green << 8) | this.blue;
case CF_INT_BGRA:
return (this.blue << 24) | (this.green << 16) | (this.red << 8) | this.alpha;
case CF_INT_RGBA:
return (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!");
}
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!");
};
}
/**
@ -293,42 +272,41 @@ public final class Color
{
switch (colorFormat)
{
case CF_3BYTE_BGR:
case CF_3BYTE_BGR -> {
dataOut[offset++] = (byte) this.blue;
dataOut[offset++] = (byte) this.green;
dataOut[offset] = (byte) this.red;
break;
case CF_3BYTE_RGB:
}
case CF_3BYTE_RGB -> {
dataOut[offset++] = (byte) this.red;
dataOut[offset++] = (byte) this.green;
dataOut[offset] = (byte) this.blue;
break;
case CF_4BYTE_ABGR:
}
case CF_4BYTE_ABGR -> {
dataOut[offset++] = (byte) this.alpha;
dataOut[offset++] = (byte) this.blue;
dataOut[offset++] = (byte) this.green;
dataOut[offset] = (byte) this.red;
break;
case CF_4BYTE_ARGB:
}
case CF_4BYTE_ARGB -> {
dataOut[offset++] = (byte) this.alpha;
dataOut[offset++] = (byte) this.red;
dataOut[offset++] = (byte) this.green;
dataOut[offset] = (byte) this.blue;
break;
case CF_4BYTE_BGRA:
}
case CF_4BYTE_BGRA -> {
dataOut[offset++] = (byte) this.blue;
dataOut[offset++] = (byte) this.green;
dataOut[offset++] = (byte) this.red;
dataOut[offset] = (byte) this.alpha;
break;
case CF_4BYTE_RGBA:
}
case CF_4BYTE_RGBA -> {
dataOut[offset++] = (byte) this.red;
dataOut[offset++] = (byte) this.green;
dataOut[offset++] = (byte) this.blue;
dataOut[offset] = (byte) this.alpha;
break;
default:
throw new UnsupportedOperationException("Use the get(ColorFormat) for int color formats!");
}
default -> throw new UnsupportedOperationException("Use the get(ColorFormat) for int color formats!");
}
}

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.util.color;
package org.plutoengine.util.color;
/**
* TODO

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.util.color;
package org.plutoengine.util.color;
import org.apache.commons.lang3.Range;
@ -78,36 +78,30 @@ public class HSB
float p = this.b * (1 - this.s);
float hueFractCCW = h6 - hueSide;
// The second nearest color component on the hue wheel - counter-clockwise
// 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
// The second-nearest color component on the hue wheel - clockwise
float t = this.b * (1 - hueFractCW * this.s);
switch (hueSide % 6)
return switch (hueSide % 6)
{
case 1: // Hues 60°-119° -- Green is the brightest color, no blue is present at max saturation
return new RGBA(q, this.b, p, alpha);
// 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);
case 2: // Hues 120°-179° -- Green is the brightest color, no red is present at max saturation
return new RGBA(p, this.b, t, alpha);
case 3: // Hues 180°-239° -- Blue is the brightest color, no red is present at max saturation
return new RGBA(p, q, this.b, alpha);
case 4: // Hues 240°-299° -- Blue is the brightest color, no green is present at max saturation
return new RGBA(t, p, this.b, alpha);
case 5: // Hues 300°-359° -- Red is the brightest color, no green is present at max saturation
return new RGBA(this.b, p, q, alpha);
case 0: // Hues 0°-59° -- Red is the brightest color, no blue is present at max saturation
return new RGBA(this.b, t, p, alpha);
default:
throw new IllegalStateException("This HSB object's hue is negative - this is not legal.");
}
default -> throw new IllegalStateException("This HSB object's hue is negative - this is not legal.");
};
}
/**

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.util.color;
package org.plutoengine.util.color;
import org.apache.commons.lang3.Range;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.util.color;
package org.plutoengine.util.color;
/**
* An interface for single precision RGB color objects.

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.util.color;
package org.plutoengine.util.color;
/**
* An interface for single precision RGBA color objects.

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.util.color;
package org.plutoengine.util.color;
import org.apache.commons.lang3.math.NumberUtils;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.util.color;
package org.plutoengine.util.color;
/**
* TODO

View File

@ -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();
}
}

View File

@ -0,0 +1,10 @@
plugins {
java
`java-library`
}
description = ""
dependencies {
api(project(":plutoengine:plutodisplay"))
}

View File

@ -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
{

View File

@ -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

View File

@ -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
{

View File

@ -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
{

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.engine.graphics.gl.vao.attrib;
package org.plutoengine.graphics.gl.vao.attrib;
public class ReservedAttributes
{

View File

@ -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;

View File

@ -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;

View File

@ -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
{

View File

@ -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[]>>
{

View File

@ -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[]>>
{

View File

@ -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)
}

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto;
package org.plutoengine;
class PlutoVersionConfig
{

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto;
package org.plutoengine;
public interface IVersion<T> extends Comparable<T>
{

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto;
package org.plutoengine;
/**
* Constants shared by all Pluto libraries.
@ -9,6 +9,13 @@ package cz.tefek.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"));
/**

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -1,11 +1,10 @@
package cz.tefek.pluto;
package org.plutoengine;
import org.plutoengine.address.ConstantExpression;
import javax.annotation.Nonnull;
import java.util.Objects;
import cz.tefek.pluto.annotation.ConstantExpression;
public final class PlutoVersion implements IVersion<PlutoVersion>
{
private final int year;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.annotation;
package org.plutoengine.address;
import javax.annotation.meta.TypeQualifier;
import java.lang.annotation.*;

View File

@ -1,4 +1,4 @@
package cz.tefek.pluto.annotation;
package org.plutoengine.address;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

Some files were not shown because too many files have changed in this diff Show More