Version bump, preparation for the layer subsystem implementation

This commit is contained in:
Natty 2022-04-16 14:40:59 +02:00
parent 7daa8c00c7
commit 0e4ec26b62
No known key found for this signature in database
GPG Key ID: 40AB22FA416C7019
34 changed files with 435 additions and 341 deletions

View File

@ -1,33 +1,22 @@
## Features targeted for 22.1.0.0-alpha.0
* `[PlutoGUI]` Initial implementation of the new font renderer
* Full rewrite
* High quality font rendering
* Subpixel rendering support [?]
* Possibly a new system for bitmap fonts
* Improve upon the support of thread-local Pluto instances
* The long term goal is to allow an unlimited amount of Pluto applications at any given time
## Features targeted for 22.2.0.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.
Stage switching and asset management are handled by the engine.
* Upon stage switch
* The layer subsystem
* A "layer", in this context, is a set of assets bound together
by programming logic.
Layer switching and asset management are handled by the engine.
* Layers can be stacked on top of each other and run sequentially
from bottom to top
* Upon layer switch
1. Unload unused assets
2. Load new assets
* Provide multiple means of stage switching
* Three modes with the initial release
* Provide multiple means of layer switching
* Two modes with the initial release, asynchronous switching will come at a later date
1. Instant switch
* Assets will be loaded and unloaded synchronously
* The stage switch will happen in one frame
* The layer switch will happen in one frame
2. Deferred switch
* The stage will continue running until all assets load
* The layer will continue running until all assets load
* Assets will load synchronously, but at a slower pace
to avoid frame stutter
3. Asynchronous switch
* Assets will be loaded in asynchronously, where applicable
* Falls back to deferred switching for synchronous loading,
such as OpenGL texture upload
* Automated asset loading
* All asset management will eventually be handled by `PlutoCore`
* This includes audio clips, textures, sprites
@ -39,3 +28,23 @@
instances
* Allow stages to be inherited from, creating a stack-like structure
* `[PlutoAudio]` Integrate the Audio API with the Stage API
## Features targeted for an unspecified future release
* `[PlutoSpritesheet]` Expanded capabilities
* Support for 9-slice rendering
* Support for animated sprite rendering
* Support for multidirectional sprite rendering
* A spritesheet skeleton editor
* `[PlutoRuntime]`
* Asynchronous switch
* Assets will be loaded in asynchronously, where applicable
* Falls back to deferred switching for synchronous loading,
such as OpenGL texture upload
* `[PlutoGUI]` A fully-fledged GUI engine
* Improve font-rendering capabilities
* Subpixel rendering support [?]
* Reimplement support for bitmap fonts
* Improve upon the support of thread-local Pluto instances
* The long term goal is to allow an unlimited amount of Pluto applications at any given time

View File

@ -50,7 +50,6 @@ See `NEXT_RELEASE_DRAFT.md` for details.
### Very high priority
[ *Implemented in the current release.* ]
* Improve image loading capabilities, possibly rewrite PlutoLib#TPL
* The stage system and automated asset loading
### High priority
[ *Implemented in the next release.* ]

View File

@ -1,3 +1,15 @@
## 22.2.0.0-alpha.0
* `[PlutoRuntime,PlutoCore]` **Initial implementation of the layer system (formerly known as "stage")**
* `[PlutoComponent]` **Added support for dependencies and strengtened type checks**
* `[PlutoComponent]` *Removed* `IComponent` as it was redundant to `AbstractComponent`
* `[Pluto*]` *Removed* JSR 305 annotations in favor of JetBrains annotations
* `[Pluto*]` *Removed* the `ConstantExpression` annotation in favor of `Contract`
* `[PlutoRuntime]` *Moved* `ThreadSensitive` to `org.plutoengine.address`
* `[PlutoAudio]` Transformed into a PlutoLocalComponent
* `[PlutoAudio]` `IAudioStream` is now `AutoCloseable`
* `[PlutoCore]` `InputBus` now tied to a specific `Display` instead of searching for one in the Local
* `[PlutoCore]` Separated `Mouse` and `Keyboard` from `InputBus` into child components
## 22.1.0.0-alpha.1
* `plutoengine-demos/basic-application` Made the gradient in the fragment font shader rotatable

