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/display/package-info.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/display/package-info.java
new file mode 100644
index 0000000..f32cb0e
--- /dev/null
+++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/display/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * Utilities for display handling.
+ *
+ * @author 493msi
+ *
+ */
+package cz.tefek.pluto.engine.display;
diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/event/IEvent.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/event/IEvent.java
new file mode 100644
index 0000000..9aaae0d
--- /dev/null
+++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/event/IEvent.java
@@ -0,0 +1,12 @@
+package cz.tefek.pluto.engine.event;
+
+/**
+ * Base interface of an event, extend this to create custom events.
+ *
+ * @author 493msi
+ * @since 0.1
+ */
+public interface IEvent
+{
+
+}
diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/event/package-info.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/event/package-info.java
new file mode 100644
index 0000000..252e530
--- /dev/null
+++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/event/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * Tools to create both annotation-based and interface based handlers.
+ *
+ * @author 493msi
+ *
+ */
+package cz.tefek.pluto.engine.event;
diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/gl/GLDebugInfo.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/gl/GLDebugInfo.java
new file mode 100644
index 0000000..fbf76a8
--- /dev/null
+++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/gl/GLDebugInfo.java
@@ -0,0 +1,41 @@
+package cz.tefek.pluto.engine.gl;
+
+import org.lwjgl.opengl.ARBFramebufferObject;
+import org.lwjgl.opengl.ARBUniformBufferObject;
+import org.lwjgl.opengl.GL33;
+import org.lwjgl.opengl.GLCapabilities;
+
+import cz.tefek.io.pluto.debug.Logger;
+import cz.tefek.io.pluto.debug.SmartSeverity;
+
+public class GLDebugInfo
+{
+ public static void printDebugInfo(GLCapabilities glCapabilities)
+ {
+ Logger.logf(SmartSeverity.INFO, "OpenGL20: %b\n", glCapabilities.OpenGL20);
+ Logger.logf(SmartSeverity.INFO, "OpenGL21: %b\n", glCapabilities.OpenGL21);
+
+ Logger.logf(SmartSeverity.INFO, "OpenGL30: %b\n", glCapabilities.OpenGL30);
+
+ Logger.logf(SmartSeverity.INFO, "OpenGL33: %b\n", glCapabilities.OpenGL33);
+
+ Logger.logf(SmartSeverity.INFO, "OpenGL40: %b\n", glCapabilities.OpenGL40);
+
+ Logger.logf(SmartSeverity.INFO, "OpenGL45: %b\n", glCapabilities.OpenGL45);
+
+ Logger.logf(SmartSeverity.INFO, "GL_MAX_TEXTURE_SIZE: %d\n", GL33.glGetInteger(GL33.GL_MAX_TEXTURE_SIZE));
+ Logger.logf(SmartSeverity.INFO, "GL_MAX_VERTEX_ATTRIBS: %d\n", GL33.glGetInteger(GL33.GL_MAX_VERTEX_ATTRIBS));
+ Logger.logf(SmartSeverity.INFO, "GL_MAX_TEXTURE_IMAGE_UNITS: %d\n", GL33.glGetInteger(GL33.GL_MAX_TEXTURE_IMAGE_UNITS));
+
+ Logger.logf(SmartSeverity.INFO, "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d\n", GL33.glGetInteger(GL33.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS));
+
+ Logger.logf(SmartSeverity.INFO, "ARBFramebufferObject.GL_MAX_RENDERBUFFER_SIZE: %d\n", GL33.glGetInteger(ARBFramebufferObject.GL_MAX_RENDERBUFFER_SIZE));
+ Logger.logf(SmartSeverity.INFO, "ARBFramebufferObject.GL_MAX_SAMPLES: %d\n", GL33.glGetInteger(ARBFramebufferObject.GL_MAX_SAMPLES));
+ Logger.logf(SmartSeverity.INFO, "ARBFramebufferObject.GL_MAX_COLOR_ATTACHMENTS: %d\n", GL33.glGetInteger(ARBFramebufferObject.GL_MAX_COLOR_ATTACHMENTS));
+
+ Logger.logf(SmartSeverity.INFO, "GL_ARB_uniform_buffer_object: %b\n", glCapabilities.GL_ARB_uniform_buffer_object);
+
+ Logger.logf(SmartSeverity.INFO, "GL_MAX_UNIFORM_BLOCK_SIZE : %d bytes\n", GL33.glGetInteger(ARBUniformBufferObject.GL_MAX_UNIFORM_BLOCK_SIZE));
+
+ }
+}
diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/gl/IOpenGLEnum.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/gl/IOpenGLEnum.java
new file mode 100644
index 0000000..d212783
--- /dev/null
+++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/gl/IOpenGLEnum.java
@@ -0,0 +1,13 @@
+package cz.tefek.pluto.engine.gl;
+
+/**
+ * Denotes the implementing class is a set of OpenGL enums.
+ *
+ *
+ * @author 493msi
+ *
+ */
+public interface IOpenGLEnum
+{
+ public int getGLID();
+}
diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/ClampedSineWave.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/ClampedSineWave.java
new file mode 100644
index 0000000..7a4a04c
--- /dev/null
+++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/ClampedSineWave.java
@@ -0,0 +1,72 @@
+package cz.tefek.pluto.engine.math;
+
+/**
+ * A clamped sine wave generator, for animations, mostly.
+ *
+ * @since 0.2
+ * @author 493msi
+ */
+public class ClampedSineWave
+{
+ /**
+ * Gets a clamped value of the abs(sine) function from the given parameters as a
+ * {@link double}.
+ *
+ * @param animationProgress The animation progress in frames.
+ * @param frameOffset The animation offset in frames.
+ * @param animationFrames The total amount of animation frames.
+ * @param bottomClamp The sine wave clamp, minimum value.
+ * @param topClamp The sine wave clamp, maximum value.
+ *
+ * @return The resulting value
+ *
+ * @since 0.2
+ * @author 493msi
+ */
+ public static double getAbsolute(double animationProgress, double frameOffset, double animationFrames, double bottomClamp, double topClamp)
+ {
+ var actualProgress = (animationProgress + frameOffset) / animationFrames;
+ return Math.min(Math.max(Math.abs(Math.sin(actualProgress * Math.PI)), bottomClamp), topClamp);
+ }
+
+ /**
+ * Gets a clamped value of the sine function from the given parameters as a
+ * {@link double}.
+ *
+ * @param animationProgress The animation progress in frames.
+ * @param frameOffset The animation offset in frames.
+ * @param animationFrames The total amount of animation frames.
+ * @param bottomClamp The sine wave clamp, minimum value.
+ * @param topClamp The sine wave clamp, maximum value.
+ *
+ * @return The resulting value
+ *
+ * @since 0.2
+ * @author 493msi
+ */
+ public static double get(double animationProgress, double frameOffset, double animationFrames, float bottomClamp, float topClamp)
+ {
+ var actualProgress = (animationProgress + frameOffset) / animationFrames;
+ return Math.min(Math.max(Math.sin(actualProgress * Math.PI), bottomClamp), topClamp);
+ }
+
+ /**
+ * Gets a clamped value of the sine function from the given parameters as a
+ * {@link double}.
+ *
+ * @param animationProgress The animation progress in frames.
+ * @param animationFrames The total amount of animation frames.
+ * @param bottomClamp The sine wave clamp, minimum value.
+ * @param topClamp The sine wave clamp, maximum value.
+ *
+ * @return The resulting value
+ *
+ * @since 0.2
+ * @author 493msi
+ */
+ public static double get(double animationProgress, double animationFrames, float bottomClamp, float topClamp)
+ {
+ var progress = animationProgress / animationFrames;
+ return Math.min(Math.max(Math.sin(progress * Math.PI), bottomClamp), topClamp);
+ }
+}
diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/CubicBezier.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/CubicBezier.java
new file mode 100644
index 0000000..eccc8f2
--- /dev/null
+++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/CubicBezier.java
@@ -0,0 +1,80 @@
+package cz.tefek.pluto.engine.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.2
+ * @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.2
+ * @author 493msi
+ */
+ public double forX(double xIn)
+ {
+ if (xIn < 0)
+ return forX(0);
+
+ if (xIn > 1)
+ return forX(1);
+
+ double t = 0.5;
+
+ double x;
+ double y = 3 * (1 - t) * (1 - t) * t * b + 3 * (1 - t) * t * t * d + t * t * t;
+
+ double delta = 0.25;
+ boolean uh;
+
+ for (int i = 0; i < iterations; i++)
+ {
+ x = 3 * (1 - t) * (1 - t) * t * a + 3 * (1 - t) * t * t * c + t * t * t;
+ y = 3 * (1 - t) * (1 - t) * t * b + 3 * (1 - t) * t * t * d + t * t * t;
+
+ uh = x > xIn;
+
+ t += uh ? -delta : delta;
+
+ delta /= 2;
+ }
+
+ return y;
+ }
+}
diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/CubicBezierLT.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/CubicBezierLT.java
new file mode 100644
index 0000000..3703dba
--- /dev/null
+++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/CubicBezierLT.java
@@ -0,0 +1,65 @@
+package cz.tefek.pluto.engine.math;
+
+/**
+ * A class to generate a cubic bezier interpolation function. This
+ * implementation creates a lookup table.
+ *
+ * @since 0.2
+ * @author 493msi
+ */
+public class CubicBezierLT
+{
+ private static final int tableSize = 1 << 11;
+ private final double[] values;
+ private final double upperBound = tableSize - 1.0;
+
+ /**
+ * Creates a new {@code CubicBezierLT} 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.2
+ * @author 493msi
+ */
+ public CubicBezierLT(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).");
+ }
+
+ var cubicBezier = new CubicBezier(cpx1, cpy1, cpx2, cpy2);
+
+ this.values = new double[tableSize];
+
+ for (int i = 0; i < tableSize; i++)
+ {
+ values[i] = cubicBezier.forX(i / upperBound);
+ }
+ }
+
+ /**
+ * Retrives the approximate value 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.2
+ * @author 493msi
+ */
+ public double forX(double xIn)
+ {
+ if (xIn < 0)
+ return forX(0);
+
+ if (xIn > 1)
+ return forX(1);
+
+ return values[(int) (xIn * upperBound)];
+ }
+}
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
new file mode 100644
index 0000000..2660725
--- /dev/null
+++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/ProjectionMatrix.java
@@ -0,0 +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;
+ }
+}
diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/TransformationMatrix.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/TransformationMatrix.java
new file mode 100644
index 0000000..03859f5
--- /dev/null
+++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/TransformationMatrix.java
@@ -0,0 +1,113 @@
+package cz.tefek.pluto.engine.math;
+
+import org.joml.Matrix3x2f;
+import org.joml.Matrix4f;
+import org.joml.Vector2fc;
+import org.joml.Vector3fc;
+
+public class TransformationMatrix
+{
+ /**
+ *
+ * Creates a transformation matrix from the given translation, rotation and
+ * scale.
+ *
+ *
+ * The rotation is specified as rotating around the Z axis (roll), then rotating
+ * around the X axis (pitch) and finally rotating around the Y axis (yaw).
+ *
+ *
+ * @param translation The translation vector
+ * @param rotation The rotation angles
+ * @param scale The scale vector
+ *
+ * @author 493msi
+ * @since 0.1
+ */
+ public static Matrix4f create(Vector3fc translation, Vector3fc yxzRotation, Vector3fc scale)
+ {
+ var transformation = new Matrix4f();
+ transformation.translate(translation);
+ transformation.rotateAffineYXZ(yxzRotation.x(), yxzRotation.y(), yxzRotation.z());
+ transformation.scale(scale);
+
+ return transformation;
+ }
+
+ /**
+ *
+ * Creates a transformation matrix from the given translation, rotation and
+ * scale.
+ *
+ *
+ * @param x The translation on the X axis
+ * @param y The translation on the Y axis
+ * @param z The translation on the Z axis
+ * @param pitch The pitch rotation
+ * @param yaw The yaw rotation
+ * @param roll The roll rotation
+ * @param scaleX The scale on the X axis
+ * @param scaleY The scale on the Y axis
+ * @param scaleY The scale on the Z axis
+ *
+ * @author 493msi
+ * @since 0.3
+ */
+ public static Matrix4f create(float x, float y, float z, float pitch, float yaw, float roll, float scaleX, float scaleY, float scaleZ)
+ {
+ var transformation = new Matrix4f();
+ transformation.translate(x, y, z);
+ transformation.rotateAffineYXZ(pitch, yaw, roll);
+ transformation.scale(scaleX, scaleY, scaleZ);
+
+ return transformation;
+ }
+
+ /**
+ *
+ * Creates a 2D transformation matrix from the given translation, rotation and
+ * scale.
+ *
+ *
+ * @param translation The translation vector
+ * @param rotation The rotation angle
+ * @param scale The scale vector
+ *
+ * @author 493msi
+ * @since 0.3
+ */
+ public static Matrix3x2f create2D(Vector2fc translation, float rotation, Vector2fc scale)
+ {
+ var transformation = new Matrix3x2f();
+ transformation.translate(translation.x(), translation.y());
+ transformation.rotate(rotation);
+ transformation.scale(scale);
+
+ return transformation;
+ }
+
+ /**
+ *
+ * Creates a 2D transformation matrix from the given translation, rotation and
+ * scale.
+ *
+ *
+ * @param x The translation on the X axis
+ * @param y The translation on the Y axis
+ * @param rotation The rotation angle
+ * @param scaleX The scale on the X axis
+ * @param scaleY The scale on the Y axis
+ *
+ * @author 493msi
+ * @since 0.3
+ */
+ public static Matrix3x2f create2D(float x, float y, float rotation, float scaleX, float scaleY)
+ {
+ var transformation = new Matrix3x2f();
+ transformation.translate(x, y);
+ transformation.rotate(rotation);
+ transformation.scale(scaleX, scaleY);
+
+ return transformation;
+ }
+}
diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/ViewMatrix.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/ViewMatrix.java
new file mode 100644
index 0000000..43078e9
--- /dev/null
+++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/ViewMatrix.java
@@ -0,0 +1,33 @@
+package cz.tefek.pluto.engine.math;
+
+import org.joml.Matrix3x2f;
+
+/**
+ * A class with factory methods for the most used view transformations.
+ *
+ * @author 493msi
+ * @since 0.3
+ */
+public class ViewMatrix
+{
+ /**
+ * Create a 2D
+ *
+ * @param x The X camera translation
+ * @param y The Y camera translation
+ * @param zoom The zoom
+ *
+ * @return the view matrix
+ *
+ * @author 493msi
+ * @since 0.3
+ */
+ public static Matrix3x2f createView(int x, int y, float zoom)
+ {
+ var view = new Matrix3x2f();
+ view.scale(zoom);
+ view.translate(x, y);
+
+ return view;
+ }
+}
diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionClass.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionClass.java
new file mode 100644
index 0000000..8982a38
--- /dev/null
+++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionClass.java
@@ -0,0 +1,24 @@
+package cz.tefek.pluto.engine.math.collision;
+
+public class CollisionClass
+{
+ private static int idSource = 0;
+
+ private int id = idSource++;
+ private boolean selfCollision;
+
+ public CollisionClass(boolean selfCollision)
+ {
+ this.selfCollision = selfCollision;
+ }
+
+ public int getID()
+ {
+ return this.id;
+ }
+
+ public boolean hasSelfCollision()
+ {
+ return this.selfCollision;
+ }
+}
diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionSurface.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionSurface.java
new file mode 100644
index 0000000..20f0d33
--- /dev/null
+++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionSurface.java
@@ -0,0 +1,83 @@
+package cz.tefek.pluto.engine.math.collision;
+
+public class CollisionSurface
+{
+ protected float friction; // 0..1 where 0 is no friction and 1 is superglue
+ protected float bounciness; // 0..n where 0 is no bounciness and 1 is is a perfect bounce
+ protected float drag; // 0..n where the value defines how much the surface is affected by air
+
+ public CollisionSurface(float friction, float bounciness, float drag)
+ {
+ this.friction = friction;
+ this.bounciness = bounciness;
+ this.drag = drag;
+ }
+
+ /**
+ * Returns the friction; a value 0..1 where 0 is no friction and 1 is infinite
+ * friction, making the surfaces impossible to slide on.
+ *
+ * @return the friction value
+ */
+ public float getFriction()
+ {
+ return this.friction;
+ }
+
+ /**
+ * Sets the friction; a value 0..1 where 0 is no friction and 1 is infinite
+ * friction, making the surfaces impossible to slide on.
+ *
+ * @param friction the friction value
+ */
+ public void setFriction(float friction)
+ {
+ this.friction = friction;
+ }
+
+ /**
+ * Returns the bounciness; a value 0..1..n where 0 means the surface doesn't
+ * bounce off at all and 1 means the object bounces off at the exact same
+ * velocity, values above 1 might have some undesirable effects and are
+ * discouraged.
+ *
+ * @return the bounciness
+ */
+ public float getBounciness()
+ {
+ return this.bounciness;
+ }
+
+ /**
+ * Sets the bounciness; a value 0..1..n where 0 means the surface doesn't bounce
+ * off at all and 1 means the object bounces off at the exact same velocity,
+ * values above 1 might have some undesirable effects and are discouraged.
+ *
+ * @param bounciness the bounciness value
+ */
+ public void setBounciness(float bounciness)
+ {
+ this.bounciness = bounciness;
+ }
+
+ /**
+ * @return the drag
+ */
+ public float getDrag()
+ {
+ return this.drag;
+ }
+
+ /**
+ * Sets the drag modifier; a value 0..n which defines how much this surface is
+ * affected by air resistance. A value of 1 should be the golden standard for
+ * most objects.
+ *
+ * @param drag the drag modifier
+ */
+ public void setDrag(float drag)
+ {
+ this.drag = drag;
+ }
+
+}
diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionSurfaceCircle.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionSurfaceCircle.java
new file mode 100644
index 0000000..1c400da
--- /dev/null
+++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionSurfaceCircle.java
@@ -0,0 +1,13 @@
+package cz.tefek.pluto.engine.math.collision;
+
+import org.joml.Circlef;
+
+public class CollisionSurfaceCircle
+{
+ private Circlef circle;
+
+ public Circlef getCircle()
+ {
+ return this.circle;
+ }
+}
diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionSurfaceLine.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionSurfaceLine.java
new file mode 100644
index 0000000..ca4a2ce
--- /dev/null
+++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/collision/CollisionSurfaceLine.java
@@ -0,0 +1,18 @@
+package cz.tefek.pluto.engine.math.collision;
+
+import org.joml.LineSegmentf;
+
+public class CollisionSurfaceLine extends CollisionSurface
+{
+ public CollisionSurfaceLine(float friction, float bounciness, float drag)
+ {
+ super(friction, bounciness, drag);
+ }
+
+ protected LineSegmentf lineSegment;
+
+ public LineSegmentf getLineSegment()
+ {
+ return this.lineSegment;
+ }
+}
diff --git a/plutostatic/src/main/java/cz/tefek/pluto/engine/math/package-info.java b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/package-info.java
new file mode 100644
index 0000000..dc3367b
--- /dev/null
+++ b/plutostatic/src/main/java/cz/tefek/pluto/engine/math/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * Math utility classes.
+ *
+ * @author 493msi
+ *
+ */
+package cz.tefek.pluto.engine.math;
diff --git a/plutotexturing/pom.xml b/plutotexturing/pom.xml
new file mode 100644
index 0000000..9a8fbb9
--- /dev/null
+++ b/plutotexturing/pom.xml
@@ -0,0 +1,20 @@
+
+ 4.0.0
+ cz.tefek
+ plutotexturing
+ 0.1
+ plutotexturing
+
+ 11
+ 11
+ UTF-8
+ UTF-8
+
+
+
+ cz.tefek
+ plutostatic
+ 0.3
+
+
+
\ No newline at end of file
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
new file mode 100644
index 0000000..7dbeb60
--- /dev/null
+++ b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/MagFilter.java
@@ -0,0 +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;
+ }
+}
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
new file mode 100644
index 0000000..3eac01d
--- /dev/null
+++ b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/MinFilter.java
@@ -0,0 +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;
+ }
+}
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
new file mode 100644
index 0000000..1fc902d
--- /dev/null
+++ b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/Texture.java
@@ -0,0 +1,241 @@
+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 cz.tefek.io.asl.resource.ResourceAddress;
+import cz.tefek.io.pluto.debug.Logger;
+import cz.tefek.io.pluto.debug.Severity;
+import cz.tefek.io.pluto.debug.SmartSeverity;
+import cz.tefek.tpl.TPL;
+import cz.tefek.tpl.TPNImage;
+
+public abstract class Texture
+{
+ protected int glID = 0;
+ protected final int type;
+ protected final int dimensions;
+
+ protected int width = 1;
+ protected int height = 1;
+ protected int depth = 1;
+
+ protected MinFilter minFilter;
+ protected MagFilter magFilter;
+
+ protected WrapMode wrapModeU;
+ protected WrapMode wrapModeV;
+ protected WrapMode wrapModeW;
+
+ public Texture(int type, int dimensions)
+ {
+ this.glID = GL33.glGenTextures();
+ this.type = type;
+ this.dimensions = dimensions;
+
+ Logger.logf(SmartSeverity.ADDED, "Texture with ID %d of type '%s' created...\n", this.glID, this.getClass().getSimpleName());
+ }
+
+ public void bind()
+ {
+ GL33.glBindTexture(this.type, this.glID);
+ }
+
+ public void unbind()
+ {
+ GL33.glBindTexture(this.type, 0);
+ }
+
+ public void delete()
+ {
+ Logger.logf(SmartSeverity.REMOVED, "Texture with ID %d of type '%s' deleted...\n", this.glID, this.getClass().getSimpleName());
+
+ // Delete unbinds automatically
+ GL33.glDeleteTextures(this.glID);
+
+ this.glID = 0;
+
+ this.width = 0;
+ this.height = 0;
+ this.depth = 0;
+
+ this.wrapModeU = null;
+ this.wrapModeV = null;
+ this.wrapModeW = null;
+
+ this.minFilter = null;
+ this.magFilter = null;
+ }
+
+ public int getDepth()
+ {
+ return this.depth;
+ }
+
+ public int getHeight()
+ {
+ return this.height;
+ }
+
+ public int getWidth()
+ {
+ return this.width;
+ }
+
+ public MagFilter getMagFilter()
+ {
+ return this.magFilter;
+ }
+
+ public MinFilter getMinFilter()
+ {
+ return this.minFilter;
+ }
+
+ public WrapMode getWrapModeU()
+ {
+ return this.wrapModeU;
+ }
+
+ public WrapMode getWrapModeV()
+ {
+ return this.wrapModeV;
+ }
+
+ public WrapMode getWrapModeW()
+ {
+ return this.wrapModeW;
+ }
+
+ public Texture setFilteringOptions(MagFilter magFilter, MinFilter minFilter)
+ {
+ if (minFilter.isMipMapped())
+ {
+ if (!this.supportsMipMapping())
+ {
+ Logger.logf(SmartSeverity.ERROR, "The texture of type '%s' does not support mipmaps.\n", this.getClass().getSimpleName());
+ return this;
+ }
+
+ GL33.glGenerateMipmap(this.type);
+ }
+
+ this.magFilter = magFilter;
+ GL33.glTexParameteri(this.type, GL33.GL_TEXTURE_MAG_FILTER, this.magFilter.getGLID());
+
+ this.minFilter = minFilter;
+ GL33.glTexParameteri(this.type, GL33.GL_TEXTURE_MIN_FILTER, this.minFilter.getGLID());
+
+ return this;
+ }
+
+ public Texture setWrapOptions(WrapMode... wrapOptions)
+ {
+ if (wrapOptions.length != this.dimensions)
+ {
+ Logger.log(Severity.ERROR, "Error: WrapMode option count does not match texture's dimensions.");
+ return this;
+ }
+
+ this.wrapModeU = wrapOptions[0];
+ GL33.glTexParameteri(this.type, GL33.GL_TEXTURE_WRAP_S, this.wrapModeU.getGLID());
+
+ if (this.dimensions >= 2)
+ {
+ this.wrapModeV = wrapOptions[1];
+ GL33.glTexParameteri(this.type, GL33.GL_TEXTURE_WRAP_T, this.wrapModeV.getGLID());
+
+ if (this.dimensions >= 3)
+ {
+ this.wrapModeW = wrapOptions[2];
+ GL33.glTexParameteri(this.type, GL33.GL_TEXTURE_WRAP_R, this.wrapModeW.getGLID());
+ }
+ }
+
+ return this;
+ }
+
+ public void writeData(ByteBuffer buffer)
+ {
+ this.writeData(MemoryUtil.memAddress(buffer));
+ }
+
+ public abstract boolean supportsMipMapping();
+
+ public abstract void writeData(long address);
+
+ public int getID()
+ {
+ return this.glID;
+ }
+
+ protected WrapMode getDefaultWrapMode()
+ {
+ return WrapMode.REPEAT;
+ }
+
+ public void load(String file)
+ {
+ var wrap = new WrapMode[this.dimensions];
+ Arrays.fill(wrap, this.getDefaultWrapMode());
+ this.load(file, MagFilter.LINEAR, MinFilter.LINEAR, wrap);
+ }
+
+ public void load(ResourceAddress file)
+ {
+ var wrap = new WrapMode[this.dimensions];
+ Arrays.fill(wrap, this.getDefaultWrapMode());
+ this.load(file.toPath(), MagFilter.LINEAR, MinFilter.LINEAR, wrap);
+ }
+
+ public void load(ResourceAddress file, MagFilter magFilter, MinFilter minFilter, WrapMode... wrap)
+ {
+ this.load(file.toPath(), magFilter, minFilter, wrap);
+ }
+
+ public void load(String file, MagFilter magFilter, MinFilter minFilter, WrapMode... wrap)
+ {
+ TPNImage image = TPL.load(file);
+ this.load(image, magFilter, minFilter, wrap);
+ }
+
+ public void load(BufferedImage imageIn, MagFilter magFilter, MinFilter minFilter, WrapMode... wrap)
+ {
+ TPNImage image = TPL.loadImage(imageIn);
+ this.load(image, magFilter, minFilter, wrap);
+ }
+
+ public void load(TPNImage image, MagFilter magFilter, MinFilter minFilter, WrapMode... wrap)
+ {
+ this.load(image.getData(), image.getWidth(), image.getHeight(), magFilter, minFilter, wrap);
+ }
+
+ public void load(ByteBuffer imageData, int width, int height, MagFilter magFilter, MinFilter minFilter, WrapMode... wrap)
+ {
+ this.width = width;
+ this.height = height;
+
+ this.bind();
+
+ GL33.glTexParameteriv(this.type, GL33.GL_TEXTURE_SWIZZLE_RGBA, new int[] { GL33.GL_ALPHA, GL33.GL_BLUE, GL33.GL_GREEN, GL33.GL_RED }); // TODO: Temp solution until I find a smart way to swizzle BufferedImage data
+
+ this.setFilteringOptions(magFilter, minFilter);
+ this.setWrapOptions(wrap);
+ this.writeData(imageData);
+ }
+
+ public boolean isValid()
+ {
+ return this.glID > 0;
+ }
+
+ public int getType()
+ {
+ return this.type;
+ }
+}
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
new file mode 100644
index 0000000..1dfe740
--- /dev/null
+++ b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/WrapMode.java
@@ -0,0 +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;
+ }
+}
diff --git a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/sampler/Sampler2D.java b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/sampler/Sampler2D.java
new file mode 100644
index 0000000..145fdb8
--- /dev/null
+++ b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/sampler/Sampler2D.java
@@ -0,0 +1,54 @@
+package cz.tefek.pluto.engine.graphics.texture.sampler;
+
+import java.util.HashSet;
+
+import org.lwjgl.opengl.GL33;
+
+import cz.tefek.pluto.engine.graphics.texture.MagFilter;
+import cz.tefek.pluto.engine.graphics.texture.MinFilter;
+import cz.tefek.pluto.engine.graphics.texture.WrapMode;
+
+public class Sampler2D
+{
+ private int id;
+
+ private HashSet usedUnits;
+
+ public Sampler2D()
+ {
+ this.id = GL33.glGenSamplers();
+ this.usedUnits = new HashSet<>(GL33.glGetInteger(GL33.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS));
+ }
+
+ public void setFilteringParameters(MagFilter magFilter, MinFilter minFilter, WrapMode wrapModeS, WrapMode wrapModeT)
+ {
+ GL33.glSamplerParameteri(this.id, GL33.GL_TEXTURE_MIN_FILTER, minFilter.getGLID());
+ GL33.glSamplerParameteri(this.id, GL33.GL_TEXTURE_MAG_FILTER, magFilter.getGLID());
+ GL33.glSamplerParameteri(this.id, GL33.GL_TEXTURE_WRAP_S, wrapModeS.getGLID());
+ GL33.glSamplerParameteri(this.id, GL33.GL_TEXTURE_WRAP_T, wrapModeT.getGLID());
+ }
+
+ public void bind(int unit)
+ {
+ this.usedUnits.add(unit);
+ GL33.glBindSampler(unit, this.id);
+ }
+
+ public void delete()
+ {
+ this.unbind();
+ GL33.glDeleteSamplers(this.id);
+ }
+
+ public void unbind(int unit)
+ {
+ GL33.glBindSampler(unit, 0);
+ this.usedUnits.remove(unit);
+ }
+
+ public void unbind()
+ {
+ this.usedUnits.forEach(this::unbind);
+ this.usedUnits.clear();
+ }
+}
diff --git a/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/sampler/Sampler3D.java b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/sampler/Sampler3D.java
new file mode 100644
index 0000000..c632859
--- /dev/null
+++ b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/sampler/Sampler3D.java
@@ -0,0 +1,55 @@
+package cz.tefek.pluto.engine.graphics.texture.sampler;
+
+import java.util.HashSet;
+
+import org.lwjgl.opengl.GL33;
+
+import cz.tefek.pluto.engine.graphics.texture.MagFilter;
+import cz.tefek.pluto.engine.graphics.texture.MinFilter;
+import cz.tefek.pluto.engine.graphics.texture.WrapMode;
+
+public class Sampler3D
+{
+ private int id;
+
+ private HashSet usedUnits;
+
+ public Sampler3D()
+ {
+ this.id = GL33.glGenSamplers();
+ this.usedUnits = new HashSet<>(GL33.glGetInteger(GL33.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS));
+ }
+
+ public void setFilteringParameters(MinFilter minFilter, MagFilter magFilter, WrapMode wrapModeS, WrapMode wrapModeT, WrapMode wrapModeR)
+ {
+ GL33.glSamplerParameteri(this.id, GL33.GL_TEXTURE_MIN_FILTER, minFilter.getGLID());
+ GL33.glSamplerParameteri(this.id, GL33.GL_TEXTURE_MAG_FILTER, magFilter.getGLID());
+ GL33.glSamplerParameteri(this.id, GL33.GL_TEXTURE_WRAP_S, wrapModeS.getGLID());
+ GL33.glSamplerParameteri(this.id, GL33.GL_TEXTURE_WRAP_T, wrapModeT.getGLID());
+ GL33.glSamplerParameteri(this.id, GL33.GL_TEXTURE_WRAP_R, wrapModeR.getGLID());
+ }
+
+ public void bind(int unit)
+ {
+ this.usedUnits.add(unit);
+ GL33.glBindSampler(unit, this.id);
+ }
+
+ public void delete()
+ {
+ this.unbind();
+ GL33.glDeleteSamplers(this.id);
+ }
+
+ public void unbind(int unit)
+ {
+ GL33.glBindSampler(unit, 0);
+ this.usedUnits.remove(unit);
+ }
+
+ public void unbind()
+ {
+ this.usedUnits.forEach(this::unbind);
+ this.usedUnits.clear();
+ }
+}
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
new file mode 100644
index 0000000..f5ce622
--- /dev/null
+++ b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/texture2d/RectangleTexture.java
@@ -0,0 +1,49 @@
+package cz.tefek.pluto.engine.graphics.texture.texture2d;
+
+import java.util.Arrays;
+
+import org.lwjgl.opengl.GL33;
+
+import cz.tefek.io.pluto.debug.Logger;
+import cz.tefek.io.pluto.debug.Severity;
+import cz.tefek.pluto.engine.graphics.texture.Texture;
+import cz.tefek.pluto.engine.graphics.texture.WrapMode;
+
+public class RectangleTexture extends Texture
+{
+ public RectangleTexture()
+ {
+ super(GL33.GL_TEXTURE_RECTANGLE, 2);
+ }
+
+ @Override
+ public boolean supportsMipMapping()
+ {
+ return false;
+ }
+
+ @Override
+ public Texture setWrapOptions(WrapMode... wrapOptions)
+ {
+ if (Arrays.stream(wrapOptions).anyMatch(WrapMode.repeatModes::contains))
+ {
+ Logger.log(Severity.ERROR, "Error: Rectangle textures do not support repeat wrap modes!");
+
+ return this;
+ }
+
+ return super.setWrapOptions(wrapOptions);
+ }
+
+ @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
+ protected WrapMode getDefaultWrapMode()
+ {
+ return WrapMode.CLAMP_TO_EDGE;
+ }
+}
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
new file mode 100644
index 0000000..3916226
--- /dev/null
+++ b/plutotexturing/src/main/java/cz/tefek/pluto/engine/graphics/texture/texture2d/Texture2D.java
@@ -0,0 +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;
+ }
+}