diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..d43212b --- /dev/null +++ b/.classpath @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..568be6b --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + GameCore-LWJGL + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..7341ab1 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/bin/mightypork/gamecore/backends/lwjgl/BufferHelper.class b/bin/mightypork/gamecore/backends/lwjgl/BufferHelper.class new file mode 100644 index 0000000..18a29e2 Binary files /dev/null and b/bin/mightypork/gamecore/backends/lwjgl/BufferHelper.class differ diff --git a/bin/mightypork/gamecore/backends/lwjgl/InitTaskRedirectSlickLog.class b/bin/mightypork/gamecore/backends/lwjgl/InitTaskRedirectSlickLog.class new file mode 100644 index 0000000..a831b8c Binary files /dev/null and b/bin/mightypork/gamecore/backends/lwjgl/InitTaskRedirectSlickLog.class differ diff --git a/bin/mightypork/gamecore/backends/lwjgl/LwjglBackend.class b/bin/mightypork/gamecore/backends/lwjgl/LwjglBackend.class new file mode 100644 index 0000000..f020ed7 Binary files /dev/null and b/bin/mightypork/gamecore/backends/lwjgl/LwjglBackend.class differ diff --git a/bin/mightypork/gamecore/backends/lwjgl/LwjglInputModule$1.class b/bin/mightypork/gamecore/backends/lwjgl/LwjglInputModule$1.class new file mode 100644 index 0000000..f24e14a Binary files /dev/null and b/bin/mightypork/gamecore/backends/lwjgl/LwjglInputModule$1.class differ diff --git a/bin/mightypork/gamecore/backends/lwjgl/LwjglInputModule.class b/bin/mightypork/gamecore/backends/lwjgl/LwjglInputModule.class new file mode 100644 index 0000000..66698c9 Binary files /dev/null and b/bin/mightypork/gamecore/backends/lwjgl/LwjglInputModule.class differ diff --git a/bin/mightypork/gamecore/backends/lwjgl/SlickLogRedirector.class b/bin/mightypork/gamecore/backends/lwjgl/SlickLogRedirector.class new file mode 100644 index 0000000..ad14f4d Binary files /dev/null and b/bin/mightypork/gamecore/backends/lwjgl/SlickLogRedirector.class differ diff --git a/bin/mightypork/gamecore/backends/lwjgl/audio/SlickAudio.class b/bin/mightypork/gamecore/backends/lwjgl/audio/SlickAudio.class new file mode 100644 index 0000000..fd27244 Binary files /dev/null and b/bin/mightypork/gamecore/backends/lwjgl/audio/SlickAudio.class differ diff --git a/bin/mightypork/gamecore/backends/lwjgl/audio/SlickAudioModule.class b/bin/mightypork/gamecore/backends/lwjgl/audio/SlickAudioModule.class new file mode 100644 index 0000000..7d1a397 Binary files /dev/null and b/bin/mightypork/gamecore/backends/lwjgl/audio/SlickAudioModule.class differ diff --git a/bin/mightypork/gamecore/backends/lwjgl/graphics/AwtScreenshot.class b/bin/mightypork/gamecore/backends/lwjgl/graphics/AwtScreenshot.class new file mode 100644 index 0000000..cacf9cf Binary files /dev/null and b/bin/mightypork/gamecore/backends/lwjgl/graphics/AwtScreenshot.class differ diff --git a/bin/mightypork/gamecore/backends/lwjgl/graphics/LwjglGraphicsModule$1.class b/bin/mightypork/gamecore/backends/lwjgl/graphics/LwjglGraphicsModule$1.class new file mode 100644 index 0000000..7a2effe Binary files /dev/null and b/bin/mightypork/gamecore/backends/lwjgl/graphics/LwjglGraphicsModule$1.class differ diff --git a/bin/mightypork/gamecore/backends/lwjgl/graphics/LwjglGraphicsModule.class b/bin/mightypork/gamecore/backends/lwjgl/graphics/LwjglGraphicsModule.class new file mode 100644 index 0000000..aa6b4a4 Binary files /dev/null and b/bin/mightypork/gamecore/backends/lwjgl/graphics/LwjglGraphicsModule.class differ diff --git a/bin/mightypork/gamecore/backends/lwjgl/graphics/SlickTexture.class b/bin/mightypork/gamecore/backends/lwjgl/graphics/SlickTexture.class new file mode 100644 index 0000000..855a97e Binary files /dev/null and b/bin/mightypork/gamecore/backends/lwjgl/graphics/SlickTexture.class differ diff --git a/bin/mightypork/gamecore/backends/lwjgl/graphics/font/DeferredLwjglFont.class b/bin/mightypork/gamecore/backends/lwjgl/graphics/font/DeferredLwjglFont.class new file mode 100644 index 0000000..18f3720 Binary files /dev/null and b/bin/mightypork/gamecore/backends/lwjgl/graphics/font/DeferredLwjglFont.class differ diff --git a/bin/mightypork/gamecore/backends/lwjgl/graphics/font/DeferredLwjglFontFromSystem.class b/bin/mightypork/gamecore/backends/lwjgl/graphics/font/DeferredLwjglFontFromSystem.class new file mode 100644 index 0000000..fbca3d7 Binary files /dev/null and b/bin/mightypork/gamecore/backends/lwjgl/graphics/font/DeferredLwjglFontFromSystem.class differ diff --git a/bin/mightypork/gamecore/backends/lwjgl/graphics/font/LwjglTextureBackedFont$1LoadedGlyph.class b/bin/mightypork/gamecore/backends/lwjgl/graphics/font/LwjglTextureBackedFont$1LoadedGlyph.class new file mode 100644 index 0000000..01f0fa5 Binary files /dev/null and b/bin/mightypork/gamecore/backends/lwjgl/graphics/font/LwjglTextureBackedFont$1LoadedGlyph.class differ diff --git a/bin/mightypork/gamecore/backends/lwjgl/graphics/font/LwjglTextureBackedFont$CharTile.class b/bin/mightypork/gamecore/backends/lwjgl/graphics/font/LwjglTextureBackedFont$CharTile.class new file mode 100644 index 0000000..84ecbd9 Binary files /dev/null and b/bin/mightypork/gamecore/backends/lwjgl/graphics/font/LwjglTextureBackedFont$CharTile.class differ diff --git a/bin/mightypork/gamecore/backends/lwjgl/graphics/font/LwjglTextureBackedFont.class b/bin/mightypork/gamecore/backends/lwjgl/graphics/font/LwjglTextureBackedFont.class new file mode 100644 index 0000000..a91e3ea Binary files /dev/null and b/bin/mightypork/gamecore/backends/lwjgl/graphics/font/LwjglTextureBackedFont.class differ diff --git a/lib/OpenAL32.dll b/lib/OpenAL32.dll new file mode 100644 index 0000000..6dd2600 Binary files /dev/null and b/lib/OpenAL32.dll differ diff --git a/lib/OpenAL64.dll b/lib/OpenAL64.dll new file mode 100644 index 0000000..00c98c0 Binary files /dev/null and b/lib/OpenAL64.dll differ diff --git a/lib/jinput-dx8.dll b/lib/jinput-dx8.dll new file mode 100644 index 0000000..6d27ad5 Binary files /dev/null and b/lib/jinput-dx8.dll differ diff --git a/lib/jinput-dx8_64.dll b/lib/jinput-dx8_64.dll new file mode 100644 index 0000000..6730589 Binary files /dev/null and b/lib/jinput-dx8_64.dll differ diff --git a/lib/jinput-raw.dll b/lib/jinput-raw.dll new file mode 100644 index 0000000..ce1d162 Binary files /dev/null and b/lib/jinput-raw.dll differ diff --git a/lib/jinput-raw_64.dll b/lib/jinput-raw_64.dll new file mode 100644 index 0000000..3d2b3ad Binary files /dev/null and b/lib/jinput-raw_64.dll differ diff --git a/lib/jinput.jar b/lib/jinput.jar new file mode 100644 index 0000000..7c2b6b0 Binary files /dev/null and b/lib/jinput.jar differ diff --git a/lib/jogg-0.0.7.jar b/lib/jogg-0.0.7.jar new file mode 100644 index 0000000..ecb0260 Binary files /dev/null and b/lib/jogg-0.0.7.jar differ diff --git a/lib/jorbis-0.0.15.jar b/lib/jorbis-0.0.15.jar new file mode 100644 index 0000000..4cf51f9 Binary files /dev/null and b/lib/jorbis-0.0.15.jar differ diff --git a/lib/libjinput-linux.so b/lib/libjinput-linux.so new file mode 100644 index 0000000..3cdc439 Binary files /dev/null and b/lib/libjinput-linux.so differ diff --git a/lib/libjinput-linux64.so b/lib/libjinput-linux64.so new file mode 100644 index 0000000..de1ee5f Binary files /dev/null and b/lib/libjinput-linux64.so differ diff --git a/lib/libjinput-osx.jnilib b/lib/libjinput-osx.jnilib new file mode 100644 index 0000000..59a3eab Binary files /dev/null and b/lib/libjinput-osx.jnilib differ diff --git a/lib/liblwjgl.jnilib b/lib/liblwjgl.jnilib new file mode 100644 index 0000000..cbb5b4c Binary files /dev/null and b/lib/liblwjgl.jnilib differ diff --git a/lib/liblwjgl.so b/lib/liblwjgl.so new file mode 100644 index 0000000..5a02874 Binary files /dev/null and b/lib/liblwjgl.so differ diff --git a/lib/liblwjgl64.so b/lib/liblwjgl64.so new file mode 100644 index 0000000..4572589 Binary files /dev/null and b/lib/liblwjgl64.so differ diff --git a/lib/libopenal.so b/lib/libopenal.so new file mode 100644 index 0000000..7742faf Binary files /dev/null and b/lib/libopenal.so differ diff --git a/lib/libopenal64.so b/lib/libopenal64.so new file mode 100644 index 0000000..d1e45e5 Binary files /dev/null and b/lib/libopenal64.so differ diff --git a/lib/lwjgl-source-2.8.4.zip b/lib/lwjgl-source-2.8.4.zip new file mode 100644 index 0000000..f48fd31 Binary files /dev/null and b/lib/lwjgl-source-2.8.4.zip differ diff --git a/lib/lwjgl.dll b/lib/lwjgl.dll new file mode 100644 index 0000000..6819404 Binary files /dev/null and b/lib/lwjgl.dll differ diff --git a/lib/lwjgl.jar b/lib/lwjgl.jar new file mode 100644 index 0000000..a0fb56d Binary files /dev/null and b/lib/lwjgl.jar differ diff --git a/lib/lwjgl64.dll b/lib/lwjgl64.dll new file mode 100644 index 0000000..e66ab2a Binary files /dev/null and b/lib/lwjgl64.dll differ diff --git a/lib/lwjgl_util.jar b/lib/lwjgl_util.jar new file mode 100644 index 0000000..9973b24 Binary files /dev/null and b/lib/lwjgl_util.jar differ diff --git a/lib/slick-util-src.zip b/lib/slick-util-src.zip new file mode 100644 index 0000000..7ca6790 Binary files /dev/null and b/lib/slick-util-src.zip differ diff --git a/lib/slick-util.jar b/lib/slick-util.jar new file mode 100644 index 0000000..86a64a7 Binary files /dev/null and b/lib/slick-util.jar differ diff --git a/src/mightypork/gamecore/backends/lwjgl/BufferHelper.java b/src/mightypork/gamecore/backends/lwjgl/BufferHelper.java new file mode 100644 index 0000000..3e38b3b --- /dev/null +++ b/src/mightypork/gamecore/backends/lwjgl/BufferHelper.java @@ -0,0 +1,52 @@ +package mightypork.gamecore.backends.lwjgl; + + +import java.nio.FloatBuffer; + +import org.lwjgl.BufferUtils; + + +/** + * Calc subclass with buffer utils. + * + * @author Ondřej Hruška (MightyPork) + */ +public class BufferHelper { + + /** + * Create java.nio.FloatBuffer of given floats, and flip it. + * + * @param obj floats or float array + * @return float buffer + */ + public static FloatBuffer mkFillBuff(float... obj) + { + return (FloatBuffer) BufferUtils.createFloatBuffer(obj.length).put(obj).flip(); + } + + + /** + * Fill java.nio.FloatBuffer with floats or float array + * + * @param buff + * @param obj + */ + public static void fill(FloatBuffer buff, float... obj) + { + buff.put(obj); + buff.flip(); + } + + + /** + * Create new java.nio.FloatBuffer of given length + * + * @param count elements + * @return the new java.nio.FloatBuffer + */ + public static FloatBuffer alloc(int count) + { + return BufferUtils.createFloatBuffer(count); + } + +} diff --git a/src/mightypork/gamecore/backends/lwjgl/InitTaskRedirectSlickLog.java b/src/mightypork/gamecore/backends/lwjgl/InitTaskRedirectSlickLog.java new file mode 100644 index 0000000..a951141 --- /dev/null +++ b/src/mightypork/gamecore/backends/lwjgl/InitTaskRedirectSlickLog.java @@ -0,0 +1,38 @@ +package mightypork.gamecore.backends.lwjgl; + + +import mightypork.gamecore.core.InitTask; +import mightypork.gamecore.core.OptionalInitTask; +import mightypork.utils.logging.writers.LogWriter; + + +/** + * Initializer that redirects slick logging to main logger. + * + * @author Ondřej Hruška (MightyPork) + */ +@OptionalInitTask +public class InitTaskRedirectSlickLog extends InitTask { + + @Override + public void run() + { + LogWriter ml = mightypork.utils.logging.Log.getMainLogger(); + SlickLogRedirector slr = new SlickLogRedirector(ml); + org.newdawn.slick.util.Log.setLogSystem(slr); + } + + + @Override + public String getName() + { + return "slick_log"; + } + + + @Override + public String[] getDependencies() + { + return new String[] { "log" }; + } +} diff --git a/src/mightypork/gamecore/backends/lwjgl/LwjglBackend.java b/src/mightypork/gamecore/backends/lwjgl/LwjglBackend.java new file mode 100644 index 0000000..d11b58b --- /dev/null +++ b/src/mightypork/gamecore/backends/lwjgl/LwjglBackend.java @@ -0,0 +1,58 @@ +package mightypork.gamecore.backends.lwjgl; + + +import mightypork.gamecore.audio.AudioModule; +import mightypork.gamecore.backends.lwjgl.audio.SlickAudioModule; +import mightypork.gamecore.backends.lwjgl.graphics.LwjglGraphicsModule; +import mightypork.gamecore.core.AppBackend; +import mightypork.gamecore.graphics.GraphicsModule; +import mightypork.gamecore.input.InputModule; + + +/** + * Game backend using LWJGL and SlickUtil + * + * @author MightyPork + */ +public class LwjglBackend extends AppBackend { + + private LwjglGraphicsModule graphics; + private SlickAudioModule audio; + private LwjglInputModule input; + + + @Override + public void initialize() + { + addChildClient(graphics = new LwjglGraphicsModule()); + addChildClient(audio = new SlickAudioModule()); + addChildClient(input = new LwjglInputModule()); + + graphics.init(); + audio.init(); + input.init(); + + app.addInitTask(new InitTaskRedirectSlickLog()); + } + + + @Override + public GraphicsModule getGraphics() + { + return graphics; + } + + + @Override + public AudioModule getAudio() + { + return audio; + } + + + @Override + public InputModule getInput() + { + return input; + } +} diff --git a/src/mightypork/gamecore/backends/lwjgl/LwjglInputModule.java b/src/mightypork/gamecore/backends/lwjgl/LwjglInputModule.java new file mode 100644 index 0000000..698a676 --- /dev/null +++ b/src/mightypork/gamecore/backends/lwjgl/LwjglInputModule.java @@ -0,0 +1,302 @@ +package mightypork.gamecore.backends.lwjgl; + + +import mightypork.gamecore.core.App; +import mightypork.gamecore.input.InputModule; +import mightypork.gamecore.input.Key; +import mightypork.gamecore.input.Keys; +import mightypork.gamecore.input.events.KeyEvent; +import mightypork.gamecore.input.events.MouseButtonEvent; +import mightypork.gamecore.input.events.MouseMotionEvent; +import mightypork.utils.interfaces.Updateable; +import mightypork.utils.math.constraints.vect.Vect; +import mightypork.utils.math.constraints.vect.var.VectVar; + +import org.lwjgl.LWJGLException; +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; +import org.lwjgl.opengl.Display; + + +/** + * Lwjgl Input Module. + * + * @author Ondřej Hruška (MightyPork) + */ +public class LwjglInputModule extends InputModule implements Updateable { + + /** Current mouse position */ + private static final Vect mousePos = new Vect() { + + @Override + public double x() + { + if (!Mouse.isInsideWindow()) return Integer.MIN_VALUE; + + return Mouse.getX(); + } + + + @Override + public double y() + { + if (!Mouse.isInsideWindow()) return Integer.MIN_VALUE; + // flip Y axis + return Display.getHeight() - Mouse.getY(); + } + }; + + + @Override + protected void initDevices() + { + try { + Mouse.create(); + Keyboard.create(); + Keyboard.enableRepeatEvents(false); + } catch (final LWJGLException e) { + throw new RuntimeException("Failed to initialize input devices.", e); + } + } + + + @Override + protected void initKeyCodes() + { + Keys.NONE.setCode(Keyboard.KEY_NONE); + + Keys.NUM_1.setCode(Keyboard.KEY_1); + Keys.NUM_2.setCode(Keyboard.KEY_2); + Keys.NUM_3.setCode(Keyboard.KEY_3); + Keys.NUM_4.setCode(Keyboard.KEY_4); + Keys.NUM_5.setCode(Keyboard.KEY_5); + Keys.NUM_6.setCode(Keyboard.KEY_6); + Keys.NUM_7.setCode(Keyboard.KEY_7); + Keys.NUM_8.setCode(Keyboard.KEY_8); + Keys.NUM_9.setCode(Keyboard.KEY_9); + Keys.NUM_0.setCode(Keyboard.KEY_0); + + Keys.Q.setCode(Keyboard.KEY_Q); + Keys.W.setCode(Keyboard.KEY_W); + Keys.E.setCode(Keyboard.KEY_E); + Keys.R.setCode(Keyboard.KEY_R); + Keys.T.setCode(Keyboard.KEY_T); + Keys.Y.setCode(Keyboard.KEY_Y); + Keys.U.setCode(Keyboard.KEY_U); + Keys.I.setCode(Keyboard.KEY_I); + Keys.O.setCode(Keyboard.KEY_O); + Keys.P.setCode(Keyboard.KEY_P); + Keys.A.setCode(Keyboard.KEY_A); + Keys.S.setCode(Keyboard.KEY_S); + Keys.D.setCode(Keyboard.KEY_D); + Keys.F.setCode(Keyboard.KEY_F); + Keys.G.setCode(Keyboard.KEY_G); + Keys.H.setCode(Keyboard.KEY_H); + Keys.J.setCode(Keyboard.KEY_J); + Keys.K.setCode(Keyboard.KEY_K); + Keys.L.setCode(Keyboard.KEY_L); + Keys.Z.setCode(Keyboard.KEY_Z); + Keys.X.setCode(Keyboard.KEY_X); + Keys.C.setCode(Keyboard.KEY_C); + Keys.V.setCode(Keyboard.KEY_V); + Keys.B.setCode(Keyboard.KEY_B); + Keys.N.setCode(Keyboard.KEY_N); + Keys.M.setCode(Keyboard.KEY_M); + + Keys.MINUS.setCode(Keyboard.KEY_MINUS); + Keys.EQUALS.setCode(Keyboard.KEY_EQUALS); + Keys.SLASH.setCode(Keyboard.KEY_SLASH); + Keys.BACKSLASH.setCode(Keyboard.KEY_BACKSLASH); + Keys.BRACKET_LEFT.setCode(Keyboard.KEY_LBRACKET); + Keys.BRACKET_RIGHT.setCode(Keyboard.KEY_RBRACKET); + Keys.SEMICOLON.setCode(Keyboard.KEY_SEMICOLON); + Keys.APOSTROPHE.setCode(Keyboard.KEY_APOSTROPHE); + Keys.GRAVE.setCode(Keyboard.KEY_GRAVE); + Keys.COMMA.setCode(Keyboard.KEY_COMMA); + Keys.PERIOD.setCode(Keyboard.KEY_PERIOD); + + Keys.SPACE.setCode(Keyboard.KEY_SPACE); + Keys.BACKSPACE.setCode(Keyboard.KEY_BACK); + Keys.TAB.setCode(Keyboard.KEY_TAB); + Keys.ESCAPE.setCode(Keyboard.KEY_ESCAPE); + + Keys.APPS.setCode(Keyboard.KEY_APPS); + Keys.POWER.setCode(Keyboard.KEY_POWER); + Keys.SLEEP.setCode(Keyboard.KEY_SLEEP); + //Keys.MENU.setCode(Keyboard.KEY_MENU); // not defined + + Keys.F1.setCode(Keyboard.KEY_F1); + Keys.F2.setCode(Keyboard.KEY_F2); + Keys.F3.setCode(Keyboard.KEY_F3); + Keys.F4.setCode(Keyboard.KEY_F4); + Keys.F5.setCode(Keyboard.KEY_F5); + Keys.F6.setCode(Keyboard.KEY_F6); + Keys.F7.setCode(Keyboard.KEY_F7); + Keys.F8.setCode(Keyboard.KEY_F8); + Keys.F9.setCode(Keyboard.KEY_F9); + Keys.F10.setCode(Keyboard.KEY_F10); + Keys.F11.setCode(Keyboard.KEY_F11); + Keys.F12.setCode(Keyboard.KEY_F12); + Keys.F13.setCode(Keyboard.KEY_F13); + Keys.F14.setCode(Keyboard.KEY_F14); + Keys.F15.setCode(Keyboard.KEY_F15); + + Keys.CAPS_LOCK.setCode(Keyboard.KEY_CAPITAL); + Keys.SCROLL_LOCK.setCode(Keyboard.KEY_SCROLL); + Keys.NUM_LOCK.setCode(Keyboard.KEY_NUMLOCK); + + Keys.NUMPAD_MINUS.setCode(Keyboard.KEY_SUBTRACT); + Keys.NUMPAD_PLUSS.setCode(Keyboard.KEY_ADD); + Keys.NUMPAD_0.setCode(Keyboard.KEY_NUMPAD0); + Keys.NUMPAD_1.setCode(Keyboard.KEY_NUMPAD1); + Keys.NUMPAD_2.setCode(Keyboard.KEY_NUMPAD2); + Keys.NUMPAD_3.setCode(Keyboard.KEY_NUMPAD3); + Keys.NUMPAD_4.setCode(Keyboard.KEY_NUMPAD4); + Keys.NUMPAD_5.setCode(Keyboard.KEY_NUMPAD5); + Keys.NUMPAD_6.setCode(Keyboard.KEY_NUMPAD6); + Keys.NUMPAD_7.setCode(Keyboard.KEY_NUMPAD7); + Keys.NUMPAD_8.setCode(Keyboard.KEY_NUMPAD8); + Keys.NUMPAD_9.setCode(Keyboard.KEY_NUMPAD9); + Keys.NUMPAD_DECIMAL.setCode(Keyboard.KEY_DECIMAL); + Keys.NUMPAD_ENTER.setCode(Keyboard.KEY_NUMPADENTER); + Keys.NUMPAD_DIVIDE.setCode(Keyboard.KEY_DIVIDE); + Keys.NUMPAD_MULTIPLY.setCode(Keyboard.KEY_MULTIPLY); + + Keys.CONTROL_LEFT.setCode(Keyboard.KEY_LCONTROL); + Keys.CONTROL_RIGHT.setCode(Keyboard.KEY_RCONTROL); + Keys.ALT_LEFT.setCode(Keyboard.KEY_LMENU); + Keys.ALT_RIGHT.setCode(Keyboard.KEY_RMENU); + Keys.SHIFT_LEFT.setCode(Keyboard.KEY_LSHIFT); + Keys.SHIFT_RIGHT.setCode(Keyboard.KEY_RSHIFT); + Keys.META_LEFT.setCode(Keyboard.KEY_LMETA); + Keys.META_RIGHT.setCode(Keyboard.KEY_RMETA); + + Keys.UP.setCode(Keyboard.KEY_UP); + Keys.DOWN.setCode(Keyboard.KEY_DOWN); + Keys.LEFT.setCode(Keyboard.KEY_LEFT); + Keys.RIGHT.setCode(Keyboard.KEY_RIGHT); + + Keys.HOME.setCode(Keyboard.KEY_HOME); + Keys.END.setCode(Keyboard.KEY_END); + + Keys.PAGE_UP.setCode(Keyboard.KEY_PRIOR); + Keys.PAGE_DOWN.setCode(Keyboard.KEY_NEXT); + + Keys.RETURN.setCode(Keyboard.KEY_RETURN); + Keys.PAUSE.setCode(Keyboard.KEY_PAUSE); + Keys.INSERT.setCode(Keyboard.KEY_INSERT); + Keys.DELETE.setCode(Keyboard.KEY_DELETE); + Keys.SYSRQ.setCode(Keyboard.KEY_SYSRQ); + } + + + @Override + public void destroy() + { + Mouse.destroy(); + Keyboard.destroy(); + } + + private final VectVar mouseMove = Vect.makeVar(); + private final VectVar mouseLastPos = Vect.makeVar(); + + + @Override + public synchronized void update(double delta) + { + // was destroyed or not initialized + if (!Display.isCreated()) return; + if (!Mouse.isCreated()) return; + if (!Keyboard.isCreated()) return; + + Display.processMessages(); + + // sum the moves + mouseMove.reset(); + mouseLastPos.reset(); + boolean wasMouse = false; + while (Mouse.next()) { + onMouseEvent(mouseMove, mouseLastPos); + wasMouse = true; + } + + if (wasMouse && !mouseMove.isZero()) { + App.bus().send(new MouseMotionEvent(mouseLastPos, mouseMove)); + } + + while (Keyboard.next()) { + onKeyEvent(); + } + + if (Display.isCloseRequested()) { + App.shutdown(); + } + } + + + private void onMouseEvent(VectVar moveSum, VectVar lastPos) + { + final int button = Mouse.getEventButton(); + final boolean down = Mouse.getEventButtonState(); + + final VectVar pos = Vect.makeVar(Mouse.getEventX(), Mouse.getEventY()); + final VectVar move = Vect.makeVar(Mouse.getEventDX(), Mouse.getEventDY()); + + final int wheeld = Mouse.getEventDWheel(); + + // flip Y axis + pos.setY(Display.getHeight() - pos.y()); + + if (button != -1 || wheeld != 0) { + App.bus().send(new MouseButtonEvent(pos.freeze(), button, down, wheeld)); + } + + moveSum.setTo(moveSum.add(move)); + lastPos.setTo(pos); + } + + + private void onKeyEvent() + { + final int key = Keyboard.getEventKey(); + final boolean down = Keyboard.getEventKeyState(); + final char c = Keyboard.getEventCharacter(); + + App.bus().send(new KeyEvent(key, c, down)); + } + + + @Override + public Vect getMousePos() + { + return mousePos; + } + + + @Override + public boolean isMouseInside() + { + return Mouse.isInsideWindow(); + } + + + @Override + public void grabMouse(boolean grab) + { + Mouse.setGrabbed(grab); + } + + + @Override + public boolean isKeyDown(Key key) + { + return key.isDefined() && Keyboard.isKeyDown(key.getCode()); + } + + + @Override + public boolean isMouseButtonDown(int button) + { + return Mouse.isButtonDown(button); + } +} diff --git a/src/mightypork/gamecore/backends/lwjgl/SlickLogRedirector.java b/src/mightypork/gamecore/backends/lwjgl/SlickLogRedirector.java new file mode 100644 index 0000000..3aec98f --- /dev/null +++ b/src/mightypork/gamecore/backends/lwjgl/SlickLogRedirector.java @@ -0,0 +1,75 @@ +package mightypork.gamecore.backends.lwjgl; + + +import java.util.logging.Level; + +import mightypork.utils.logging.writers.LogWriter; + + +/** + * Used to redirect slick log into main logger. + * + * @author Ondřej Hruška (MightyPork) + */ +class SlickLogRedirector implements org.newdawn.slick.util.LogSystem { + + LogWriter writer; + + + /** + * @param log log to redirect into + */ + public SlickLogRedirector(LogWriter log) { + this.writer = log; + } + + + @Override + public void error(String msg, Throwable e) + { + writer.log(Level.SEVERE, msg, e); + } + + + @Override + public void error(Throwable e) + { + writer.log(Level.SEVERE, null, e); + } + + + @Override + public void error(String msg) + { + writer.log(Level.SEVERE, msg); + } + + + @Override + public void warn(String msg) + { + writer.log(Level.WARNING, msg); + } + + + @Override + public void warn(String msg, Throwable e) + { + writer.log(Level.WARNING, msg, e); + } + + + @Override + public void info(String msg) + { + writer.log(Level.INFO, msg); + } + + + @Override + public void debug(String msg) + { + writer.log(Level.FINEST, msg); + } + +} diff --git a/src/mightypork/gamecore/backends/lwjgl/audio/SlickAudio.java b/src/mightypork/gamecore/backends/lwjgl/audio/SlickAudio.java new file mode 100644 index 0000000..2928712 --- /dev/null +++ b/src/mightypork/gamecore/backends/lwjgl/audio/SlickAudio.java @@ -0,0 +1,146 @@ +package mightypork.gamecore.backends.lwjgl.audio; + + +import java.io.IOException; +import java.io.InputStream; + +import mightypork.gamecore.audio.DeferredAudio; +import mightypork.utils.files.FileUtil; + +import org.lwjgl.openal.AL10; +import org.newdawn.slick.openal.Audio; +import org.newdawn.slick.openal.SoundStore; + + +/** + * SlickUtil-based deferred audio resource. + * + * @author Ondřej Hruška (MightyPork) + */ +public class SlickAudio extends DeferredAudio { + + private double pauseLoopPosition = 0; + private boolean looping = false; + private boolean paused = false; + private double lastPlayPitch = 1; + private double lastPlayGain = 1; + + /** Audio resource */ + private Audio backingAudio = null; + private int sourceID; + + + public SlickAudio(String resourceName) { + super(resourceName); + } + + + @Override + protected void loadResource(String resource) throws IOException + { + final String ext = FileUtil.getExtension(resource); + + try (final InputStream stream = FileUtil.getResource(resource)) { + + if (ext.equalsIgnoreCase("ogg")) { + backingAudio = SoundStore.get().getOgg(resource, stream); + + } else if (ext.equalsIgnoreCase("wav")) { + backingAudio = SoundStore.get().getWAV(resource, stream); + + } else if (ext.equalsIgnoreCase("aif")) { + backingAudio = SoundStore.get().getAIF(resource, stream); + + } else if (ext.equalsIgnoreCase("mod")) { + backingAudio = SoundStore.get().getMOD(resource, stream); + + } else { + throw new RuntimeException("Invalid audio file extension."); + } + } + } + + + @Override + public void pauseLoop() + { + if (!ensureLoaded()) return; + + if (isPlaying() && looping) { + pauseLoopPosition = backingAudio.getPosition(); + stop(); + paused = true; + } + } + + + @Override + public void resumeLoop() + { + if (!ensureLoaded()) return; + + if (looping && paused) { + sourceID = backingAudio.playAsSoundEffect((float) lastPlayPitch, (float) lastPlayGain, true); + backingAudio.setPosition((float) pauseLoopPosition); + paused = false; + } + } + + + @Override + public void adjustGain(double gain) + { + AL10.alSourcef(sourceID, AL10.AL_GAIN, (float) gain); + } + + + @Override + public void stop() + { + if (!isLoaded()) return; + + backingAudio.stop(); + paused = false; + } + + + @Override + public boolean isPlaying() + { + if (!isLoaded()) return false; + + return backingAudio.isPlaying(); + } + + + @Override + public boolean isPaused() + { + if (!isLoaded()) return false; + + return backingAudio.isPaused(); + } + + + @Override + public void play(double pitch, double gain, boolean loop, double x, double y, double z) + { + if (!ensureLoaded()) return; + + this.lastPlayPitch = pitch; + this.lastPlayGain = gain; + looping = loop; + + sourceID = backingAudio.playAsSoundEffect((float) pitch, (float) gain, loop, (float) x, (float) y, (float) z); + } + + + @Override + public void destroy() + { + if (!isLoaded() || backingAudio == null) return; + + backingAudio.release(); + backingAudio = null; + } +} diff --git a/src/mightypork/gamecore/backends/lwjgl/audio/SlickAudioModule.java b/src/mightypork/gamecore/backends/lwjgl/audio/SlickAudioModule.java new file mode 100644 index 0000000..87384ca --- /dev/null +++ b/src/mightypork/gamecore/backends/lwjgl/audio/SlickAudioModule.java @@ -0,0 +1,75 @@ +package mightypork.gamecore.backends.lwjgl.audio; + + +import java.nio.FloatBuffer; + +import mightypork.gamecore.audio.AudioModule; +import mightypork.gamecore.audio.DeferredAudio; +import mightypork.gamecore.backends.lwjgl.BufferHelper; +import mightypork.utils.math.constraints.vect.Vect; +import mightypork.utils.math.constraints.vect.var.VectVar; + +import org.lwjgl.openal.AL; +import org.lwjgl.openal.AL10; +import org.newdawn.slick.openal.SoundStore; + + +/** + * SlickUtil-based audio module + * + * @author Ondřej Hruška (MightyPork) + */ +public class SlickAudioModule extends AudioModule { + + private final VectVar listenerPos = Vect.makeVar(); + + + @Override + public void init() + { + SoundStore.get().setMaxSources(256); + SoundStore.get().init(); + setListenerPos(Vect.ZERO); + } + + + @Override + public void setListenerPos(Vect pos) + { + listenerPos.setTo(pos); + final FloatBuffer buf3 = BufferHelper.alloc(3); + final FloatBuffer buf6 = BufferHelper.alloc(6); + buf3.clear(); + BufferHelper.fill(buf3, (float) pos.x(), (float) pos.y(), (float) pos.z()); + AL10.alListener(AL10.AL_POSITION, buf3); + buf3.clear(); + BufferHelper.fill(buf3, 0, 0, 0); + AL10.alListener(AL10.AL_VELOCITY, buf3); + buf6.clear(); + BufferHelper.fill(buf6, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f); + AL10.alListener(AL10.AL_ORIENTATION, buf6); + } + + + @Override + public Vect getListenerPos() + { + return listenerPos; + } + + + @Override + protected void deinitSoundSystem() + { + SoundStore.get().clear(); + AL.destroy(); + } + + + @Override + protected DeferredAudio doCreateResource(String res) + { + return new SlickAudio(res); + } + +} diff --git a/src/mightypork/gamecore/backends/lwjgl/graphics/AwtScreenshot.java b/src/mightypork/gamecore/backends/lwjgl/graphics/AwtScreenshot.java new file mode 100644 index 0000000..fa11ce7 --- /dev/null +++ b/src/mightypork/gamecore/backends/lwjgl/graphics/AwtScreenshot.java @@ -0,0 +1,84 @@ +package mightypork.gamecore.backends.lwjgl.graphics; + + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; + +import javax.imageio.ImageIO; + +import mightypork.gamecore.graphics.Screenshot; + + +/** + * Screenshot object, can be used to extract image or write to file.
+ * Screenshot, once taken, can be safely processed in separate thread.
+ * Based on {@link BufferedImage} and {@link ImageIO}. + * + * @author Ondřej Hruška (MightyPork) + */ +public class AwtScreenshot implements Screenshot { + + private final int width; + private final int height; + private final int bpp; + private final ByteBuffer bytes; + private BufferedImage image; + + + /** + * @param width image width + * @param height image height + * @param bpp bits per pixel (typically 4) + * @param buffer + */ + public AwtScreenshot(int width, int height, int bpp, ByteBuffer buffer) { + this.width = width; + this.height = height; + this.bpp = bpp; + this.bytes = buffer; + } + + + /** + * Extract to an image.
+ * Subsequent calls will use a cached value. + * + * @return image + */ + public BufferedImage getImage() + { + if (image != null) return image; + + image = new BufferedImage(this.width, this.height, BufferedImage.TYPE_INT_RGB); + + // convert to a buffered image + for (int x = 0; x < this.width; x++) { + for (int y = 0; y < this.height; y++) { + final int i = (x + (this.width * y)) * this.bpp; + final int r = this.bytes.get(i) & 0xFF; + final int g = this.bytes.get(i + 1) & 0xFF; + final int b = this.bytes.get(i + 2) & 0xFF; + image.setRGB(x, this.height - (y + 1), (0xFF << 24) | (r << 16) | (g << 8) | b); + } + } + + return image; + } + + + /** + * Save to a file.
+ * Cached value is used if any. + * + * @param file target file + * @throws IOException on error writing to file + */ + @Override + public void save(File file) throws IOException + { + file.getParentFile().mkdirs(); + ImageIO.write(getImage(), "PNG", file); + } +} diff --git a/src/mightypork/gamecore/backends/lwjgl/graphics/LwjglGraphicsModule.java b/src/mightypork/gamecore/backends/lwjgl/graphics/LwjglGraphicsModule.java new file mode 100644 index 0000000..59ff484 --- /dev/null +++ b/src/mightypork/gamecore/backends/lwjgl/graphics/LwjglGraphicsModule.java @@ -0,0 +1,620 @@ +package mightypork.gamecore.backends.lwjgl.graphics; + + +import static org.lwjgl.opengl.GL11.*; + +import java.nio.ByteBuffer; +import java.util.Stack; + +import mightypork.gamecore.core.App; +import mightypork.gamecore.graphics.GraphicsModule; +import mightypork.gamecore.graphics.Screenshot; +import mightypork.gamecore.graphics.textures.DeferredTexture; +import mightypork.gamecore.graphics.textures.TxQuad; +import mightypork.gamecore.gui.events.ViewportChangeEvent; +import mightypork.utils.logging.Log; +import mightypork.utils.math.color.Color; +import mightypork.utils.math.color.Grad; +import mightypork.utils.math.color.pal.RGB; +import mightypork.utils.math.constraints.rect.Rect; +import mightypork.utils.math.constraints.rect.caching.RectDigest; +import mightypork.utils.math.constraints.vect.Vect; +import mightypork.utils.math.timing.FpsMeter; + +import org.lwjgl.BufferUtils; +import org.lwjgl.LWJGLException; +import org.lwjgl.opengl.Display; +import org.lwjgl.opengl.DisplayMode; +import org.lwjgl.opengl.GL11; + + +/** + * LWJGL rendering module + * + * @author MightyPork + */ +public class LwjglGraphicsModule extends GraphicsModule { + + /** Currently binded color */ + private Color activeColor = null; + /** Currently binded color's alpha multiplier */ + private double activeColorAlpha = 1; + /** Stack of pushed colors */ + private Stack colorPushStack = new Stack<>(); + /** Currently binded texture */ + private SlickTexture activeTexture; + + /** Display mode used currently for the window */ + private DisplayMode windowDisplayMode; + /** FPS the user wants */ + private int targetFps; + /** FPS meter used for measuring actual FPS */ + private FpsMeter fpsMeter = new FpsMeter(); + + /** Flag that at the end of frame, fullscreen should be toggled. */ + private boolean fullscreenToggleRequested; + /** Flag that at the end of frame, fullscreen should be set. */ + private boolean fullscreenSetRequested; + /** State to which fullscreen should be set. */ + private boolean fullscreenSetState; + + /** Current screen size */ + private static final Vect screenSize = new Vect() { + + @Override + public double y() + { + return Display.getHeight(); + } + + + @Override + public double x() + { + return Display.getWidth(); + } + }; + + /** Current screen rectangle */ + private static final Rect rect = Rect.make(screenSize); + + + @Override + public void init() + { + try { + Display.create(); + } catch (final Exception e) { + throw new RuntimeException("Could not initialize display.", e); + } + } + + + @Override + public void setColor(Color color) + { + setColor(color, 1); + } + + + @Override + public void setColor(Color color, double alpha) + { + if (color == null) color = RGB.WHITE; + + // color components can change over time - must use equals() + if (activeColorAlpha == alpha && color.equals(activeColor)) return; + + activeColor = color; + activeColorAlpha = alpha; + GL11.glColor4d(color.r(), color.g(), color.b(), alpha * color.a()); + } + + + @Override + public void translate(double x, double y) + { + glTranslated(x, y, 0); + } + + + @Override + public void translate(double x, double y, double z) + { + glTranslated(x, y, z); + } + + + @Override + public void translate(Vect offset) + { + glTranslated(offset.x(), offset.y(), offset.z()); + } + + + @Override + public void translateXY(Vect offset) + { + glTranslated(offset.x(), offset.y(), 0); + } + + + @Override + public void scale(double x, double y) + { + glScaled(x, y, 0); + } + + + @Override + public void scale(double x, double y, double z) + { + glScaled(x, y, z); + } + + + @Override + public void scale(Vect scale) + { + glScaled(scale.x(), scale.y(), scale.z()); + } + + + @Override + public void scaleXY(double scale) + { + glScaled(scale, scale, 1); + } + + + @Override + public void scaleX(double scale) + { + glScaled(scale, 1, 1); + } + + + @Override + public void scaleY(double scale) + { + glScaled(1, scale, 1); + } + + + @Override + public void scaleZ(double scale) + { + glScaled(1, 1, scale); + } + + + @Override + public void rotateX(double angle) + { + rotate(angle, AXIS_X); + } + + + @Override + public void rotateY(double angle) + { + rotate(angle, AXIS_Y); + } + + + @Override + public void rotateZ(double angle) + { + rotate(angle, AXIS_Z); + } + + + @Override + public void rotate(double angle, Vect axis) + { + final Vect vec = axis.norm(1); + glRotated(angle, vec.x(), vec.y(), vec.z()); + } + + + @Override + public void pushState() + { + GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); + GL11.glPushClientAttrib(GL11.GL_ALL_CLIENT_ATTRIB_BITS); + GL11.glPushMatrix(); + } + + + @Override + public void popState() + { + GL11.glPopMatrix(); + GL11.glPopClientAttrib(); + GL11.glPopAttrib(); + } + + + @Override + public void pushGeometry() + { + GL11.glPushMatrix(); + } + + + @Override + public void popGeometry() + { + GL11.glPopMatrix(); + } + + + @Override + public void pushColor() + { + colorPushStack.push(activeColor); + } + + + @Override + public void popColor() + { + setColor(colorPushStack.pop()); + } + + + @Override + public void quad(Rect rect) + { + final RectDigest q = rect.digest(); + + // disable texture + if (activeTexture != null) { + activeTexture = null; + glDisable(GL_TEXTURE_2D); + } + + // quad + glBegin(GL_QUADS); + glVertex2d(q.left, q.bottom); + glVertex2d(q.right, q.bottom); + glVertex2d(q.right, q.top); + glVertex2d(q.left, q.top); + glEnd(); + } + + + @Override + public void quad(Rect rect, Color color) + { + setColor(color); + quad(rect); + } + + + @Override + public void quad(Rect rect, Grad grad) + { + final RectDigest r = rect.digest(); + + // disable texture + if (activeTexture != null) { + activeTexture = null; + glDisable(GL_TEXTURE_2D); + } + + // quad + glBegin(GL_QUADS); + setColor(grad.leftBottom); + glVertex2d(r.left, r.bottom); + + setColor(grad.rightBottom); + glVertex2d(r.right, r.bottom); + + setColor(grad.rightTop); + glVertex2d(r.right, r.top); + + setColor(grad.leftTop); + glVertex2d(r.left, r.top); + glEnd(); + } + + + @Override + public void quad(Rect rect, TxQuad txquad) + { + quad(rect, txquad, RGB.WHITE); + } + + + @Override + public void quad(Rect rect, TxQuad txquad, Color color) + { + // texture is loaded uniquely -> can compare with == + if (activeTexture != txquad.tx) { + glEnable(GL_TEXTURE_2D); + activeTexture = (SlickTexture) txquad.tx; + activeTexture.bind(); + } + + glBegin(GL_QUADS); + setColor(color); + + final RectDigest q = rect.digest(); + final RectDigest u = txquad.uvs.digest(); + + final double offs = 0.0001;// hack to avoid white stitching + + double tL = u.left + offs, tR = u.right - offs, tT = u.top + offs, tB = u.bottom - offs; + + // handle flip + if (txquad.isFlippedY()) { + final double swap = tT; + tT = tB; + tB = swap; + } + + if (txquad.isFlippedX()) { + final double swap = tL; + tL = tR; + tR = swap; + } + + final double w = activeTexture.getWidth01(); + final double h = activeTexture.getHeight01(); + + // quad with texture + glTexCoord2d(tL * w, tB * h); + glVertex2d(q.left, q.bottom); + + glTexCoord2d(tR * w, tB * h); + glVertex2d(q.right, q.bottom); + + glTexCoord2d(tR * w, tT * h); + glVertex2d(q.right, q.top); + + glTexCoord2d(tL * w, tT * h); + glVertex2d(q.left, q.top); + + glEnd(); + } + + + @Override + public void setupProjection() + { + // fix projection for changed size + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + int w = Display.getWidth(); + int h = Display.getHeight(); + + glViewport(0, 0, w, h); + glOrtho(0, w, h, 0, -1000, 1000); + + // back to modelview + glMatrixMode(GL_MODELVIEW); + + glLoadIdentity(); + + glDisable(GL_LIGHTING); + + glClearDepth(1f); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + + glEnable(GL_NORMALIZE); + + glShadeModel(GL_SMOOTH); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + + @Override + public DeferredTexture getLazyTexture(String path) + { + return new SlickTexture(path); + } + + + @Override + public void destroy() + { + Display.destroy(); + } + + + @Override + public void setTargetFps(int fps) + { + this.targetFps = fps; + } + + + @Override + public void setFullscreen(boolean fs) + { + fullscreenSetRequested = true; + fullscreenSetState = fs; + } + + + @Override + public void switchFullscreen() + { + fullscreenToggleRequested = true; + } + + + @Override + public boolean isFullscreen() + { + return Display.isFullscreen(); + } + + + private void doToggleFullscreen() + { + doSetFullscreen(!Display.isFullscreen()); + } + + + private void doSetFullscreen(boolean fs) + { + try { + + if (Display.isFullscreen() == fs) return; // no work + + if (fs) { + Log.f3("Entering fullscreen."); + // save window resize + windowDisplayMode = new DisplayMode(Display.getWidth(), Display.getHeight()); + + Display.setDisplayMode(Display.getDesktopDisplayMode()); + Display.setFullscreen(true); + Display.update(); + } else { + Log.f3("Leaving fullscreen."); + Display.setDisplayMode(windowDisplayMode); + Display.update(); + } + + App.bus().send(new ViewportChangeEvent(getSize())); + + } catch (final Throwable t) { + Log.e("Failed to change fullscreen mode.", t); + try { + Display.setDisplayMode(windowDisplayMode); + Display.update(); + } catch (final Throwable t1) { + throw new RuntimeException("Failed to revert failed fullscreen toggle.", t1); + } + } + } + + + @Override + public Screenshot takeScreenshot() + { + glReadBuffer(GL_FRONT); + final int width = Display.getWidth(); + final int height = Display.getHeight(); + final int bpp = 4; + final ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * bpp); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + + final AwtScreenshot sc = new AwtScreenshot(width, height, bpp, buffer); + + return sc; + } + + + @Override + public void beginFrame() + { + // handle resize + if (Display.wasResized()) { + App.bus().send(new ViewportChangeEvent(getSize())); + } + + if (fullscreenToggleRequested) { + fullscreenToggleRequested = false; + doToggleFullscreen(); + } + + if (fullscreenSetRequested) { + fullscreenSetRequested = false; + doSetFullscreen(fullscreenSetState); + } + + glLoadIdentity(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + fpsMeter.frame(); + } + + + @Override + public void endFrame() + { + Display.update(false); // don't poll input devices + Display.sync(targetFps); + } + + + @Override + public void setSize(int width, int height) + { + try { + Display.setDisplayMode(windowDisplayMode = new DisplayMode(width, height)); + } catch (LWJGLException e) { + throw new RuntimeException(e); + } + } + + + @Override + public void setTitle(String title) + { + Display.setTitle(title); + } + + + @Override + public void setVSync(boolean vsync) + { + Display.setVSyncEnabled(vsync); + } + + + @Override + public void setResizable(boolean resizable) + { + Display.setResizable(resizable); + } + + + @Override + public Rect getRect() + { + return rect; + } + + + @Override + public long getFps() + { + return fpsMeter.getFPS(); + } + + + @Override + public Vect getCenter() + { + return rect.center(); + } + + + @Override + public int getWidth() + { + return Display.getWidth(); + } + + + @Override + public int getHeight() + { + return Display.getHeight(); + } + + + @Override + public Vect getSize() + { + return screenSize; + } + +} diff --git a/src/mightypork/gamecore/backends/lwjgl/graphics/SlickTexture.java b/src/mightypork/gamecore/backends/lwjgl/graphics/SlickTexture.java new file mode 100644 index 0000000..2498c4b --- /dev/null +++ b/src/mightypork/gamecore/backends/lwjgl/graphics/SlickTexture.java @@ -0,0 +1,194 @@ +package mightypork.gamecore.backends.lwjgl.graphics; + + +import java.io.IOException; + +import mightypork.gamecore.graphics.textures.DeferredTexture; +import mightypork.gamecore.resources.loading.MustLoadInRenderingContext; +import mightypork.utils.annotations.Alias; +import mightypork.utils.exceptions.IllegalValueException; +import mightypork.utils.files.FileUtil; +import mightypork.utils.logging.Log; + +import org.lwjgl.opengl.GL11; +import org.newdawn.slick.opengl.Texture; +import org.newdawn.slick.opengl.TextureLoader; + + +/** + * Deferred texture + * + * @author Ondřej Hruška (MightyPork) + */ +@Alias(name = "Texture") +@MustLoadInRenderingContext +public class SlickTexture extends DeferredTexture { + + private org.newdawn.slick.opengl.Texture backingTexture; + private boolean alpha; + private boolean alphal; + + + /** + * @param resourcePath resource path + */ + public SlickTexture(String resourcePath) { + super(resourcePath); + } + + + @Override + protected synchronized void loadResource(String path) + { + try { + final String ext = FileUtil.getExtension(path).toUpperCase(); + + final int filtering; + switch (filter) { + case NEAREST: + filtering = GL11.GL_NEAREST; + break; + case LINEAR: + filtering = GL11.GL_LINEAR; + break; + default: + throw new IllegalValueException("Unsupported filtering mode."); + } + + final Texture texture = TextureLoader.getTexture(ext, FileUtil.getResource(path), false, filtering); + + if (texture == null) { + Log.w("Texture " + path + " could not be loaded."); + } + + backingTexture = texture; + + } catch (final IOException e) { + Log.e("Loading of texture " + path + " failed.", e); + throw new RuntimeException("Could not load texture " + path + ".", e); + } + } + + + @Override + public boolean hasAlpha() + { + if (!ensureLoaded()) return false; + + if (!alphal) { + alphal = true; + alpha = backingTexture.hasAlpha(); + } + + return alpha; + } + + + /** + * Bind to GL context, applying the filters prescribed. + */ + public void bind() + { + if (!ensureLoaded()) return; + + //GL11.glEnable(GL11.GL_TEXTURE_2D); + + GL11.glBindTexture(GL11.GL_TEXTURE_2D, getTextureID()); + + GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE); + + final int wrapping; + switch (wrap) { + case CLAMP: + wrapping = GL11.GL_CLAMP; + break; + case REPEAT: + wrapping = GL11.GL_REPEAT; + break; + default: + throw new IllegalValueException("Unsupported wrapping mode."); + } + + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, wrapping); + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, wrapping); + + final int filtering; + switch (filter) { + case NEAREST: + filtering = GL11.GL_NEAREST; + break; + case LINEAR: + filtering = GL11.GL_LINEAR; + break; + default: + throw new IllegalValueException("Unsupported filtering mode."); + } + + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, filtering); + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, filtering); + } + + + @Override + public int getImageHeight() + { + if (!ensureLoaded()) return 0; + + return backingTexture.getImageHeight(); + } + + + @Override + public int getImageWidth() + { + if (!ensureLoaded()) return 0; + + return backingTexture.getImageWidth(); + } + + + @Override + public void destroy() + { + if (!isLoaded()) return; + + backingTexture.release(); + } + + + /** + * Get the height of the texture, 0..1.
+ * + * @return height 0..1 + */ + public float getHeight01() + { + if (!ensureLoaded()) return 0; + + return backingTexture.getHeight(); + } + + + /** + * Get the width of the texture, 0..1.
+ * + * @return width 0..1 + */ + public float getWidth01() + { + if (!ensureLoaded()) return 0; + + return backingTexture.getWidth(); + } + + + /** + * @return OpenGL texture ID + */ + public int getTextureID() + { + if (!ensureLoaded()) return -1; + + return backingTexture.getTextureID(); + } +} diff --git a/src/mightypork/gamecore/backends/lwjgl/graphics/font/DeferredLwjglFont.java b/src/mightypork/gamecore/backends/lwjgl/graphics/font/DeferredLwjglFont.java new file mode 100644 index 0000000..127b203 --- /dev/null +++ b/src/mightypork/gamecore/backends/lwjgl/graphics/font/DeferredLwjglFont.java @@ -0,0 +1,143 @@ +package mightypork.gamecore.backends.lwjgl.graphics.font; + + +import java.awt.Font; +import java.awt.FontFormatException; +import java.io.IOException; +import java.io.InputStream; + +import mightypork.gamecore.graphics.fonts.DeferredFont; +import mightypork.gamecore.graphics.fonts.IFont; +import mightypork.gamecore.resources.loading.MustLoadInRenderingContext; +import mightypork.utils.annotations.Alias; +import mightypork.utils.files.FileUtil; +import mightypork.utils.math.color.Color; +import mightypork.utils.math.constraints.vect.Vect; + + +/** + * Font obtained from a resource file (TTF). + * + * @author Ondřej Hruška (MightyPork) + */ +@MustLoadInRenderingContext +@Alias(name = "Font") +public class DeferredLwjglFont extends DeferredFont { + + private IFont font = null; + + + /** + * A font from resource + * + * @param resourcePath resource to load + * @param chars chars to load; null to load basic chars only + * @param size size (px) + */ + public DeferredLwjglFont(String resourcePath, String chars, double size) { + super(resourcePath); + this.size = size; + this.chars = chars; + } + + + @Override + protected synchronized final void loadResource(String path) throws IOException + { + final Font awtFont = getAwtFont(path, (float) size, style.numval); + + font = new LwjglTextureBackedFont(awtFont, antialias, filter, chars); + font.setDiscardRatio(discardTop, discardBottom); + } + + + /** + * Get a font for a resource path / name + * + * @param resource resource to load + * @param size font size (pt) + * @param style font style + * @return the {@link Font} + * @throws IOException + */ + protected Font getAwtFont(String resource, float size, int style) throws IOException + { + try (InputStream in = FileUtil.getResource(resource)) { + + Font awtFont = Font.createFont(Font.TRUETYPE_FONT, in); + + awtFont = awtFont.deriveFont(size); + awtFont = awtFont.deriveFont(style); + + return awtFont; + } catch (final FontFormatException e) { + throw new IOException("Could not load font, bad format.", e); + } + } + + + /** + * Draw string + * + * @param str string to draw + * @param color draw color + */ + @Override + public void draw(String str, Color color) + { + if (!ensureLoaded()) return; + + font.draw(str, color); + } + + + /** + * Get size needed to render give string + * + * @param text string to check + * @return coord (width, height) + */ + @Override + public Vect getNeededSpace(String text) + { + if (!ensureLoaded()) return Vect.ZERO; + + return font.getNeededSpace(text); + } + + + /** + * @return font height + */ + @Override + public int getLineHeight() + { + if (!ensureLoaded()) return 0; + + return font.getLineHeight(); + } + + + @Override + public int getFontSize() + { + if (!ensureLoaded()) return 0; + + return font.getFontSize(); + } + + + @Override + public int getWidth(String text) + { + return font.getWidth(text); + } + + + @Override + public void destroy() + { + // this will have to suffice + font = null; + } +} diff --git a/src/mightypork/gamecore/backends/lwjgl/graphics/font/DeferredLwjglFontFromSystem.java b/src/mightypork/gamecore/backends/lwjgl/graphics/font/DeferredLwjglFontFromSystem.java new file mode 100644 index 0000000..841b15b --- /dev/null +++ b/src/mightypork/gamecore/backends/lwjgl/graphics/font/DeferredLwjglFontFromSystem.java @@ -0,0 +1,36 @@ +package mightypork.gamecore.backends.lwjgl.graphics.font; + + +import java.awt.Font; +import java.io.IOException; + +import mightypork.utils.annotations.Alias; + + +/** + * Font obtained from the OS + * + * @author Ondřej Hruška (MightyPork) + */ +@Alias(name = "FontNative") +public class DeferredLwjglFontFromSystem extends DeferredLwjglFont { + + /** + * A font from OS, found by name + * + * @param fontName font family name + * @param chars chars to load; null to load basic chars only + * @param size size (pt) + */ + public DeferredLwjglFontFromSystem(String fontName, String chars, double size) { + super(fontName, chars, size); + } + + + @Override + protected Font getAwtFont(String resource, float size, int style) throws IOException + { + return new Font(resource, style, (int) size); + } + +} diff --git a/src/mightypork/gamecore/backends/lwjgl/graphics/font/LwjglTextureBackedFont.java b/src/mightypork/gamecore/backends/lwjgl/graphics/font/LwjglTextureBackedFont.java new file mode 100644 index 0000000..6042349 --- /dev/null +++ b/src/mightypork/gamecore/backends/lwjgl/graphics/font/LwjglTextureBackedFont.java @@ -0,0 +1,494 @@ +package mightypork.gamecore.backends.lwjgl.graphics.font; + + +import static org.lwjgl.opengl.GL11.*; + +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.DataBufferInt; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import mightypork.gamecore.graphics.fonts.IFont; +import mightypork.gamecore.graphics.textures.FilterMode; +import mightypork.utils.exceptions.IllegalValueException; +import mightypork.utils.logging.Log; +import mightypork.utils.math.color.Color; +import mightypork.utils.math.constraints.vect.Vect; +import mightypork.utils.math.constraints.vect.VectConst; + +import org.lwjgl.BufferUtils; +import org.lwjgl.util.glu.GLU; +import org.newdawn.slick.opengl.GLUtils; + + +/** + * A TrueType font renderer with backing texture. + * + * @author James Chambers (Jimmy) + * @author Jeremy Adams (elias4444) + * @author Kevin Glass (kevglass) + * @author Peter Korzuszek (genail) + * @author David Aaron Muhar (bobjob) + * @author Ondřej Hruška (MightyPork) + */ +public class LwjglTextureBackedFont implements IFont { + + private class CharTile { + + public int width; + public int height; + public int texPosX; + public int texPosY; + } + + /* char bank */ + private final Map chars = new HashMap<>(255); + + /* use antialiasing for rendering */ + private final boolean antiAlias; + + /* loaded font size (requested) */ + private final int fontSize; + + /* actual height of drawn glyphs */ + private int fontHeight; + + /* texture id */ + private int textureID; + + /* texture width */ + private int textureWidth; + + /* texture height */ + private int textureHeight; + + /* AWT font source */ + private final java.awt.Font font; + + private final FilterMode filter; + + private double discardTop; + + private double discardBottom; + + + /** + * Make a font + * + * @param font original awt font to load + * @param antialias use antialiasing when rendering to cache texture + * @param filter used Gl filter + * @param chars chars to load + */ + public LwjglTextureBackedFont(java.awt.Font font, boolean antialias, FilterMode filter, String chars) { + this(font, antialias, filter, (" " + chars).toCharArray()); + } + + + /** + * Make a font + * + * @param font original awt font to load + * @param antialias use antialiasing when rendering to cache texture + * @param filter used Gl filter + * @param chars chars to load + */ + public LwjglTextureBackedFont(java.awt.Font font, boolean antialias, FilterMode filter, char[] chars) { + GLUtils.checkGLContext(); + + this.font = font; + this.filter = filter; + this.fontSize = font.getSize(); + this.antiAlias = antialias; + + createSet(chars); + } + + + /** + * Create a BufferedImage of the given character + * + * @param ch the character + * @return BufferedImage containing the drawn character + */ + private BufferedImage getFontImage(char ch) + { + FontMetrics metrics; + BufferedImage img; + Graphics2D g; + + // Create a temporary image to extract the character's size + img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); + + g = (Graphics2D) img.getGraphics(); + if (antiAlias == true) g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setFont(font); + + metrics = g.getFontMetrics(); + + final int charwidth = Math.max(1, metrics.charWidth(ch)); + final int charheight = Math.max(fontSize, metrics.getHeight()); + + // Create another image holding the character we are creating + final BufferedImage fontImage = new BufferedImage(charwidth, charheight, BufferedImage.TYPE_INT_ARGB); + + g = (Graphics2D) fontImage.getGraphics(); + if (antiAlias == true) g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setFont(font); + g.setColor(java.awt.Color.WHITE); + g.drawString(String.valueOf(ch), 0, metrics.getAscent()); + + return fontImage; + } + + + private void createSet(char[] charsToLoad) + { + try { + class LoadedGlyph { + + public char c; + public BufferedImage image; + public int width; + public int height; + + + public LoadedGlyph(char c, BufferedImage image) { + this.image = image; + this.c = c; + this.width = image.getWidth(); + this.height = image.getHeight(); + } + } + + final List glyphs = new ArrayList<>(); + final List loaded = new ArrayList<>(); + for (final char ch : charsToLoad) { + if (!loaded.contains(ch)) { + glyphs.add(new LoadedGlyph(ch, getFontImage(ch))); + loaded.add(ch); + } + } + + int lineHeight = 0; + + int beginX = 0, beginY = 0; + int canvasW = 128, canvasH = 128; + + boolean needsLarger = false; + + // find smallest 2^x size for texture + while (true) { + needsLarger = false; + + for (final LoadedGlyph glyph : glyphs) { + if (beginX + glyph.width > canvasW) { + beginY += lineHeight; + lineHeight = 0; + beginX = 0; + } + + if (lineHeight < glyph.height) { + lineHeight = glyph.height; + } + + if (beginY + lineHeight > canvasH) { + needsLarger = true; + break; + } + + // draw. + beginX += glyph.width; + } + + if (needsLarger) { + canvasW *= 2; + canvasH *= 2; + beginX = 0; + beginY = 0; + lineHeight = 0; + } else { + Log.f3(String.format("Generating font texture: %d×%d", canvasW, canvasH)); + break; + } + } + + textureWidth = canvasW; + textureHeight = canvasH; + + BufferedImage imag = new BufferedImage(textureWidth, textureHeight, BufferedImage.TYPE_INT_ARGB); + final Graphics2D g = (Graphics2D) imag.getGraphics(); + + g.setColor(new java.awt.Color(0, 0, 0, 1)); + g.fillRect(0, 0, textureWidth, textureHeight); + + int rowHeight = 0, posX = 0, posY = 0; + + for (final LoadedGlyph glyph : glyphs) { + final CharTile cht = new CharTile(); + + cht.width = glyph.width; + cht.height = glyph.height; + + if (posX + cht.width >= textureWidth) { + posX = 0; + posY += rowHeight; + rowHeight = 0; + } + + cht.texPosX = posX; + cht.texPosY = posY; + + if (cht.height > fontHeight) { + fontHeight = cht.height; + } + + if (cht.height > rowHeight) { + rowHeight = cht.height; + } + + // Draw it here + g.drawImage(glyph.image, posX, posY, null); + + posX += cht.width; + + chars.put(glyph.c, cht); + } + + textureID = loadImage(imag); + + imag = null; + + } catch (final Throwable t) { + Log.e("Failed to load font.", t); + } + } + + + private int loadImage(BufferedImage bufferedImage) + { + try { + final short width = (short) bufferedImage.getWidth(); + final short height = (short) bufferedImage.getHeight(); + final int bpp = (byte) bufferedImage.getColorModel().getPixelSize(); + + ByteBuffer byteBuffer; + final DataBuffer db = bufferedImage.getData().getDataBuffer(); + if (db instanceof DataBufferInt) { + final int intI[] = ((DataBufferInt) (bufferedImage.getData().getDataBuffer())).getData(); + final byte newI[] = new byte[intI.length * 4]; + for (int i = 0; i < intI.length; i++) { + final byte b[] = intToByteArray(intI[i]); + final int newIndex = i * 4; + + newI[newIndex] = b[1]; + newI[newIndex + 1] = b[2]; + newI[newIndex + 2] = b[3]; + newI[newIndex + 3] = b[0]; + } + + byteBuffer = ByteBuffer.allocateDirect(width * height * (bpp / 8)).order(ByteOrder.nativeOrder()).put(newI); + } else { + byteBuffer = ByteBuffer.allocateDirect(width * height * (bpp / 8)).order(ByteOrder.nativeOrder()).put(((DataBufferByte) (bufferedImage.getData().getDataBuffer())).getData()); + } + + byteBuffer.flip(); + + final int internalFormat = GL_RGBA8, format = GL_RGBA; + final IntBuffer textureId = BufferUtils.createIntBuffer(1); + + glGenTextures(textureId); + glBindTexture(GL_TEXTURE_2D, textureId.get(0)); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + + final int filtering; + switch (filter) { + case NEAREST: + filtering = GL_NEAREST; + break; + case LINEAR: + filtering = GL_LINEAR; + break; + default: + throw new IllegalValueException("Unsupported filtering mode."); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); + + GLU.gluBuild2DMipmaps(GL_TEXTURE_2D, internalFormat, width, height, format, GL_UNSIGNED_BYTE, byteBuffer); + return textureId.get(0); + + } catch (final Throwable t) { + Log.e("Failed to load font.", t); + } + + return -1; + } + + + private static byte[] intToByteArray(int value) + { + return new byte[] { (byte) (value >>> 24), (byte) (value >>> 16), (byte) (value >>> 8), (byte) value }; + } + + + /** + * Get size needed to draw given string + * + * @param text drawn text + * @return needed width + */ + @Override + public int getWidth(String text) + { + int totalwidth = 0; + CharTile ch = null; + for (int i = 0; i < text.length(); i++) { + + ch = chars.get(text.charAt(i)); + + if (ch != null) totalwidth += ch.width; + } + return totalwidth; + } + + + @Override + public int getLineHeight() + { + return (int) Math.round(fontHeight * ((1 - discardTop) - discardBottom)); + } + + + @Override + public int getFontSize() + { + return fontSize; + } + + + @Override + public void draw(String text, Color color) + { + GLUtils.checkGLContext(); + + // PUSH + glPushAttrib(GL_ENABLE_BIT); + + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, textureID); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + + final int filtering; + switch (filter) { + case NEAREST: + filtering = GL_NEAREST; + break; + case LINEAR: + filtering = GL_LINEAR; + break; + default: + throw new IllegalValueException("Unsupported filtering mode."); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); + + glColor4d(color.r(), color.g(), color.b(), color.a()); + + glBegin(GL_QUADS); + + CharTile chtx = null; + char charCurrent; + int minx = 0; + + for (int i = 0; i < text.length(); i++) { + charCurrent = text.charAt(i); + + chtx = chars.get(charCurrent); + + if (chtx != null) { + + // draw quad + + final float txmin = chtx.texPosX; + final float tymin = chtx.texPosY; + final float draw_width = minx + chtx.width - minx; + final float draw_height = chtx.height; + final float drawy0 = (float) (0f - draw_height * discardTop); + + final float txmin01 = txmin / textureWidth; + final float tymin01 = tymin / textureHeight; + final float twidth01 = ((chtx.texPosX + chtx.width - txmin) / textureWidth); + final float theight01 = ((chtx.texPosY + chtx.height - tymin) / textureHeight); + + glTexCoord2f(txmin01, tymin01); + glVertex2f(minx, drawy0); + + glTexCoord2f(txmin01, tymin01 + theight01); + glVertex2f(minx, drawy0 + draw_height); + + glTexCoord2f(txmin01 + twidth01, tymin01 + theight01); + glVertex2f(minx + draw_width, drawy0 + draw_height); + + glTexCoord2f(txmin01 + twidth01, tymin01); + glVertex2f(minx + draw_width, drawy0); + minx += chtx.width; + } + } + + glEnd(); + + // POP + glPopAttrib(); + } + + + @Override + public VectConst getNeededSpace(String text) + { + return Vect.make(getWidth(text), getLineHeight()); + } + + + @Override + public void setDiscardRatio(double top, double bottom) + { + discardTop = top; + discardBottom = bottom; + } + + + @Override + public double getTopDiscardRatio() + { + return discardTop; + } + + + @Override + public double getBottomDiscardRatio() + { + return discardBottom; + } +}