View File

@ -17,13 +17,13 @@ object Versions {
const val steamworks4jServerVersion = "1.8.0"
const val versionYear = 22
const val versionMajor = 1
const val versionMajor = 2
const val versionMinor = 0
const val versionPatch = 0
const val isPrerelease = true
const val prereleaseName = "alpha"
const val prerealeaseUpdate = 1
const val prerealeaseUpdate = 0
val versionFull =
if (isPrerelease)

View File

@ -4,6 +4,9 @@ import org.lwjgl.stb.STBVorbis;
import org.lwjgl.stb.STBVorbisInfo;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;
import org.plutoengine.buffer.BufferHelper;
import org.plutoengine.logger.Logger;
import org.plutoengine.logger.SmartSeverity;
import java.io.IOException;
import java.nio.ByteBuffer;
@ -12,10 +15,6 @@ import java.nio.ShortBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import org.plutoengine.buffer.BufferHelper;
import org.plutoengine.logger.Logger;
import org.plutoengine.logger.SmartSeverity;
public class AudioLoader
{
/**

View File

@ -2,7 +2,7 @@ package org.plutoengine.audio;
import java.nio.ShortBuffer;
public interface IAudioStream
public interface IAudioStream extends AutoCloseable
{
int getSamples(ShortBuffer pcm);

View File

@ -4,10 +4,11 @@ import org.joml.Vector3f;
import org.lwjgl.openal.*;
import org.lwjgl.system.MemoryUtil;
import org.plutoengine.Pluto;
import org.plutoengine.component.ComponentToken;
import org.plutoengine.component.PlutoLocalComponent;
import org.plutoengine.logger.Logger;
import org.plutoengine.logger.SmartSeverity;
import javax.annotation.concurrent.ThreadSafe;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
@ -15,16 +16,21 @@ import java.nio.IntBuffer;
* @author 493msi
*
*/
@ThreadSafe
public class AudioEngine
public class AudioEngine extends PlutoLocalComponent
{
private static final ThreadLocal<Long> device = ThreadLocal.withInitial(() -> MemoryUtil.NULL);
private long device = MemoryUtil.NULL;
private long context = MemoryUtil.NULL;
private ALCapabilities capabilities;
private static final ThreadLocal<Long> context = ThreadLocal.withInitial(() -> MemoryUtil.NULL);
public static final ComponentToken<AudioEngine> TOKEN = ComponentToken.create(AudioEngine::new);
private static final ThreadLocal<ALCapabilities> capabilities = new ThreadLocal<>();
private AudioEngine()
{
public static void initialize()
}
@Override
protected void onMount(ComponentDependencyManager manager)
{
var devicePtr = ALC10.alcOpenDevice((ByteBuffer) null);
if (devicePtr == MemoryUtil.NULL)
@ -35,7 +41,7 @@ public class AudioEngine
return;
}
device.set(devicePtr);
this.device = devicePtr;
var contextPtr = ALC10.alcCreateContext(devicePtr, (IntBuffer) null);
if (contextPtr == MemoryUtil.NULL)
@ -48,7 +54,7 @@ public class AudioEngine
return;
}
context.set(contextPtr);
this.context = contextPtr;
EXTThreadLocalContext.alcSetThreadContext(contextPtr);
@ -61,25 +67,27 @@ public class AudioEngine
Logger.logf(SmartSeverity.AUDIO, "OpenAL11: %b\n", alCapabilities.OpenAL11);
}
capabilities.set(alCapabilities);
this.capabilities = alCapabilities;
Logger.log(SmartSeverity.AUDIO_PLUS, "Audio engine started.");
}
public static void setSpeed(Vector3f speed)
public void setSpeed(Vector3f speed)
{
AL10.alListener3f(AL10.AL_VELOCITY, speed.x, speed.y, speed.z);
}
public static void setPosition(Vector3f position)
public void setPosition(Vector3f position)
{
AL10.alListener3f(AL10.AL_POSITION, position.x, position.y, position.z);
}
public static void setVolume(float volume)
public void setVolume(float volume)
{
AL10.alListenerf(AL10.AL_GAIN, volume);
}
public static void setOrientation(Vector3f at, Vector3f up)
public void setOrientation(Vector3f at, Vector3f up)
{
float[] data = new float[6];
data[0] = at.x;
@ -91,18 +99,29 @@ public class AudioEngine
AL10.alListenerfv(AL10.AL_ORIENTATION, data);
}
public static boolean isReady()
public boolean isReady()
{
return capabilities.get() != null;
return this.capabilities != null;
}
public static void exit()
@Override
protected void onUnmount()
{
Logger.log(SmartSeverity.AUDIO_MINUS, "Shutting down the audio engine.");
EXTThreadLocalContext.alcSetThreadContext(MemoryUtil.NULL);
ALC10.alcDestroyContext(context.get());
ALC10.alcCloseDevice(device.get());
context.remove();
device.remove();
ALC10.alcDestroyContext(this.context);
ALC10.alcCloseDevice(this.device);
this.context = MemoryUtil.NULL;
this.device = MemoryUtil.NULL;
this.capabilities = null;
}
@Override
public boolean isUnique()
{
return true;
}
}

