From 2b065249eee6e49114637f91a6e37cb75df9d707 Mon Sep 17 00:00:00 2001 From: Tefek <493msi@gmail.com> Date: Tue, 8 Sep 2020 23:32:20 +0200 Subject: [PATCH] TPL refactor, unified line endings, JavaDoc and full switch to SmartSeverity --- README.md | 1 + UPDATE_NOTES.md | 20 +- .../tefek/pluto/engine/audio/AudioLoader.java | 550 +++++++-------- .../pluto/engine/audio/al/AudioEngine.java | 256 +++---- .../pluto/engine/audio/al/AudioTrack.java | 248 +++---- .../engine/input/CursorPositionCallback.java | 108 ++- .../engine/input/KeyboardInputCallback.java | 116 ++-- .../engine/input/MouseButtonCallback.java | 66 +- .../engine/input/ScrollInputCallback.java | 92 +-- .../engine/graphics/font/FontManager.java | 204 +++--- .../engine/graphics/font/FontShader.java | 82 +-- .../pluto/engine/gui/font/CharacterInfo.java | 60 +- .../cz/tefek/pluto/engine/gui/font/Font.java | 118 ++-- .../pluto/engine/gui/font/FontRenderer.java | 622 ++++++++--------- .../src/main/java/cz/tefek/pluto/Pluto.java | 25 +- .../java/cz/tefek/pluto/chrono/MiniTime.java | 634 +++++++++--------- .../cz/tefek/pluto/eventsystem/EventData.java | 20 +- .../io/asl/resource/ResourceAddress.java | 37 +- .../io/asl/resource/type/ResourceImage.java | 9 +- .../resource/type/ResourceInputStream.java | 80 +-- .../cz/tefek/pluto/io/asl/textio/TextIn.java | 156 ++--- .../cz/tefek/pluto/io/asl/textio/TextOut.java | 56 +- .../java/cz/tefek/pluto/io/logger/Logger.java | 2 +- .../cz/tefek/pluto/io/logger/Severity.java | 74 +- .../java/cz/tefek/pluto/math/CubicBezier.java | 168 ++--- .../java/cz/tefek/pluto/modloader/Mod.java | 282 ++++---- .../tefek/pluto/modloader/ModClassLoader.java | 308 ++++----- .../cz/tefek/pluto/modloader/ModEntry.java | 78 +-- .../tefek/pluto/modloader/ModInstaller.java | 238 +++---- .../tefek/pluto/modloader/ModLoaderCore.java | 542 +++++++-------- .../pluto/modloader/ModLoadingPhase.java | 34 +- .../tefek/pluto/modloader/event/ModLoad.java | 42 +- .../pluto/modloader/event/ModLoadEvent.java | 30 +- .../pluto/modloader/event/ModPostLoad.java | 42 +- .../modloader/event/ModPostLoadEvent.java | 36 +- .../pluto/modloader/event/ModPreLoad.java | 42 +- .../modloader/event/ModPreLoadEvent.java | 38 +- .../pluto/modloader/event/ModUnload.java | 40 +- .../java/cz/tefek/pluto/tpl/TPJImage.java | 35 - .../src/main/java/cz/tefek/pluto/tpl/TPL.java | 499 ++++++++------ .../java/cz/tefek/pluto/tpl/TPNImage.java | 105 ++- .../engine/graphics/gl/vao/QuadPresets.java | 114 ++-- .../engine/graphics/gl/vao/VertexArray.java | 290 ++++---- .../graphics/gl/vao/VertexArrayBuilder.java | 84 +-- .../engine/graphics/gl/vbo/ArrayBuffer.java | 124 ++-- .../graphics/gl/vbo/FloatArrayBuffer.java | 50 +- .../graphics/gl/vbo/IndexArrayBuffer.java | 100 +-- .../tefek/pluto/engine/shader/ShaderBase.java | 72 +- .../pluto/engine/graphics/Renderer2D.java | 484 ++++++------- .../tefek/pluto/engine/graphics/Shader2D.java | 140 ++-- .../engine/graphics/ShaderRectangle2D.java | 140 ++-- .../sprite/DisposablePlaceholderSprite.java | 4 +- .../spritesheet/TiledSpriteSheet.java | 6 +- .../pluto/engine/buffer/BufferHelper.java | 394 +++++------ .../pluto/engine/buffer/GLFWImageUtil.java | 28 +- .../tefek/pluto/engine/display/Display.java | 9 +- .../tefek/pluto/engine/display/Framerate.java | 150 ++--- .../pluto/engine/math/ProjectionMatrix.java | 132 ++-- .../engine/graphics/texture/MagFilter.java | 48 +- .../engine/graphics/texture/MinFilter.java | 70 +- .../engine/graphics/texture/Texture.java | 9 +- .../engine/graphics/texture/WrapMode.java | 68 +- .../texture/texture2d/RectangleTexture.java | 8 +- .../graphics/texture/texture2d/Texture2D.java | 58 +- 64 files changed, 4471 insertions(+), 4306 deletions(-) delete mode 100644 plutolib/src/main/java/cz/tefek/pluto/tpl/TPJImage.java diff --git a/README.md b/README.md index 0f9bfa3..00c1d49 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ version numbers.* ### Normal priority * The collision system for PlutoStatic + * Improve image loading capabilities, possibly rewrite PlutoLib#TPL ### Low priority * Polishing PlutoLib diff --git a/UPDATE_NOTES.md b/UPDATE_NOTES.md index 8f0a652..30cdab7 100644 --- a/UPDATE_NOTES.md +++ b/UPDATE_NOTES.md @@ -6,6 +6,22 @@ `OutputSplitStream` * `Logger`'s output filenames now look cleaner with `log--YYYY-MM-DD--HH-MM-SS.txt` * `[Logger#setup]` can now throw `IOException` - * `[PlutoCore]` As a result, `[PlutoApplication#run]` can now throw `Exception` + * `[PlutoCore]` As a result, `[PlutoApplication#run]` can now throw `Exception` +* `[PlutoLib]` Updated JavaDoc in `ResourceAddress`, `TPL`, `TPNImage` +* `[PlutoLib]` Added `ResourceAddress#openRead` and `ResourceAddress#openWrite` +* `[PlutoLib]` Code cleanup in `MiniTime`, `TPL` + * `[PlutoLib]` Deprecated `TPL#load(String)` in favor of `TPL#load(ResourceAddress)`, + `TPL#load(File)` and `TPL#load(Path)` + * `[PlutoTexturing]` Deprecated the `String` variant of `Texture#load` + to reflect this change + * `[PlutoSpritesheet]` Removed the usage of this method + in `DisposablePlaceholderSprite` + * `[PlutoLib]` Added an option to flip loaded images with `TPL#loadImageSpecial` + and added respective `TPL#loadSpecial` for every `TPL#load` + * `[PlutoLib]` *Removed* `TPJImage` + * `[PlutoLib]` Removed `TPL#loadPixels` + * `[PlutoCore]` Updated `GLFWImageUtil` to remove the usage of `TPJImage` * `[PlutoCore]` `[PlutoApplication]` now properly closes the `Logger` on exit -* `[PlutoLib]` Various typo fixes \ No newline at end of file +* `[PlutoLib]` Various typo fixes +* `[Pluto*]` Deprecated `Severity` for `SmartSeverity` and replaced all usages +* `[Pluto*]` Deprecated `CRLF` with `LF` in all Java source files \ No newline at end of file diff --git a/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/AudioLoader.java b/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/AudioLoader.java index 5edbda1..dedb0a2 100644 --- a/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/AudioLoader.java +++ b/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/AudioLoader.java @@ -1,275 +1,275 @@ -package cz.tefek.pluto.engine.audio; - -import org.lwjgl.stb.STBVorbis; -import org.lwjgl.stb.STBVorbisInfo; -import org.lwjgl.system.MemoryStack; -import org.lwjgl.system.MemoryUtil; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.IntBuffer; -import java.nio.ShortBuffer; -import java.nio.file.Files; - -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; - -public class AudioLoader -{ - /** - * Loads an audio track denoted by this {@link ResourceAddress} 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 - * in multiple audio sources at once due to the cost of seeking. - */ - public static ISeekableAudioTrack loadMemoryDecoded(ResourceAddress address) - { - - Logger.logf(SmartSeverity.AUDIO_PLUS, "Loading audio file: %s\n", address.toString()); - - try - { - return new MemoryDecodedVorbisTrack(address); - } - catch (IOException e) - { - Logger.logf(SmartSeverity.AUDIO_ERROR, "Failed to load an audio file: '%s'\n", address.toString()); - e.printStackTrace(); - return null; - } - } - - /** - * Loads an audio track denoted by this {@link ResourceAddress} into memory - * for from-memory PCM streaming. Good for frequently used small audio - * files. - */ - public static ISeekableAudioTrack loadMemoryPCM(ResourceAddress address) - { - Logger.logf(SmartSeverity.AUDIO_PLUS, "Loading audio file: %s\n", address.toString()); - - try - { - return new MemoryPCMTrack(address); - } - catch (IOException e) - { - Logger.logf(SmartSeverity.AUDIO_ERROR, "Failed to load an audio file: '%s'\n", address.toString()); - e.printStackTrace(); - return null; - } - } - - private static ByteBuffer loadIntoMemory(ResourceAddress addr) 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!"); - } - - var readData = MemoryUtil.memAlloc((int) size); - - return BufferHelper.readToByteBuffer(path, readData); - } - - private abstract static class StreamableTrack implements IAudioStream - { - protected int channels; - protected int sampleRate; - - @Override - public int getChannels() - { - return this.channels; - } - - @Override - public int getSampleRate() - { - return this.sampleRate; - } - } - - private abstract static class SeekableTrack extends StreamableTrack implements ISeekableAudioTrack - { - protected int samplesLength; - - @Override - public int getLengthInSamples() - { - return this.samplesLength; - } - } - - public static class MemoryPCMTrack extends SeekableTrack - { - private ShortBuffer pcmAudio = null; - - private int sampleOffset = 0; - - private MemoryPCMTrack(ResourceAddress address) throws IOException - { - long handle = MemoryUtil.NULL; - ByteBuffer audioBytes = null; - - try (MemoryStack stack = MemoryStack.stackPush()) - { - audioBytes = loadIntoMemory(address); - - IntBuffer error = stack.mallocInt(1); - handle = STBVorbis.stb_vorbis_open_memory(audioBytes, error, null); - - if (handle == MemoryUtil.NULL) - { - this.close(); - throw new IOException(String.format("Failed to load '%s', error code %d.\n", address.toString(), error.get(0))); - } - - STBVorbisInfo info = STBVorbisInfo.mallocStack(stack); - STBVorbis.stb_vorbis_get_info(handle, info); - - this.channels = info.channels(); - this.sampleRate = info.sample_rate(); - - // Downmix to mono, SOUNDS HORRIBLE - // - // this.channels = 1; - // this.samplesLength = STBVorbis.stb_vorbis_stream_length_in_samples(handle) * this.channels; - // this.pcmAudio = MemoryUtil.memAllocShort(this.samplesLength); - // var ptr = stack.pointers(this.pcmAudio); - // STBVorbis.stb_vorbis_get_samples_short(handle, ptr, this.samplesLength); - - this.samplesLength = STBVorbis.stb_vorbis_stream_length_in_samples(handle) * this.channels; - this.pcmAudio = MemoryUtil.memAllocShort(this.samplesLength); - STBVorbis.stb_vorbis_get_samples_short_interleaved(handle, this.channels, this.pcmAudio); - } - catch (IOException e) - { - this.close(); - throw e; - } - finally - { - MemoryUtil.memFree(audioBytes); - - if (handle != MemoryUtil.NULL) - { - STBVorbis.stb_vorbis_close(handle); - } - } - } - - @Override - public void seek(int sampleIndex) - { - this.sampleOffset = sampleIndex * this.getChannels(); - } - - @Override - public synchronized int getSamples(ShortBuffer pcm) - { - this.pcmAudio.limit(Math.min(this.sampleOffset + pcm.remaining(), this.pcmAudio.capacity())); - int read = this.pcmAudio.remaining(); - pcm.put(this.pcmAudio); - this.sampleOffset += read; - pcm.clear(); - - return read / this.getChannels(); - } - - @Override - public int getSampleOffset() - { - return this.sampleOffset / this.getChannels(); - } - - @Override - public void close() - { - MemoryUtil.memFree(this.pcmAudio); - } - } - - public static class MemoryDecodedVorbisTrack extends SeekableTrack - { - protected long handle; - - private final ByteBuffer encodedAudio; - - private MemoryDecodedVorbisTrack(ResourceAddress address) throws IOException - { - try - { - this.encodedAudio = loadIntoMemory(address); - } - catch (IOException e) - { - this.close(); - throw e; - } - - try (MemoryStack stack = MemoryStack.stackPush()) - { - IntBuffer error = stack.mallocInt(1); - this.handle = STBVorbis.stb_vorbis_open_memory(this.encodedAudio, error, null); - - 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))); - } - - STBVorbisInfo info = STBVorbisInfo.mallocStack(stack); - STBVorbis.stb_vorbis_get_info(this.handle, info); - - this.channels = info.channels(); - this.sampleRate = info.sample_rate(); - - } - - 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); - } - - @Override - public synchronized int getSamples(ShortBuffer pcm) - { - if (this.handle == MemoryUtil.NULL) - { - return -1; - } - - return STBVorbis.stb_vorbis_get_samples_short_interleaved(this.handle, this.getChannels(), pcm); - } - - @Override - public void close() - { - MemoryUtil.memFree(this.encodedAudio); - - if (this.handle != MemoryUtil.NULL) - { - STBVorbis.stb_vorbis_close(this.handle); - this.handle = MemoryUtil.NULL; - } - } - - @Override - public synchronized void seek(int sampleIndex) - { - STBVorbis.stb_vorbis_seek(this.handle, sampleIndex); - } - - @Override - public int getSampleOffset() - { - return STBVorbis.stb_vorbis_get_sample_offset(this.handle); - } - } -} +package cz.tefek.pluto.engine.audio; + +import org.lwjgl.stb.STBVorbis; +import org.lwjgl.stb.STBVorbisInfo; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; +import java.nio.file.Files; + +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; + +public class AudioLoader +{ + /** + * Loads an audio track denoted by this {@link ResourceAddress} 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 + * in multiple audio sources at once due to the cost of seeking. + */ + public static ISeekableAudioTrack loadMemoryDecoded(ResourceAddress address) + { + + Logger.logf(SmartSeverity.AUDIO_PLUS, "Loading audio file: %s\n", address.toString()); + + try + { + return new MemoryDecodedVorbisTrack(address); + } + catch (IOException e) + { + Logger.logf(SmartSeverity.AUDIO_ERROR, "Failed to load an audio file: '%s'\n", address.toString()); + e.printStackTrace(); + return null; + } + } + + /** + * Loads an audio track denoted by this {@link ResourceAddress} into memory + * for from-memory PCM streaming. Good for frequently used small audio + * files. + */ + public static ISeekableAudioTrack loadMemoryPCM(ResourceAddress address) + { + Logger.logf(SmartSeverity.AUDIO_PLUS, "Loading audio file: %s\n", address.toString()); + + try + { + return new MemoryPCMTrack(address); + } + catch (IOException e) + { + Logger.logf(SmartSeverity.AUDIO_ERROR, "Failed to load an audio file: '%s'\n", address.toString()); + e.printStackTrace(); + return null; + } + } + + private static ByteBuffer loadIntoMemory(ResourceAddress addr) 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!"); + } + + var readData = MemoryUtil.memAlloc((int) size); + + return BufferHelper.readToByteBuffer(path, readData); + } + + private abstract static class StreamableTrack implements IAudioStream + { + protected int channels; + protected int sampleRate; + + @Override + public int getChannels() + { + return this.channels; + } + + @Override + public int getSampleRate() + { + return this.sampleRate; + } + } + + private abstract static class SeekableTrack extends StreamableTrack implements ISeekableAudioTrack + { + protected int samplesLength; + + @Override + public int getLengthInSamples() + { + return this.samplesLength; + } + } + + public static class MemoryPCMTrack extends SeekableTrack + { + private ShortBuffer pcmAudio = null; + + private int sampleOffset = 0; + + private MemoryPCMTrack(ResourceAddress address) throws IOException + { + long handle = MemoryUtil.NULL; + ByteBuffer audioBytes = null; + + try (MemoryStack stack = MemoryStack.stackPush()) + { + audioBytes = loadIntoMemory(address); + + IntBuffer error = stack.mallocInt(1); + handle = STBVorbis.stb_vorbis_open_memory(audioBytes, error, null); + + if (handle == MemoryUtil.NULL) + { + this.close(); + throw new IOException(String.format("Failed to load '%s', error code %d.\n", address.toString(), error.get(0))); + } + + STBVorbisInfo info = STBVorbisInfo.mallocStack(stack); + STBVorbis.stb_vorbis_get_info(handle, info); + + this.channels = info.channels(); + this.sampleRate = info.sample_rate(); + + // Downmix to mono, SOUNDS HORRIBLE + // + // this.channels = 1; + // this.samplesLength = STBVorbis.stb_vorbis_stream_length_in_samples(handle) * this.channels; + // this.pcmAudio = MemoryUtil.memAllocShort(this.samplesLength); + // var ptr = stack.pointers(this.pcmAudio); + // STBVorbis.stb_vorbis_get_samples_short(handle, ptr, this.samplesLength); + + this.samplesLength = STBVorbis.stb_vorbis_stream_length_in_samples(handle) * this.channels; + this.pcmAudio = MemoryUtil.memAllocShort(this.samplesLength); + STBVorbis.stb_vorbis_get_samples_short_interleaved(handle, this.channels, this.pcmAudio); + } + catch (IOException e) + { + this.close(); + throw e; + } + finally + { + MemoryUtil.memFree(audioBytes); + + if (handle != MemoryUtil.NULL) + { + STBVorbis.stb_vorbis_close(handle); + } + } + } + + @Override + public void seek(int sampleIndex) + { + this.sampleOffset = sampleIndex * this.getChannels(); + } + + @Override + public synchronized int getSamples(ShortBuffer pcm) + { + this.pcmAudio.limit(Math.min(this.sampleOffset + pcm.remaining(), this.pcmAudio.capacity())); + int read = this.pcmAudio.remaining(); + pcm.put(this.pcmAudio); + this.sampleOffset += read; + pcm.clear(); + + return read / this.getChannels(); + } + + @Override + public int getSampleOffset() + { + return this.sampleOffset / this.getChannels(); + } + + @Override + public void close() + { + MemoryUtil.memFree(this.pcmAudio); + } + } + + public static class MemoryDecodedVorbisTrack extends SeekableTrack + { + protected long handle; + + private final ByteBuffer encodedAudio; + + private MemoryDecodedVorbisTrack(ResourceAddress address) throws IOException + { + try + { + this.encodedAudio = loadIntoMemory(address); + } + catch (IOException e) + { + this.close(); + throw e; + } + + try (MemoryStack stack = MemoryStack.stackPush()) + { + IntBuffer error = stack.mallocInt(1); + this.handle = STBVorbis.stb_vorbis_open_memory(this.encodedAudio, error, null); + + 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))); + } + + STBVorbisInfo info = STBVorbisInfo.mallocStack(stack); + STBVorbis.stb_vorbis_get_info(this.handle, info); + + this.channels = info.channels(); + this.sampleRate = info.sample_rate(); + + } + + 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); + } + + @Override + public synchronized int getSamples(ShortBuffer pcm) + { + if (this.handle == MemoryUtil.NULL) + { + return -1; + } + + return STBVorbis.stb_vorbis_get_samples_short_interleaved(this.handle, this.getChannels(), pcm); + } + + @Override + public void close() + { + MemoryUtil.memFree(this.encodedAudio); + + if (this.handle != MemoryUtil.NULL) + { + STBVorbis.stb_vorbis_close(this.handle); + this.handle = MemoryUtil.NULL; + } + } + + @Override + public synchronized void seek(int sampleIndex) + { + STBVorbis.stb_vorbis_seek(this.handle, sampleIndex); + } + + @Override + public int getSampleOffset() + { + return STBVorbis.stb_vorbis_get_sample_offset(this.handle); + } + } +} diff --git a/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/al/AudioEngine.java b/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/al/AudioEngine.java index 6fd0220..4327533 100644 --- a/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/al/AudioEngine.java +++ b/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/al/AudioEngine.java @@ -1,128 +1,128 @@ -package cz.tefek.pluto.engine.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.system.MemoryUtil; - -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; - -/** - * @author 493msi - * - */ -@ThreadSafe -public class AudioEngine -{ - private static ThreadLocal device = new ThreadLocal<>() { - @Override - protected Long initialValue() - { - return MemoryUtil.NULL; - } - }; - - private static ThreadLocal context = new ThreadLocal<>() { - @Override - protected Long initialValue() - { - return MemoryUtil.NULL; - } - }; - - private static ThreadLocal capabilities = new ThreadLocal<>(); - - public static void initialize() - { - var devicePtr = ALC10.alcOpenDevice((ByteBuffer) null); - if (devicePtr == MemoryUtil.NULL) - { - Logger.log(SmartSeverity.AUDIO_ERROR, "Failed to open the default audio device."); - - // No audio device found, but the game should not crash just because we have no audio - return; - } - - device.set(devicePtr); - - var contextPtr = ALC10.alcCreateContext(devicePtr, (IntBuffer) null); - if (contextPtr == MemoryUtil.NULL) - { - ALC10.alcCloseDevice(devicePtr); - - Logger.log(SmartSeverity.AUDIO_ERROR, "Failed to create an OpenAL context."); - - // The game should not crash just because we have no audio - return; - } - - context.set(contextPtr); - - EXTThreadLocalContext.alcSetThreadContext(contextPtr); - - ALCCapabilities deviceCaps = ALC.createCapabilities(devicePtr); - var alCapabilities = AL.createCapabilities(deviceCaps); - - if (Pluto.DEBUG_MODE) - { - Logger.logf(SmartSeverity.AUDIO, "OpenAL10: %b\n", alCapabilities.OpenAL10); - Logger.logf(SmartSeverity.AUDIO, "OpenAL11: %b\n", alCapabilities.OpenAL11); - } - - capabilities.set(alCapabilities); - } - - public static void setSpeed(Vector3f speed) - { - AL10.alListener3f(AL10.AL_VELOCITY, speed.x, speed.y, speed.z); - } - - public static void setPosition(Vector3f position) - { - AL10.alListener3f(AL10.AL_POSITION, position.x, position.y, position.z); - } - - public static void setVolume(float volume) - { - AL10.alListenerf(AL10.AL_GAIN, volume); - } - - public static void setOrientation(Vector3f at, Vector3f up) - { - float[] data = new float[6]; - data[0] = at.x; - data[1] = at.y; - data[2] = at.z; - data[3] = up.x; - data[4] = up.y; - data[5] = up.z; - AL10.alListenerfv(AL10.AL_ORIENTATION, data); - } - - public static boolean isReady() - { - return capabilities.get() != null; - } - - public static void exit() - { - EXTThreadLocalContext.alcSetThreadContext(MemoryUtil.NULL); - ALC10.alcDestroyContext(context.get()); - ALC10.alcCloseDevice(device.get()); - - context.remove(); - device.remove(); - } -} +package cz.tefek.pluto.engine.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.system.MemoryUtil; + +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; + +/** + * @author 493msi + * + */ +@ThreadSafe +public class AudioEngine +{ + private static ThreadLocal device = new ThreadLocal<>() { + @Override + protected Long initialValue() + { + return MemoryUtil.NULL; + } + }; + + private static ThreadLocal context = new ThreadLocal<>() { + @Override + protected Long initialValue() + { + return MemoryUtil.NULL; + } + }; + + private static ThreadLocal capabilities = new ThreadLocal<>(); + + public static void initialize() + { + var devicePtr = ALC10.alcOpenDevice((ByteBuffer) null); + if (devicePtr == MemoryUtil.NULL) + { + Logger.log(SmartSeverity.AUDIO_ERROR, "Failed to open the default audio device."); + + // No audio device found, but the game should not crash just because we have no audio + return; + } + + device.set(devicePtr); + + var contextPtr = ALC10.alcCreateContext(devicePtr, (IntBuffer) null); + if (contextPtr == MemoryUtil.NULL) + { + ALC10.alcCloseDevice(devicePtr); + + Logger.log(SmartSeverity.AUDIO_ERROR, "Failed to create an OpenAL context."); + + // The game should not crash just because we have no audio + return; + } + + context.set(contextPtr); + + EXTThreadLocalContext.alcSetThreadContext(contextPtr); + + ALCCapabilities deviceCaps = ALC.createCapabilities(devicePtr); + var alCapabilities = AL.createCapabilities(deviceCaps); + + if (Pluto.DEBUG_MODE) + { + Logger.logf(SmartSeverity.AUDIO, "OpenAL10: %b\n", alCapabilities.OpenAL10); + Logger.logf(SmartSeverity.AUDIO, "OpenAL11: %b\n", alCapabilities.OpenAL11); + } + + capabilities.set(alCapabilities); + } + + public static void setSpeed(Vector3f speed) + { + AL10.alListener3f(AL10.AL_VELOCITY, speed.x, speed.y, speed.z); + } + + public static void setPosition(Vector3f position) + { + AL10.alListener3f(AL10.AL_POSITION, position.x, position.y, position.z); + } + + public static void setVolume(float volume) + { + AL10.alListenerf(AL10.AL_GAIN, volume); + } + + public static void setOrientation(Vector3f at, Vector3f up) + { + float[] data = new float[6]; + data[0] = at.x; + data[1] = at.y; + data[2] = at.z; + data[3] = up.x; + data[4] = up.y; + data[5] = up.z; + AL10.alListenerfv(AL10.AL_ORIENTATION, data); + } + + public static boolean isReady() + { + return capabilities.get() != null; + } + + public static void exit() + { + EXTThreadLocalContext.alcSetThreadContext(MemoryUtil.NULL); + ALC10.alcDestroyContext(context.get()); + ALC10.alcCloseDevice(device.get()); + + context.remove(); + device.remove(); + } +} diff --git a/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/al/AudioTrack.java b/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/al/AudioTrack.java index 68898a9..2559450 100644 --- a/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/al/AudioTrack.java +++ b/plutoaudio/src/main/java/cz/tefek/pluto/engine/audio/al/AudioTrack.java @@ -1,124 +1,124 @@ -package cz.tefek.pluto.engine.audio.al; - -import org.lwjgl.openal.AL10; -import org.lwjgl.openal.SOFTDirectChannels; -import org.lwjgl.system.MemoryUtil; - -import java.nio.ShortBuffer; - -import cz.tefek.pluto.engine.audio.IAudioStream; -import cz.tefek.pluto.engine.audio.ISeekableAudioTrack; - -public class AudioTrack extends AudioSource -{ - private static final int BUFFER_SIZE_PER_CHANNEL = 16384; - - private final IAudioStream track; - - private final int format; - - private static final int DOUBLE_BUFFER = 2; - - private final int[] buffers; - - private boolean closeOnFinish; - - private final ShortBuffer pcm; - - public AudioTrack(IAudioStream track) - { - 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()); - } - - int bufferSize = track.getChannels() * BUFFER_SIZE_PER_CHANNEL; - - this.pcm = MemoryUtil.memAllocShort(bufferSize); - - this.buffers = new int[DOUBLE_BUFFER]; - AL10.alGenBuffers(this.buffers); - - AL10.alSourcei(this.source, SOFTDirectChannels.AL_DIRECT_CHANNELS_SOFT, AL10.AL_TRUE); - } - - @Override - public void close() - { - AL10.alDeleteBuffers(this.buffers); - AL10.alDeleteSources(this.source); - - MemoryUtil.memFree(this.pcm); - } - - public boolean play() - { - if (this.track instanceof ISeekableAudioTrack) - { - ((ISeekableAudioTrack) this.track).rewind(); - } - - for (int buf : this.buffers) - { - this.stream(buf); - } - - AL10.alSourcePlay(this.source); - - return true; - } - - public void setCloseOnFinish() - { - this.closeOnFinish = true; - } - - private void stream(int buffer) - { - this.pcm.clear(); - int samplesPerChannel = this.track.getSamples(this.pcm); - - if (samplesPerChannel == 0) - { - return; - } - - var samples = samplesPerChannel * this.track.getChannels(); - this.pcm.limit(samples); - AL10.alBufferData(buffer, this.format, this.pcm, this.track.getSampleRate()); - AL10.alSourceQueueBuffers(this.source, buffer); - } - - public boolean update() - { - int processed = AL10.alGetSourcei(this.source, AL10.AL_BUFFERS_PROCESSED); - - for (int i = 0; i < processed; i++) - { - int buffer = AL10.alSourceUnqueueBuffers(this.source); - this.stream(buffer); - } - - if (AL10.alGetSourcei(this.source, AL10.AL_SOURCE_STATE) == AL10.AL_STOPPED) - { - if (this.closeOnFinish) - { - this.close(); - } - - return false; - } - - return true; - } - -} +package cz.tefek.pluto.engine.audio.al; + +import org.lwjgl.openal.AL10; +import org.lwjgl.openal.SOFTDirectChannels; +import org.lwjgl.system.MemoryUtil; + +import java.nio.ShortBuffer; + +import cz.tefek.pluto.engine.audio.IAudioStream; +import cz.tefek.pluto.engine.audio.ISeekableAudioTrack; + +public class AudioTrack extends AudioSource +{ + private static final int BUFFER_SIZE_PER_CHANNEL = 16384; + + private final IAudioStream track; + + private final int format; + + private static final int DOUBLE_BUFFER = 2; + + private final int[] buffers; + + private boolean closeOnFinish; + + private final ShortBuffer pcm; + + public AudioTrack(IAudioStream track) + { + 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()); + } + + int bufferSize = track.getChannels() * BUFFER_SIZE_PER_CHANNEL; + + this.pcm = MemoryUtil.memAllocShort(bufferSize); + + this.buffers = new int[DOUBLE_BUFFER]; + AL10.alGenBuffers(this.buffers); + + AL10.alSourcei(this.source, SOFTDirectChannels.AL_DIRECT_CHANNELS_SOFT, AL10.AL_TRUE); + } + + @Override + public void close() + { + AL10.alDeleteBuffers(this.buffers); + AL10.alDeleteSources(this.source); + + MemoryUtil.memFree(this.pcm); + } + + public boolean play() + { + if (this.track instanceof ISeekableAudioTrack) + { + ((ISeekableAudioTrack) this.track).rewind(); + } + + for (int buf : this.buffers) + { + this.stream(buf); + } + + AL10.alSourcePlay(this.source); + + return true; + } + + public void setCloseOnFinish() + { + this.closeOnFinish = true; + } + + private void stream(int buffer) + { + this.pcm.clear(); + int samplesPerChannel = this.track.getSamples(this.pcm); + + if (samplesPerChannel == 0) + { + return; + } + + var samples = samplesPerChannel * this.track.getChannels(); + this.pcm.limit(samples); + AL10.alBufferData(buffer, this.format, this.pcm, this.track.getSampleRate()); + AL10.alSourceQueueBuffers(this.source, buffer); + } + + public boolean update() + { + int processed = AL10.alGetSourcei(this.source, AL10.AL_BUFFERS_PROCESSED); + + for (int i = 0; i < processed; i++) + { + int buffer = AL10.alSourceUnqueueBuffers(this.source); + this.stream(buffer); + } + + if (AL10.alGetSourcei(this.source, AL10.AL_SOURCE_STATE) == AL10.AL_STOPPED) + { + if (this.closeOnFinish) + { + this.close(); + } + + return false; + } + + return true; + } + +} diff --git a/plutocore/src/main/java/cz/tefek/pluto/engine/input/CursorPositionCallback.java b/plutocore/src/main/java/cz/tefek/pluto/engine/input/CursorPositionCallback.java index a655b96..4d2f967 100644 --- a/plutocore/src/main/java/cz/tefek/pluto/engine/input/CursorPositionCallback.java +++ b/plutocore/src/main/java/cz/tefek/pluto/engine/input/CursorPositionCallback.java @@ -1,55 +1,53 @@ -package cz.tefek.pluto.engine.input; - -import org.lwjgl.glfw.GLFWCursorPosCallback; - -public class CursorPositionCallback extends GLFWCursorPosCallback -{ - private double posX; - private double posY; - - private double deltaX; - private double deltaY; - - @Override - public void invoke(long window, double xpos, double ypos) - { - this.deltaX = this.posX - xpos; - this.deltaY = this.posY - ypos; - - this.posX = xpos; - this.posY = ypos; - } - - public void reset() - { - this.deltaX = 0; - this.deltaY = 0; - } - - public double getX() - { - return this.posX; - } - - public double getY() - { - return this.posY; - } - - public double getDeltaX() - { - return this.deltaX; - } - - public double getDeltaY() - { - return this.deltaY; - } - - public boolean isInside(int x, int y, int x2, int y2) - { - boolean inside = this.getX() > x && this.getX() < x2 && this.getY() > y && this.getY() < y2; - - return inside; - } -} +package cz.tefek.pluto.engine.input; + +import org.lwjgl.glfw.GLFWCursorPosCallback; + +public class CursorPositionCallback extends GLFWCursorPosCallback +{ + private double posX; + private double posY; + + private double deltaX; + private double deltaY; + + @Override + public void invoke(long window, double xpos, double ypos) + { + this.deltaX = this.posX - xpos; + this.deltaY = this.posY - ypos; + + this.posX = xpos; + this.posY = ypos; + } + + public void reset() + { + this.deltaX = 0; + this.deltaY = 0; + } + + public double getX() + { + return this.posX; + } + + public double getY() + { + return this.posY; + } + + public double getDeltaX() + { + return this.deltaX; + } + + public double getDeltaY() + { + return this.deltaY; + } + + public boolean isInside(int x, int y, int x2, int y2) + { + return this.getX() > x && this.getX() < x2 && this.getY() > y && this.getY() < y2; + } +} diff --git a/plutocore/src/main/java/cz/tefek/pluto/engine/input/KeyboardInputCallback.java b/plutocore/src/main/java/cz/tefek/pluto/engine/input/KeyboardInputCallback.java index 9da815c..d64a62d 100644 --- a/plutocore/src/main/java/cz/tefek/pluto/engine/input/KeyboardInputCallback.java +++ b/plutocore/src/main/java/cz/tefek/pluto/engine/input/KeyboardInputCallback.java @@ -1,58 +1,58 @@ -package cz.tefek.pluto.engine.input; - -import static org.lwjgl.glfw.GLFW.GLFW_PRESS; -import static org.lwjgl.glfw.GLFW.GLFW_RELEASE; - -import java.util.HashSet; -import java.util.Set; - -import org.lwjgl.glfw.GLFWKeyCallback; - -public class KeyboardInputCallback extends GLFWKeyCallback -{ - private Set keyPressed = new HashSet<>(); - private Set keyDown = new HashSet<>(); - private Set keyReleased = new HashSet<>(); - - @Override - public void invoke(long window, int key, int scancode, int action, int mods) - { - if (key < 0) - { - return; - } - - if (action == GLFW_PRESS) - { - this.keyDown.add(key); - this.keyPressed.add(key); - } - - if (action == GLFW_RELEASE) - { - this.keyDown.remove(key); - this.keyReleased.add(key); - } - } - - public void resetPressed() - { - this.keyPressed.clear(); - this.keyReleased.clear(); - } - - public boolean hasBeenPressed(int key) - { - return this.keyPressed.contains(key); - } - - public boolean hasBeenReleased(int key) - { - return this.keyReleased.contains(key); - } - - public boolean isKeyDown(int key) - { - return this.keyDown.contains(key); - } -} +package cz.tefek.pluto.engine.input; + +import static org.lwjgl.glfw.GLFW.GLFW_PRESS; +import static org.lwjgl.glfw.GLFW.GLFW_RELEASE; + +import java.util.HashSet; +import java.util.Set; + +import org.lwjgl.glfw.GLFWKeyCallback; + +public class KeyboardInputCallback extends GLFWKeyCallback +{ + private Set keyPressed = new HashSet<>(); + private Set keyDown = new HashSet<>(); + private Set keyReleased = new HashSet<>(); + + @Override + public void invoke(long window, int key, int scancode, int action, int mods) + { + if (key < 0) + { + return; + } + + if (action == GLFW_PRESS) + { + this.keyDown.add(key); + this.keyPressed.add(key); + } + + if (action == GLFW_RELEASE) + { + this.keyDown.remove(key); + this.keyReleased.add(key); + } + } + + public void resetPressed() + { + this.keyPressed.clear(); + this.keyReleased.clear(); + } + + public boolean hasBeenPressed(int key) + { + return this.keyPressed.contains(key); + } + + public boolean hasBeenReleased(int key) + { + return this.keyReleased.contains(key); + } + + public boolean isKeyDown(int key) + { + return this.keyDown.contains(key); + } +} diff --git a/plutocore/src/main/java/cz/tefek/pluto/engine/input/MouseButtonCallback.java b/plutocore/src/main/java/cz/tefek/pluto/engine/input/MouseButtonCallback.java index 7602e31..abcfb07 100644 --- a/plutocore/src/main/java/cz/tefek/pluto/engine/input/MouseButtonCallback.java +++ b/plutocore/src/main/java/cz/tefek/pluto/engine/input/MouseButtonCallback.java @@ -1,33 +1,33 @@ -package cz.tefek.pluto.engine.input; - -import org.lwjgl.glfw.GLFW; -import org.lwjgl.glfw.GLFWMouseButtonCallback; - -public class MouseButtonCallback extends GLFWMouseButtonCallback -{ - public boolean[] buttonClicked = new boolean[32]; - public boolean[] buttonDown = new boolean[32]; - public boolean[] buttonReleased = new boolean[32]; - - @Override - public void invoke(long window, int button, int action, int mods) - { - this.buttonClicked[button] = action == GLFW.GLFW_PRESS; - this.buttonDown[button] = action != GLFW.GLFW_RELEASE; - this.buttonReleased[button] = action == GLFW.GLFW_RELEASE; - } - - public void reset() - { - for (int i = 0; i < this.buttonClicked.length; i++) - { - this.buttonClicked[i] = false; - } - - for (int i = 0; i < this.buttonClicked.length; i++) - { - this.buttonReleased[i] = false; - } - } - -} +package cz.tefek.pluto.engine.input; + +import org.lwjgl.glfw.GLFW; +import org.lwjgl.glfw.GLFWMouseButtonCallback; + +public class MouseButtonCallback extends GLFWMouseButtonCallback +{ + public boolean[] buttonClicked = new boolean[32]; + public boolean[] buttonDown = new boolean[32]; + public boolean[] buttonReleased = new boolean[32]; + + @Override + public void invoke(long window, int button, int action, int mods) + { + this.buttonClicked[button] = action == GLFW.GLFW_PRESS; + this.buttonDown[button] = action != GLFW.GLFW_RELEASE; + this.buttonReleased[button] = action == GLFW.GLFW_RELEASE; + } + + public void reset() + { + for (int i = 0; i < this.buttonClicked.length; i++) + { + this.buttonClicked[i] = false; + } + + for (int i = 0; i < this.buttonClicked.length; i++) + { + this.buttonReleased[i] = false; + } + } + +} diff --git a/plutocore/src/main/java/cz/tefek/pluto/engine/input/ScrollInputCallback.java b/plutocore/src/main/java/cz/tefek/pluto/engine/input/ScrollInputCallback.java index 406090a..353a782 100644 --- a/plutocore/src/main/java/cz/tefek/pluto/engine/input/ScrollInputCallback.java +++ b/plutocore/src/main/java/cz/tefek/pluto/engine/input/ScrollInputCallback.java @@ -1,46 +1,46 @@ -package cz.tefek.pluto.engine.input; - -import org.lwjgl.glfw.GLFWScrollCallback; - -/** - * @author 493msi - * - */ -public class ScrollInputCallback extends GLFWScrollCallback -{ - private double xScroll; - private double yScroll; - - @Override - public void invoke(long window, double xoffset, double yoffset) - { - xScroll = xoffset; - yScroll = yoffset; - } - - public void reset() - { - xScroll = 0; - yScroll = 0; - } - - public double getYScroll() - { - return yScroll; - } - - public void setYScroll(double yScroll) - { - this.yScroll = yScroll; - } - - public double getXScroll() - { - return xScroll; - } - - public void setXScroll(double xScroll) - { - this.xScroll = xScroll; - } -} +package cz.tefek.pluto.engine.input; + +import org.lwjgl.glfw.GLFWScrollCallback; + +/** + * @author 493msi + * + */ +public class ScrollInputCallback extends GLFWScrollCallback +{ + private double xScroll; + private double yScroll; + + @Override + public void invoke(long window, double xoffset, double yoffset) + { + xScroll = xoffset; + yScroll = yoffset; + } + + public void reset() + { + xScroll = 0; + yScroll = 0; + } + + public double getYScroll() + { + return yScroll; + } + + public void setYScroll(double yScroll) + { + this.yScroll = yScroll; + } + + public double getXScroll() + { + return xScroll; + } + + public void setXScroll(double xScroll) + { + this.xScroll = xScroll; + } +} diff --git a/plutogui/src/main/java/cz/tefek/pluto/engine/graphics/font/FontManager.java b/plutogui/src/main/java/cz/tefek/pluto/engine/graphics/font/FontManager.java index b26746d..e94315e 100644 --- a/plutogui/src/main/java/cz/tefek/pluto/engine/graphics/font/FontManager.java +++ b/plutogui/src/main/java/cz/tefek/pluto/engine/graphics/font/FontManager.java @@ -1,102 +1,102 @@ -package cz.tefek.pluto.engine.graphics.font; - -import java.util.HashMap; -import java.util.Map; - -import java.io.BufferedReader; -import java.nio.file.Files; - -import cz.tefek.pluto.engine.graphics.texture.MagFilter; -import cz.tefek.pluto.engine.graphics.texture.MinFilter; -import cz.tefek.pluto.engine.graphics.texture.Texture; -import cz.tefek.pluto.engine.graphics.texture.WrapMode; -import cz.tefek.pluto.engine.graphics.texture.texture2d.RectangleTexture; -import cz.tefek.pluto.engine.gui.font.CharacterInfo; -import cz.tefek.pluto.engine.gui.font.Font; -import cz.tefek.pluto.io.asl.resource.ResourceAddress; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; - -public class FontManager -{ - private static Map fonts = new HashMap<>(); - - public static void loadFont(ResourceAddress address) - { - String fontname = null; - int width = 0; - int height = 0; - var def = new HashMap(); - - int row = 0; - - try (BufferedReader br = Files.newBufferedReader(address.copy().branch("definitions").fileExtension("txt").toNIOPath())) - { - String line; - while ((line = br.readLine()) != null) - { - if (line.startsWith("//")) - { - continue; - } - - if (row == 0) - { - String[] fontinfo; - fontinfo = line.split(","); - - fontname = fontinfo[0]; - - String[] dim = fontinfo[1].split("x"); - - width = Integer.parseInt(dim[0]); - height = Integer.parseInt(dim[1]); - } - - if (row > 0) - { - String[] offs = null; - - offs = line.split(" ")[1].split(";"); - - def.put(line.charAt(0), new CharacterInfo(row - 1, Integer.parseInt(offs[0]), Integer.parseInt(offs[1]))); - } - - row++; - } - - br.close(); - } - catch (Exception e) - { - Logger.log(SmartSeverity.ERROR, "Could not load font: " + address.toString()); - e.printStackTrace(); - } - - Font font = new Font(fontname, width, height, def); - RectangleTexture texture = new RectangleTexture(); - texture.load(address.copy().branch("tex").fileExtension("png"), MagFilter.NEAREST, MinFilter.LINEAR, WrapMode.CLAMP_TO_EDGE, WrapMode.CLAMP_TO_EDGE); - font.setTexture(texture); - - fonts.put(fontname, font); - } - - public static void unloadAll() - { - fonts.values().stream().map(Font::getTexture).forEach(Texture::delete); - fonts.clear(); - } - - public static Font getFontByName(String fontname) - { - var font = fonts.get(fontname); - - if (font == null) - { - Logger.log(SmartSeverity.WARNING, "Font with name " + fontname + " could not be found, using the default one instead (if there is one)."); - return fonts.get("default"); - } - - return font; - } -} +package cz.tefek.pluto.engine.graphics.font; + +import java.util.HashMap; +import java.util.Map; + +import java.io.BufferedReader; +import java.nio.file.Files; + +import cz.tefek.pluto.engine.graphics.texture.MagFilter; +import cz.tefek.pluto.engine.graphics.texture.MinFilter; +import cz.tefek.pluto.engine.graphics.texture.Texture; +import cz.tefek.pluto.engine.graphics.texture.WrapMode; +import cz.tefek.pluto.engine.graphics.texture.texture2d.RectangleTexture; +import cz.tefek.pluto.engine.gui.font.CharacterInfo; +import cz.tefek.pluto.engine.gui.font.Font; +import cz.tefek.pluto.io.asl.resource.ResourceAddress; +import cz.tefek.pluto.io.logger.Logger; +import cz.tefek.pluto.io.logger.SmartSeverity; + +public class FontManager +{ + private static Map fonts = new HashMap<>(); + + public static void loadFont(ResourceAddress address) + { + String fontname = null; + int width = 0; + int height = 0; + var def = new HashMap(); + + int row = 0; + + try (BufferedReader br = Files.newBufferedReader(address.copy().branch("definitions").fileExtension("txt").toNIOPath())) + { + String line; + while ((line = br.readLine()) != null) + { + if (line.startsWith("//")) + { + continue; + } + + if (row == 0) + { + String[] fontinfo; + fontinfo = line.split(","); + + fontname = fontinfo[0]; + + String[] dim = fontinfo[1].split("x"); + + width = Integer.parseInt(dim[0]); + height = Integer.parseInt(dim[1]); + } + + if (row > 0) + { + String[] offs = null; + + offs = line.split(" ")[1].split(";"); + + def.put(line.charAt(0), new CharacterInfo(row - 1, Integer.parseInt(offs[0]), Integer.parseInt(offs[1]))); + } + + row++; + } + + br.close(); + } + catch (Exception e) + { + Logger.log(SmartSeverity.ERROR, "Could not load font: " + address.toString()); + e.printStackTrace(); + } + + Font font = new Font(fontname, width, height, def); + RectangleTexture texture = new RectangleTexture(); + texture.load(address.copy().branch("tex").fileExtension("png"), MagFilter.NEAREST, MinFilter.LINEAR, WrapMode.CLAMP_TO_EDGE, WrapMode.CLAMP_TO_EDGE); + font.setTexture(texture); + + fonts.put(fontname, font); + } + + public static void unloadAll() + { + fonts.values().stream().map(Font::getTexture).forEach(Texture::delete); + fonts.clear(); + } + + public static Font getFontByName(String fontname) + { + var font = fonts.get(fontname); + + if (font == null) + { + Logger.log(SmartSeverity.WARNING, "Font with name " + fontname + " could not be found, using the default one instead (if there is one)."); + return fonts.get("default"); + } + + return font; + } +} diff --git a/plutogui/src/main/java/cz/tefek/pluto/engine/graphics/font/FontShader.java b/plutogui/src/main/java/cz/tefek/pluto/engine/graphics/font/FontShader.java index 09298b3..f4fc5a3 100644 --- a/plutogui/src/main/java/cz/tefek/pluto/engine/graphics/font/FontShader.java +++ b/plutogui/src/main/java/cz/tefek/pluto/engine/graphics/font/FontShader.java @@ -1,41 +1,41 @@ -package cz.tefek.pluto.engine.graphics.font; - -import cz.tefek.pluto.engine.graphics.gl.vao.attrib.ReservedAttributes; -import cz.tefek.pluto.engine.shader.ShaderBase; -import cz.tefek.pluto.engine.shader.ShaderProgram; -import cz.tefek.pluto.engine.shader.VertexArrayAttribute; -import cz.tefek.pluto.engine.shader.uniform.Uniform; -import cz.tefek.pluto.engine.shader.uniform.UniformBoolean; -import cz.tefek.pluto.engine.shader.uniform.UniformMat4; -import cz.tefek.pluto.engine.shader.uniform.UniformVec2; -import cz.tefek.pluto.engine.shader.uniform.UniformVec4; -import cz.tefek.pluto.engine.shader.uniform.auto.AutoViewportProjection; - -@ShaderProgram -public final class FontShader extends ShaderBase -{ - @AutoViewportProjection - @Uniform(name = "projection") - public UniformMat4 projectionMatrix; - - @Uniform(name = "transformation") - public UniformMat4 transformationMatrix; - - @Uniform - public UniformVec2 uvBase; - - @Uniform - public UniformVec2 uvDelta; - - @Uniform - public UniformVec4 recolor; - - @Uniform - public UniformBoolean italic; - - @VertexArrayAttribute(ReservedAttributes.POSITION) - public int position; - - @VertexArrayAttribute(ReservedAttributes.UV) - public int uvCoords; -} +package cz.tefek.pluto.engine.graphics.font; + +import cz.tefek.pluto.engine.graphics.gl.vao.attrib.ReservedAttributes; +import cz.tefek.pluto.engine.shader.ShaderBase; +import cz.tefek.pluto.engine.shader.ShaderProgram; +import cz.tefek.pluto.engine.shader.VertexArrayAttribute; +import cz.tefek.pluto.engine.shader.uniform.Uniform; +import cz.tefek.pluto.engine.shader.uniform.UniformBoolean; +import cz.tefek.pluto.engine.shader.uniform.UniformMat4; +import cz.tefek.pluto.engine.shader.uniform.UniformVec2; +import cz.tefek.pluto.engine.shader.uniform.UniformVec4; +import cz.tefek.pluto.engine.shader.uniform.auto.AutoViewportProjection; + +@ShaderProgram +public final class FontShader extends ShaderBase +{ + @AutoViewportProjection + @Uniform(name = "projection") + public UniformMat4 projectionMatrix; + + @Uniform(name = "transformation") + public UniformMat4 transformationMatrix; + + @Uniform + public UniformVec2 uvBase; + + @Uniform + public UniformVec2 uvDelta; + + @Uniform + public UniformVec4 recolor; + + @Uniform + public UniformBoolean italic; + + @VertexArrayAttribute(ReservedAttributes.POSITION) + public int position; + + @VertexArrayAttribute(ReservedAttributes.UV) + public int uvCoords; +} diff --git a/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/CharacterInfo.java b/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/CharacterInfo.java index 5155544..ddb262f 100644 --- a/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/CharacterInfo.java +++ b/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/CharacterInfo.java @@ -1,30 +1,30 @@ -package cz.tefek.pluto.engine.gui.font; - -public class CharacterInfo -{ - int leftOffs; - private int rightOffs; - int number; - - public CharacterInfo(int number, int leftOffs, int rightOffs) - { - this.number = number; - this.leftOffs = leftOffs; - this.rightOffs = rightOffs; - } - - public int getLeftOffset() - { - return this.leftOffs; - } - - public int getNumber() - { - return this.number; - } - - public int getRightOffset() - { - return this.rightOffs; - } -} +package cz.tefek.pluto.engine.gui.font; + +public class CharacterInfo +{ + int leftOffs; + private int rightOffs; + int number; + + public CharacterInfo(int number, int leftOffs, int rightOffs) + { + this.number = number; + this.leftOffs = leftOffs; + this.rightOffs = rightOffs; + } + + public int getLeftOffset() + { + return this.leftOffs; + } + + public int getNumber() + { + return this.number; + } + + public int getRightOffset() + { + return this.rightOffs; + } +} diff --git a/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/Font.java b/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/Font.java index ce72c11..b678a72 100644 --- a/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/Font.java +++ b/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/Font.java @@ -1,59 +1,59 @@ -package cz.tefek.pluto.engine.gui.font; - -import java.util.HashMap; - -import cz.tefek.pluto.engine.graphics.texture.texture2d.RectangleTexture; - -public class Font -{ - private String name; - private int width; - private int height; - private HashMap definitions; - private RectangleTexture texture; - - public Font(String name, int width, int height, HashMap def) - { - this.name = name; - this.width = width; - this.height = height; - this.definitions = def; - } - - public String getName() - { - return this.name; - } - - public void setName(String name) - { - this.name = name; - } - - public int getTextureWidth() - { - return this.width; - } - - public int getTextureHeight() - { - return this.height; - } - - public RectangleTexture getTexture() - { - return this.texture; - } - - public void setTexture(RectangleTexture texture) - { - this.texture = texture; - this.width = texture.getWidth(); - this.height = texture.getHeight(); - } - - public HashMap getDefinitions() - { - return this.definitions; - } -} +package cz.tefek.pluto.engine.gui.font; + +import java.util.HashMap; + +import cz.tefek.pluto.engine.graphics.texture.texture2d.RectangleTexture; + +public class Font +{ + private String name; + private int width; + private int height; + private HashMap definitions; + private RectangleTexture texture; + + public Font(String name, int width, int height, HashMap def) + { + this.name = name; + this.width = width; + this.height = height; + this.definitions = def; + } + + public String getName() + { + return this.name; + } + + public void setName(String name) + { + this.name = name; + } + + public int getTextureWidth() + { + return this.width; + } + + public int getTextureHeight() + { + return this.height; + } + + public RectangleTexture getTexture() + { + return this.texture; + } + + public void setTexture(RectangleTexture texture) + { + this.texture = texture; + this.width = texture.getWidth(); + this.height = texture.getHeight(); + } + + public HashMap getDefinitions() + { + return this.definitions; + } +} diff --git a/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/FontRenderer.java b/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/FontRenderer.java index 4e6f67b..a9eba36 100644 --- a/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/FontRenderer.java +++ b/plutogui/src/main/java/cz/tefek/pluto/engine/gui/font/FontRenderer.java @@ -1,311 +1,311 @@ -package cz.tefek.pluto.engine.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; - -public class FontRenderer -{ - private static final int LINE_HEIGHT = 30; - private static final int SPACE_WIDTH = 12; - - private static final int CHAR_WIDTH = 16; - private static final int CHAR_HEIGHT = 24; - - private static VertexArray charVAO; - private static FontShader fontShader; - - public static void load(FontShader defaultFontShaderIn) - { - charVAO = QuadPresets.basicNoNeg(); - fontShader = defaultFontShaderIn; - } - - public static void unload() - { - if (charVAO != null) - { - charVAO.delete(); - } - } - - public static void prepareInstance(Font font) - { - GL11.glEnable(GL11.GL_BLEND); - fontShader.start(); - charVAO.bind(); - charVAO.enableAllAttributes(); - font.getTexture().bind(); - } - - public static void drawString(float xPos, float yPos, Object string, Object color, float relativeSize, String fontname, boolean isShadow) - { - Font font = FontManager.getFontByName(fontname); - - if (font == null) - { - System.err.println("Font doesn't exist: " + fontname); - return; - } - - float charWidth = CHAR_WIDTH * relativeSize; - float charHeight = CHAR_HEIGHT * relativeSize; - - float lineHeight = LINE_HEIGHT * relativeSize; - float spaceWidth = SPACE_WIDTH * relativeSize; - - prepareInstance(font); - - color(color); - - String text = String.valueOf(string); - - float drawX = xPos; - float drawY = yPos; - - for (int characterIndex = 0; characterIndex < text.length(); characterIndex++) - { - int column = 0; - int row = 0; - - var currentChar = text.charAt(characterIndex); - - if (text.length() > characterIndex + 1) - { - var nextChar = text.charAt(characterIndex + 1); - - if (currentChar == '\\' && nextChar == '&') - { - continue; - } - - // Inline coloring (tm) :) -> &c[0xff770077] - if (text.length() > characterIndex + 13) - { - if (currentChar == '&' && nextChar == 'c' && text.charAt(characterIndex + 2) == '[' && text.charAt(characterIndex + 13) == ']') - { - char c = '0'; - char cBef = '0'; - - if (characterIndex > 0) - { - c = text.charAt(characterIndex - 1); - } - - if (characterIndex > 1) - { - cBef = text.charAt(characterIndex - 2); - } - - if (c != '\\' || cBef == '\\' && c == '\\') - { - if (!isShadow) - { - char[] col = new char[10]; - - text.getChars(characterIndex + 3, characterIndex + 13, col, 0); - - String clr = String.valueOf(col); - - color(clr); - } - - characterIndex += 13; - - continue; - } - } - } - - if (text.length() > characterIndex + 2) - { - if (currentChar == '&' && nextChar == 'i' && (text.charAt(characterIndex + 2) == '1' || text.charAt(characterIndex + 2) == '0')) - { - char c = '0'; - char cBef = '0'; - - if (characterIndex > 0) - { - c = text.charAt(characterIndex - 1); - } - - if (characterIndex > 1) - { - cBef = text.charAt(characterIndex - 2); - } - - if (c != '\\' || cBef == '\\' && c == '\\') - { - if (text.charAt(characterIndex + 2) == '1') - { - italic(true); - } - else - { - italic(false); - } - - characterIndex += 2; - - continue; - } - } - } - } - - float shift = 0; - - switch (currentChar) - { - case '\n': - color(color); - drawX = xPos; - drawY += lineHeight; - continue; - - case ' ': - drawX += spaceWidth; - continue; - - case 'g': - case 'y': - case 'p': - case 'j': - shift = 6 * relativeSize; - - break; - - default: - break; - } - - var fontDefs = font.getDefinitions(); - var charInf = fontDefs.get(currentChar); - - if (charInf == null) - { - charInf = fontDefs.get('?'); - } - - var atlasIndex = charInf.getNumber(); - - row = atlasIndex / CHAR_WIDTH; - column = atlasIndex % CHAR_WIDTH; - - // Position of the current character in the texture atlas in pixels - float u = column * CHAR_WIDTH; - float v = row * CHAR_HEIGHT; - - // Offset from the left - drawX -= charInf.getLeftOffset() * relativeSize; - - float posY = shift + drawY; - - fontShader.uvBase.load(u, font.getTextureHeight() - v - CHAR_HEIGHT); - fontShader.uvDelta.load(CHAR_WIDTH, CHAR_HEIGHT); - - Matrix4f transformation = TransformationMatrix.create(new Vector3f(drawX, posY, 0), new Vector3f(0, 0, 0), new Vector3f(charWidth, charHeight, 0)); - - fontShader.transformationMatrix.load(transformation); - - charVAO.draw(DrawMode.TRIANGLES); - - drawX += charWidth; - drawX -= charInf.getRightOffset() * relativeSize; - drawX += relativeSize; - } - - italic(false); - } - - public static void color(Object color) - { - color(color, false); - } - - public static void color(Object color, boolean darker) - { - float dark = 0; - - if (darker) - { - dark = 0.35f; - } - - if (color instanceof float[]) - { - float[] c = (float[]) color; - - if (c.length == 4) - { - recolor(c[0] - dark, c[1] - dark, c[2] - dark, c[3]); - } - else if (c.length == 3) - { - recolor(c[0] - dark, c[1] - dark, c[2] - dark, 1); - } - } - - if (color instanceof String) - { - 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); - } - - if (col.length() == 10) - { - recolor(Integer.valueOf(col.substring(4, 6), 16) / 256f - dark, Integer.valueOf(col.substring(6, 8), 16) / 256f - dark, Integer.valueOf(col.substring(8, 10), 16) / 256f - dark, Integer.valueOf(col.substring(2, 4), 16) / 256f); - } - } - } - - private static void recolor(float r, float g, float b, float a) - { - fontShader.recolor.load(r, g, b, a); - } - - private static void italic(boolean useItalic) - { - fontShader.italic.load(useItalic); - } - - public static void drawString(float x, float y, Object text, float r, float g, float b, float a, float size) - { - drawString(x, y, text, new float[] { r, g, b, a }, size, "default", false); - } - - public static void drawString(float x, float y, Object text) - { - drawString(x, y, text, new float[] { 0, 0, 0, 1 }, 1, "default", false); - } - - public static void drawString(float x, float y, Object text, float r, float g, float b, float a, float size, boolean isShadow) - { - drawString(x, y, text, new float[] { r, g, b, a }, size, "default", isShadow); - } - - public static void drawString(float x, float y, Object text, float r, float g, float b, float a, float size, String fontname, boolean isShadow) - { - drawString(x, y, text, new float[] { r, g, b, a }, size, fontname, isShadow); - } - - public static void drawString(float x, float y, Object text, float r, float g, float b, float a, float size, String fontname) - { - drawString(x, y, text, new float[] { r, g, b, a }, size, fontname, false); - } - - public static void drawString(float xPos, float yPos, Object string, Object color, float relativeSize, String fontname) - { - drawString(xPos, yPos, string, color, relativeSize, fontname, false); - } -} +package cz.tefek.pluto.engine.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; + +public class FontRenderer +{ + private static final int LINE_HEIGHT = 30; + private static final int SPACE_WIDTH = 12; + + private static final int CHAR_WIDTH = 16; + private static final int CHAR_HEIGHT = 24; + + private static VertexArray charVAO; + private static FontShader fontShader; + + public static void load(FontShader defaultFontShaderIn) + { + charVAO = QuadPresets.basicNoNeg(); + fontShader = defaultFontShaderIn; + } + + public static void unload() + { + if (charVAO != null) + { + charVAO.delete(); + } + } + + public static void prepareInstance(Font font) + { + GL11.glEnable(GL11.GL_BLEND); + fontShader.start(); + charVAO.bind(); + charVAO.enableAllAttributes(); + font.getTexture().bind(); + } + + public static void drawString(float xPos, float yPos, Object string, Object color, float relativeSize, String fontname, boolean isShadow) + { + Font font = FontManager.getFontByName(fontname); + + if (font == null) + { + System.err.println("Font doesn't exist: " + fontname); + return; + } + + float charWidth = CHAR_WIDTH * relativeSize; + float charHeight = CHAR_HEIGHT * relativeSize; + + float lineHeight = LINE_HEIGHT * relativeSize; + float spaceWidth = SPACE_WIDTH * relativeSize; + + prepareInstance(font); + + color(color); + + String text = String.valueOf(string); + + float drawX = xPos; + float drawY = yPos; + + for (int characterIndex = 0; characterIndex < text.length(); characterIndex++) + { + int column = 0; + int row = 0; + + var currentChar = text.charAt(characterIndex); + + if (text.length() > characterIndex + 1) + { + var nextChar = text.charAt(characterIndex + 1); + + if (currentChar == '\\' && nextChar == '&') + { + continue; + } + + // Inline coloring (tm) :) -> &c[0xff770077] + if (text.length() > characterIndex + 13) + { + if (currentChar == '&' && nextChar == 'c' && text.charAt(characterIndex + 2) == '[' && text.charAt(characterIndex + 13) == ']') + { + char c = '0'; + char cBef = '0'; + + if (characterIndex > 0) + { + c = text.charAt(characterIndex - 1); + } + + if (characterIndex > 1) + { + cBef = text.charAt(characterIndex - 2); + } + + if (c != '\\' || cBef == '\\' && c == '\\') + { + if (!isShadow) + { + char[] col = new char[10]; + + text.getChars(characterIndex + 3, characterIndex + 13, col, 0); + + String clr = String.valueOf(col); + + color(clr); + } + + characterIndex += 13; + + continue; + } + } + } + + if (text.length() > characterIndex + 2) + { + if (currentChar == '&' && nextChar == 'i' && (text.charAt(characterIndex + 2) == '1' || text.charAt(characterIndex + 2) == '0')) + { + char c = '0'; + char cBef = '0'; + + if (characterIndex > 0) + { + c = text.charAt(characterIndex - 1); + } + + if (characterIndex > 1) + { + cBef = text.charAt(characterIndex - 2); + } + + if (c != '\\' || cBef == '\\' && c == '\\') + { + if (text.charAt(characterIndex + 2) == '1') + { + italic(true); + } + else + { + italic(false); + } + + characterIndex += 2; + + continue; + } + } + } + } + + float shift = 0; + + switch (currentChar) + { + case '\n': + color(color); + drawX = xPos; + drawY += lineHeight; + continue; + + case ' ': + drawX += spaceWidth; + continue; + + case 'g': + case 'y': + case 'p': + case 'j': + shift = 6 * relativeSize; + + break; + + default: + break; + } + + var fontDefs = font.getDefinitions(); + var charInf = fontDefs.get(currentChar); + + if (charInf == null) + { + charInf = fontDefs.get('?'); + } + + var atlasIndex = charInf.getNumber(); + + row = atlasIndex / CHAR_WIDTH; + column = atlasIndex % CHAR_WIDTH; + + // Position of the current character in the texture atlas in pixels + float u = column * CHAR_WIDTH; + float v = row * CHAR_HEIGHT; + + // Offset from the left + drawX -= charInf.getLeftOffset() * relativeSize; + + float posY = shift + drawY; + + fontShader.uvBase.load(u, font.getTextureHeight() - v - CHAR_HEIGHT); + fontShader.uvDelta.load(CHAR_WIDTH, CHAR_HEIGHT); + + Matrix4f transformation = TransformationMatrix.create(new Vector3f(drawX, posY, 0), new Vector3f(0, 0, 0), new Vector3f(charWidth, charHeight, 0)); + + fontShader.transformationMatrix.load(transformation); + + charVAO.draw(DrawMode.TRIANGLES); + + drawX += charWidth; + drawX -= charInf.getRightOffset() * relativeSize; + drawX += relativeSize; + } + + italic(false); + } + + public static void color(Object color) + { + color(color, false); + } + + public static void color(Object color, boolean darker) + { + float dark = 0; + + if (darker) + { + dark = 0.35f; + } + + if (color instanceof float[]) + { + float[] c = (float[]) color; + + if (c.length == 4) + { + recolor(c[0] - dark, c[1] - dark, c[2] - dark, c[3]); + } + else if (c.length == 3) + { + recolor(c[0] - dark, c[1] - dark, c[2] - dark, 1); + } + } + + if (color instanceof String) + { + 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); + } + + if (col.length() == 10) + { + recolor(Integer.valueOf(col.substring(4, 6), 16) / 256f - dark, Integer.valueOf(col.substring(6, 8), 16) / 256f - dark, Integer.valueOf(col.substring(8, 10), 16) / 256f - dark, Integer.valueOf(col.substring(2, 4), 16) / 256f); + } + } + } + + private static void recolor(float r, float g, float b, float a) + { + fontShader.recolor.load(r, g, b, a); + } + + private static void italic(boolean useItalic) + { + fontShader.italic.load(useItalic); + } + + public static void drawString(float x, float y, Object text, float r, float g, float b, float a, float size) + { + drawString(x, y, text, new float[] { r, g, b, a }, size, "default", false); + } + + public static void drawString(float x, float y, Object text) + { + drawString(x, y, text, new float[] { 0, 0, 0, 1 }, 1, "default", false); + } + + public static void drawString(float x, float y, Object text, float r, float g, float b, float a, float size, boolean isShadow) + { + drawString(x, y, text, new float[] { r, g, b, a }, size, "default", isShadow); + } + + public static void drawString(float x, float y, Object text, float r, float g, float b, float a, float size, String fontname, boolean isShadow) + { + drawString(x, y, text, new float[] { r, g, b, a }, size, fontname, isShadow); + } + + public static void drawString(float x, float y, Object text, float r, float g, float b, float a, float size, String fontname) + { + drawString(x, y, text, new float[] { r, g, b, a }, size, fontname, false); + } + + public static void drawString(float xPos, float yPos, Object string, Object color, float relativeSize, String fontname) + { + drawString(xPos, yPos, string, color, relativeSize, fontname, false); + } +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/Pluto.java b/plutolib/src/main/java/cz/tefek/pluto/Pluto.java index e11437e..94194f7 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/Pluto.java +++ b/plutolib/src/main/java/cz/tefek/pluto/Pluto.java @@ -1,5 +1,12 @@ package cz.tefek.pluto; +/** + * Constants shared by all Pluto libraries. + * + * @since pre-alpha + * + * @author 493msi + */ public class Pluto { public static final boolean DEBUG_MODE = Boolean.valueOf(System.getProperty("cz.tefek.pluto.debug")); @@ -8,41 +15,57 @@ public class Pluto /** * The year version number, changes with breaking API changes. + * + * @since pre-alpha * */ public static final int VERSION_YEAR = 20; /** * The major version number, changes with breaking API changes. + * + * @since pre-alpha * */ public static final int VERSION_MAJOR = 2; /** * The minor version number, changes with backwards-compatible API changes. + * + * @since pre-alpha * */ public static final int VERSION_MINOR = 0; /** * The patch number, changes with backwards-compatible fixes and patches. + * + * @since pre-alpha * */ public static final int VERSION_PATCH = 0; /** * Denotes whether this build is a pre-release build. + * + * @since pre-alpha * */ public static final boolean PRERELEASE = true; /** * The name of this pre-release, e.g. alpha, beta, RC and similar. + * + * @since pre-alpha * */ public static final String PRERELEASE_NAME = "alpha"; /** - * The pre-release patch number, incremented by 1 with *any* pre-release change. + * The pre-release patch number, incremented by 1 with *any* pre-release update. + * + * @since pre-alpha * */ public static final int PRERELEASE_PATCH = 1; /** * The combined version string. + * + * @since pre-alpha * */ public static final String VERSION = VERSION_YEAR + "." + VERSION_MAJOR + "." + VERSION_MINOR + "." + VERSION_PATCH + (PRERELEASE ? "-" + PRERELEASE_NAME + "." + PRERELEASE_PATCH : ""); } diff --git a/plutolib/src/main/java/cz/tefek/pluto/chrono/MiniTime.java b/plutolib/src/main/java/cz/tefek/pluto/chrono/MiniTime.java index 099f36d..2465ae4 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/chrono/MiniTime.java +++ b/plutolib/src/main/java/cz/tefek/pluto/chrono/MiniTime.java @@ -1,315 +1,319 @@ -package cz.tefek.pluto.chrono; - -import java.util.concurrent.TimeUnit; - -/** - * A helper class to convert from a time span in milliseconds to a simplified - * time span {@link String} format and vice versa. Note this action is fully - * reversible at the cost of losing millisecond precision. - * - *