View File

@ -3,12 +3,11 @@ package org.plutoengine.audio.al;
import org.lwjgl.openal.AL10;
import org.lwjgl.openal.SOFTDirectChannels;
import org.lwjgl.system.MemoryUtil;
import org.plutoengine.audio.IAudioStream;
import org.plutoengine.audio.ISeekableAudioTrack;
import java.nio.ShortBuffer;
import org.plutoengine.audio.ISeekableAudioTrack;
import org.plutoengine.audio.IAudioStream;
public class AudioTrack extends AudioSource
{
private static final int BUFFER_SIZE_PER_CHANNEL = 16384;
@ -56,10 +55,8 @@ public class AudioTrack extends AudioSource
public boolean play()
{
if (this.track instanceof ISeekableAudioTrack)
{
((ISeekableAudioTrack) this.track).rewind();
}
if (this.track instanceof ISeekableAudioTrack seekableAudioTrack)
seekableAudioTrack.rewind();
for (int buf : this.buffers)
{
@ -82,9 +79,7 @@ public class AudioTrack extends AudioSource
int samplesPerChannel = this.track.getSamples(this.pcm);
if (samplesPerChannel == 0)
{
return;
}
var samples = samplesPerChannel * this.track.getChannels();
this.pcm.limit(samples);

View File

@ -6,8 +6,9 @@ plugins {
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("org.jetbrains", "annotations", "23.0.0")
implementation("org.apache.commons", "commons-lang3", "3.12.0")
implementation("org.apache.commons", "commons-collections4", "4.4")
api("com.google.code.findbugs:jsr305:3.0.2")
}

View File

@ -1,33 +1,61 @@
package org.plutoengine.component;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
public abstract class AbstractComponent implements IComponent
public abstract class AbstractComponent<T extends AbstractComponent<? super T>>
{
private static final AtomicLong ID_SOURCE = new AtomicLong();
private final long id;
private ComponentDependencyManager manager;
protected AbstractComponent()
{
this.id = ID_SOURCE.getAndIncrement();
}
@Override
public long getID()
{
return this.id;
}
@Override
public void onMount() throws Exception
/**
* 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
*/
public abstract boolean isUnique();
void initialize(ComponentManager<? super T> manager) throws Exception
{
this.manager = this.new ComponentDependencyManager(manager);
onMount(this.manager);
}
protected void onMount(ComponentDependencyManager manager) throws Exception
{
}
@Override
public void onUnmount() throws Exception
void destroy(ComponentManager<? super T> manager) throws Exception
{
if (this.manager.dependencies != null)
{
this.manager.dependencies.forEach(manager::removeComponent);
this.manager.dependencies.clear();
}
this.onUnmount();
}
protected void onUnmount() throws Exception
{
}
@ -37,7 +65,7 @@ public abstract class AbstractComponent implements IComponent
{
if (this == o) return true;
if (o == null || this.getClass() != o.getClass()) return false;
AbstractComponent that = (AbstractComponent) o;
AbstractComponent<?> that = (AbstractComponent<?>) o;
return this.id == that.id;
}
@ -46,4 +74,27 @@ public abstract class AbstractComponent implements IComponent
{
return Objects.hash(this.id);
}
public class ComponentDependencyManager
{
private final ComponentManager<? super T> manager;
private Deque<T> dependencies;
private ComponentDependencyManager(ComponentManager<? super T> componentManager)
{
this.manager = componentManager;
}
public <R extends T> R declareDependency(ComponentToken<R> token)
{
if (this.dependencies == null)
this.dependencies = new ArrayDeque<>();
var dependency = this.manager.addComponent(token);
this.dependencies.push(dependency);
return dependency;
}
}
}

View File

@ -4,21 +4,21 @@ 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 org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
import java.util.*;
import java.util.stream.Stream;
public class ComponentManager<R extends AbstractComponent>
public class ComponentManager<R extends AbstractComponent<R>>
{
private final Class<R> base;
protected final MultiValuedMap<ComponentToken<? extends R>, R> components;
protected final Map<R, ComponentToken<? extends R>> tokens;
protected final MultiValuedMap<ComponentToken<?>, R> components;
protected final Map<R, ComponentToken<?>> tokens;
protected final MultiValuedMap<Class<?>, R> implementationProviders;
protected final MultiValuedMap<R, Class<?>> implementationReceivers;
public ComponentManager(@Nonnull Class<R> base)
public ComponentManager(@NotNull Class<R> base)
{
this.base = base;
this.components = new HashSetValuedHashMap<>();
@ -27,7 +27,7 @@ public class ComponentManager<R extends AbstractComponent>
this.implementationReceivers = new ArrayListValuedHashMap<>();
}
public <T extends R> T addComponent(@Nonnull ComponentToken<T> token)
public <T extends R> T addComponent(@NotNull ComponentToken<T> token)
{
T component = token.createInstance();
var clazz = component.getClass();
@ -52,7 +52,7 @@ public class ComponentManager<R extends AbstractComponent>
try
{
component.onMount();
component.initialize(this);
}
catch (Exception e)
{
@ -67,33 +67,33 @@ public class ComponentManager<R extends AbstractComponent>
return this.base;
}
public <T extends R> Stream<T> streamComponents(@Nonnull Class<T> componentClazz)
public <T extends R> Stream<T> streamComponents(@NotNull 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
public <T extends R> T getComponent(@NotNull 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
public <T extends R> T getComponent(@NotNull Class<T> componentClazz, @NotNull Comparator<T> heuristic) throws NoSuchElementException
{
return this.streamComponents(componentClazz)
.max(heuristic)
.orElseThrow();
}
public <T extends R> List<T> getComponents(@Nonnull Class<T> componentClazz)
public <T extends R> List<T> getComponents(@NotNull Class<T> componentClazz)
{
return this.streamComponents(componentClazz).toList();
}
public void removeComponent(@Nonnull R component) throws IllegalArgumentException
public void removeComponent(@NotNull R component) throws IllegalArgumentException
{
var token = this.tokens.remove(component);
@ -108,7 +108,7 @@ public class ComponentManager<R extends AbstractComponent>
try
{
component.onUnmount();
component.destroy(this);
}
catch (Exception e)
{
@ -116,7 +116,7 @@ public class ComponentManager<R extends AbstractComponent>
}
}
public <T extends R> void removeComponents(@Nonnull ComponentToken<T> componentToken)
public <T extends R> void removeComponents(@NotNull ComponentToken<T> componentToken)
{
var activeComponents = this.components.remove(componentToken);

View File

@ -1,11 +1,12 @@
package org.plutoengine.component;
import javax.annotation.Nonnull;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
public final class ComponentToken<T extends IComponent>
public final class ComponentToken<T extends AbstractComponent<? super T>>
{
private static final AtomicLong ID_SOURCE = new AtomicLong();
@ -18,7 +19,7 @@ public final class ComponentToken<T extends IComponent>
this.supplier = valueSupplier;
}
public static <T extends IComponent> ComponentToken<T> create(@Nonnull Supplier<T> valueSupplier)
public static <G extends AbstractComponent<? super G>> ComponentToken<G> create(@NotNull Supplier<G> valueSupplier)
{
return new ComponentToken<>(valueSupplier);
}

View File

@ -1,22 +0,0 @@
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

@ -243,9 +243,8 @@ public abstract class PlutoApplication
this.display.createOpenGLCapabilities();
components.addComponent(InputBus.TOKEN);
AudioEngine.initialize();
var inputBus = components.addComponent(InputBus.fromDisplay(this.display));
var audioEngine = components.addComponent(AudioEngine.TOKEN);
var modLoader = components.addComponent(ModLoader.TOKEN);
@ -259,7 +258,6 @@ public abstract class PlutoApplication
this.display.setIcons(icons);
}
while (!this.display.isClosing())
{
GL33.glViewport(0, 0, this.display.getWidth(), this.display.getHeight());
@ -270,20 +268,19 @@ public abstract class PlutoApplication
this.display.swapBuffers();
InputBus.resetStates();
inputBus.resetStates();
this.display.pollEvents();
}
AudioEngine.exit();
components.removeComponent(audioEngine);
modLoader.unload();
GL.destroy();
components.removeComponent(modLoader);
components.removeComponents(InputBus.TOKEN);
components.removeComponent(inputBus);
this.display.destroy();

View File

@ -1,100 +1,37 @@
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()
public static ComponentToken<InputBus> fromDisplay(Display display)
{
return ComponentToken.create(() -> new InputBus(display));
}
private static InputBus instance()
private final Display display;
private Keyboard keyboard;
private Mouse mouse;
private InputBus(Display display)
{
return PlutoLocal.components().getComponent(InputBus.class);
this.display = display;
}
@Override
public void onMount()
protected void onMount(ComponentDependencyManager manager)
{
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);
this.keyboard = manager.declareDependency(ComponentToken.create(() -> new Keyboard(this.display.getWindowPointer())));
this.mouse = manager.declareDependency(ComponentToken.create(() -> new Mouse(this.display.getWindowPointer())));
}
@Override
public void onUnmount()
public void resetStates()
{
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();
this.keyboard.resetStates();
this.mouse.resetStates();
}
@Override
@ -103,81 +40,4 @@ public class InputBus extends PlutoLocalComponent
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

@ -0,0 +1,78 @@
package org.plutoengine.input;
import org.lwjgl.glfw.GLFW;
import org.plutoengine.component.PlutoLocalComponent;
import org.plutoengine.input.callback.KeyboardCharInput;
import org.plutoengine.input.callback.KeyboardInputCallback;
public class Keyboard extends PlutoLocalComponent
{
private final KeyboardInputCallback keyboard = new KeyboardInputCallback();
private final KeyboardCharInput charInput = new KeyboardCharInput();
private final long windowPointer;
Keyboard(long windowPointer)
{
this.windowPointer = windowPointer;
}
public KeyboardInputCallback keyboard()
{
return this.keyboard;
}
public KeyboardCharInput charInput()
{
return this.charInput;
}
void resetStates()
{
this.keyboard.resetPressed();
this.charInput.reset();
}
@Override
protected void onMount(ComponentDependencyManager manager)
{
GLFW.glfwSetKeyCallback(this.windowPointer, this.keyboard);
GLFW.glfwSetCharCallback(this.windowPointer, this.charInput);
}
@Override
protected void onUnmount() throws Exception
{
GLFW.glfwSetKeyCallback(this.windowPointer, null);
GLFW.glfwSetCharCallback(this.windowPointer, null);
this.keyboard.free();
this.charInput.free();
}
public boolean pressed(int key)
{
return this.keyboard.hasBeenPressed(key);
}
public boolean released(int key)
{
return this.keyboard.hasBeenReleased(key);
}
public boolean isKeyDown(int key)
{
return this.keyboard.isKeyDown(key);
}
public String getTypedText()
{
return this.charInput.getTypedText();
}
@Override
public boolean isUnique()
{
return false;
}
}

View File

@ -0,0 +1,119 @@
package org.plutoengine.input;
import org.lwjgl.glfw.GLFW;
import org.plutoengine.component.PlutoLocalComponent;
import org.plutoengine.input.callback.CursorPositionCallback;
import org.plutoengine.input.callback.MouseButtonCallback;
import org.plutoengine.input.callback.ScrollInputCallback;
public class Mouse extends PlutoLocalComponent
{
private final MouseButtonCallback mouseButton = new MouseButtonCallback();
private final CursorPositionCallback cursorPosition = new CursorPositionCallback();
private final ScrollInputCallback scroll = new ScrollInputCallback();
private final long windowPointer;
Mouse(long windowPointer)
{
this.windowPointer = windowPointer;
}
public MouseButtonCallback mouseButtons()
{
return this.mouseButton;
}
public ScrollInputCallback scroll()
{
return this.scroll;
}
public CursorPositionCallback cursorPosition()
{
return this.cursorPosition;
}
void resetStates()
{
this.mouseButton.reset();
this.scroll.reset();
this.cursorPosition.reset();
}
@Override
protected void onMount(ComponentDependencyManager manager)
{
GLFW.glfwSetMouseButtonCallback(this.windowPointer, this.mouseButton);
GLFW.glfwSetCursorPosCallback(this.windowPointer, this.cursorPosition);
GLFW.glfwSetScrollCallback(this.windowPointer, this.scroll);
}
@Override
protected void onUnmount() throws Exception
{
GLFW.glfwSetMouseButtonCallback(this.windowPointer, null);
GLFW.glfwSetCursorPosCallback(this.windowPointer, null);
GLFW.glfwSetScrollCallback(this.windowPointer, null);
this.mouseButton.free();
this.cursorPosition.free();
this.scroll.free();
}
public boolean clicked(int button)
{
return this.mouseButton.buttonClicked[button];
}
public boolean released(int button)
{
return this.mouseButton.buttonReleased[button];
}
public boolean isButtonDown(int button)
{
return this.mouseButton.buttonDown[button];
}
public double getX()
{
return this.cursorPosition.getX();
}
public double getY()
{
return this.cursorPosition.getY();
}
public boolean isInside(int x1, int y1, int x2, int y2)
{
return this.cursorPosition.isInside(x1, y1, x2, y2);
}
public double getDX()
{
return this.cursorPosition.getDeltaX();
}
public double getDY()
{
return this.cursorPosition.getDeltaY();
}
public double getScrollX()
{
return this.scroll.getXScroll();
}
public double getScrollY()
{
return this.scroll.getYScroll();
}
@Override
public boolean isUnique()
{
return false;
}
}

View File

@ -1,4 +1,4 @@
package org.plutoengine.input;
package org.plutoengine.input.callback;
import org.lwjgl.glfw.GLFWCursorPosCallback;

View File

@ -1,4 +1,4 @@
package org.plutoengine.input;
package org.plutoengine.input.callback;
import org.lwjgl.glfw.GLFWCharCallback;

View File

@ -1,4 +1,4 @@
package org.plutoengine.input;
package org.plutoengine.input.callback;
import org.lwjgl.glfw.GLFWKeyCallback;

View File

@ -1,4 +1,4 @@
package org.plutoengine.input;
package org.plutoengine.input.callback;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWMouseButtonCallback;

View File

@ -1,4 +1,4 @@
package org.plutoengine.input;
package org.plutoengine.input.callback;
import org.lwjgl.glfw.GLFWScrollCallback;

View File

@ -5,7 +5,7 @@ import org.lwjgl.opengl.*;
import org.lwjgl.system.MemoryUtil;
import org.plutoengine.Pluto;
import org.plutoengine.address.ThreadSensitive;
import org.plutoengine.annotation.ThreadSensitive;
import org.plutoengine.component.PlutoLocalComponent;
import org.plutoengine.gl.GLDebugInfo;
import org.plutoengine.logger.Logger;

View File

@ -1,6 +1,6 @@
package org.plutoengine;
import org.plutoengine.address.ThreadSensitive;
import org.plutoengine.annotation.ThreadSensitive;
import org.plutoengine.component.PlutoLocalComponent;
import org.plutoengine.component.ComponentManager;

View File

@ -1,8 +1,7 @@
package org.plutoengine;
import org.plutoengine.address.ConstantExpression;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
import java.util.Objects;
public final class PlutoVersion implements IVersion<PlutoVersion>
@ -75,12 +74,11 @@ public final class PlutoVersion implements IVersion<PlutoVersion>
return this.prereleaseNumber;
}
@ConstantExpression
public static PlutoVersion of(@Nonnull String str)
public static PlutoVersion of(@NotNull String str)
{
assert !str.isBlank();
assert str.matches("[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+([+\\-][a-zA-Z0-9]+(\\.[0-9]+)?)?");
assert str.matches("\\d+\\.\\d+\\.\\d+\\.\\d+([+\\-][a-zA-Z\\d]+(\\.\\d+)?)?");
var parts = str.split("[+\\-]");
@ -141,7 +139,7 @@ public final class PlutoVersion implements IVersion<PlutoVersion>
}
@Override
public int compareTo(@Nonnull PlutoVersion o)
public int compareTo(@NotNull PlutoVersion o)
{
int yearDiff = this.year - o.year;
if (yearDiff != 0)

View File

@ -1,22 +0,0 @@
package org.plutoengine.address;
import javax.annotation.meta.TypeQualifier;
import java.lang.annotation.*;
/**
* Denotes that the target field or method should be a constant expression - it is final, has no state
* and always yields the same deterministic result for given input. Generally, annotated methods
* should be thread-safe, however this is not required.
*
* @author 493msi
*
* @since 20.2.0.0-alpha.3
* */
@TypeQualifier
@Documented
@Retention(RetentionPolicy.CLASS)
@Target({ ElementType.METHOD, ElementType.FIELD })
public @interface ConstantExpression
{
}

View File

@ -1,9 +1,6 @@
package org.plutoengine.address;
package org.plutoengine.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.*;
/**
* <p>
@ -22,6 +19,7 @@ import java.lang.annotation.Target;
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
@Inherited
public @interface ThreadSensitive
{
/**

View File

@ -1,5 +1,5 @@
package org.plutoengine.component;
public abstract class PlutoGlobalComponent extends AbstractComponent
public abstract class PlutoGlobalComponent extends AbstractComponent<PlutoGlobalComponent>
{
}

View File

@ -1,6 +1,9 @@
package org.plutoengine.component;
public abstract class PlutoLocalComponent extends AbstractComponent
import org.plutoengine.annotation.ThreadSensitive;
@ThreadSensitive(localContexts = true)
public abstract class PlutoLocalComponent extends AbstractComponent<PlutoLocalComponent>
{
}

View File

@ -1,14 +1,14 @@
package org.plutoengine.logger;
import org.plutoengine.component.ComponentToken;
import org.plutoengine.component.PlutoGlobalComponent;
import org.plutoengine.resource.filesystem.ResourceManager;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.file.Files;
import java.time.LocalDateTime;
import org.plutoengine.component.ComponentToken;
import org.plutoengine.component.PlutoGlobalComponent;
import org.plutoengine.resource.filesystem.ResourceManager;
/**
* <p>
* A simple static logger writing to both standard output and a log file.
@ -48,7 +48,7 @@ public class Logger extends PlutoGlobalComponent
* @since pre-alpha
* */
@Override
public void onMount() throws Exception
protected void onMount(ComponentDependencyManager manager) throws Exception
{
this.onUnmount();
@ -93,7 +93,7 @@ public class Logger extends PlutoGlobalComponent
* @since pre-alpha
* */
@Override
public void onUnmount() throws Exception
protected void onUnmount() throws Exception
{
if (fileLog != null)
fileLog.close();

View File

@ -1,6 +1,7 @@
package org.plutoengine.logger;
import javax.annotation.Nonnull;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.io.OutputStream;
@ -37,14 +38,14 @@ public class OutputSplitStream extends OutputStream
}
@Override
public void write(@Nonnull byte[] b) throws IOException
public void write(byte @NotNull [] b) throws IOException
{
this.outputStreamA.write(b);
this.outputStreamB.write(b);
}
@Override
public void write(@Nonnull byte[] b, int off, int len) throws IOException
public void write(byte @NotNull [] b, int off, int len) throws IOException
{
this.outputStreamA.write(b, off, len);
this.outputStreamB.write(b, off, len);

View File

@ -3,19 +3,15 @@ package org.plutoengine.resource.filesystem;
import org.plutoengine.address.VirtualAddress;
import org.plutoengine.mod.Mod;
import javax.annotation.concurrent.ThreadSafe;
import java.io.Closeable;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.ProviderNotFoundException;
import java.nio.file.spi.FileSystemProvider;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
@ThreadSafe
public class ResourceManager implements Closeable
{
public static final Path GLOBAL_ROOT = Path.of("");

View File

@ -1,18 +1,20 @@
package org.plutoengine.tpl;
import javax.annotation.Nullable;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Path;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.BufferUtils;
import org.plutoengine.logger.Logger;
import org.plutoengine.logger.SmartSeverity;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* Quick ABGR (8-bit per channel, 32 bits per pixel) and grayscale image loader for OpenGL textures.
* Color component swizzling may be needed.

View File

@ -12,7 +12,7 @@ import org.plutoengine.graphics.ImmediateFontRenderer;
import org.plutoengine.graphics.PlutoGUIMod;
import org.plutoengine.graphics.RectangleRenderer2D;
import org.plutoengine.graphics.gui.FontShader;
import org.plutoengine.input.InputBus;
import org.plutoengine.input.Keyboard;
import org.plutoengine.libra.paint.LiGradientPaint;
import org.plutoengine.libra.paint.LiPaint;
import org.plutoengine.libra.text.shaping.TextStyleOptions;
@ -92,7 +92,7 @@ public class Main extends PlutoApplication
welcomeStyle.setPaint(LiPaint.horizontaLinearGradient(stops));
ImmediateFontRenderer.drawString(0, 100, "Welcome to PlutoEngine v. %s!".formatted(Pluto.VERSION), BasicApplicationDemoMod.font, welcomeStyle);
if (InputBus.Keyboard.pressed(GLFW.GLFW_KEY_R))
if (PlutoLocal.components().getComponent(Keyboard.class).pressed(GLFW.GLFW_KEY_R))
{
var shader = PlutoGUIMod.fontShader;