MiniTime format specification:

- * - *
- * [Nw][Nd][Nh][Nm][Ns]
- * 
- *  w - weeks
- *  d - days
- *  h - hours
- *  m - minutes
- *  s - seconds
- *  
- *  N - a decimal integer
- * 
- *
    - *
  • At least one value is required.
  • - *
  • At least time unit is required.
  • - *
  • Time units are case insensitive.
  • - *
  • String cannot start with a letter or end with a number.
  • - *
  • All values must be valid positive 32-bit signed integers.
  • - *
  • All values except the first one must be less than 1 of the previous time - * unit.
  • - *
  • Skipping a time unit assumes zero of that time unit.
  • - *
- * - *

Permitted values:

- *
    - *
  • standard MiniTime scheme, from 0s to - * 2147483647w6d23h59m59s
  • - *
  • string "forever" (parses as {@link Long#MAX_VALUE})
  • - *
- * - * @author 493msi - * @since 0.2 - */ -public class MiniTime -{ - private static class MiniTimeCouldNotBeParsedException extends RuntimeException - { - /** - * - */ - private static final long serialVersionUID = -5403949842120041373L; - - public MiniTimeCouldNotBeParsedException() - { - super("Time period could not be parsed. Correct format: \\_w\\_d\\_h\\_m\\_s **without spaces** between the units. You can skip a time unit. Example: 1h15m"); - } - } - - private static final TimeUnit miliseconds = TimeUnit.MILLISECONDS; - - private static final int DAYS_IN_WEEK = 7; - private static final int HOURS_IN_DAY = 24; - private static final int MINUTES_IN_HOUR = 60; - private static final int SECONDS_IN_MINUTE = 60; - private static final int MILLIS_IN_MINUTE = 1000; - - /** - * Converts a MiniTime spec string to the respective time duration in - * milliseconds. - * - * @param input The source MiniTime non-null string - * @return The resulting time span in milliseconds - * - * @author 493msi - * @since 0.2 - */ - public static long parse(String input) - { - if (input == null) - { - throw new IllegalArgumentException("MiniTime string cannot be null!"); - } - - // Nothing to parse - if (input.isEmpty()) - { - throw new MiniTimeCouldNotBeParsedException(); - } - - if (input.equalsIgnoreCase("forever")) - { - return Long.MAX_VALUE; - } - - // Follow the scheme - if (!input.matches("[0-9]*[wW]?[0-9]*[dD]?[0-9]*[hH]?[0-9]*[mM]?[0-9]*[sS]?")) - { - throw new MiniTimeCouldNotBeParsedException(); - } - - // 4584 of what? Potatoes? - if (input.matches("[0-9]+")) - { - throw new MiniTimeCouldNotBeParsedException(); - } - - // Where are the numbers? - if (input.matches("[a-zA-Z]+")) - { - throw new MiniTimeCouldNotBeParsedException(); - } - - // It shouldn't start with a letter - if (input.matches("^[a-zA-Z].+")) - { - throw new MiniTimeCouldNotBeParsedException(); - } - - var nrs = input.split("[a-zA-Z]"); - var letters = input.split("[0-9]+"); - - if (nrs.length != letters.length) - { - throw new MiniTimeCouldNotBeParsedException(); - } - - long time = 0; - - for (int i = 1; i < nrs.length; i++) - { - var type = letters[i - 1]; - int number = 0; - - try - { - // The only time this fails is when the number is too long - number = Integer.parseUnsignedInt(nrs[i]); - } - catch (NumberFormatException nfe) - { - throw new MiniTimeCouldNotBeParsedException(); - } - - var allow = 0L; - var multiplier = 0; - - switch (type.toLowerCase()) - { - 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": - allow = DAYS_IN_WEEK; - multiplier = SECONDS_IN_MINUTE * MINUTES_IN_HOUR * HOURS_IN_DAY * MILLIS_IN_MINUTE; - break; - case "h": - case "H": - allow = HOURS_IN_DAY; - multiplier = SECONDS_IN_MINUTE * MINUTES_IN_HOUR * MILLIS_IN_MINUTE; - break; - case "m": - case "M": - allow = MINUTES_IN_HOUR; - multiplier = SECONDS_IN_MINUTE * MILLIS_IN_MINUTE; - break; - case "s": - case "S": - allow = SECONDS_IN_MINUTE; - multiplier = MILLIS_IN_MINUTE; - break; - default: - break; - } - - // The top one can be more than it normally could have, for example you can - // issue a ban for 48h but not 46h120m (it looks dumb) - if (i == 1) - { - allow = Integer.MAX_VALUE; - } - - if (number > allow) - { - throw new MiniTimeCouldNotBeParsedException(); - } - - time += multiplier * number; - } - - return System.currentTimeMillis() + time; - } - - /** - * Converts a time span between two Unix-epoch millisecond time points to a - * MiniTime string. Note ALL time spans larger or equal than - * {@link Integer#MAX_VALUE} will be permanently converted to "forever". - * - * @param before The first time point in Unix-time milliseconds - * @param after The first time point in Unix-time milliseconds - * @return The resulting MiniTime string - * - * @throws IllegalArgumentException on a negative time duration - * - * @author 493msi - * @since 0.2 - */ - public static String fromMillisDiffNow(long after, long before) - { - if (after == Long.MAX_VALUE) - { - return "forever"; - } - - return formatDiff(after - before); - } - - /** - * Converts a time span between now and a future time point in Unix-epoch - * milliseconds to a MiniTime string. Note ALL time spans larger or equal - * than {@link Integer#MAX_VALUE} will be permanently converted to - * "forever". - * - * @param future The source time span in milliseconds - * @return The resulting MiniTime string - * - * @throws IllegalArgumentException on a negative time duration - * - * @author 493msi - * @since 0.2 - */ - public static String fromMillisDiffNow(long future) - { - if (future == Long.MAX_VALUE) - { - return "forever"; - } - - var diff = future - System.currentTimeMillis(); - - return formatDiff(diff); - } - - /** - * Converts a time span milliseconds to a MiniTime string. Note ALL time - * spans larger or equal than {@link Integer#MAX_VALUE} will be permanently - * converted to "forever". - * - * @param diff The source time span in milliseconds - * @return The resulting MiniTime string - * - * @throws IllegalArgumentException on a negative time duration - * - * @author 493msi - * @since 0.2 - */ - public static String formatDiff(long diff) - { - if (diff < 0) - { - throw new IllegalArgumentException("Negative time span cannot be converted to MiniTime."); - } - - var xweeks = miliseconds.toDays(diff) / DAYS_IN_WEEK; - - if (xweeks > Integer.MAX_VALUE) - { - return "forever"; - } - - var xdays = miliseconds.toDays(diff) % DAYS_IN_WEEK; - var xhours = miliseconds.toHours(diff) % HOURS_IN_DAY; - var xminutes = miliseconds.toMinutes(diff) % MINUTES_IN_HOUR; - var xseconds = miliseconds.toSeconds(diff) % SECONDS_IN_MINUTE; - - return formatTime(xweeks, xdays, xhours, xminutes, xseconds); - } - - private static String formatTime(long weeks, long days, long hours, long minutes, long seconds) - { - var sb = new StringBuilder(); - - if (weeks > 0) - { - sb.append(weeks); - sb.append('w'); - } - if (days > 0) - { - sb.append(days); - sb.append('d'); - } - if (hours > 0) - { - sb.append(hours); - sb.append('h'); - } - if (minutes > 0) - { - sb.append(minutes); - sb.append('m'); - } - if (seconds > 0) - { - sb.append(seconds); - sb.append('s'); - } - - var timeStr = sb.toString(); - - return timeStr.isEmpty() ? "0s" : timeStr; - } -} +package cz.tefek.pluto.chrono; + +import java.util.concurrent.TimeUnit; + +/** + * A helper class to convert from a time span in milliseconds to a simplified + * time span {@link String} format and vice versa. Note this action is fully + * reversible at the cost of losing millisecond precision. + * + *

MiniTime format specification:

+ * + *
+ * [Nw][Nd][Nh][Nm][Ns]
+ * 
+ *  w - weeks
+ *  d - days
+ *  h - hours
+ *  m - minutes
+ *  s - seconds
+ *  
+ *  N - a decimal integer
+ * 
+ *
    + *
  • At least one value is required.
  • + *
  • At least time unit is required.
  • + *
  • Time units are case insensitive.
  • + *
  • String cannot start with a letter or end with a number.
  • + *
  • All values must be valid positive 32-bit signed integers.
  • + *
  • All values except the first one must be less than 1 of the previous time + * unit.
  • + *
  • Skipping a time unit assumes zero of that time unit.
  • + *
+ * + *

Permitted values:

+ *
    + *
  • standard MiniTime scheme, from 0s to + * 2147483647w6d23h59m59s
  • + *
  • string "forever" (parses as {@link Long#MAX_VALUE})
  • + *
+ * + * @author 493msi + * @since 0.2 + */ +public class MiniTime +{ + private static class MiniTimeCouldNotBeParsedException extends RuntimeException + { + /** + * + */ + private static final long serialVersionUID = -5403949842120041373L; + + public MiniTimeCouldNotBeParsedException() + { + super("Time period could not be parsed. Correct format: \\_w\\_d\\_h\\_m\\_s **without spaces** between the units. You can skip a time unit. Example: 1h15m"); + } + } + + private static final TimeUnit miliseconds = TimeUnit.MILLISECONDS; + + private static final int DAYS_IN_WEEK = 7; + private static final int HOURS_IN_DAY = 24; + private static final int MINUTES_IN_HOUR = 60; + private static final int SECONDS_IN_MINUTE = 60; + private static final int MILLIS_IN_MINUTE = 1000; + + /** + * Converts a MiniTime spec string to the respective time duration in + * milliseconds. + * + * @param input The source MiniTime non-null string + * @return The resulting time span in milliseconds + * + * @author 493msi + * @since 0.2 + */ + public static long parse(String input) + { + if (input == null) + { + throw new IllegalArgumentException("MiniTime string cannot be null!"); + } + + // Nothing to parse + if (input.isEmpty()) + { + throw new MiniTimeCouldNotBeParsedException(); + } + + if (input.equalsIgnoreCase("forever")) + { + return Long.MAX_VALUE; + } + + // Follow the scheme + if (!input.matches("[0-9]*[wW]?[0-9]*[dD]?[0-9]*[hH]?[0-9]*[mM]?[0-9]*[sS]?")) + { + throw new MiniTimeCouldNotBeParsedException(); + } + + // 4584 of what? Potatoes? + if (input.matches("[0-9]+")) + { + throw new MiniTimeCouldNotBeParsedException(); + } + + // Where are the numbers? + if (input.matches("[a-zA-Z]+")) + { + throw new MiniTimeCouldNotBeParsedException(); + } + + // It shouldn't start with a letter + if (input.matches("^[a-zA-Z].+")) + { + throw new MiniTimeCouldNotBeParsedException(); + } + + var nrs = input.split("[a-zA-Z]"); + var letters = input.split("[0-9]+"); + + if (nrs.length != letters.length) + { + throw new MiniTimeCouldNotBeParsedException(); + } + + long time = 0; + + for (int i = 1; i < nrs.length; i++) + { + var type = letters[i - 1]; + int number; + + try + { + // The only time this fails is when the number is too long + number = Integer.parseUnsignedInt(nrs[i]); + } + catch (NumberFormatException nfe) + { + throw new MiniTimeCouldNotBeParsedException(); + } + + var allow = 0L; + var multiplier = 0; + + switch (type.toLowerCase()) + { + 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": + allow = DAYS_IN_WEEK; + multiplier = SECONDS_IN_MINUTE * MINUTES_IN_HOUR * HOURS_IN_DAY * MILLIS_IN_MINUTE; + break; + case "h": + case "H": + allow = HOURS_IN_DAY; + multiplier = SECONDS_IN_MINUTE * MINUTES_IN_HOUR * MILLIS_IN_MINUTE; + break; + case "m": + case "M": + allow = MINUTES_IN_HOUR; + multiplier = SECONDS_IN_MINUTE * MILLIS_IN_MINUTE; + break; + case "s": + case "S": + allow = SECONDS_IN_MINUTE; + multiplier = MILLIS_IN_MINUTE; + break; + default: + break; + } + + // The top one can be more than it normally could have, for example you can + // issue a ban for 48h but not 46h120m (it looks dumb) + if (i == 1) + { + allow = Integer.MAX_VALUE; + } + + if (number > allow) + { + throw new MiniTimeCouldNotBeParsedException(); + } + + time += multiplier * number; + } + + return System.currentTimeMillis() + time; + } + + /** + * Converts a time span between two Unix-epoch millisecond time points to a + * MiniTime string. Note ALL time spans larger or equal than + * {@link Integer#MAX_VALUE} weeks will be permanently converted to "forever". + * Inputting {@link Long#MAX_VALUE} for the future time point has the same effect. + * + * @param before The first time point in Unix-time milliseconds + * @param after The first time point in Unix-time milliseconds + * + * @return The resulting MiniTime string + * + * @throws IllegalArgumentException on a negative time duration + * + * @author 493msi + * @since 0.2 + */ + public static String fromMillisDiffNow(long after, long before) + { + if (after == Long.MAX_VALUE) + { + return "forever"; + } + + return formatDiff(after - before); + } + + /** + * Converts a time span between now and a future time point in Unix-epoch + * milliseconds to a MiniTime string. Note ALL time spans larger or equal + * than {@link Integer#MAX_VALUE} weeks will be permanently converted to + * "forever". Inputting {@link Long#MAX_VALUE} for the future time point + * has the same effect. + * + * @param future The future time point in Unix time milliseconds + * + * @return The resulting MiniTime string + * + * @throws IllegalArgumentException on a negative time duration + * + * @author 493msi + * @since 0.2 + */ + public static String fromMillisDiffNow(long future) + { + if (future == Long.MAX_VALUE) + { + return "forever"; + } + + var diff = future - System.currentTimeMillis(); + + return formatDiff(diff); + } + + /** + * Converts a time span milliseconds to a MiniTime string. Note ALL time + * spans larger or equal than {@link Integer#MAX_VALUE} weeks will be permanently + * converted to "forever". + * + * @param diff The source time span in milliseconds + * @return The resulting MiniTime string + * + * @throws IllegalArgumentException on a negative time duration + * + * @author 493msi + * @since 0.2 + */ + public static String formatDiff(long diff) + { + if (diff < 0) + { + throw new IllegalArgumentException("Negative time span cannot be converted to MiniTime."); + } + + var xweeks = miliseconds.toDays(diff) / DAYS_IN_WEEK; + + if (xweeks > Integer.MAX_VALUE) + { + return "forever"; + } + + var xdays = miliseconds.toDays(diff) % DAYS_IN_WEEK; + var xhours = miliseconds.toHours(diff) % HOURS_IN_DAY; + var xminutes = miliseconds.toMinutes(diff) % MINUTES_IN_HOUR; + var xseconds = miliseconds.toSeconds(diff) % SECONDS_IN_MINUTE; + + return formatTime(xweeks, xdays, xhours, xminutes, xseconds); + } + + private static String formatTime(long weeks, long days, long hours, long minutes, long seconds) + { + var sb = new StringBuilder(); + + if (weeks > 0) + { + sb.append(weeks); + sb.append('w'); + } + if (days > 0) + { + sb.append(days); + sb.append('d'); + } + if (hours > 0) + { + sb.append(hours); + sb.append('h'); + } + if (minutes > 0) + { + sb.append(minutes); + sb.append('m'); + } + if (seconds > 0) + { + sb.append(seconds); + sb.append('s'); + } + + var timeStr = sb.toString(); + + return timeStr.isEmpty() ? "0s" : timeStr; + } +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/eventsystem/EventData.java b/plutolib/src/main/java/cz/tefek/pluto/eventsystem/EventData.java index e67af4e..a15f3ec 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/eventsystem/EventData.java +++ b/plutolib/src/main/java/cz/tefek/pluto/eventsystem/EventData.java @@ -1,10 +1,10 @@ -package cz.tefek.pluto.eventsystem; - -/** - * @author 493msi - * - */ -public class EventData -{ - -} +package cz.tefek.pluto.eventsystem; + +/** + * @author 493msi + * + */ +public class EventData +{ + +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/ResourceAddress.java b/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/ResourceAddress.java index fbc86ff..0a62edb 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/ResourceAddress.java +++ b/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/ResourceAddress.java @@ -1,15 +1,14 @@ package cz.tefek.pluto.io.asl.resource; +import javax.annotation.Nullable; +import java.nio.file.FileSystems; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import java.nio.file.FileSystems; -import java.nio.file.Path; import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.Severity; +import cz.tefek.pluto.io.logger.SmartSeverity; import cz.tefek.pluto.modloader.ModLoaderCore; /** @@ -94,13 +93,11 @@ public class ResourceAddress if (as.length == 1) { - Logger.log(Severity.WARNING, "Please do not use tier 1 addresses, so it doesn't conflict with core assets."); + Logger.log(SmartSeverity.WARNING, "Please do not use tier 1 addresses, so it doesn't conflict with core assets."); } - for (int i = 0; i < as.length; i++) + for (String branch : as) { - var branch = as[i]; - if (branch.length() < 1 || branch.length() > MAX_BRANCH_STRING_LENGTH) { throw new IllegalArgumentException("Length of branch must be higher than 0 and lower than 33, this is not an essay."); @@ -151,9 +148,9 @@ public class ResourceAddress return raddress; } - public ResourceAddress fileExtension(String ext) + public ResourceAddress fileExtension(@Nullable String ext) { - if (ext == null || ext == "") + if (ext == null || "".equals(ext)) { this.fileExtension = null; return this; @@ -213,15 +210,7 @@ public class ResourceAddress @Override public String toString() { - StringBuilder sbPath = new StringBuilder(this.resSubscriber.getMod().getModID()); - - sbPath.append("$"); - - sbPath.append(this.subAddressToString()); - - String path = sbPath.toString(); - - return path; + return this.resSubscriber.getMod().getModID() + "$" + this.subAddressToString(); } public ResourceAddress copy() @@ -246,12 +235,10 @@ public class ResourceAddress if (this.hasFileExtension()) { - sbPath.append("." + this.fileExtension); + sbPath.append(".").append(this.fileExtension); } - String path = sbPath.toString(); - - return path; + return sbPath.toString(); } public String subAddressToString() @@ -293,7 +280,7 @@ public class ResourceAddress var pathBuilder = new StringBuilder(this.resSubscriber.getRootPath()); final var separator = FileSystems.getDefault().getSeparator(); pathBuilder.append(separator); - pathBuilder.append(this.subAddress.stream().collect(Collectors.joining(separator))); + pathBuilder.append(String.join(separator, this.subAddress)); if (this.hasFileExtension()) { diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/type/ResourceImage.java b/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/type/ResourceImage.java index 296416b..11e0c5c 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/type/ResourceImage.java +++ b/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/type/ResourceImage.java @@ -1,16 +1,15 @@ package cz.tefek.pluto.io.asl.resource.type; +import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; -import javax.imageio.ImageIO; - import cz.tefek.pluto.io.asl.resource.Resource; import cz.tefek.pluto.io.asl.resource.ResourceAddress; import cz.tefek.pluto.io.asl.resource.ResourceHelper; import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.Severity; +import cz.tefek.pluto.io.logger.SmartSeverity; /** * {@link ResourceAddress} in, {@link BufferedImage} out. @@ -38,7 +37,7 @@ public class ResourceImage extends Resource } catch (IOException e) { - Logger.log(Severity.ERROR, "Could not load BufferedImage: " + this.address.toString() + ", will load placeholder."); + Logger.log(SmartSeverity.ERROR, "Could not load BufferedImage: " + this.address.toString() + ", will load placeholder."); Logger.log(e); try @@ -47,7 +46,7 @@ public class ResourceImage extends Resource } catch (IOException e1) { - Logger.log(Severity.ERROR, "Placeholder BufferedImage not found: " + ResourceHelper.GLOBAL_ROOT + "data/assets/err/missingTex.png"); + Logger.log(SmartSeverity.ERROR, "Placeholder BufferedImage not found: " + ResourceHelper.GLOBAL_ROOT + "data/assets/err/missingTex.png"); Logger.log("This is not good! :C"); Logger.log(e1); diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/type/ResourceInputStream.java b/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/type/ResourceInputStream.java index 7b2b0d9..eeed8dc 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/type/ResourceInputStream.java +++ b/plutolib/src/main/java/cz/tefek/pluto/io/asl/resource/type/ResourceInputStream.java @@ -1,40 +1,40 @@ -package cz.tefek.pluto.io.asl.resource.type; - -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; - -import cz.tefek.pluto.io.asl.resource.Resource; -import cz.tefek.pluto.io.asl.resource.ResourceAddress; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.Severity; - -/** - * {@link ResourceAddress} in, {@link InputStream} out. - * - * @author 493msi - */ -public class ResourceInputStream extends Resource -{ - public ResourceInputStream(ResourceAddress raddress) - { - super(raddress, false); - } - - @Override - protected InputStream loadFromFile() - { - try - { - return new FileInputStream(this.address.toPath()); - } - catch (IOException e) - { - Logger.log(Severity.EXCEPTION, "Failed to open " + this.address + "!"); - Logger.log(Severity.EXCEPTION, e); - } - - return null; - } - -} +package cz.tefek.pluto.io.asl.resource.type; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; + +import cz.tefek.pluto.io.asl.resource.Resource; +import cz.tefek.pluto.io.asl.resource.ResourceAddress; +import cz.tefek.pluto.io.logger.Logger; +import cz.tefek.pluto.io.logger.SmartSeverity; + +/** + * {@link ResourceAddress} in, {@link InputStream} out. + * + * @author 493msi + */ +public class ResourceInputStream extends Resource +{ + public ResourceInputStream(ResourceAddress raddress) + { + super(raddress, false); + } + + @Override + protected InputStream loadFromFile() + { + try + { + return Files.newInputStream(this.address.toNIOPath()); + } + catch (IOException e) + { + Logger.log(SmartSeverity.ERROR, "Failed to open " + this.address + "!"); + Logger.log(e); + } + + return null; + } + +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/asl/textio/TextIn.java b/plutolib/src/main/java/cz/tefek/pluto/io/asl/textio/TextIn.java index ce2b621..57f1a2c 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/io/asl/textio/TextIn.java +++ b/plutolib/src/main/java/cz/tefek/pluto/io/asl/textio/TextIn.java @@ -1,78 +1,78 @@ -package cz.tefek.pluto.io.asl.textio; - -import java.io.File; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - -import cz.tefek.pluto.io.asl.resource.ResourceAddress; -import cz.tefek.pluto.io.logger.Logger; - -/** - * A simple text file reader. Apart from generic methods of loading, you can use - * a {@link ResourceAddress}. For writing use {@link TextOut}. - * - * @author 493msi - */ -public class TextIn -{ - public static String load(URL url) - { - try - { - load(url.toURI()); - } - catch (URISyntaxException e) - { - Logger.log(e); - } - - return null; - } - - public static String load(URI uri) - { - try - { - return Files.readString(Paths.get(uri)); - } - catch (Exception e) - { - Logger.log(e); - } - - return null; - } - - public static String load(Path path) - { - try - { - return Files.readString(path); - } - catch (Exception e) - { - Logger.log(e); - } - - return null; - } - - public static String loadInternal(String filename) - { - return load(TextIn.class.getResource("/" + filename)); - } - - public static String loadExternal(String filename) - { - return load(new File(filename).toURI()); - } - - public static String fromAddress(ResourceAddress address) - { - return load(address.toNIOPath()); - } -} +package cz.tefek.pluto.io.asl.textio; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import cz.tefek.pluto.io.asl.resource.ResourceAddress; +import cz.tefek.pluto.io.logger.Logger; + +/** + * A simple text file reader. Apart from generic methods of loading, you can use + * a {@link ResourceAddress}. For writing use {@link TextOut}. + * + * @author 493msi + */ +public class TextIn +{ + public static String load(URL url) + { + try + { + load(url.toURI()); + } + catch (URISyntaxException e) + { + Logger.log(e); + } + + return null; + } + + public static String load(URI uri) + { + try + { + return Files.readString(Paths.get(uri)); + } + catch (Exception e) + { + Logger.log(e); + } + + return null; + } + + public static String load(Path path) + { + try + { + return Files.readString(path); + } + catch (Exception e) + { + Logger.log(e); + } + + return null; + } + + public static String loadInternal(String filename) + { + return load(TextIn.class.getResource("/" + filename)); + } + + public static String loadExternal(String filename) + { + return load(new File(filename).toURI()); + } + + public static String fromAddress(ResourceAddress address) + { + return load(address.toNIOPath()); + } +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/asl/textio/TextOut.java b/plutolib/src/main/java/cz/tefek/pluto/io/asl/textio/TextOut.java index 75a45ec..8f47577 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/io/asl/textio/TextOut.java +++ b/plutolib/src/main/java/cz/tefek/pluto/io/asl/textio/TextOut.java @@ -1,28 +1,28 @@ -package cz.tefek.pluto.io.asl.textio; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintStream; - -/** - * Simplifies text writer creation. For reading use {@link TextIn}. - * - * @author 493msi - */ - -public class TextOut -{ - public static PrintStream createPrintStream(String filePath) throws IOException - { - PrintStream printstream = new PrintStream(createFOStream(filePath)); - - return printstream; - } - - public static FileOutputStream createFOStream(String filePath) throws IOException - { - FileOutputStream fos = new FileOutputStream(filePath); - - return fos; - } -} +package cz.tefek.pluto.io.asl.textio; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; + +/** + * Simplifies text writer creation. For reading use {@link TextIn}. + * + * @author 493msi + */ + +public class TextOut +{ + public static PrintStream createPrintStream(String filePath) throws IOException + { + PrintStream printstream = new PrintStream(createFOStream(filePath)); + + return printstream; + } + + public static FileOutputStream createFOStream(String filePath) throws IOException + { + FileOutputStream fos = new FileOutputStream(filePath); + + return fos; + } +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/logger/Logger.java b/plutolib/src/main/java/cz/tefek/pluto/io/logger/Logger.java index 063114c..98ee8fb 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/io/logger/Logger.java +++ b/plutolib/src/main/java/cz/tefek/pluto/io/logger/Logger.java @@ -30,7 +30,7 @@ public class Logger private static OutputStream fileLog = null; /** - * Initializes up the logger and replaces the standard output and standard error output with the logger methods. + * Initializes the logger and replaces the standard output and standard error output with the logger methods. * *

* diff --git a/plutolib/src/main/java/cz/tefek/pluto/io/logger/Severity.java b/plutolib/src/main/java/cz/tefek/pluto/io/logger/Severity.java index 76c3aed..9fd47ba 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/io/logger/Severity.java +++ b/plutolib/src/main/java/cz/tefek/pluto/io/logger/Severity.java @@ -1,35 +1,39 @@ -package cz.tefek.pluto.io.logger; - -/** - * Message severity. - * - * @author 493msi - */ -public enum Severity implements ISeverity -{ - INFO("[INFO] ", false), - WARNING("[WARNING] ", true), - ERROR("[ERROR] ", true), - EXCEPTION("[EXCEPTION] ", true), - NONE("", false); - - private String displayName; - private boolean usesStdErr; - - Severity(String name, boolean usesStdErr) - { - this.displayName = name; - } - - @Override - public String getDisplayName() - { - return this.displayName; - } - - @Override - public boolean isStdErr() - { - return this.usesStdErr; - } -} +package cz.tefek.pluto.io.logger; + +/** + * Message severity. + * + * @author 493msi + * + * @deprecated Use {@link SmartSeverity} instead. + */ +@Deprecated +public enum Severity implements ISeverity +{ + INFO("[INFO] ", false), + WARNING("[WARNING] ", true), + ERROR("[ERROR] ", true), + EXCEPTION("[EXCEPTION] ", true), + NONE("", false); + + private String displayName; + private boolean usesStdErr; + + Severity(String name, boolean usesStdErr) + { + this.displayName = name; + this.usesStdErr = usesStdErr; + } + + @Override + public String getDisplayName() + { + return this.displayName; + } + + @Override + public boolean isStdErr() + { + return this.usesStdErr; + } +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/math/CubicBezier.java b/plutolib/src/main/java/cz/tefek/pluto/math/CubicBezier.java index 4eacf6f..b4f4379 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/math/CubicBezier.java +++ b/plutolib/src/main/java/cz/tefek/pluto/math/CubicBezier.java @@ -1,84 +1,84 @@ -package cz.tefek.pluto.math; - -/** - * A class to generate a cubic bezier interpolation function. Not very - * optimized, so use it sparsely. - * - * @since 0.2 - * @author 493msi - */ -public class CubicBezier -{ - private static final int iterations = 16; - private double a, b, c, d; - - /** - * Creates a new {@code CubicBezier} from the given parameters. - * - * @param cpx1 the X position of the direction the function "steers towards" - * @param cpy1 the Y position of the direction the function "steers towards" - * @param cpx2 the X position of the direction the function "arrives from" - * @param cpy2 the Y position of the direction the function "arrives from" - * - * @since 0.3 - * @author 493msi - */ - public CubicBezier(double cpx1, double cpy1, double cpx2, double cpy2) - { - if (cpx1 < 0 || cpx1 > 1 || cpx2 < 0 || cpx2 > 1) - { - throw new IllegalArgumentException("Parameter out of range, only 0..1 is supported (only one Y value in 0..1 for each X in 0..1)."); - } - - this.a = cpx1; - this.b = cpy1; - this.c = cpx2; - this.d = cpy2; - } - - /** - * Solves this {@code CubicBezier} for the given x and the supplied - * parameters in the constructor. - * - * @param xIn the input X position - * - * @return the approximate Y position for the given X position - * - * @since 0.3 - * @author 493msi - */ - public double forX(double xIn) - { - if (xIn < 0) - { - return this.forX(0); - } - - if (xIn > 1) - { - return this.forX(1); - } - - double t = 0.5; - - double x; - double y = 3 * (1 - t) * (1 - t) * t * this.b + 3 * (1 - t) * t * t * this.d + t * t * t; - - double delta = 0.25; - boolean uh; - - for (int i = 0; i < iterations; i++) - { - x = 3 * (1 - t) * (1 - t) * t * this.a + 3 * (1 - t) * t * t * this.c + t * t * t; - y = 3 * (1 - t) * (1 - t) * t * this.b + 3 * (1 - t) * t * t * this.d + t * t * t; - - uh = x > xIn; - - t += uh ? -delta : delta; - - delta /= 2; - } - - return y; - } -} +package cz.tefek.pluto.math; + +/** + * A class to generate a cubic bezier interpolation function. Not very + * optimized, so use it sparsely. + * + * @since 0.2 + * @author 493msi + */ +public class CubicBezier +{ + private static final int iterations = 16; + private double a, b, c, d; + + /** + * Creates a new {@code CubicBezier} from the given parameters. + * + * @param cpx1 the X position of the direction the function "steers towards" + * @param cpy1 the Y position of the direction the function "steers towards" + * @param cpx2 the X position of the direction the function "arrives from" + * @param cpy2 the Y position of the direction the function "arrives from" + * + * @since 0.3 + * @author 493msi + */ + public CubicBezier(double cpx1, double cpy1, double cpx2, double cpy2) + { + if (cpx1 < 0 || cpx1 > 1 || cpx2 < 0 || cpx2 > 1) + { + throw new IllegalArgumentException("Parameter out of range, only 0..1 is supported (only one Y value in 0..1 for each X in 0..1)."); + } + + this.a = cpx1; + this.b = cpy1; + this.c = cpx2; + this.d = cpy2; + } + + /** + * Solves this {@code CubicBezier} for the given x and the supplied + * parameters in the constructor. + * + * @param xIn the input X position + * + * @return the approximate Y position for the given X position + * + * @since 0.3 + * @author 493msi + */ + public double forX(double xIn) + { + if (xIn < 0) + { + return this.forX(0); + } + + if (xIn > 1) + { + return this.forX(1); + } + + double t = 0.5; + + double x; + double y = 3 * (1 - t) * (1 - t) * t * this.b + 3 * (1 - t) * t * t * this.d + t * t * t; + + double delta = 0.25; + boolean uh; + + for (int i = 0; i < iterations; i++) + { + x = 3 * (1 - t) * (1 - t) * t * this.a + 3 * (1 - t) * t * t * this.c + t * t * t; + y = 3 * (1 - t) * (1 - t) * t * this.b + 3 * (1 - t) * t * t * this.d + t * t * t; + + uh = x > xIn; + + t += uh ? -delta : delta; + + delta /= 2; + } + + return y; + } +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/Mod.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/Mod.java index 4544bc3..6686989 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/Mod.java +++ b/plutolib/src/main/java/cz/tefek/pluto/modloader/Mod.java @@ -1,141 +1,141 @@ -package cz.tefek.pluto.modloader; - -import cz.tefek.pluto.io.asl.resource.ResourceSubscriber; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.pluto.pp.PlutoPackage; - -/** - * Mod object. Can be used to create a {@link ResourceSubscriber}. - * {@link ModLoaderCore} automatically creates a Mod object for each class - * annotated by {@link ModEntry @ModEntry} (assuming it is registered or class - * loaded by ModClassLoader, which auto-detects and registers {@link ModEntry - * ModEntries}). - * - * @see PlutoModLoader tutorial for - * more information. - * - * @author 493msi - */ -public class Mod extends PlutoPackage -{ - private Class mainClass; - private Object instance; - - private boolean clientSupport; - private boolean serverSupport; - - private ResourceSubscriber defaultResourceSubscriber; - - private String rootDataFolder; - - Mod(Class mainClass, String rootDataFolder) - { - super(extractModID(mainClass)); - - if (mainClass != null) - { - ModEntry modInterface = mainClass.getDeclaredAnnotation(ModEntry.class); - - if (modInterface != null) - { - this.mainClass = mainClass; - this.name = modInterface.displayName().isBlank() ? modInterface.modid() : modInterface.displayName(); - this.author = modInterface.author(); - this.version = modInterface.version(); - this.build = modInterface.build(); - this.earliestCompatibleBuild = modInterface.earliestCompatibleBuild(); - this.dependencies = modInterface.dependencies(); - this.description = modInterface.description(); - this.iconURL = modInterface.iconURL(); - - this.rootDataFolder = rootDataFolder; - this.defaultResourceSubscriber = new ResourceSubscriber(this, rootDataFolder); - - this.clientSupport = modInterface.clientSupport(); - this.serverSupport = modInterface.serverSupport(); - } - } - } - - private static String extractModID(Class mainClass) - { - if (mainClass != null) - { - ModEntry modInterface = mainClass.getDeclaredAnnotation(ModEntry.class); - - if (modInterface != null) - { - return modInterface.modid(); - } - } - - return null; - } - - Class getMainClass() - { - return this.mainClass; - } - - Object getClassInstance() throws ReflectiveOperationException - { - if (this.instance == null) - { - Logger.log("|Pluto Mod Loader| Loading mod instance: " + this.name); - this.instance = this.mainClass.getDeclaredConstructor().newInstance(); - } - - return this.instance; - } - - public String getModID() - { - return this.id; - } - - public String getModName() - { - return this.name; - } - - public String getModAuthor() - { - return this.author; - } - - public String getModVersion() - { - return this.version; - } - - public int getModBuild() - { - return this.build; - } - - public boolean isClientSupported() - { - return this.clientSupport; - } - - public boolean isServerSupported() - { - return this.serverSupport; - } - - public ResourceSubscriber getDefaultResourceSubscriber() - { - return this.defaultResourceSubscriber; - } - - public void setRootDataFolder(String rootDataFolder) - { - this.rootDataFolder = rootDataFolder; - this.defaultResourceSubscriber = new ResourceSubscriber(this, rootDataFolder); - } - - public String getRootDataFolder() - { - return this.rootDataFolder; - } -} +package cz.tefek.pluto.modloader; + +import cz.tefek.pluto.io.asl.resource.ResourceSubscriber; +import cz.tefek.pluto.io.logger.Logger; +import cz.tefek.pluto.io.pluto.pp.PlutoPackage; + +/** + * Mod object. Can be used to create a {@link ResourceSubscriber}. + * {@link ModLoaderCore} automatically creates a Mod object for each class + * annotated by {@link ModEntry @ModEntry} (assuming it is registered or class + * loaded by ModClassLoader, which auto-detects and registers {@link ModEntry + * ModEntries}). + * + * @see PlutoModLoader tutorial for + * more information. + * + * @author 493msi + */ +public class Mod extends PlutoPackage +{ + private Class mainClass; + private Object instance; + + private boolean clientSupport; + private boolean serverSupport; + + private ResourceSubscriber defaultResourceSubscriber; + + private String rootDataFolder; + + Mod(Class mainClass, String rootDataFolder) + { + super(extractModID(mainClass)); + + if (mainClass != null) + { + ModEntry modInterface = mainClass.getDeclaredAnnotation(ModEntry.class); + + if (modInterface != null) + { + this.mainClass = mainClass; + this.name = modInterface.displayName().isBlank() ? modInterface.modid() : modInterface.displayName(); + this.author = modInterface.author(); + this.version = modInterface.version(); + this.build = modInterface.build(); + this.earliestCompatibleBuild = modInterface.earliestCompatibleBuild(); + this.dependencies = modInterface.dependencies(); + this.description = modInterface.description(); + this.iconURL = modInterface.iconURL(); + + this.rootDataFolder = rootDataFolder; + this.defaultResourceSubscriber = new ResourceSubscriber(this, rootDataFolder); + + this.clientSupport = modInterface.clientSupport(); + this.serverSupport = modInterface.serverSupport(); + } + } + } + + private static String extractModID(Class mainClass) + { + if (mainClass != null) + { + ModEntry modInterface = mainClass.getDeclaredAnnotation(ModEntry.class); + + if (modInterface != null) + { + return modInterface.modid(); + } + } + + return null; + } + + Class getMainClass() + { + return this.mainClass; + } + + Object getClassInstance() throws ReflectiveOperationException + { + if (this.instance == null) + { + Logger.log("|Pluto Mod Loader| Loading mod instance: " + this.name); + this.instance = this.mainClass.getDeclaredConstructor().newInstance(); + } + + return this.instance; + } + + public String getModID() + { + return this.id; + } + + public String getModName() + { + return this.name; + } + + public String getModAuthor() + { + return this.author; + } + + public String getModVersion() + { + return this.version; + } + + public int getModBuild() + { + return this.build; + } + + public boolean isClientSupported() + { + return this.clientSupport; + } + + public boolean isServerSupported() + { + return this.serverSupport; + } + + public ResourceSubscriber getDefaultResourceSubscriber() + { + return this.defaultResourceSubscriber; + } + + public void setRootDataFolder(String rootDataFolder) + { + this.rootDataFolder = rootDataFolder; + this.defaultResourceSubscriber = new ResourceSubscriber(this, rootDataFolder); + } + + public String getRootDataFolder() + { + return this.rootDataFolder; + } +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/ModClassLoader.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/ModClassLoader.java index 3141abe..efe52d3 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/ModClassLoader.java +++ b/plutolib/src/main/java/cz/tefek/pluto/modloader/ModClassLoader.java @@ -1,154 +1,154 @@ -package cz.tefek.pluto.modloader; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -import java.io.File; -import java.net.URL; -import java.net.URLClassLoader; - -import cz.tefek.pluto.io.asl.resource.ResourceHelper; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; - -/** - * Class-loads all valid mods. The only requirement for the mod is to have a - * mod.jar file inside the base folder (for example - * mods/spaghetti/mod.jar). - * - * @author 493msi - */ -public class ModClassLoader -{ - public static ArrayList mods; - - public static void loadJars() - { - Logger.log(SmartSeverity.MODULE, "Looking for installed mods."); - - File modDir = new File(ResourceHelper.GLOBAL_ROOT + "mods/"); - - if (!modDir.exists()) - { - modDir.mkdir(); - - Logger.log(SmartSeverity.MODULE, " No mod found."); - - return; - } - - mods = new ArrayList<>(Arrays.asList(modDir.list())); - - if (mods.size() == 0) - { - Logger.log(SmartSeverity.MODULE, "No mod found."); - } - else - { - Logger.log(SmartSeverity.MODULE, "Found " + mods.size() + " mod(s) to load."); - - try - { - loadAll(); - } - catch (Exception e) - { - Logger.log(e); - } - } - } - - private static void loadAll() throws Exception - { - int i = 0; - - if (mods.size() > 0) - { - if (mods.size() == 1) - { - Logger.log(SmartSeverity.MODULE, "There is one mod to load."); - } - else - { - Logger.log(SmartSeverity.MODULE, "There are " + mods.size() + " mods to load."); - } - - for (String modname : mods) - { - var modFolder = ResourceHelper.GLOBAL_ROOT + "mods/" + modname; - var dataDir = modFolder + "/data"; - - if (new File(modFolder + "/mod.jar").exists()) - { - var dataDirFile = new File(dataDir); - - if (!dataDirFile.isDirectory()) - { - dataDirFile.mkdirs(); - } - - String pathToJar = modFolder + "/mod.jar"; - - JarFile jarFile = new JarFile(pathToJar); - Enumeration e = jarFile.entries(); - - URL[] urls = { new URL("jar:file:" + pathToJar + "!/") }; - - URLClassLoader sysLoader = URLClassLoader.newInstance(urls, ClassLoader.getSystemClassLoader()); - - while (e.hasMoreElements()) - { - JarEntry je = e.nextElement(); - - if (je.isDirectory()) - { - continue; - } - - // Not sure what to do with non-java files. - // They are ignored (for now). - if (!je.getName().endsWith(".class")) - { - continue; - } - - String className = je.getName().replaceAll("\\.class$", "").replace('/', '.'); - - Class modClass = sysLoader.loadClass(className); - - if (modClass.getDeclaredAnnotation(ModEntry.class) != null) - { - ModLoaderCore.registerMod(modClass, dataDir); - } - } - - jarFile.close(); - - Logger.log(SmartSeverity.MODULE_PLUS, "Loaded mod jar file: " + modname + "/mod.jar"); - i++; - } - else - { - Logger.log(SmartSeverity.MODULE_WARNING, "Failed to load mod jar file: " + modname + "."); - Logger.log(SmartSeverity.MODULE_WARNING, "Reason: Missing mod.jar file."); - } - } - - if (i < 1 || i == 0) - { - Logger.log(SmartSeverity.MODULE, "Loading mods complete, loaded " + i + " mods."); - } - else - { - Logger.log(SmartSeverity.MODULE, "Loading mods complete, loaded " + i + " mod."); - } - } - else - { - Logger.log(SmartSeverity.MODULE, "There aren't any mods, skipping initialising phase."); - } - } -} +package cz.tefek.pluto.modloader; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; + +import cz.tefek.pluto.io.asl.resource.ResourceHelper; +import cz.tefek.pluto.io.logger.Logger; +import cz.tefek.pluto.io.logger.SmartSeverity; + +/** + * Class-loads all valid mods. The only requirement for the mod is to have a + * mod.jar file inside the base folder (for example + * mods/spaghetti/mod.jar). + * + * @author 493msi + */ +public class ModClassLoader +{ + public static ArrayList mods; + + public static void loadJars() + { + Logger.log(SmartSeverity.MODULE, "Looking for installed mods."); + + File modDir = new File(ResourceHelper.GLOBAL_ROOT + "mods/"); + + if (!modDir.exists()) + { + modDir.mkdir(); + + Logger.log(SmartSeverity.MODULE, " No mod found."); + + return; + } + + mods = new ArrayList<>(Arrays.asList(modDir.list())); + + if (mods.size() == 0) + { + Logger.log(SmartSeverity.MODULE, "No mod found."); + } + else + { + Logger.log(SmartSeverity.MODULE, "Found " + mods.size() + " mod(s) to load."); + + try + { + loadAll(); + } + catch (Exception e) + { + Logger.log(e); + } + } + } + + private static void loadAll() throws Exception + { + int i = 0; + + if (mods.size() > 0) + { + if (mods.size() == 1) + { + Logger.log(SmartSeverity.MODULE, "There is one mod to load."); + } + else + { + Logger.log(SmartSeverity.MODULE, "There are " + mods.size() + " mods to load."); + } + + for (String modname : mods) + { + var modFolder = ResourceHelper.GLOBAL_ROOT + "mods/" + modname; + var dataDir = modFolder + "/data"; + + if (new File(modFolder + "/mod.jar").exists()) + { + var dataDirFile = new File(dataDir); + + if (!dataDirFile.isDirectory()) + { + dataDirFile.mkdirs(); + } + + String pathToJar = modFolder + "/mod.jar"; + + JarFile jarFile = new JarFile(pathToJar); + Enumeration e = jarFile.entries(); + + URL[] urls = { new URL("jar:file:" + pathToJar + "!/") }; + + URLClassLoader sysLoader = URLClassLoader.newInstance(urls, ClassLoader.getSystemClassLoader()); + + while (e.hasMoreElements()) + { + JarEntry je = e.nextElement(); + + if (je.isDirectory()) + { + continue; + } + + // Not sure what to do with non-java files. + // They are ignored (for now). + if (!je.getName().endsWith(".class")) + { + continue; + } + + String className = je.getName().replaceAll("\\.class$", "").replace('/', '.'); + + Class modClass = sysLoader.loadClass(className); + + if (modClass.getDeclaredAnnotation(ModEntry.class) != null) + { + ModLoaderCore.registerMod(modClass, dataDir); + } + } + + jarFile.close(); + + Logger.log(SmartSeverity.MODULE_PLUS, "Loaded mod jar file: " + modname + "/mod.jar"); + i++; + } + else + { + Logger.log(SmartSeverity.MODULE_WARNING, "Failed to load mod jar file: " + modname + "."); + Logger.log(SmartSeverity.MODULE_WARNING, "Reason: Missing mod.jar file."); + } + } + + if (i < 1 || i == 0) + { + Logger.log(SmartSeverity.MODULE, "Loading mods complete, loaded " + i + " mods."); + } + else + { + Logger.log(SmartSeverity.MODULE, "Loading mods complete, loaded " + i + " mod."); + } + } + else + { + Logger.log(SmartSeverity.MODULE, "There aren't any mods, skipping initialising phase."); + } + } +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/ModEntry.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/ModEntry.java index b2e33dc..0df0350 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/ModEntry.java +++ b/plutolib/src/main/java/cz/tefek/pluto/modloader/ModEntry.java @@ -1,39 +1,39 @@ -package cz.tefek.pluto.modloader; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * The heart of any Pluto mod. Annotate your class with this, so the - * PlutoModLoader can load it. The class must be directly registered or - * processed by {@link ModClassLoader} (as an external mod). - * - * @see PlutoModLoader tutorial for - * more information. - * - * @author 493msi - */ -@Retention(RetentionPolicy.RUNTIME) -public @interface ModEntry { - String modid(); - - String displayName() default ""; - - String author() default "anonymous author"; - - String version() default "unknown version"; - - Class[] dependencies() default {}; - - String iconURL() default ""; - - String description() default "No description available"; - - int build() default 0; - - int earliestCompatibleBuild() default 0; - - boolean clientSupport() default true; - - boolean serverSupport() default false; -} +package cz.tefek.pluto.modloader; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * The heart of any Pluto mod. Annotate your class with this, so the + * PlutoModLoader can load it. The class must be directly registered or + * processed by {@link ModClassLoader} (as an external mod). + * + * @see PlutoModLoader tutorial for + * more information. + * + * @author 493msi + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface ModEntry { + String modid(); + + String displayName() default ""; + + String author() default "anonymous author"; + + String version() default "unknown version"; + + Class[] dependencies() default {}; + + String iconURL() default ""; + + String description() default "No description available"; + + int build() default 0; + + int earliestCompatibleBuild() default 0; + + boolean clientSupport() default true; + + boolean serverSupport() default false; +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/ModInstaller.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/ModInstaller.java index 59cd18f..02a0e71 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/ModInstaller.java +++ b/plutolib/src/main/java/cz/tefek/pluto/modloader/ModInstaller.java @@ -1,119 +1,119 @@ -package cz.tefek.pluto.modloader; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; - -import cz.tefek.pluto.io.asl.resource.ResourceHelper; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; - -/** - * Unzips mod packages from the packages folder into the mods folder. WIP - * - * 2020 Update: Still WIP - * - * @author 493msi - */ -public class ModInstaller -{ - public static void unpackNewMods() - { - Logger.log(SmartSeverity.MODULE, "Looking for new mod packages to install."); - - File f = new File(ResourceHelper.GLOBAL_ROOT + "packages/"); - if (!f.exists()) - { - f.mkdir(); - - Logger.log(SmartSeverity.MODULE, "Package folder does not exist, creating it and aborting unpacking."); - - return; - } - - ArrayList files = new ArrayList(Arrays.asList(f.list())); - - if (files.size() == 0) - { - Logger.log(SmartSeverity.MODULE, "No mod package found."); - } - else - { - Logger.log(SmartSeverity.MODULE_PLUS, "Found " + files.size() + " mod packages."); - - for (String file : files) - { - Logger.log(SmartSeverity.MODULE_PLUS, "Mod package found: " + file + ", installing it."); - - try - { - extract(file); - } - catch (IOException e) - { - Logger.log(SmartSeverity.MODULE_ERROR, "Unpacking of " + file + " failed!"); - Logger.log(e); - } - - new File(ResourceHelper.GLOBAL_ROOT + "packages/" + file).delete(); - } - } - } - - private static void extract(String filepath) throws IOException - { - byte[] buffer = new byte[2048]; - - InputStream fileInput = null; - fileInput = new FileInputStream(ResourceHelper.GLOBAL_ROOT + "packages/" + filepath); - - ZipInputStream stream = new ZipInputStream(fileInput); - String outdir = ResourceHelper.GLOBAL_ROOT + "mods/"; - - if (!new File(outdir).exists()) - { - new File(outdir).mkdir(); - } - - try - { - String filename = filepath.replaceAll("\\.zip$", ""); - - new File(outdir + filename).mkdir(); - - ZipEntry entry; - while ((entry = stream.getNextEntry()) != null) - { - String outpath = outdir + filename + "/" + entry.getName(); - FileOutputStream output = null; - try - { - output = new FileOutputStream(outpath); - int len = 0; - while ((len = stream.read(buffer)) > 0) - { - output.write(buffer, 0, len); - } - } - finally - { - if (output != null) - { - output.close(); - } - } - } - } - finally - { - stream.close(); - } - } -} +package cz.tefek.pluto.modloader; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import cz.tefek.pluto.io.asl.resource.ResourceHelper; +import cz.tefek.pluto.io.logger.Logger; +import cz.tefek.pluto.io.logger.SmartSeverity; + +/** + * Unzips mod packages from the packages folder into the mods folder. WIP + * + * 2020 Update: Still WIP + * + * @author 493msi + */ +public class ModInstaller +{ + public static void unpackNewMods() + { + Logger.log(SmartSeverity.MODULE, "Looking for new mod packages to install."); + + File f = new File(ResourceHelper.GLOBAL_ROOT + "packages/"); + if (!f.exists()) + { + f.mkdir(); + + Logger.log(SmartSeverity.MODULE, "Package folder does not exist, creating it and aborting unpacking."); + + return; + } + + ArrayList files = new ArrayList(Arrays.asList(f.list())); + + if (files.size() == 0) + { + Logger.log(SmartSeverity.MODULE, "No mod package found."); + } + else + { + Logger.log(SmartSeverity.MODULE_PLUS, "Found " + files.size() + " mod packages."); + + for (String file : files) + { + Logger.log(SmartSeverity.MODULE_PLUS, "Mod package found: " + file + ", installing it."); + + try + { + extract(file); + } + catch (IOException e) + { + Logger.log(SmartSeverity.MODULE_ERROR, "Unpacking of " + file + " failed!"); + Logger.log(e); + } + + new File(ResourceHelper.GLOBAL_ROOT + "packages/" + file).delete(); + } + } + } + + private static void extract(String filepath) throws IOException + { + byte[] buffer = new byte[2048]; + + InputStream fileInput = null; + fileInput = new FileInputStream(ResourceHelper.GLOBAL_ROOT + "packages/" + filepath); + + ZipInputStream stream = new ZipInputStream(fileInput); + String outdir = ResourceHelper.GLOBAL_ROOT + "mods/"; + + if (!new File(outdir).exists()) + { + new File(outdir).mkdir(); + } + + try + { + String filename = filepath.replaceAll("\\.zip$", ""); + + new File(outdir + filename).mkdir(); + + ZipEntry entry; + while ((entry = stream.getNextEntry()) != null) + { + String outpath = outdir + filename + "/" + entry.getName(); + FileOutputStream output = null; + try + { + output = new FileOutputStream(outpath); + int len = 0; + while ((len = stream.read(buffer)) > 0) + { + output.write(buffer, 0, len); + } + } + finally + { + if (output != null) + { + output.close(); + } + } + } + } + finally + { + stream.close(); + } + } +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/ModLoaderCore.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/ModLoaderCore.java index b11f5bb..902512f 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/ModLoaderCore.java +++ b/plutolib/src/main/java/cz/tefek/pluto/modloader/ModLoaderCore.java @@ -1,271 +1,271 @@ -package cz.tefek.pluto.modloader; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; - -import cz.tefek.pluto.eventsystem.staticmode.StaticPlutoEventManager; -import cz.tefek.pluto.io.asl.resource.ResourceHelper; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; -import cz.tefek.pluto.modloader.event.ModLoad; -import cz.tefek.pluto.modloader.event.ModLoadEvent; -import cz.tefek.pluto.modloader.event.ModPostLoad; -import cz.tefek.pluto.modloader.event.ModPostLoadEvent; -import cz.tefek.pluto.modloader.event.ModPreLoad; -import cz.tefek.pluto.modloader.event.ModPreLoadEvent; -import cz.tefek.pluto.modloader.event.ModUnload; -import cz.tefek.pluto.modloader.event.ModUnloadEvent; - -public class ModLoaderCore -{ - public static final String MOD_ID_STRING_PATTERN = "[a-z0-9]+[a-z0-9_]*"; - public static final String FULL_MOD_ID_STRING_PATTERN = "^" + MOD_ID_STRING_PATTERN + "$"; - - static ModLoadingPhase loadingPhase = ModLoadingPhase.WAITING; - - private static final List modArchive = new ArrayList<>(); - - private static final Queue loadBuffer = new LinkedList<>(); - - public static void registerMod(Class modClass, String modDataRoot) - { - if (loadingPhase != ModLoadingPhase.WAITING && loadingPhase != ModLoadingPhase.CLASSLOADING) - { - Logger.log(SmartSeverity.MODULE_ERROR, "Cannot register mod during loading phase " + loadingPhase.name() + "!"); - return; - } - - if (getModByMainClass(modClass) != null) - { - Logger.log(SmartSeverity.MODULE_WARNING, "Mod class " + modClass.getCanonicalName() + " is already registered, skipping it."); - return; - } - - if (modDataRoot == null) - { - modDataRoot = ResourceHelper.DEFAULT_RESOURCE_ROOT; - } - - var registeredMod = loadBuffer.stream().filter(presentMod -> presentMod.getMainClass().equals(modClass)).findAny(); - - if (registeredMod.isPresent()) - { - if (modDataRoot != null) - { - var mod = registeredMod.get(); - - mod.setRootDataFolder(modDataRoot); - } - - return; - } - - Mod mod = new Mod(modClass, modDataRoot); - - if (!mod.getModID().matches(FULL_MOD_ID_STRING_PATTERN)) - { - Logger.log(SmartSeverity.MODULE_WARNING, "Mod id " + mod.getModID() + " contains invalid characters (or none at all), mod will not be loaded."); - Logger.log(SmartSeverity.MODULE_WARNING, "Only lowercase letters (a to z) and numbers (0 to 9) are allowed characters."); - return; - } - - var deps = mod.getDependencies(); - - for (var dependency : deps) - { - var registeredDependency = loadBuffer.stream().filter(presentMod -> presentMod.getMainClass().equals(dependency)).findAny(); - - if (registeredDependency.isPresent()) - { - continue; - } - - registerMod(dependency); - } - - loadBuffer.add(mod); - } - - public static void registerMod(Class modClass) - { - registerMod(modClass, null); - } - - public static List getAllMods() - { - return Collections.unmodifiableList(modArchive); - } - - public static Mod getModByID(String id) - { - for (Mod mod : modArchive) - { - if (mod.getModID().equals(id)) - { - return mod; - } - } - - return null; - } - - private static Mod getModByMainClass(Class modClass) - { - for (Mod mod : modArchive) - { - if (modClass.equals(mod.getMainClass())) - { - return mod; - } - } - - return null; - } - - public static void loadProcedure() - { - loadingPhase = ModLoadingPhase.PREPARING; - - StaticPlutoEventManager.registerEvent(ModPreLoad.class); - StaticPlutoEventManager.registerEvent(ModLoad.class); - StaticPlutoEventManager.registerEvent(ModPostLoad.class); - StaticPlutoEventManager.registerEvent(ModUnload.class); - - Logger.log("[Pluto Mod Loader] Number of manually added mods: " + modArchive.size()); - loadingPhase = ModLoadingPhase.UPACKING; - ModInstaller.unpackNewMods(); - loadingPhase = ModLoadingPhase.CLASSLOADING; - ModClassLoader.loadJars(); - loadingPhase = ModLoadingPhase.INITIALIZING; - - while (loadBuffer.peek() != null) - { - var mod = loadBuffer.remove(); - StaticPlutoEventManager.registerEventHandler(mod.getMainClass()); - modArchive.add(mod); - } - - if (!modArchive.isEmpty()) - { - Logger.log(SmartSeverity.MODULE, "Initializing mod(s)..."); - initMods(); - - if (loadingPhase == ModLoadingPhase.FINISHING) - { - Logger.log(SmartSeverity.MODULE, "Initializing mod(s) finished."); - } - else - { - Logger.log(SmartSeverity.MODULE_ERROR, "Initializing mod(s) was canceled due to error(s)."); - } - } - } - - static void initMods() - { - while (loadingPhase != ModLoadingPhase.CANCELED && loadingPhase != ModLoadingPhase.FINISHING) - { - switch (loadingPhase) - { - case INITIALIZING: - preLoadMods(); - break; - case PRELOADING: - loadMods(); - break; - case LOADING: - postLoadMods(); - break; - case POSTLOADING: - complete(); - break; - default: - break; - } - } - } - - public static void unloadProcedure() - { - Logger.log(SmartSeverity.MODULE_MINUS, "Unloading all mods."); - StaticPlutoEventManager.fireEvent(ModUnload.class, new ModUnloadEvent()); - modArchive.clear(); - } - - private static void preLoadMods() - { - loadingPhase = ModLoadingPhase.PRELOADING; - - try - { - ModPreLoadEvent preLoadData = new ModPreLoadEvent(); - preLoadData.mods = modArchive; - - StaticPlutoEventManager.fireEvent(ModPreLoad.class, preLoadData); - } - catch (Exception e) - { - Logger.log(SmartSeverity.MODULE_ERROR, "Problem encountered while pre-loading mods."); - Logger.log(SmartSeverity.MODULE_ERROR, "Mod loading stopped."); - - Logger.log(e); - - cancelLoading(); - } - } - - private static void loadMods() - { - loadingPhase = ModLoadingPhase.LOADING; - - try - { - ModLoadEvent loadData = new ModLoadEvent(); - - StaticPlutoEventManager.fireEvent(ModLoad.class, loadData); - } - catch (Exception e) - { - Logger.log(SmartSeverity.MODULE_ERROR, "Problem encountered while loading mods."); - Logger.log(SmartSeverity.MODULE_ERROR, "Mod loading stopped."); - - Logger.log(e); - - cancelLoading(); - } - } - - private static void postLoadMods() - { - loadingPhase = ModLoadingPhase.POSTLOADING; - - try - { - ModPostLoadEvent postLoadData = new ModPostLoadEvent(); - - StaticPlutoEventManager.fireEvent(ModPostLoad.class, postLoadData); - } - catch (Exception e) - { - Logger.log(SmartSeverity.MODULE_ERROR, "Problem encountered while post-loading mods."); - Logger.log(SmartSeverity.MODULE_ERROR, "Mod loading stopped."); - - Logger.log(e); - - cancelLoading(); - } - } - - private static void complete() - { - loadingPhase = ModLoadingPhase.FINISHING; - } - - private static void cancelLoading() - { - loadingPhase = ModLoadingPhase.CANCELED; - } -} +package cz.tefek.pluto.modloader; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +import cz.tefek.pluto.eventsystem.staticmode.StaticPlutoEventManager; +import cz.tefek.pluto.io.asl.resource.ResourceHelper; +import cz.tefek.pluto.io.logger.Logger; +import cz.tefek.pluto.io.logger.SmartSeverity; +import cz.tefek.pluto.modloader.event.ModLoad; +import cz.tefek.pluto.modloader.event.ModLoadEvent; +import cz.tefek.pluto.modloader.event.ModPostLoad; +import cz.tefek.pluto.modloader.event.ModPostLoadEvent; +import cz.tefek.pluto.modloader.event.ModPreLoad; +import cz.tefek.pluto.modloader.event.ModPreLoadEvent; +import cz.tefek.pluto.modloader.event.ModUnload; +import cz.tefek.pluto.modloader.event.ModUnloadEvent; + +public class ModLoaderCore +{ + public static final String MOD_ID_STRING_PATTERN = "[a-z0-9]+[a-z0-9_]*"; + public static final String FULL_MOD_ID_STRING_PATTERN = "^" + MOD_ID_STRING_PATTERN + "$"; + + static ModLoadingPhase loadingPhase = ModLoadingPhase.WAITING; + + private static final List modArchive = new ArrayList<>(); + + private static final Queue loadBuffer = new LinkedList<>(); + + public static void registerMod(Class modClass, String modDataRoot) + { + if (loadingPhase != ModLoadingPhase.WAITING && loadingPhase != ModLoadingPhase.CLASSLOADING) + { + Logger.log(SmartSeverity.MODULE_ERROR, "Cannot register mod during loading phase " + loadingPhase.name() + "!"); + return; + } + + if (getModByMainClass(modClass) != null) + { + Logger.log(SmartSeverity.MODULE_WARNING, "Mod class " + modClass.getCanonicalName() + " is already registered, skipping it."); + return; + } + + if (modDataRoot == null) + { + modDataRoot = ResourceHelper.DEFAULT_RESOURCE_ROOT; + } + + var registeredMod = loadBuffer.stream().filter(presentMod -> presentMod.getMainClass().equals(modClass)).findAny(); + + if (registeredMod.isPresent()) + { + if (modDataRoot != null) + { + var mod = registeredMod.get(); + + mod.setRootDataFolder(modDataRoot); + } + + return; + } + + Mod mod = new Mod(modClass, modDataRoot); + + if (!mod.getModID().matches(FULL_MOD_ID_STRING_PATTERN)) + { + Logger.log(SmartSeverity.MODULE_WARNING, "Mod id " + mod.getModID() + " contains invalid characters (or none at all), mod will not be loaded."); + Logger.log(SmartSeverity.MODULE_WARNING, "Only lowercase letters (a to z) and numbers (0 to 9) are allowed characters."); + return; + } + + var deps = mod.getDependencies(); + + for (var dependency : deps) + { + var registeredDependency = loadBuffer.stream().filter(presentMod -> presentMod.getMainClass().equals(dependency)).findAny(); + + if (registeredDependency.isPresent()) + { + continue; + } + + registerMod(dependency); + } + + loadBuffer.add(mod); + } + + public static void registerMod(Class modClass) + { + registerMod(modClass, null); + } + + public static List getAllMods() + { + return Collections.unmodifiableList(modArchive); + } + + public static Mod getModByID(String id) + { + for (Mod mod : modArchive) + { + if (mod.getModID().equals(id)) + { + return mod; + } + } + + return null; + } + + private static Mod getModByMainClass(Class modClass) + { + for (Mod mod : modArchive) + { + if (modClass.equals(mod.getMainClass())) + { + return mod; + } + } + + return null; + } + + public static void loadProcedure() + { + loadingPhase = ModLoadingPhase.PREPARING; + + StaticPlutoEventManager.registerEvent(ModPreLoad.class); + StaticPlutoEventManager.registerEvent(ModLoad.class); + StaticPlutoEventManager.registerEvent(ModPostLoad.class); + StaticPlutoEventManager.registerEvent(ModUnload.class); + + Logger.log("[Pluto Mod Loader] Number of manually added mods: " + modArchive.size()); + loadingPhase = ModLoadingPhase.UPACKING; + ModInstaller.unpackNewMods(); + loadingPhase = ModLoadingPhase.CLASSLOADING; + ModClassLoader.loadJars(); + loadingPhase = ModLoadingPhase.INITIALIZING; + + while (loadBuffer.peek() != null) + { + var mod = loadBuffer.remove(); + StaticPlutoEventManager.registerEventHandler(mod.getMainClass()); + modArchive.add(mod); + } + + if (!modArchive.isEmpty()) + { + Logger.log(SmartSeverity.MODULE, "Initializing mod(s)..."); + initMods(); + + if (loadingPhase == ModLoadingPhase.FINISHING) + { + Logger.log(SmartSeverity.MODULE, "Initializing mod(s) finished."); + } + else + { + Logger.log(SmartSeverity.MODULE_ERROR, "Initializing mod(s) was canceled due to error(s)."); + } + } + } + + static void initMods() + { + while (loadingPhase != ModLoadingPhase.CANCELED && loadingPhase != ModLoadingPhase.FINISHING) + { + switch (loadingPhase) + { + case INITIALIZING: + preLoadMods(); + break; + case PRELOADING: + loadMods(); + break; + case LOADING: + postLoadMods(); + break; + case POSTLOADING: + complete(); + break; + default: + break; + } + } + } + + public static void unloadProcedure() + { + Logger.log(SmartSeverity.MODULE_MINUS, "Unloading all mods."); + StaticPlutoEventManager.fireEvent(ModUnload.class, new ModUnloadEvent()); + modArchive.clear(); + } + + private static void preLoadMods() + { + loadingPhase = ModLoadingPhase.PRELOADING; + + try + { + ModPreLoadEvent preLoadData = new ModPreLoadEvent(); + preLoadData.mods = modArchive; + + StaticPlutoEventManager.fireEvent(ModPreLoad.class, preLoadData); + } + catch (Exception e) + { + Logger.log(SmartSeverity.MODULE_ERROR, "Problem encountered while pre-loading mods."); + Logger.log(SmartSeverity.MODULE_ERROR, "Mod loading stopped."); + + Logger.log(e); + + cancelLoading(); + } + } + + private static void loadMods() + { + loadingPhase = ModLoadingPhase.LOADING; + + try + { + ModLoadEvent loadData = new ModLoadEvent(); + + StaticPlutoEventManager.fireEvent(ModLoad.class, loadData); + } + catch (Exception e) + { + Logger.log(SmartSeverity.MODULE_ERROR, "Problem encountered while loading mods."); + Logger.log(SmartSeverity.MODULE_ERROR, "Mod loading stopped."); + + Logger.log(e); + + cancelLoading(); + } + } + + private static void postLoadMods() + { + loadingPhase = ModLoadingPhase.POSTLOADING; + + try + { + ModPostLoadEvent postLoadData = new ModPostLoadEvent(); + + StaticPlutoEventManager.fireEvent(ModPostLoad.class, postLoadData); + } + catch (Exception e) + { + Logger.log(SmartSeverity.MODULE_ERROR, "Problem encountered while post-loading mods."); + Logger.log(SmartSeverity.MODULE_ERROR, "Mod loading stopped."); + + Logger.log(e); + + cancelLoading(); + } + } + + private static void complete() + { + loadingPhase = ModLoadingPhase.FINISHING; + } + + private static void cancelLoading() + { + loadingPhase = ModLoadingPhase.CANCELED; + } +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/ModLoadingPhase.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/ModLoadingPhase.java index 78dcfd8..292d756 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/ModLoadingPhase.java +++ b/plutolib/src/main/java/cz/tefek/pluto/modloader/ModLoadingPhase.java @@ -1,17 +1,17 @@ -package cz.tefek.pluto.modloader; - -public enum ModLoadingPhase -{ - UPACKING, - PREPARING, - INITIALIZING, - WAITING, - PRELOADING, - LOADING, - POSTLOADING, - FINISHING, - CANCELED, - INSTANTIATING, - CLASSLOADING, - UNLOADING; -} +package cz.tefek.pluto.modloader; + +public enum ModLoadingPhase +{ + UPACKING, + PREPARING, + INITIALIZING, + WAITING, + PRELOADING, + LOADING, + POSTLOADING, + FINISHING, + CANCELED, + INSTANTIATING, + CLASSLOADING, + UNLOADING; +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModLoad.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModLoad.java index c7ef6d4..12c72b1 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModLoad.java +++ b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModLoad.java @@ -1,21 +1,21 @@ -package cz.tefek.pluto.modloader.event; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import cz.tefek.pluto.eventsystem.staticmode.StaticPlutoEvent; - -@Retention(RUNTIME) -@Target(METHOD) -@StaticPlutoEvent(passingParamClass = ModLoadEvent.class) -/** - * @author 493msi - * - */ -public @interface ModLoad -{ - -} +package cz.tefek.pluto.modloader.event; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import cz.tefek.pluto.eventsystem.staticmode.StaticPlutoEvent; + +@Retention(RUNTIME) +@Target(METHOD) +@StaticPlutoEvent(passingParamClass = ModLoadEvent.class) +/** + * @author 493msi + * + */ +public @interface ModLoad +{ + +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModLoadEvent.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModLoadEvent.java index 77e52c9..d095b56 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModLoadEvent.java +++ b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModLoadEvent.java @@ -1,15 +1,15 @@ -package cz.tefek.pluto.modloader.event; - -import cz.tefek.pluto.eventsystem.EventData; -import cz.tefek.pluto.modloader.ModEntry; - -/** - * Instances are passed to {@link ModEntry ModEntries} on load. Currently does - * nothing. - * - * @author 493msi - * - */ -public class ModLoadEvent extends EventData -{ -} +package cz.tefek.pluto.modloader.event; + +import cz.tefek.pluto.eventsystem.EventData; +import cz.tefek.pluto.modloader.ModEntry; + +/** + * Instances are passed to {@link ModEntry ModEntries} on load. Currently does + * nothing. + * + * @author 493msi + * + */ +public class ModLoadEvent extends EventData +{ +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPostLoad.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPostLoad.java index be0e5b6..98fc6c7 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPostLoad.java +++ b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPostLoad.java @@ -1,21 +1,21 @@ -package cz.tefek.pluto.modloader.event; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import cz.tefek.pluto.eventsystem.staticmode.StaticPlutoEvent; - -@Retention(RUNTIME) -@Target(METHOD) -@StaticPlutoEvent(passingParamClass = ModPostLoadEvent.class) -/** - * @author 493msi - * - */ -public @interface ModPostLoad -{ - -} +package cz.tefek.pluto.modloader.event; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import cz.tefek.pluto.eventsystem.staticmode.StaticPlutoEvent; + +@Retention(RUNTIME) +@Target(METHOD) +@StaticPlutoEvent(passingParamClass = ModPostLoadEvent.class) +/** + * @author 493msi + * + */ +public @interface ModPostLoad +{ + +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPostLoadEvent.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPostLoadEvent.java index 1cddaba..dea04aa 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPostLoadEvent.java +++ b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPostLoadEvent.java @@ -1,18 +1,18 @@ -package cz.tefek.pluto.modloader.event; - -import java.util.ArrayList; -import java.util.List; - -import cz.tefek.pluto.eventsystem.EventData; -import cz.tefek.pluto.modloader.ModEntry; - -/** - * Instances are passed to {@link ModEntry ModEntries} on post-load. - * - * @author 493msi - */ -public class ModPostLoadEvent extends EventData -{ - // TODO Cross-mod messaging - public final List crossMessages = new ArrayList(); -} +package cz.tefek.pluto.modloader.event; + +import java.util.ArrayList; +import java.util.List; + +import cz.tefek.pluto.eventsystem.EventData; +import cz.tefek.pluto.modloader.ModEntry; + +/** + * Instances are passed to {@link ModEntry ModEntries} on post-load. + * + * @author 493msi + */ +public class ModPostLoadEvent extends EventData +{ + // TODO Cross-mod messaging + public final List crossMessages = new ArrayList(); +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPreLoad.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPreLoad.java index 7070f79..10d6a69 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPreLoad.java +++ b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPreLoad.java @@ -1,21 +1,21 @@ -package cz.tefek.pluto.modloader.event; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import cz.tefek.pluto.eventsystem.staticmode.StaticPlutoEvent; - -@Retention(RUNTIME) -@Target(METHOD) -@StaticPlutoEvent(passingParamClass = ModPreLoadEvent.class) -/** - * @author 493msi - * - */ -public @interface ModPreLoad -{ - -} +package cz.tefek.pluto.modloader.event; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import cz.tefek.pluto.eventsystem.staticmode.StaticPlutoEvent; + +@Retention(RUNTIME) +@Target(METHOD) +@StaticPlutoEvent(passingParamClass = ModPreLoadEvent.class) +/** + * @author 493msi + * + */ +public @interface ModPreLoad +{ + +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPreLoadEvent.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPreLoadEvent.java index 7b4e448..de9963a 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPreLoadEvent.java +++ b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModPreLoadEvent.java @@ -1,19 +1,19 @@ -package cz.tefek.pluto.modloader.event; - -import java.util.List; - -import cz.tefek.pluto.eventsystem.EventData; -import cz.tefek.pluto.modloader.Mod; -import cz.tefek.pluto.modloader.ModEntry; - -/** - * Instances are passed to {@link ModEntry ModEntries} on load. Carries a list - * of all {@link Mod} objects. - * - * @author 493msi - * - */ -public class ModPreLoadEvent extends EventData -{ - public List mods; -} +package cz.tefek.pluto.modloader.event; + +import java.util.List; + +import cz.tefek.pluto.eventsystem.EventData; +import cz.tefek.pluto.modloader.Mod; +import cz.tefek.pluto.modloader.ModEntry; + +/** + * Instances are passed to {@link ModEntry ModEntries} on load. Carries a list + * of all {@link Mod} objects. + * + * @author 493msi + * + */ +public class ModPreLoadEvent extends EventData +{ + public List mods; +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModUnload.java b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModUnload.java index 0592183..5f8925a 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModUnload.java +++ b/plutolib/src/main/java/cz/tefek/pluto/modloader/event/ModUnload.java @@ -1,20 +1,20 @@ -package cz.tefek.pluto.modloader.event; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import cz.tefek.pluto.eventsystem.staticmode.StaticPlutoEvent; - -@Retention(RUNTIME) -@Target(METHOD) -@StaticPlutoEvent(passingParamClass = ModUnloadEvent.class) -/** - * @author 493msi - * - */ -public @interface ModUnload { - -} +package cz.tefek.pluto.modloader.event; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import cz.tefek.pluto.eventsystem.staticmode.StaticPlutoEvent; + +@Retention(RUNTIME) +@Target(METHOD) +@StaticPlutoEvent(passingParamClass = ModUnloadEvent.class) +/** + * @author 493msi + * + */ +public @interface ModUnload { + +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/tpl/TPJImage.java b/plutolib/src/main/java/cz/tefek/pluto/tpl/TPJImage.java deleted file mode 100644 index 85a0f88..0000000 --- a/plutolib/src/main/java/cz/tefek/pluto/tpl/TPJImage.java +++ /dev/null @@ -1,35 +0,0 @@ -package cz.tefek.pluto.tpl; - -public class TPJImage -{ - int[] data; - int width; - int height; - - public TPJImage(int[] pixels, int width, int height) - { - this.data = pixels; - this.width = width; - this.height = height; - } - - public int getWidth() - { - return this.width; - } - - public int getHeight() - { - return this.height; - } - - public int[] getData() - { - return this.data; - } - - public int pixelAt(int x, int y) - { - return this.data[x + y * this.width]; - } -} diff --git a/plutolib/src/main/java/cz/tefek/pluto/tpl/TPL.java b/plutolib/src/main/java/cz/tefek/pluto/tpl/TPL.java index 76ae19b..296c97f 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/tpl/TPL.java +++ b/plutolib/src/main/java/cz/tefek/pluto/tpl/TPL.java @@ -1,187 +1,312 @@ -package cz.tefek.pluto.tpl; - -import java.awt.Graphics2D; -import java.awt.image.BufferedImage; -import java.awt.image.DataBuffer; -import java.awt.image.DataBufferByte; -import java.awt.image.Raster; -import java.io.File; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -import javax.imageio.ImageIO; - -import cz.tefek.pluto.io.asl.resource.ResourceAddress; -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; - -/** - * Quick ABGR (8-bit per channel, 32 bits per pixel) texture loader for OpenGL - * use. Color component swizzling may be needed. - * - * @author 493msi - */ -public class TPL -{ - private static final int PLACEHOLDER_SIZE = 16; - private static final int PLACEHOLDER_CHECKEDBOARD = 8; - private static final int PLACEHOLDER_CHECKEDBOARD_SQUARE_SIZE = PLACEHOLDER_SIZE / PLACEHOLDER_CHECKEDBOARD; - - public static TPNImage load(ResourceAddress file) - { - return file == null ? loadImage(null) : load(file.toPath()); - } - - public static TPNImage load(String file) - { - if (file == null) - { - return loadImage(null); - } - - try - { - return loadImage(ImageIO.read(new File(file))); - } - catch (Exception e) - { - Logger.log(SmartSeverity.ERROR, "[TPL] Image could not be loaded: " + file); - Logger.log(e); - - return loadImage(null); - } - } - - public static TPNImage loadImage(BufferedImage image) - { - boolean remake = false; - int width = 0; - int height = 0; - - if (image == null) - { - Logger.log(SmartSeverity.WARNING, "[TPL] Null BufferedImage supplied, generating a placeholder."); - - remake = true; - } - else - { - width = image.getWidth(); - height = image.getHeight(); - - if (width > 16384 || height > 16384 || width < 1 || height < 1) - { - Logger.log(SmartSeverity.ERROR, "[TPL] BufferedImage size is invalid (< 1 or > 16384), generating a placeholder."); - - remake = true; - } - } - - if (remake) - { - width = PLACEHOLDER_SIZE; - height = PLACEHOLDER_SIZE; - - Logger.log(SmartSeverity.INFO, "[TPL] Generating a substitute image..."); - - ByteBuffer buffer = ByteBuffer.allocateDirect(width * height * 4); - buffer.order(ByteOrder.nativeOrder()); - - for (int i = 0; i < width * height; i++) - { - int x = i % width; - int y = i / width; - boolean checker = x / PLACEHOLDER_CHECKEDBOARD_SQUARE_SIZE % 2 == y / PLACEHOLDER_CHECKEDBOARD_SQUARE_SIZE % 2; - - buffer.put((byte) 0xff); // A - buffer.put((byte) 0x00); // B - buffer.put((byte) 0x00); // G - buffer.put((byte) (checker ? 0xff : 0x00)); // R - } - - buffer.flip(); - return new TPNImage(buffer, width, height); - } - - ByteBuffer buffer = ByteBuffer.allocateDirect(width * height * 4).order(ByteOrder.nativeOrder()); - - BufferedImage copy = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_4BYTE_ABGR); - Graphics2D imgGraphics = copy.createGraphics(); - imgGraphics.drawImage(image, 0, copy.getHeight(), copy.getWidth(), 0, 0, 0, image.getWidth(), image.getHeight(), null); // I wonder if this is pixel-perfect - imgGraphics.dispose(); - - Raster data = copy.getData(); - DataBuffer dataBuffer = data.getDataBuffer(); - DataBufferByte byteBuffer = (DataBufferByte) dataBuffer; - byte[] byteData = byteBuffer.getData(); - buffer.put(byteData); - buffer.flip(); - - return new TPNImage(buffer, width, height); - } - - public static TPJImage loadPixels(String file) - { - TPJImage tImg = null; - BufferedImage image = null; - - boolean remake = false; - - int width = 0; - int height = 0; - - try - { - image = ImageIO.read(new File(file)); - width = image.getWidth(); - height = image.getHeight(); - } - catch (Exception e) - { - Logger.log(SmartSeverity.ERROR, "[TPL] Image could not be loaded: " + file); - Logger.log(e); - - remake = true; - } - - if (width > 16384 || height > 16384 || width < 1 || height < 1) - { - Logger.log(SmartSeverity.ERROR, "[TPL] Image size is invalid (< 1 or > 16384): " + file); - Logger.log(SmartSeverity.ERROR, "[TPL] A replacement will be generated."); - - remake = true; - } - - if (remake) - { - width = PLACEHOLDER_SIZE; - height = PLACEHOLDER_SIZE; - - tImg = new TPJImage(new int[width * height], width, height); - - Logger.log(SmartSeverity.INFO, "[TPL] Generating a substitute image..."); - - for (int i = 0; i < width * height; i++) - { - int x = i % width; - int y = i / width; - boolean checker = x / PLACEHOLDER_CHECKEDBOARD_SQUARE_SIZE % 2 == y / PLACEHOLDER_CHECKEDBOARD_SQUARE_SIZE % 2; - - tImg.data[i] = checker ? 0xffff0000 : 0xff000000; - } - - return tImg; - } - - tImg = new TPJImage(new int[width * height], width, height); - - for (int i = 0; i < width * height; i++) - { - int pixel = image.getRGB(i % width, i / width); - - tImg.data[i] = pixel; - } - - return tImg; - } -} +package cz.tefek.pluto.tpl; + +import java.awt.Graphics2D; +import java.awt.image.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.file.Files; +import java.nio.file.Path; + +import javax.annotation.Nullable; +import javax.imageio.ImageIO; + +import cz.tefek.pluto.io.asl.resource.ResourceAddress; +import cz.tefek.pluto.io.logger.Logger; +import cz.tefek.pluto.io.logger.SmartSeverity; + +/** + * Quick ABGR (8-bit per channel, 32 bits per pixel) image loader for OpenGL textures. + * Color component swizzling may be needed. + * + * FIXME: Refactor {@link TPL#loadBufferedImage} + * + * @author 493msi + * + * @see TPNImage + * + * @since pre-alpha + */ +public class TPL +{ + private static final int PLACEHOLDER_SIZE = 16; + + private static final int PLACEHOLDER_CHECKEDBOARD = 8; + private static final int PLACEHOLDER_CHECKEDBOARD_SQUARE_SIZE = PLACEHOLDER_SIZE / PLACEHOLDER_CHECKEDBOARD; + + private static final BufferedImage placeholder; + + static + { + placeholder = new BufferedImage(PLACEHOLDER_SIZE, PLACEHOLDER_SIZE, BufferedImage.TYPE_INT_ARGB); + var data = placeholder.getData(); + var dataBuffer = (DataBufferInt) data.getDataBuffer(); + + for (int i = 0; i < PLACEHOLDER_SIZE * PLACEHOLDER_SIZE; i++) + { + int x = i % PLACEHOLDER_SIZE; + int y = i / PLACEHOLDER_SIZE; + boolean checker = x / PLACEHOLDER_CHECKEDBOARD_SQUARE_SIZE % 2 == y / PLACEHOLDER_CHECKEDBOARD_SQUARE_SIZE % 2; + + dataBuffer.setElem(i, checker ? 0xFFFF0000 : 0xFF000000); + } + } + + // TODO: Fix this mess + private static BufferedImage loadBufferedImage(@Nullable Object source) + { + var inputStream = (InputStream) null; + + try + { + if (source instanceof ResourceAddress) + { + inputStream = Files.newInputStream(((ResourceAddress) source).toNIOPath()); + } + else if (source instanceof String) + { + inputStream = new FileInputStream((String) source); + } + else if (source instanceof File) + { + inputStream = new FileInputStream((File) source); + } + else if (source instanceof Path) + { + inputStream = Files.newInputStream((Path) source); + } + + if (inputStream != null) + { + return ImageIO.read(inputStream); + } + } + catch (Exception e) + { + Logger.log(SmartSeverity.ERROR, "[TPL] Image could not be loaded: " + source); + Logger.log(e); + } + finally + { + try + { + if (inputStream != null) + inputStream.close(); + } + catch (IOException e) + { + Logger.log(SmartSeverity.ERROR, "[TPL] Failed to close: " + source); + Logger.log(e); + } + } + + return placeholder; + } + + /** + * Loads an image from a file the denoted by the input {@link ResourceAddress} into a {@link TPNImage} buffer. + * + * If the input {@link ResourceAddress} is null, a placeholder will be generated. + * + * @param address The source {@link ResourceAddress}, from which the image will be loaded + * + * @return The output {@link TPNImage}, never null + * + * @see TPNImage + * + * @since pre-alpha + * */ + public static TPNImage load(@Nullable ResourceAddress address) + { + return loadImage(loadBufferedImage(address)); + } + + /** + * Loads an image from the denoted filename to a {@link TPNImage} buffer. + * + * If the input filename is null, a placeholder will be generated. + * + * @deprecated Use the {@link TPL#load(ResourceAddress)} or {@link TPL#load(File)} variants. + * + * @param filename The source filename, from which the image will be loaded + * + * @return The output {@link TPNImage}, never null + * + * @see TPNImage + * + * @since pre-alpha + * */ + @Deprecated + public static TPNImage load(@Nullable String filename) + { + return loadImage(loadBufferedImage(filename)); + } + + /** + * Loads an image from the input {@link File} into a {@link TPNImage} buffer. + * + * If the input {@link File} is null, a placeholder will be generated. + * + * @param file The source {@link File}, from which the image will be loaded + * + * @return The output {@link TPNImage}, never null + * + * @see TPNImage + * + * @since 20.2.0.0-alpha.1 + * */ + public static TPNImage load(@Nullable File file) + { + return loadImage(loadBufferedImage(file)); + } + + /** + * Loads an image from a file the denoted by the input {@link Path} into a {@link TPNImage} buffer. + * + * If the input {@link Path} is null, a placeholder will be generated. + * + * @param path The source {@link Path}, from which the image will be loaded + * + * @return The output {@link TPNImage}, never null + * + * @see TPNImage + * + * @since 20.2.0.0-alpha.1 + * */ + public static TPNImage load(@Nullable Path path) + { + return loadImage(loadBufferedImage(path)); + } + + /** + * Loads an image from a file the denoted by the input {@link ResourceAddress} into a {@link TPNImage} buffer. + * + * If the input {@link ResourceAddress} is null, a placeholder will be generated. + * + * @param address The source {@link ResourceAddress}, from which the image will be loaded + * @param flipY Whether the image should flipped vertically (for OpenGL uses) + * + * @return The output {@link TPNImage}, never null + * + * @see TPNImage + * + * @since 20.2.0.0-alpha.1 + * */ + public static TPNImage loadSpecial(@Nullable ResourceAddress address, boolean flipY) + { + return loadImageSpecial(loadBufferedImage(address), flipY); + } + + + /** + * Loads an image from the input {@link File} into a {@link TPNImage} buffer. + * + * If the input {@link File} is null, a placeholder will be generated. + * + * @param file The source {@link File}, from which the image will be loaded + * @param flipY Whether the image should flipped vertically (for OpenGL uses) + * + * @return The output {@link TPNImage}, never null + * + * @see TPNImage + * + * @since 20.2.0.0-alpha.1 + * */ + public static TPNImage load(@Nullable File file, boolean flipY) + { + return loadImageSpecial(loadBufferedImage(file), flipY); + } + + /** + * Loads an image from a file the denoted by the input {@link Path} into a {@link TPNImage} buffer. + * + * If the input {@link Path} is null, a placeholder will be generated. + * + * @param path The source {@link Path}, from which the image will be loaded + * @param flipY Whether the image should flipped vertically (for OpenGL uses) + * + * @return The output {@link TPNImage}, never null + * + * @see TPNImage + * + * @since 20.2.0.0-alpha.1 + * */ + public static TPNImage loadSpecial(@Nullable Path path, boolean flipY) + { + return loadImageSpecial(loadBufferedImage(path), flipY); + } + + /** + * Writes a {@link BufferedImage} into a {@link TPNImage} buffer. + * + * If the input {@link Path} is null, a placeholder will be generated. + * + * @param image The source {@link BufferedImage} + * @param flipY Whether the image should flipped vertically (for OpenGL uses) + * + * @return The output {@link TPNImage}, never null + * + * @see TPNImage + * + * @since 20.2.0.0-alpha.1 + * */ + public static TPNImage loadImageSpecial(@Nullable BufferedImage image, boolean flipY) + { + if (image == null) + { + Logger.log(SmartSeverity.WARNING, "[TPL] Null BufferedImage supplied, generating a placeholder."); + + return loadImageSpecial(placeholder, flipY); + } + + int width = image.getWidth(); + int height = image.getHeight(); + + if (width > 16384 || height > 16384 || width < 1 || height < 1) + { + Logger.log(SmartSeverity.ERROR, "[TPL] BufferedImage size is invalid (< 1 or > 16384), generating a placeholder."); + + return loadImageSpecial(placeholder, flipY); + } + + BufferedImage copy = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_4BYTE_ABGR); + Graphics2D imgGraphics = copy.createGraphics(); + imgGraphics.drawImage(image, + 0, flipY ? copy.getHeight() : 0, copy.getWidth(), flipY ? 0 : copy.getHeight(), + 0, 0, image.getWidth(), image.getHeight(), + null); // I wonder if this is pixel-perfect + imgGraphics.dispose(); + + Raster data = copy.getRaster(); + DataBuffer dataBuffer = data.getDataBuffer(); + DataBufferByte byteBuffer = (DataBufferByte) dataBuffer; + byte[] byteData = byteBuffer.getData(); + ByteBuffer buffer = ByteBuffer.allocateDirect(width * height * 4).order(ByteOrder.nativeOrder()); + buffer.put(byteData); + buffer.flip(); + + return new TPNImage(buffer, width, height); + } + + + /** + * Writes a {@link BufferedImage} into a {@link TPNImage} buffer. + * + * If the input {@link Path} is null, a placeholder will be generated. + * + * @param image The source {@link BufferedImage} + * + * @return The output {@link TPNImage}, never null + * + * @see TPNImage + * + * @since pre-alpha + * */ + public static TPNImage loadImage(@Nullable BufferedImage image) + { + return loadImageSpecial(image, true); + } +} diff --git a/plutolib/src/main/java/cz/tefek/pluto/tpl/TPNImage.java b/plutolib/src/main/java/cz/tefek/pluto/tpl/TPNImage.java index 7468cbf..18ba7a0 100644 --- a/plutolib/src/main/java/cz/tefek/pluto/tpl/TPNImage.java +++ b/plutolib/src/main/java/cz/tefek/pluto/tpl/TPNImage.java @@ -1,32 +1,73 @@ -package cz.tefek.pluto.tpl; - -import java.nio.ByteBuffer; - -public class TPNImage -{ - ByteBuffer data; - int width; - int height; - - public TPNImage(ByteBuffer bfr, int width, int height) - { - this.data = bfr; - this.width = width; - this.height = height; - } - - public int getWidth() - { - return this.width; - } - - public int getHeight() - { - return this.height; - } - - public ByteBuffer getData() - { - return this.data; - } -} +package cz.tefek.pluto.tpl; + +import java.nio.ByteBuffer; + +/** + * A wrapper around a native color buffer for easier handling + * by various APIs, such as OpenGL and GLFW. + * + * @implNote TPNImage is always ABGR due to image format + * limitations of {@link java.awt.image.BufferedImage} + * + * @author 493msi + * + * @since pre-alpha + */ +public class TPNImage +{ + private final ByteBuffer data; + private final int width; + private final int height; + + /** + * Creates a new {@link TPNImage} from the specified buffer, width and height. + * + * @param bfr The input {@link ByteBuffer} + * @param width This image's width + * @param height This image's height + * + * @since pre-alpha + * */ + public TPNImage(ByteBuffer bfr, int width, int height) + { + this.data = bfr; + this.width = width; + this.height = height; + } + + /** + * Returns the width of the color buffer. + * + * @return The width of this {@link TPNImage} + * + * @since pre-alpha + * */ + public int getWidth() + { + return this.width; + } + + /** + * Returns the height of the color buffer. + * + * @return The height of this {@link TPNImage} + * + * @since pre-alpha + * */ + public int getHeight() + { + return this.height; + } + + /** + * Returns a read-only view of the color buffer. + * + * @return This image's color {@link ByteBuffer} + * + * @since pre-alpha + * */ + public ByteBuffer getData() + { + return this.data.asReadOnlyBuffer(); + } +} diff --git a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/QuadPresets.java b/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/QuadPresets.java index fc6aa93..999aaa8 100644 --- a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/QuadPresets.java +++ b/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/QuadPresets.java @@ -1,57 +1,57 @@ -package cz.tefek.pluto.engine.graphics.gl.vao; - -import cz.tefek.pluto.engine.graphics.gl.vao.attrib.data.VecArray; - -/** - * @author 493msi - * - */ -public class QuadPresets -{ - public static VertexArray basicQuad() - { - float[] uvs = { 0, 0, 1, 0, 1, 1, 0, 1 }; - - float[] positions = { -1, 1, 1, 1, 1, -1, -1, -1 }; - - int[] indices = { 0, 1, 2, 0, 2, 3 }; - - VertexArrayBuilder vab = new VertexArrayBuilder(); - vab.vertices(new VecArray<>(positions, 2)); - vab.uvs(new VecArray<>(uvs, 2)); - vab.indices(new VecArray<>(indices, 1)); - - return vab.export(); - } - - public static VertexArray halvedSize() - { - float[] uvs = { 0, 0, 1, 0, 1, 1, 0, 1 }; - - float[] positions = { -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f }; - - int[] indices = { 0, 1, 2, 0, 2, 3 }; - - VertexArrayBuilder vab = new VertexArrayBuilder(); - vab.vertices(new VecArray<>(positions, 2)); - vab.uvs(new VecArray<>(uvs, 2)); - vab.indices(new VecArray<>(indices, 1)); - return vab.export(); - } - - public static VertexArray basicNoNeg() - { - float[] uvs = { 0, 0, 1, 0, 1, 1, 0, 1 }; - - float[] positions = { 0, 1, 1, 1, 1, 0, 0, 0 }; - - int[] indices = { 0, 1, 2, 0, 2, 3 }; - - VertexArrayBuilder vab = new VertexArrayBuilder(); - vab.vertices(new VecArray<>(positions, 2)); - vab.uvs(new VecArray<>(uvs, 2)); - vab.indices(new VecArray<>(indices, 1)); - - return vab.export(); - } -} +package cz.tefek.pluto.engine.graphics.gl.vao; + +import cz.tefek.pluto.engine.graphics.gl.vao.attrib.data.VecArray; + +/** + * @author 493msi + * + */ +public class QuadPresets +{ + public static VertexArray basicQuad() + { + float[] uvs = { 0, 0, 1, 0, 1, 1, 0, 1 }; + + float[] positions = { -1, 1, 1, 1, 1, -1, -1, -1 }; + + int[] indices = { 0, 1, 2, 0, 2, 3 }; + + VertexArrayBuilder vab = new VertexArrayBuilder(); + vab.vertices(new VecArray<>(positions, 2)); + vab.uvs(new VecArray<>(uvs, 2)); + vab.indices(new VecArray<>(indices, 1)); + + return vab.export(); + } + + public static VertexArray halvedSize() + { + float[] uvs = { 0, 0, 1, 0, 1, 1, 0, 1 }; + + float[] positions = { -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f }; + + int[] indices = { 0, 1, 2, 0, 2, 3 }; + + VertexArrayBuilder vab = new VertexArrayBuilder(); + vab.vertices(new VecArray<>(positions, 2)); + vab.uvs(new VecArray<>(uvs, 2)); + vab.indices(new VecArray<>(indices, 1)); + return vab.export(); + } + + public static VertexArray basicNoNeg() + { + float[] uvs = { 0, 0, 1, 0, 1, 1, 0, 1 }; + + float[] positions = { 0, 1, 1, 1, 1, 0, 0, 0 }; + + int[] indices = { 0, 1, 2, 0, 2, 3 }; + + VertexArrayBuilder vab = new VertexArrayBuilder(); + vab.vertices(new VecArray<>(positions, 2)); + vab.uvs(new VecArray<>(uvs, 2)); + vab.indices(new VecArray<>(indices, 1)); + + return vab.export(); + } +} diff --git a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/VertexArray.java b/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/VertexArray.java index 77d6836..7cebbad 100644 --- a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/VertexArray.java +++ b/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/VertexArray.java @@ -1,145 +1,145 @@ -package cz.tefek.pluto.engine.graphics.gl.vao; - -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; - -public class VertexArray -{ - protected final List usedAttribs; - protected final Vector> vertexAttribs; - - protected IndexArrayBuffer indices; - - private int vertexCount; - protected int glID = 0; - - public VertexArray() - { - int maxAttribs = GL33.glGetInteger(GL33.GL_MAX_VERTEX_ATTRIBS); - - this.usedAttribs = new ArrayList<>(maxAttribs); - this.vertexAttribs = new Vector>(maxAttribs); - this.vertexAttribs.setSize(maxAttribs); - - this.glID = GL33.glGenVertexArrays(); - - Logger.logf(SmartSeverity.ADDED, "Vertex array ID %d created...\n", this.glID); - } - - public void createArrayAttrib(ArrayBuffer buffer, int attribID) - { - this.bind(); - buffer.bind(); - GL33.glVertexAttribPointer(attribID, buffer.getVertexDimensions(), buffer.getType().getGLID(), false, 0, 0); - - this.vertexAttribs.set(attribID, buffer); - this.usedAttribs.add(attribID); - - if (!this.hasIndices()) - { - this.vertexCount = buffer.getVertexCount(); - } - } - - public List> getVertexAttribs() - { - return Collections.unmodifiableList(this.vertexAttribs); - } - - public int getVertexCount() - { - return this.vertexCount; - } - - public void enableAllAttributes() - { - this.usedAttribs.stream().forEach(GL33::glEnableVertexAttribArray); - } - - public void bindIndices(IndexArrayBuffer buffer) - { - this.bind(); - buffer.bind(); - this.indices = buffer; - this.vertexCount = buffer.getVertexCount(); - } - - public void bind() - { - GL33.glBindVertexArray(this.glID); - } - - public void unbind() - { - GL33.glBindVertexArray(0); - } - - public void draw(DrawMode mode) - { - if (this.hasIndices()) - { - GL33.glDrawElements(mode.getGLID(), this.vertexCount, this.indices.getType().getGLID(), MemoryUtil.NULL); - } - else - { - GL33.glDrawArrays(mode.getGLID(), 0, this.vertexCount); - } - } - - public void drawInstanced(DrawMode mode, int count) - { - if (this.hasIndices()) - { - GL33.glDrawElementsInstanced(mode.getGLID(), this.vertexCount, this.indices.getType().getGLID(), MemoryUtil.NULL, count); - } - else - { - GL33.glDrawArraysInstanced(mode.getGLID(), 0, this.vertexCount, count); - } - } - - public IndexArrayBuffer getIndices() - { - return this.indices; - } - - public boolean hasIndices() - { - return this.indices != null; - } - - public void delete() - { - this.usedAttribs.stream().map(this.vertexAttribs::get).forEach(ArrayBuffer::delete); - this.vertexAttribs.clear(); - this.usedAttribs.clear(); - - if (this.indices != null) - { - this.indices.delete(); - this.indices = null; - } - - Logger.logf(SmartSeverity.REMOVED, "Vertex array ID %d deleted...\n", this.glID); - - GL33.glDeleteVertexArrays(this.glID); - - this.glID = 0; - } - - public int getID() - { - return this.glID; - } -} +package cz.tefek.pluto.engine.graphics.gl.vao; + +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; + +public class VertexArray +{ + protected final List usedAttribs; + protected final Vector> vertexAttribs; + + protected IndexArrayBuffer indices; + + private int vertexCount; + protected int glID = 0; + + public VertexArray() + { + int maxAttribs = GL33.glGetInteger(GL33.GL_MAX_VERTEX_ATTRIBS); + + this.usedAttribs = new ArrayList<>(maxAttribs); + this.vertexAttribs = new Vector>(maxAttribs); + this.vertexAttribs.setSize(maxAttribs); + + this.glID = GL33.glGenVertexArrays(); + + Logger.logf(SmartSeverity.ADDED, "Vertex array ID %d created...\n", this.glID); + } + + public void createArrayAttrib(ArrayBuffer buffer, int attribID) + { + this.bind(); + buffer.bind(); + GL33.glVertexAttribPointer(attribID, buffer.getVertexDimensions(), buffer.getType().getGLID(), false, 0, 0); + + this.vertexAttribs.set(attribID, buffer); + this.usedAttribs.add(attribID); + + if (!this.hasIndices()) + { + this.vertexCount = buffer.getVertexCount(); + } + } + + public List> getVertexAttribs() + { + return Collections.unmodifiableList(this.vertexAttribs); + } + + public int getVertexCount() + { + return this.vertexCount; + } + + public void enableAllAttributes() + { + this.usedAttribs.stream().forEach(GL33::glEnableVertexAttribArray); + } + + public void bindIndices(IndexArrayBuffer buffer) + { + this.bind(); + buffer.bind(); + this.indices = buffer; + this.vertexCount = buffer.getVertexCount(); + } + + public void bind() + { + GL33.glBindVertexArray(this.glID); + } + + public void unbind() + { + GL33.glBindVertexArray(0); + } + + public void draw(DrawMode mode) + { + if (this.hasIndices()) + { + GL33.glDrawElements(mode.getGLID(), this.vertexCount, this.indices.getType().getGLID(), MemoryUtil.NULL); + } + else + { + GL33.glDrawArrays(mode.getGLID(), 0, this.vertexCount); + } + } + + public void drawInstanced(DrawMode mode, int count) + { + if (this.hasIndices()) + { + GL33.glDrawElementsInstanced(mode.getGLID(), this.vertexCount, this.indices.getType().getGLID(), MemoryUtil.NULL, count); + } + else + { + GL33.glDrawArraysInstanced(mode.getGLID(), 0, this.vertexCount, count); + } + } + + public IndexArrayBuffer getIndices() + { + return this.indices; + } + + public boolean hasIndices() + { + return this.indices != null; + } + + public void delete() + { + this.usedAttribs.stream().map(this.vertexAttribs::get).forEach(ArrayBuffer::delete); + this.vertexAttribs.clear(); + this.usedAttribs.clear(); + + if (this.indices != null) + { + this.indices.delete(); + this.indices = null; + } + + Logger.logf(SmartSeverity.REMOVED, "Vertex array ID %d deleted...\n", this.glID); + + GL33.glDeleteVertexArrays(this.glID); + + this.glID = 0; + } + + public int getID() + { + return this.glID; + } +} diff --git a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/VertexArrayBuilder.java b/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/VertexArrayBuilder.java index a6d2f3b..74a1a7a 100644 --- a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/VertexArrayBuilder.java +++ b/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vao/VertexArrayBuilder.java @@ -1,42 +1,42 @@ -package cz.tefek.pluto.engine.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; - -public class VertexArrayBuilder -{ - protected VertexArray va; - - public VertexArrayBuilder() - { - this.va = new VertexArray(); - } - - public VertexArrayBuilder vertices(VecArray vertices) - { - this.va.createArrayAttrib(new FloatArrayBuffer(vertices), ReservedAttributes.POSITION); - - return this; - } - - public VertexArrayBuilder uvs(VecArray uvs) - { - this.va.createArrayAttrib(new FloatArrayBuffer(uvs), ReservedAttributes.UV); - - return this; - } - - public VertexArrayBuilder indices(VecArray indices) - { - this.va.bindIndices(new IndexArrayBuffer(indices)); - - return this; - } - - public VertexArray export() - { - return this.va; - } -} +package cz.tefek.pluto.engine.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; + +public class VertexArrayBuilder +{ + protected VertexArray va; + + public VertexArrayBuilder() + { + this.va = new VertexArray(); + } + + public VertexArrayBuilder vertices(VecArray vertices) + { + this.va.createArrayAttrib(new FloatArrayBuffer(vertices), ReservedAttributes.POSITION); + + return this; + } + + public VertexArrayBuilder uvs(VecArray uvs) + { + this.va.createArrayAttrib(new FloatArrayBuffer(uvs), ReservedAttributes.UV); + + return this; + } + + public VertexArrayBuilder indices(VecArray indices) + { + this.va.bindIndices(new IndexArrayBuffer(indices)); + + return this; + } + + public VertexArray export() + { + return this.va; + } +} diff --git a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/ArrayBuffer.java b/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/ArrayBuffer.java index dd04790..c1d9316 100644 --- a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/ArrayBuffer.java +++ b/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/ArrayBuffer.java @@ -1,62 +1,62 @@ -package cz.tefek.pluto.engine.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 cz.tefek.pluto.engine.graphics.gl.vao.attrib.data.VecArray; - -public abstract class ArrayBuffer> -{ - protected int glID = 0; - - private final int vertexDimensions; - private final int vertexCount; - - public ArrayBuffer(T data) - { - this.glID = glGenBuffers(); - this.bind(); - this.bindData(data); - - this.vertexDimensions = data.getVecDimensions(); - this.vertexCount = data.getVertexCount(); - } - - public abstract EnumArrayBufferType getType(); - - protected abstract void bindData(T data); - - public void bind() - { - glBindBuffer(GL_ARRAY_BUFFER, this.glID); - } - - public void unbind() - { - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - - public void delete() - { - glDeleteBuffers(this.glID); - - this.glID = 0; - } - - public int getID() - { - return this.glID; - } - - public int getVertexDimensions() - { - return this.vertexDimensions; - } - - public int getVertexCount() - { - return this.vertexCount; - } -} +package cz.tefek.pluto.engine.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 cz.tefek.pluto.engine.graphics.gl.vao.attrib.data.VecArray; + +public abstract class ArrayBuffer> +{ + protected int glID = 0; + + private final int vertexDimensions; + private final int vertexCount; + + public ArrayBuffer(T data) + { + this.glID = glGenBuffers(); + this.bind(); + this.bindData(data); + + this.vertexDimensions = data.getVecDimensions(); + this.vertexCount = data.getVertexCount(); + } + + public abstract EnumArrayBufferType getType(); + + protected abstract void bindData(T data); + + public void bind() + { + glBindBuffer(GL_ARRAY_BUFFER, this.glID); + } + + public void unbind() + { + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + public void delete() + { + glDeleteBuffers(this.glID); + + this.glID = 0; + } + + public int getID() + { + return this.glID; + } + + public int getVertexDimensions() + { + return this.vertexDimensions; + } + + public int getVertexCount() + { + return this.vertexCount; + } +} diff --git a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/FloatArrayBuffer.java b/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/FloatArrayBuffer.java index cefd33a..e3b089e 100644 --- a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/FloatArrayBuffer.java +++ b/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/FloatArrayBuffer.java @@ -1,25 +1,25 @@ -package cz.tefek.pluto.engine.graphics.gl.vbo; - -import org.lwjgl.opengl.GL33; - -import cz.tefek.pluto.engine.graphics.gl.vao.attrib.data.VecArray; - -public class FloatArrayBuffer extends ArrayBuffer> -{ - public FloatArrayBuffer(VecArray data) - { - super(data); - } - - @Override - protected void bindData(VecArray vertexData) - { - GL33.glBufferData(GL33.GL_ARRAY_BUFFER, vertexData.getData(), GL33.GL_STATIC_DRAW); - } - - @Override - public EnumArrayBufferType getType() - { - return EnumArrayBufferType.FLOAT; - } -} +package cz.tefek.pluto.engine.graphics.gl.vbo; + +import org.lwjgl.opengl.GL33; + +import cz.tefek.pluto.engine.graphics.gl.vao.attrib.data.VecArray; + +public class FloatArrayBuffer extends ArrayBuffer> +{ + public FloatArrayBuffer(VecArray data) + { + super(data); + } + + @Override + protected void bindData(VecArray vertexData) + { + GL33.glBufferData(GL33.GL_ARRAY_BUFFER, vertexData.getData(), GL33.GL_STATIC_DRAW); + } + + @Override + public EnumArrayBufferType getType() + { + return EnumArrayBufferType.FLOAT; + } +} diff --git a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/IndexArrayBuffer.java b/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/IndexArrayBuffer.java index 5fa8450..ca652b9 100644 --- a/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/IndexArrayBuffer.java +++ b/plutomesher/src/main/java/cz/tefek/pluto/engine/graphics/gl/vbo/IndexArrayBuffer.java @@ -1,50 +1,50 @@ -package cz.tefek.pluto.engine.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; - -public class IndexArrayBuffer extends ArrayBuffer> -{ - public IndexArrayBuffer(int[] data) - { - super(new VecArray<>(data, 1)); - } - - public IndexArrayBuffer(VecArray data) - { - super(data); - - if (data.getVecDimensions() != 1) - { - throw new IllegalArgumentException("Index buffers must have exactly one vertex dimension!"); - } - } - - @Override - public void bind() - { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this.glID); - } - - @Override - protected void bindData(VecArray data) - { - glBufferData(GL_ELEMENT_ARRAY_BUFFER, data.getData(), GL_STATIC_DRAW); - } - - @Override - public void unbind() - { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - } - - @Override - public EnumArrayBufferType getType() - { - return EnumArrayBufferType.UNSIGNED_INT; - } -} +package cz.tefek.pluto.engine.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; + +public class IndexArrayBuffer extends ArrayBuffer> +{ + public IndexArrayBuffer(int[] data) + { + super(new VecArray<>(data, 1)); + } + + public IndexArrayBuffer(VecArray data) + { + super(data); + + if (data.getVecDimensions() != 1) + { + throw new IllegalArgumentException("Index buffers must have exactly one vertex dimension!"); + } + } + + @Override + public void bind() + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this.glID); + } + + @Override + protected void bindData(VecArray data) + { + glBufferData(GL_ELEMENT_ARRAY_BUFFER, data.getData(), GL_STATIC_DRAW); + } + + @Override + public void unbind() + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + @Override + public EnumArrayBufferType getType() + { + return EnumArrayBufferType.UNSIGNED_INT; + } +} diff --git a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/ShaderBase.java b/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/ShaderBase.java index 0371db2..979b5fe 100644 --- a/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/ShaderBase.java +++ b/plutoshader/src/main/java/cz/tefek/pluto/engine/shader/ShaderBase.java @@ -1,36 +1,36 @@ -package cz.tefek.pluto.engine.shader; - -import org.lwjgl.opengl.GL33; - -import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.SmartSeverity; - -public abstract class ShaderBase implements IShaderProgram -{ - private int programID; - - protected int getUniform(String name) - { - return GL33.glGetUniformLocation(this.programID, name); - } - - @Override - public void dispose() - { - Logger.logf(SmartSeverity.REMOVED, "Disposing of shader ID %d of type %s...\n", this.getID(), this.getClass().getCanonicalName()); - - this.stop(); - GL33.glDeleteProgram(this.programID); - } - - protected void bindAttribute(int attribute, String name) - { - GL33.glBindAttribLocation(this.programID, attribute, name); - } - - @Override - public int getID() - { - return this.programID; - } -} +package cz.tefek.pluto.engine.shader; + +import org.lwjgl.opengl.GL33; + +import cz.tefek.pluto.io.logger.Logger; +import cz.tefek.pluto.io.logger.SmartSeverity; + +public abstract class ShaderBase implements IShaderProgram +{ + private int programID; + + protected int getUniform(String name) + { + return GL33.glGetUniformLocation(this.programID, name); + } + + @Override + public void dispose() + { + Logger.logf(SmartSeverity.REMOVED, "Disposing of shader ID %d of type %s...\n", this.getID(), this.getClass().getCanonicalName()); + + this.stop(); + GL33.glDeleteProgram(this.programID); + } + + protected void bindAttribute(int attribute, String name) + { + GL33.glBindAttribLocation(this.programID, attribute, name); + } + + @Override + public int getID() + { + return this.programID; + } +} diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/Renderer2D.java b/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/Renderer2D.java index a70febb..455a55f 100644 --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/Renderer2D.java +++ b/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/Renderer2D.java @@ -1,242 +1,242 @@ -package cz.tefek.pluto.engine.graphics; - -import java.util.Stack; - -import org.joml.Matrix3x2f; -import org.joml.Matrix3x2fc; -import org.joml.Vector4f; -import org.lwjgl.opengl.GL33; - -import cz.tefek.pluto.engine.graphics.gl.DrawMode; -import cz.tefek.pluto.engine.graphics.gl.vao.QuadPresets; -import cz.tefek.pluto.engine.graphics.gl.vao.VertexArray; -import cz.tefek.pluto.engine.graphics.texture.texture2d.Texture2D; - -/** - * A builder-like renderer for 2D rectangles. Note that the internal state is - * not monitored for performance reasons and outside events may affect active - * instances, such as changing the active shader or texture. In order to restore - * the internal state to the default. - * - * @author 493msi - */ -public class Renderer2D -{ - public static VertexArray standardQuad; - public static VertexArray centeredQuad; - - private static IShader2D defaultShader; - - public static final Renderer2D INSTANCE = new Renderer2D(); - - protected Matrix3x2f transformation = new Matrix3x2f(); - - protected IShader2D customShader; - - protected VertexArray activeVA; - - protected Texture2D activeTexture; - - protected boolean modifiedTransformation = false; - - private static Stack customShaderStack = new Stack<>(); - - public static void load(IShader2D defaultShaderIn) - { - standardQuad = QuadPresets.basicNoNeg(); - centeredQuad = QuadPresets.halvedSize(); - defaultShader = defaultShaderIn; - } - - public static void unload() - { - if (standardQuad != null) - { - standardQuad.delete(); - } - - if (centeredQuad != null) - { - centeredQuad.delete(); - } - } - - /** - * Pushes a custom {@link IShader2D shader} to be used in place of the - * default one. - */ - public static void pushCustomShader(IShader2D shader) - { - customShaderStack.push(shader); - } - - /** - * Removes the top {@link IShader2D shader} from the custom shader stack. - */ - public static IShader2D popCustomShader() - { - return customShaderStack.pop(); - } - - /** - * Checks if the renderer is currently supplied with a custom - * {@link IShader2D shader}. - */ - public static boolean hasCustomShader() - { - return !customShaderStack.empty(); - } - - public static Renderer2D draw(VertexArray va, IShader2D shader) - { - - GL33.glEnable(GL33.GL_BLEND); - - INSTANCE.customShader = shader; - - INSTANCE.customShader.start(); - - INSTANCE.customShader.loadRecolor(1, 1, 1, 1); - - INSTANCE.identity(); - - INSTANCE.switchVertexArray(va); - - INSTANCE.activeTexture = null; - - return INSTANCE; - } - - public static Renderer2D draw(VertexArray va) - { - return draw(va, hasCustomShader() ? customShaderStack.peek() : defaultShader); - } - - public static Renderer2D draw() - { - return draw(standardQuad); - } - - public Renderer2D identity() - { - this.transformation.m00 = 1; - this.transformation.m01 = 0; - this.transformation.m10 = 0; - this.transformation.m11 = 1; - this.transformation.m20 = 0; - this.transformation.m21 = 0; - - return this; - } - - public Renderer2D switchVertexArray(VertexArray va) - { - va.bind(); - va.enableAllAttributes(); - - this.activeVA = va; - - return this; - } - - public Renderer2D rotate(float rotation) - { - this.transformation.rotate(rotation); - return this; - } - - public Renderer2D at(float x, float y, float width, float height) - { - this.identity(); - this.transformation.translate(x, y); - this.transformation.scale(width, height); - - this.modifiedTransformation = true; - - return this; - } - - public Renderer2D translate(float x, float y) - { - this.transformation.translate(x, y); - - this.modifiedTransformation = true; - - return this; - } - - public Renderer2D scale(float width, float height) - { - this.transformation.scale(width, height); - - this.modifiedTransformation = true; - - return this; - } - - public Renderer2D transformation(Matrix3x2fc transformationMatrix) - { - this.transformation.set(transformationMatrix); - - this.modifiedTransformation = true; - - return this; - } - - private Renderer2D writeTransformation() - { - this.customShader.loadTransformationMatrix(this.transformation); - - this.modifiedTransformation = false; - - return this; - } - - public Renderer2D texturef(Texture2D texture, float u, float v, float width, float height) - { - if (this.activeTexture != texture) - { - this.activeTexture = texture; - texture.bind(); - } - - this.customShader.loadUV(u, 1 - v - height, width, height); - - return this; - } - - public Renderer2D texture(Texture2D texture, int u, int v, int width, int height) - { - return this.texturef(texture, u / (float) texture.getWidth(), v / (float) texture.getHeight(), width / (float) texture.getWidth(), height / (float) texture.getHeight()); - } - - public Renderer2D texture(Texture2D texture) - { - return this.texturef(texture, 0.0f, 0.0f, 1.0f, 1.0f); - } - - public Renderer2D recolor(float r, float g, float b, float a) - { - this.customShader.loadRecolor(r, g, b, a); - - return this; - } - - public Renderer2D recolor(Vector4f recolor) - { - this.customShader.loadRecolor(recolor); - - return this; - } - - public void flush() - { - if (this.modifiedTransformation) - { - this.writeTransformation(); - this.modifiedTransformation = false; - } - - this.activeVA.draw(DrawMode.TRIANGLES); - } -} +package cz.tefek.pluto.engine.graphics; + +import java.util.Stack; + +import org.joml.Matrix3x2f; +import org.joml.Matrix3x2fc; +import org.joml.Vector4f; +import org.lwjgl.opengl.GL33; + +import cz.tefek.pluto.engine.graphics.gl.DrawMode; +import cz.tefek.pluto.engine.graphics.gl.vao.QuadPresets; +import cz.tefek.pluto.engine.graphics.gl.vao.VertexArray; +import cz.tefek.pluto.engine.graphics.texture.texture2d.Texture2D; + +/** + * A builder-like renderer for 2D rectangles. Note that the internal state is + * not monitored for performance reasons and outside events may affect active + * instances, such as changing the active shader or texture. In order to restore + * the internal state to the default. + * + * @author 493msi + */ +public class Renderer2D +{ + public static VertexArray standardQuad; + public static VertexArray centeredQuad; + + private static IShader2D defaultShader; + + public static final Renderer2D INSTANCE = new Renderer2D(); + + protected Matrix3x2f transformation = new Matrix3x2f(); + + protected IShader2D customShader; + + protected VertexArray activeVA; + + protected Texture2D activeTexture; + + protected boolean modifiedTransformation = false; + + private static Stack customShaderStack = new Stack<>(); + + public static void load(IShader2D defaultShaderIn) + { + standardQuad = QuadPresets.basicNoNeg(); + centeredQuad = QuadPresets.halvedSize(); + defaultShader = defaultShaderIn; + } + + public static void unload() + { + if (standardQuad != null) + { + standardQuad.delete(); + } + + if (centeredQuad != null) + { + centeredQuad.delete(); + } + } + + /** + * Pushes a custom {@link IShader2D shader} to be used in place of the + * default one. + */ + public static void pushCustomShader(IShader2D shader) + { + customShaderStack.push(shader); + } + + /** + * Removes the top {@link IShader2D shader} from the custom shader stack. + */ + public static IShader2D popCustomShader() + { + return customShaderStack.pop(); + } + + /** + * Checks if the renderer is currently supplied with a custom + * {@link IShader2D shader}. + */ + public static boolean hasCustomShader() + { + return !customShaderStack.empty(); + } + + public static Renderer2D draw(VertexArray va, IShader2D shader) + { + + GL33.glEnable(GL33.GL_BLEND); + + INSTANCE.customShader = shader; + + INSTANCE.customShader.start(); + + INSTANCE.customShader.loadRecolor(1, 1, 1, 1); + + INSTANCE.identity(); + + INSTANCE.switchVertexArray(va); + + INSTANCE.activeTexture = null; + + return INSTANCE; + } + + public static Renderer2D draw(VertexArray va) + { + return draw(va, hasCustomShader() ? customShaderStack.peek() : defaultShader); + } + + public static Renderer2D draw() + { + return draw(standardQuad); + } + + public Renderer2D identity() + { + this.transformation.m00 = 1; + this.transformation.m01 = 0; + this.transformation.m10 = 0; + this.transformation.m11 = 1; + this.transformation.m20 = 0; + this.transformation.m21 = 0; + + return this; + } + + public Renderer2D switchVertexArray(VertexArray va) + { + va.bind(); + va.enableAllAttributes(); + + this.activeVA = va; + + return this; + } + + public Renderer2D rotate(float rotation) + { + this.transformation.rotate(rotation); + return this; + } + + public Renderer2D at(float x, float y, float width, float height) + { + this.identity(); + this.transformation.translate(x, y); + this.transformation.scale(width, height); + + this.modifiedTransformation = true; + + return this; + } + + public Renderer2D translate(float x, float y) + { + this.transformation.translate(x, y); + + this.modifiedTransformation = true; + + return this; + } + + public Renderer2D scale(float width, float height) + { + this.transformation.scale(width, height); + + this.modifiedTransformation = true; + + return this; + } + + public Renderer2D transformation(Matrix3x2fc transformationMatrix) + { + this.transformation.set(transformationMatrix); + + this.modifiedTransformation = true; + + return this; + } + + private Renderer2D writeTransformation() + { + this.customShader.loadTransformationMatrix(this.transformation); + + this.modifiedTransformation = false; + + return this; + } + + public Renderer2D texturef(Texture2D texture, float u, float v, float width, float height) + { + if (this.activeTexture != texture) + { + this.activeTexture = texture; + texture.bind(); + } + + this.customShader.loadUV(u, 1 - v - height, width, height); + + return this; + } + + public Renderer2D texture(Texture2D texture, int u, int v, int width, int height) + { + return this.texturef(texture, u / (float) texture.getWidth(), v / (float) texture.getHeight(), width / (float) texture.getWidth(), height / (float) texture.getHeight()); + } + + public Renderer2D texture(Texture2D texture) + { + return this.texturef(texture, 0.0f, 0.0f, 1.0f, 1.0f); + } + + public Renderer2D recolor(float r, float g, float b, float a) + { + this.customShader.loadRecolor(r, g, b, a); + + return this; + } + + public Renderer2D recolor(Vector4f recolor) + { + this.customShader.loadRecolor(recolor); + + return this; + } + + public void flush() + { + if (this.modifiedTransformation) + { + this.writeTransformation(); + this.modifiedTransformation = false; + } + + this.activeVA.draw(DrawMode.TRIANGLES); + } +} diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/Shader2D.java b/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/Shader2D.java index 67f2195..17e2099 100644 --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/Shader2D.java +++ b/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/Shader2D.java @@ -1,70 +1,70 @@ -package cz.tefek.pluto.engine.graphics; - -import org.joml.Matrix3x2fc; -import org.joml.Matrix4fc; - -import cz.tefek.pluto.engine.graphics.gl.vao.attrib.ReservedAttributes; -import cz.tefek.pluto.engine.shader.ShaderBase; -import cz.tefek.pluto.engine.shader.ShaderProgram; -import cz.tefek.pluto.engine.shader.VertexArrayAttribute; -import cz.tefek.pluto.engine.shader.uniform.Uniform; -import cz.tefek.pluto.engine.shader.uniform.UniformMat3x2; -import cz.tefek.pluto.engine.shader.uniform.UniformMat4; -import cz.tefek.pluto.engine.shader.uniform.UniformVec2; -import cz.tefek.pluto.engine.shader.uniform.UniformVec4; -import cz.tefek.pluto.engine.shader.uniform.auto.AutoViewportProjection; - -/** - * @author 493msi - * - */ -@ShaderProgram -public final class Shader2D extends ShaderBase implements IShader2D -{ - @AutoViewportProjection - @Uniform(name = "projection") - public UniformMat4 projectionMatrix; - - @Uniform(name = "transformation") - public UniformMat3x2 transformationMatrix; - - @Uniform - public UniformVec2 uvBase; - - @Uniform - public UniformVec2 uvDelta; - - @Uniform - public UniformVec4 recolor; - - @VertexArrayAttribute(ReservedAttributes.POSITION) - public int position; - - @VertexArrayAttribute(ReservedAttributes.UV) - public int uvCoords; - - @Override - public void loadUV(float uvStartX, float uvStartY, float uWidth, float vHeight) - { - this.uvBase.load(uvStartX, uvStartY); - this.uvDelta.load(uWidth, vHeight); - } - - @Override - public void loadRecolor(float r, float g, float b, float a) - { - this.recolor.load(r, g, b, a); - } - - @Override - public void loadProjectionMatrix(Matrix4fc matrix) - { - this.projectionMatrix.load(matrix); - } - - @Override - public void loadTransformationMatrix(Matrix3x2fc matrix) - { - this.transformationMatrix.load(matrix); - } -} +package cz.tefek.pluto.engine.graphics; + +import org.joml.Matrix3x2fc; +import org.joml.Matrix4fc; + +import cz.tefek.pluto.engine.graphics.gl.vao.attrib.ReservedAttributes; +import cz.tefek.pluto.engine.shader.ShaderBase; +import cz.tefek.pluto.engine.shader.ShaderProgram; +import cz.tefek.pluto.engine.shader.VertexArrayAttribute; +import cz.tefek.pluto.engine.shader.uniform.Uniform; +import cz.tefek.pluto.engine.shader.uniform.UniformMat3x2; +import cz.tefek.pluto.engine.shader.uniform.UniformMat4; +import cz.tefek.pluto.engine.shader.uniform.UniformVec2; +import cz.tefek.pluto.engine.shader.uniform.UniformVec4; +import cz.tefek.pluto.engine.shader.uniform.auto.AutoViewportProjection; + +/** + * @author 493msi + * + */ +@ShaderProgram +public final class Shader2D extends ShaderBase implements IShader2D +{ + @AutoViewportProjection + @Uniform(name = "projection") + public UniformMat4 projectionMatrix; + + @Uniform(name = "transformation") + public UniformMat3x2 transformationMatrix; + + @Uniform + public UniformVec2 uvBase; + + @Uniform + public UniformVec2 uvDelta; + + @Uniform + public UniformVec4 recolor; + + @VertexArrayAttribute(ReservedAttributes.POSITION) + public int position; + + @VertexArrayAttribute(ReservedAttributes.UV) + public int uvCoords; + + @Override + public void loadUV(float uvStartX, float uvStartY, float uWidth, float vHeight) + { + this.uvBase.load(uvStartX, uvStartY); + this.uvDelta.load(uWidth, vHeight); + } + + @Override + public void loadRecolor(float r, float g, float b, float a) + { + this.recolor.load(r, g, b, a); + } + + @Override + public void loadProjectionMatrix(Matrix4fc matrix) + { + this.projectionMatrix.load(matrix); + } + + @Override + public void loadTransformationMatrix(Matrix3x2fc matrix) + { + this.transformationMatrix.load(matrix); + } +} diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/ShaderRectangle2D.java b/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/ShaderRectangle2D.java index cf53909..ed6622b 100644 --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/ShaderRectangle2D.java +++ b/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/ShaderRectangle2D.java @@ -1,70 +1,70 @@ -package cz.tefek.pluto.engine.graphics; - -import org.joml.Matrix3x2fc; -import org.joml.Matrix4fc; - -import cz.tefek.pluto.engine.graphics.gl.vao.attrib.ReservedAttributes; -import cz.tefek.pluto.engine.shader.ShaderBase; -import cz.tefek.pluto.engine.shader.ShaderProgram; -import cz.tefek.pluto.engine.shader.VertexArrayAttribute; -import cz.tefek.pluto.engine.shader.uniform.Uniform; -import cz.tefek.pluto.engine.shader.uniform.UniformMat3x2; -import cz.tefek.pluto.engine.shader.uniform.UniformMat4; -import cz.tefek.pluto.engine.shader.uniform.UniformVec2; -import cz.tefek.pluto.engine.shader.uniform.UniformVec4; -import cz.tefek.pluto.engine.shader.uniform.auto.AutoViewportProjection; - -/** - * @author 493msi - * - */ -@ShaderProgram -public final class ShaderRectangle2D extends ShaderBase implements IRectangleShader2D -{ - @AutoViewportProjection - @Uniform(name = "projection") - public UniformMat4 projectionMatrix; - - @Uniform(name = "transformation") - public UniformMat3x2 transformationMatrix; - - @Uniform - public UniformVec2 uvBase; - - @Uniform - public UniformVec2 uvDelta; - - @Uniform - public UniformVec4 recolor; - - @VertexArrayAttribute(ReservedAttributes.POSITION) - public int position; - - @VertexArrayAttribute(ReservedAttributes.UV) - public int uvCoords; - - @Override - public void loadUV(float uvStartX, float uvStartY, float uWidth, float vHeight) - { - this.uvBase.load(uvStartX, uvStartY); - this.uvDelta.load(uWidth, vHeight); - } - - @Override - public void loadRecolor(float r, float g, float b, float a) - { - this.recolor.load(r, g, b, a); - } - - @Override - public void loadProjectionMatrix(Matrix4fc matrix) - { - this.projectionMatrix.load(matrix); - } - - @Override - public void loadTransformationMatrix(Matrix3x2fc matrix) - { - this.transformationMatrix.load(matrix); - } -} +package cz.tefek.pluto.engine.graphics; + +import org.joml.Matrix3x2fc; +import org.joml.Matrix4fc; + +import cz.tefek.pluto.engine.graphics.gl.vao.attrib.ReservedAttributes; +import cz.tefek.pluto.engine.shader.ShaderBase; +import cz.tefek.pluto.engine.shader.ShaderProgram; +import cz.tefek.pluto.engine.shader.VertexArrayAttribute; +import cz.tefek.pluto.engine.shader.uniform.Uniform; +import cz.tefek.pluto.engine.shader.uniform.UniformMat3x2; +import cz.tefek.pluto.engine.shader.uniform.UniformMat4; +import cz.tefek.pluto.engine.shader.uniform.UniformVec2; +import cz.tefek.pluto.engine.shader.uniform.UniformVec4; +import cz.tefek.pluto.engine.shader.uniform.auto.AutoViewportProjection; + +/** + * @author 493msi + * + */ +@ShaderProgram +public final class ShaderRectangle2D extends ShaderBase implements IRectangleShader2D +{ + @AutoViewportProjection + @Uniform(name = "projection") + public UniformMat4 projectionMatrix; + + @Uniform(name = "transformation") + public UniformMat3x2 transformationMatrix; + + @Uniform + public UniformVec2 uvBase; + + @Uniform + public UniformVec2 uvDelta; + + @Uniform + public UniformVec4 recolor; + + @VertexArrayAttribute(ReservedAttributes.POSITION) + public int position; + + @VertexArrayAttribute(ReservedAttributes.UV) + public int uvCoords; + + @Override + public void loadUV(float uvStartX, float uvStartY, float uWidth, float vHeight) + { + this.uvBase.load(uvStartX, uvStartY); + this.uvDelta.load(uWidth, vHeight); + } + + @Override + public void loadRecolor(float r, float g, float b, float a) + { + this.recolor.load(r, g, b, a); + } + + @Override + public void loadProjectionMatrix(Matrix4fc matrix) + { + this.projectionMatrix.load(matrix); + } + + @Override + public void loadTransformationMatrix(Matrix3x2fc matrix) + { + this.transformationMatrix.load(matrix); + } +} diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/DisposablePlaceholderSprite.java b/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/DisposablePlaceholderSprite.java index 56d3aef..2e6cea1 100644 --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/DisposablePlaceholderSprite.java +++ b/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/sprite/DisposablePlaceholderSprite.java @@ -1,5 +1,7 @@ package cz.tefek.pluto.engine.graphics.sprite; +import java.awt.image.BufferedImage; + import cz.tefek.pluto.engine.graphics.texture.MagFilter; import cz.tefek.pluto.engine.graphics.texture.MinFilter; import cz.tefek.pluto.engine.graphics.texture.WrapMode; @@ -11,7 +13,7 @@ public class DisposablePlaceholderSprite extends DisposableTextureSprite { super(new RectangleTexture()); - this.spriteTexture.load((String) null, MagFilter.NEAREST, MinFilter.NEAREST, WrapMode.CLAMP_TO_EDGE, WrapMode.CLAMP_TO_EDGE); + this.spriteTexture.load((BufferedImage) null, MagFilter.NEAREST, MinFilter.NEAREST, WrapMode.CLAMP_TO_EDGE, WrapMode.CLAMP_TO_EDGE); this.width = this.spriteTexture.getWidth(); this.height = this.spriteTexture.getHeight(); } diff --git a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/spritesheet/TiledSpriteSheet.java b/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/spritesheet/TiledSpriteSheet.java index c69636e..15e5724 100644 --- a/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/spritesheet/TiledSpriteSheet.java +++ b/plutospritesheet/src/main/java/cz/tefek/pluto/engine/graphics/spritesheet/TiledSpriteSheet.java @@ -6,7 +6,7 @@ import cz.tefek.pluto.engine.graphics.sprite.Sprite; import cz.tefek.pluto.engine.graphics.sprite.SpriteDisposable; import cz.tefek.pluto.engine.graphics.sprite.TileSprite; import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.Severity; +import cz.tefek.pluto.io.logger.SmartSeverity; public abstract class TiledSpriteSheet extends SpriteSheet { @@ -65,7 +65,7 @@ public abstract class TiledSpriteSheet extends SpriteSheet protected void expand() { - Logger.logf(Severity.INFO, "Spritesheet #%d: Expanding from %dx%d to ", this.id, this.spriteSheetWidth, this.spriteSheetHeight); + Logger.logf(SmartSeverity.INFO, "Spritesheet #%d: Expanding from %dx%d to ", this.id, this.spriteSheetWidth, this.spriteSheetHeight); this.spriteSheetWidth *= 2; this.spriteSheetHeight *= 2; @@ -79,7 +79,7 @@ public abstract class TiledSpriteSheet extends SpriteSheet protected void upscale(int factor) { - Logger.logf(Severity.INFO, "Spritesheet #%d: Upscaling from %dx%d to ", this.id, this.tileWidth, this.tileHeight); + Logger.logf(SmartSeverity.INFO, "Spritesheet #%d: Upscaling from %dx%d to ", this.id, this.tileWidth, this.tileHeight); this.tileWidth *= factor; this.tileHeight *= factor; diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/buffer/BufferHelper.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/buffer/BufferHelper.java index a613037..e15c0dc 100644 --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/buffer/BufferHelper.java +++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/buffer/BufferHelper.java @@ -1,197 +1,197 @@ -package cz.tefek.pluto.engine.buffer; - -import org.apache.commons.io.IOUtils; -import org.lwjgl.BufferUtils; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.FloatBuffer; -import java.nio.IntBuffer; -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. - * - * @author 493msi - * @since 0.1 - * - */ -public final class BufferHelper -{ - /** - * Creates an {@link IntBuffer} from the input int array. - * - * @param data The input int array. - * @return The created {@link IntBuffer} - * - * @author 493msi - * @since 0.1 - */ - public static IntBuffer flippedIntBuffer(int[] data) - { - IntBuffer buffer = BufferUtils.createIntBuffer(data.length); - buffer.put(data); - buffer.flip(); - - return buffer; - } - - /** - * Creates an {@link FloatBuffer} from the input float array. - * - * @param data The input float array. - * @return The created {@link FloatBuffer} - * - * @author 493msi - * @since 0.1 - */ - public static FloatBuffer flippedFloatBuffer(float[] data) - { - FloatBuffer buffer = BufferUtils.createFloatBuffer(data.length); - buffer.put(data); - buffer.flip(); - - return buffer; - } - - /** - * Creates a {@link ByteBuffer} from the input byte array. - * - * @param data The input byte array. - * @return The created {@link ByteBuffer} - * - * @author 493msi - * @since 0.1 - */ - public static ByteBuffer flippedByteBuffer(byte[] data) - { - ByteBuffer buffer = BufferUtils.createByteBuffer(data.length); - buffer.put(data); - buffer.flip(); - - return buffer; - } - - /** - * Reallocates a new, bigger {@link ByteBuffer} and copies the data from the - * old one. - * - * @param buffer The input {@link ByteBuffer}. - * @param newCapacity The new buffer's capacity, can't be smaller than the - * current one. - * @return The new, bigger {@link ByteBuffer}. - * - * @author 493msi - * @since 0.1 - */ - public static ByteBuffer resizeBuffer(ByteBuffer buffer, int newCapacity) - { - if (buffer.capacity() > newCapacity) - { - throw new IllegalArgumentException("New capacity is smaller than the previous one."); - } - - ByteBuffer newBuffer = BufferUtils.createByteBuffer(newCapacity); - buffer.flip(); - newBuffer.put(buffer); - 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. - * - *

- * Make sure the buffer can hold the entire file. - *

- * - * @param path The file's path. - * @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(Path path, ByteBuffer buf) throws IOException - { - try (var fc = FileChannel.open(path)) - { - fc.read(buf); - buf.flip(); - return buf; - } - } - - /** - * {@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 - * 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.3 - */ - public static ByteBuffer readToFlippedByteBuffer(ResourceAddress path) throws IOException - { - try (var is = Files.newInputStream(path.toNIOPath())) - { - var ba = IOUtils.toByteArray(is); - return flippedByteBuffer(ba); - } - } -} +package cz.tefek.pluto.engine.buffer; + +import org.apache.commons.io.IOUtils; +import org.lwjgl.BufferUtils; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +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. + * + * @author 493msi + * @since 0.1 + * + */ +public final class BufferHelper +{ + /** + * Creates an {@link IntBuffer} from the input int array. + * + * @param data The input int array. + * @return The created {@link IntBuffer} + * + * @author 493msi + * @since 0.1 + */ + public static IntBuffer flippedIntBuffer(int[] data) + { + IntBuffer buffer = BufferUtils.createIntBuffer(data.length); + buffer.put(data); + buffer.flip(); + + return buffer; + } + + /** + * Creates an {@link FloatBuffer} from the input float array. + * + * @param data The input float array. + * @return The created {@link FloatBuffer} + * + * @author 493msi + * @since 0.1 + */ + public static FloatBuffer flippedFloatBuffer(float[] data) + { + FloatBuffer buffer = BufferUtils.createFloatBuffer(data.length); + buffer.put(data); + buffer.flip(); + + return buffer; + } + + /** + * Creates a {@link ByteBuffer} from the input byte array. + * + * @param data The input byte array. + * @return The created {@link ByteBuffer} + * + * @author 493msi + * @since 0.1 + */ + public static ByteBuffer flippedByteBuffer(byte[] data) + { + ByteBuffer buffer = BufferUtils.createByteBuffer(data.length); + buffer.put(data); + buffer.flip(); + + return buffer; + } + + /** + * Reallocates a new, bigger {@link ByteBuffer} and copies the data from the + * old one. + * + * @param buffer The input {@link ByteBuffer}. + * @param newCapacity The new buffer's capacity, can't be smaller than the + * current one. + * @return The new, bigger {@link ByteBuffer}. + * + * @author 493msi + * @since 0.1 + */ + public static ByteBuffer resizeBuffer(ByteBuffer buffer, int newCapacity) + { + if (buffer.capacity() > newCapacity) + { + throw new IllegalArgumentException("New capacity is smaller than the previous one."); + } + + ByteBuffer newBuffer = BufferUtils.createByteBuffer(newCapacity); + buffer.flip(); + newBuffer.put(buffer); + 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. + * + *

+ * Make sure the buffer can hold the entire file. + *

+ * + * @param path The file's path. + * @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(Path path, ByteBuffer buf) throws IOException + { + try (var fc = FileChannel.open(path)) + { + fc.read(buf); + buf.flip(); + return buf; + } + } + + /** + * {@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 + * 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.3 + */ + public static ByteBuffer readToFlippedByteBuffer(ResourceAddress path) throws IOException + { + try (var is = Files.newInputStream(path.toNIOPath())) + { + var ba = IOUtils.toByteArray(is); + return flippedByteBuffer(ba); + } + } +} diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/buffer/GLFWImageUtil.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/buffer/GLFWImageUtil.java index c04f2f7..8b50459 100644 --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/buffer/GLFWImageUtil.java +++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/buffer/GLFWImageUtil.java @@ -3,6 +3,8 @@ package cz.tefek.pluto.engine.buffer; import org.lwjgl.BufferUtils; import org.lwjgl.glfw.GLFWImage; +import java.nio.file.Path; + import cz.tefek.pluto.tpl.TPL; /** @@ -28,23 +30,27 @@ public class GLFWImageUtil { var icon = GLFWImage.create(icons.length); - for (int iconIndex = 0; iconIndex < icons.length; iconIndex++) + for (String iconPath : icons) { - var img = TPL.loadPixels(icons[iconIndex]); + var img = TPL.loadSpecial(Path.of(iconPath), false); var imgData = img.getData(); - var imgWidth = img.getWidth(); - var imgHeight = img.getHeight(); + int imgWidth = img.getWidth(); + int imgHeight = img.getHeight(); - var byteBuf = BufferUtils.createByteBuffer(imgWidth * imgHeight * 4); + int pixelCount = imgWidth * imgHeight; + int bytesPerPixel = 4; + var byteBuf = BufferUtils.createByteBuffer(pixelCount * bytesPerPixel); - for (int i = 0; i < imgHeight * imgWidth; i++) + byte[] px = new byte[bytesPerPixel]; + + for (int i = 0; i < pixelCount; i++) { - var data = imgData[i]; + px[3] = imgData.get(); // A + px[2] = imgData.get(); // B + px[1] = imgData.get(); // G + px[0] = imgData.get(); // R - byteBuf.put((byte) ((data & 0x00ff0000) >> 16)); - byteBuf.put((byte) ((data & 0x0000ff00) >> 8)); - byteBuf.put((byte) (data & 0x000000ff)); - byteBuf.put((byte) ((data & 0xff000000) >> 24)); + byteBuf.put(px); } byteBuf.flip(); diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/display/Display.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/display/Display.java index 9e78db1..3ee7221 100644 --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/display/Display.java +++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/display/Display.java @@ -1,10 +1,6 @@ package cz.tefek.pluto.engine.display; -import org.lwjgl.glfw.GLFW; -import org.lwjgl.glfw.GLFWErrorCallback; -import org.lwjgl.glfw.GLFWImage; -import org.lwjgl.glfw.GLFWVidMode; -import org.lwjgl.glfw.GLFWWindowSizeCallback; +import org.lwjgl.glfw.*; import org.lwjgl.opengl.ARBDebugOutput; import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GL33; @@ -13,7 +9,6 @@ import org.lwjgl.system.MemoryUtil; import cz.tefek.pluto.engine.gl.GLDebugInfo; import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.Severity; import cz.tefek.pluto.io.logger.SmartSeverity; /** @@ -72,7 +67,7 @@ public class Display { if (Display.this.debugMode) { - Logger.logf(Severity.INFO, "Resized to %dx%d.\n", width, height); + Logger.logf(SmartSeverity.INFO, "Resized to %dx%d.\n", width, height); } Display.this.width = width; diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/display/Framerate.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/display/Framerate.java index bcda317..e183aeb 100644 --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/display/Framerate.java +++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/display/Framerate.java @@ -1,75 +1,75 @@ -package cz.tefek.pluto.engine.display; - -import java.util.Deque; -import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.TimeUnit; - -/** - * @author 493msi - * - */ -public class Framerate -{ - private static long lastDraw = 0; - - private static double frameTime = Double.NaN; - - private static double FPS = Double.NaN; - - private static int interpolatedFPS; - - private static boolean firstRemoved = false; - - private static Deque drawTimestamps = new LinkedBlockingDeque<>(); - - public static double getFrameTime() - { - return frameTime; - } - - public static double getFPS() - { - return FPS; - } - - public static int getInterpolatedFPS() - { - return interpolatedFPS; - } - - public static void tick() - { - var now = System.nanoTime(); - - if (lastDraw > 0) - { - var frameTimeNs = now - lastDraw; - frameTime = frameTimeNs / (double) TimeUnit.MILLISECONDS.toNanos(1); - FPS = TimeUnit.SECONDS.toMillis(1) / frameTime; - } - - var nowMs = System.currentTimeMillis(); - - drawTimestamps.add(nowMs); - - Long oldestDraw; - long oneSecondAgo = nowMs - 1000; - - while ((oldestDraw = drawTimestamps.peek()) != null && oldestDraw < oneSecondAgo) - { - drawTimestamps.remove(); - firstRemoved = true; - } - - if (firstRemoved) - { - interpolatedFPS = drawTimestamps.size(); - } - else - { - interpolatedFPS = (int) Math.round(FPS); - } - - lastDraw = now; - } -} +package cz.tefek.pluto.engine.display; + +import java.util.Deque; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.TimeUnit; + +/** + * @author 493msi + * + */ +public class Framerate +{ + private static long lastDraw = 0; + + private static double frameTime = Double.NaN; + + private static double FPS = Double.NaN; + + private static int interpolatedFPS; + + private static boolean firstRemoved = false; + + private static Deque drawTimestamps = new LinkedBlockingDeque<>(); + + public static double getFrameTime() + { + return frameTime; + } + + public static double getFPS() + { + return FPS; + } + + public static int getInterpolatedFPS() + { + return interpolatedFPS; + } + + public static void tick() + { + var now = System.nanoTime(); + + if (lastDraw > 0) + { + var frameTimeNs = now - lastDraw; + frameTime = frameTimeNs / (double) TimeUnit.MILLISECONDS.toNanos(1); + FPS = TimeUnit.SECONDS.toMillis(1) / frameTime; + } + + var nowMs = System.currentTimeMillis(); + + drawTimestamps.add(nowMs); + + Long oldestDraw; + long oneSecondAgo = nowMs - 1000; + + while ((oldestDraw = drawTimestamps.peek()) != null && oldestDraw < oneSecondAgo) + { + drawTimestamps.remove(); + firstRemoved = true; + } + + if (firstRemoved) + { + interpolatedFPS = drawTimestamps.size(); + } + else + { + interpolatedFPS = (int) Math.round(FPS); + } + + lastDraw = now; + } +} diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/ProjectionMatrix.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/ProjectionMatrix.java index 2660725..3d02d73 100644 --- a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/ProjectionMatrix.java +++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/ProjectionMatrix.java @@ -1,66 +1,66 @@ -package cz.tefek.pluto.engine.math; - -import org.joml.Matrix4f; - -public class ProjectionMatrix -{ - /** - * Create a 2D orthogonal projection Matrix4f based on the width and height. - * - * @param width The ortho width - * @param height The ortho height - * - * @return the matrix - * - * @author 493msi - * @since 0.1 - */ - public static Matrix4f createOrtho2D(int width, int height) - { - var orthoMatrix = new Matrix4f(); - orthoMatrix.setOrtho2D(0, width, height, 0); - - return orthoMatrix; - } - - /** - * Create a centered 2D orthogonal projection Matrix3x2f based on the width and - * height. - * - * @param width The ortho width - * @param height The ortho height - * - * @return the matrix - * - * @author 493msi - * @since 0.3 - */ - public static Matrix4f createOrtho2DCentered(int width, int height) - { - var orthoMatrix = new Matrix4f(); - orthoMatrix.setOrtho2D(width / 2.0f, width / 2.0f, height / 2.0f, height / 2.0f); - - return orthoMatrix; - } - - /** - * Create a perspective frustum based on the parameters. - * - * @param aspectRatio The aspect ratio of the frustum - * @param fov The fov of the frustum - * @param zNear The distance of the zNear clipping plane - * @param zFar The distance of the zFar clipping plane - * - * @return the perspective matrix - * - * @author 493msi - * @since 0.1 - */ - public static Matrix4f createPerspective(float aspectRatio, float fov, float zNear, float zFar) - { - var perspective = new Matrix4f(); - perspective.setPerspective(fov, aspectRatio, zNear, zFar); - - return perspective; - } -} +package cz.tefek.pluto.engine.math; + +import org.joml.Matrix4f; + +public class ProjectionMatrix +{ + /** + * Create a 2D orthogonal projection Matrix4f based on the width and height. + * + * @param width The ortho width + * @param height The ortho height + * + * @return the matrix + * + * @author 493msi + * @since 0.1 + */ + public static Matrix4f createOrtho2D(int width, int height) + { + var orthoMatrix = new Matrix4f(); + orthoMatrix.setOrtho2D(0, width, height, 0); + + return orthoMatrix; + } + + /** + * Create a centered 2D orthogonal projection Matrix3x2f based on the width and + * height. + * + * @param width The ortho width + * @param height The ortho height + * + * @return the matrix + * + * @author 493msi + * @since 0.3 + */ + public static Matrix4f createOrtho2DCentered(int width, int height) + { + var orthoMatrix = new Matrix4f(); + orthoMatrix.setOrtho2D(width / 2.0f, width / 2.0f, height / 2.0f, height / 2.0f); + + return orthoMatrix; + } + + /** + * Create a perspective frustum based on the parameters. + * + * @param aspectRatio The aspect ratio of the frustum + * @param fov The fov of the frustum + * @param zNear The distance of the zNear clipping plane + * @param zFar The distance of the zFar clipping plane + * + * @return the perspective matrix + * + * @author 493msi + * @since 0.1 + */ + public static Matrix4f createPerspective(float aspectRatio, float fov, float zNear, float zFar) + { + var perspective = new Matrix4f(); + perspective.setPerspective(fov, aspectRatio, zNear, zFar); + + return perspective; + } +} diff --git a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/MagFilter.java b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/MagFilter.java index 7dbeb60..4696920 100644 --- a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/MagFilter.java +++ b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/MagFilter.java @@ -1,24 +1,24 @@ -package cz.tefek.pluto.engine.graphics.texture; - -import org.lwjgl.opengl.GL33; - -import cz.tefek.pluto.engine.gl.IOpenGLEnum; - -public enum MagFilter implements IOpenGLEnum -{ - NEAREST(GL33.GL_NEAREST), - LINEAR(GL33.GL_LINEAR); - - MagFilter(int id) - { - this.id = id; - } - - private int id; - - @Override - public int getGLID() - { - return this.id; - } -} +package cz.tefek.pluto.engine.graphics.texture; + +import org.lwjgl.opengl.GL33; + +import cz.tefek.pluto.engine.gl.IOpenGLEnum; + +public enum MagFilter implements IOpenGLEnum +{ + NEAREST(GL33.GL_NEAREST), + LINEAR(GL33.GL_LINEAR); + + MagFilter(int id) + { + this.id = id; + } + + private int id; + + @Override + public int getGLID() + { + return this.id; + } +} diff --git a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/MinFilter.java b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/MinFilter.java index 3eac01d..c8cd9ea 100644 --- a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/MinFilter.java +++ b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/MinFilter.java @@ -1,35 +1,35 @@ -package cz.tefek.pluto.engine.graphics.texture; - -import org.lwjgl.opengl.GL33; - -import cz.tefek.pluto.engine.gl.IOpenGLEnum; - -public enum MinFilter implements IOpenGLEnum -{ - NEAREST(GL33.GL_NEAREST, false), - LINEAR(GL33.GL_LINEAR, false), - NEAREST_MIPMAP_NEAREST(GL33.GL_NEAREST_MIPMAP_NEAREST, true), - LINEAR_MIPMAP_NEAREST(GL33.GL_LINEAR_MIPMAP_NEAREST, true), - NEAREST_MIPMAP_LINEAR(GL33.GL_NEAREST_MIPMAP_LINEAR, true), - LINEAR_MIPMAP_LINEAR(GL33.GL_LINEAR_MIPMAP_LINEAR, true); - - MinFilter(int id, boolean isMipMapped) - { - this.id = id; - this.mipMapped = isMipMapped; - } - - private int id; - private boolean mipMapped; - - @Override - public int getGLID() - { - return this.id; - } - - public boolean isMipMapped() - { - return this.mipMapped; - } -} +package cz.tefek.pluto.engine.graphics.texture; + +import org.lwjgl.opengl.GL33; + +import cz.tefek.pluto.engine.gl.IOpenGLEnum; + +public enum MinFilter implements IOpenGLEnum +{ + NEAREST(GL33.GL_NEAREST, false), + LINEAR(GL33.GL_LINEAR, false), + NEAREST_MIPMAP_NEAREST(GL33.GL_NEAREST_MIPMAP_NEAREST, true), + LINEAR_MIPMAP_NEAREST(GL33.GL_LINEAR_MIPMAP_NEAREST, true), + NEAREST_MIPMAP_LINEAR(GL33.GL_NEAREST_MIPMAP_LINEAR, true), + LINEAR_MIPMAP_LINEAR(GL33.GL_LINEAR_MIPMAP_LINEAR, true); + + MinFilter(int id, boolean isMipMapped) + { + this.id = id; + this.mipMapped = isMipMapped; + } + + private int id; + private boolean mipMapped; + + @Override + public int getGLID() + { + return this.id; + } + + public boolean isMipMapped() + { + return this.mipMapped; + } +} diff --git a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/Texture.java b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/Texture.java index b1aa19e..14473b3 100644 --- a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/Texture.java +++ b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/Texture.java @@ -1,23 +1,21 @@ package cz.tefek.pluto.engine.graphics.texture; -import java.util.Arrays; - import org.lwjgl.opengl.GL33; import org.lwjgl.system.MemoryUtil; import java.awt.image.BufferedImage; import java.nio.ByteBuffer; +import java.util.Arrays; import cz.tefek.pluto.io.asl.resource.ResourceAddress; import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.Severity; import cz.tefek.pluto.io.logger.SmartSeverity; import cz.tefek.pluto.tpl.TPL; import cz.tefek.pluto.tpl.TPNImage; public abstract class Texture { - protected int glID = 0; + protected int glID; protected final int type; protected final int dimensions; @@ -138,7 +136,7 @@ public abstract class Texture { if (wrapOptions.length != this.dimensions) { - Logger.log(Severity.ERROR, "Error: WrapMode option count does not match texture's dimensions."); + Logger.log(SmartSeverity.ERROR, "Error: WrapMode option count does not match texture's dimensions."); return this; } @@ -198,6 +196,7 @@ public abstract class Texture this.load(file.toPath(), magFilter, minFilter, wrap); } + @Deprecated public void load(String file, MagFilter magFilter, MinFilter minFilter, WrapMode... wrap) { TPNImage image = TPL.load(file); diff --git a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/WrapMode.java b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/WrapMode.java index 1dfe740..7433fd1 100644 --- a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/WrapMode.java +++ b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/WrapMode.java @@ -1,34 +1,34 @@ -package cz.tefek.pluto.engine.graphics.texture; - -import java.util.EnumSet; - -import org.lwjgl.opengl.GL33; -import org.lwjgl.opengl.GL44; - -import cz.tefek.pluto.engine.gl.IOpenGLEnum; - -public enum WrapMode implements IOpenGLEnum -{ - REPEAT(GL33.GL_REPEAT), - CLAMP_TO_EDGE(GL33.GL_CLAMP_TO_EDGE), - CLAMP_TO_BORDER(GL33.GL_CLAMP_TO_BORDER), - MIRROR_CLAMP_TO_EDGE(GL44.GL_MIRROR_CLAMP_TO_EDGE), - MIRRORED_REPEAT(GL33.GL_MIRRORED_REPEAT); - - WrapMode(int id) - { - this.id = id; - } - - public static final EnumSet repeatModes = EnumSet.of(WrapMode.MIRRORED_REPEAT, WrapMode.REPEAT); - public static final EnumSet clampModes = EnumSet.of(WrapMode.CLAMP_TO_EDGE, WrapMode.CLAMP_TO_BORDER, MIRROR_CLAMP_TO_EDGE); - public static final EnumSet mirrorModes = EnumSet.of(WrapMode.MIRROR_CLAMP_TO_EDGE, WrapMode.MIRRORED_REPEAT); - - private int id; - - @Override - public int getGLID() - { - return this.id; - } -} +package cz.tefek.pluto.engine.graphics.texture; + +import java.util.EnumSet; + +import org.lwjgl.opengl.GL33; +import org.lwjgl.opengl.GL44; + +import cz.tefek.pluto.engine.gl.IOpenGLEnum; + +public enum WrapMode implements IOpenGLEnum +{ + REPEAT(GL33.GL_REPEAT), + CLAMP_TO_EDGE(GL33.GL_CLAMP_TO_EDGE), + CLAMP_TO_BORDER(GL33.GL_CLAMP_TO_BORDER), + MIRROR_CLAMP_TO_EDGE(GL44.GL_MIRROR_CLAMP_TO_EDGE), + MIRRORED_REPEAT(GL33.GL_MIRRORED_REPEAT); + + WrapMode(int id) + { + this.id = id; + } + + public static final EnumSet repeatModes = EnumSet.of(WrapMode.MIRRORED_REPEAT, WrapMode.REPEAT); + public static final EnumSet clampModes = EnumSet.of(WrapMode.CLAMP_TO_EDGE, WrapMode.CLAMP_TO_BORDER, MIRROR_CLAMP_TO_EDGE); + public static final EnumSet mirrorModes = EnumSet.of(WrapMode.MIRROR_CLAMP_TO_EDGE, WrapMode.MIRRORED_REPEAT); + + private int id; + + @Override + public int getGLID() + { + return this.id; + } +} diff --git a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/texture2d/RectangleTexture.java b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/texture2d/RectangleTexture.java index cecd903..c1d7149 100644 --- a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/texture2d/RectangleTexture.java +++ b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/texture2d/RectangleTexture.java @@ -1,13 +1,13 @@ package cz.tefek.pluto.engine.graphics.texture.texture2d; -import java.util.Arrays; - import org.lwjgl.opengl.GL33; +import java.util.Arrays; + import cz.tefek.pluto.engine.graphics.texture.Texture; import cz.tefek.pluto.engine.graphics.texture.WrapMode; import cz.tefek.pluto.io.logger.Logger; -import cz.tefek.pluto.io.logger.Severity; +import cz.tefek.pluto.io.logger.SmartSeverity; public class RectangleTexture extends Texture { @@ -27,7 +27,7 @@ public class RectangleTexture extends Texture { if (Arrays.stream(wrapOptions).anyMatch(WrapMode.repeatModes::contains)) { - Logger.log(Severity.ERROR, "Error: Rectangle textures do not support repeat wrap modes!"); + Logger.log(SmartSeverity.ERROR, "Error: Rectangle textures do not support repeat wrap modes!"); return this; } diff --git a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/texture2d/Texture2D.java b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/texture2d/Texture2D.java index 3916226..f2ae05f 100644 --- a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/texture2d/Texture2D.java +++ b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/texture2d/Texture2D.java @@ -1,29 +1,29 @@ -package cz.tefek.pluto.engine.graphics.texture.texture2d; - -import org.lwjgl.opengl.GL33; - -import cz.tefek.pluto.engine.graphics.texture.Texture; - -/** - * @author 493msi - * - */ -public class Texture2D extends Texture -{ - public Texture2D() - { - super(GL33.GL_TEXTURE_2D, 2); - } - - @Override - public void writeData(long address) - { - GL33.glTexImage2D(this.type, 0, GL33.GL_RGBA8, this.width, this.height, 0, GL33.GL_RGBA, GL33.GL_UNSIGNED_BYTE, address); - } - - @Override - public boolean supportsMipMapping() - { - return true; - } -} +package cz.tefek.pluto.engine.graphics.texture.texture2d; + +import org.lwjgl.opengl.GL33; + +import cz.tefek.pluto.engine.graphics.texture.Texture; + +/** + * @author 493msi + * + */ +public class Texture2D extends Texture +{ + public Texture2D() + { + super(GL33.GL_TEXTURE_2D, 2); + } + + @Override + public void writeData(long address) + { + GL33.glTexImage2D(this.type, 0, GL33.GL_RGBA8, this.width, this.height, 0, GL33.GL_RGBA, GL33.GL_UNSIGNED_BYTE, address); + } + + @Override + public boolean supportsMipMapping() + { + return true; + } +}