Source import

master
Ondřej Hruška 11 years ago
parent 6892f5006b
commit 22e5a8a12e
  1. 13
      .classpath
  2. 17
      .project
  3. 11
      .settings/org.eclipse.jdt.core.prefs
  4. BIN
      bin/mightypork/gamecore/backends/lwjgl/BufferHelper.class
  5. BIN
      bin/mightypork/gamecore/backends/lwjgl/InitTaskRedirectSlickLog.class
  6. BIN
      bin/mightypork/gamecore/backends/lwjgl/LwjglBackend.class
  7. BIN
      bin/mightypork/gamecore/backends/lwjgl/LwjglInputModule$1.class
  8. BIN
      bin/mightypork/gamecore/backends/lwjgl/LwjglInputModule.class
  9. BIN
      bin/mightypork/gamecore/backends/lwjgl/SlickLogRedirector.class
  10. BIN
      bin/mightypork/gamecore/backends/lwjgl/audio/SlickAudio.class
  11. BIN
      bin/mightypork/gamecore/backends/lwjgl/audio/SlickAudioModule.class
  12. BIN
      bin/mightypork/gamecore/backends/lwjgl/graphics/AwtScreenshot.class
  13. BIN
      bin/mightypork/gamecore/backends/lwjgl/graphics/LwjglGraphicsModule$1.class
  14. BIN
      bin/mightypork/gamecore/backends/lwjgl/graphics/LwjglGraphicsModule.class
  15. BIN
      bin/mightypork/gamecore/backends/lwjgl/graphics/SlickTexture.class
  16. BIN
      bin/mightypork/gamecore/backends/lwjgl/graphics/font/DeferredLwjglFont.class
  17. BIN
      bin/mightypork/gamecore/backends/lwjgl/graphics/font/DeferredLwjglFontFromSystem.class
  18. BIN
      bin/mightypork/gamecore/backends/lwjgl/graphics/font/LwjglTextureBackedFont$1LoadedGlyph.class
  19. BIN
      bin/mightypork/gamecore/backends/lwjgl/graphics/font/LwjglTextureBackedFont$CharTile.class
  20. BIN
      bin/mightypork/gamecore/backends/lwjgl/graphics/font/LwjglTextureBackedFont.class
  21. BIN
      lib/OpenAL32.dll
  22. BIN
      lib/OpenAL64.dll
  23. BIN
      lib/jinput-dx8.dll
  24. BIN
      lib/jinput-dx8_64.dll
  25. BIN
      lib/jinput-raw.dll
  26. BIN
      lib/jinput-raw_64.dll
  27. BIN
      lib/jinput.jar
  28. BIN
      lib/jogg-0.0.7.jar
  29. BIN
      lib/jorbis-0.0.15.jar
  30. BIN
      lib/libjinput-linux.so
  31. BIN
      lib/libjinput-linux64.so
  32. BIN
      lib/libjinput-osx.jnilib
  33. BIN
      lib/liblwjgl.jnilib
  34. BIN
      lib/liblwjgl.so
  35. BIN
      lib/liblwjgl64.so
  36. BIN
      lib/libopenal.so
  37. BIN
      lib/libopenal64.so
  38. BIN
      lib/lwjgl-source-2.8.4.zip
  39. BIN
      lib/lwjgl.dll
  40. BIN
      lib/lwjgl.jar
  41. BIN
      lib/lwjgl64.dll
  42. BIN
      lib/lwjgl_util.jar
  43. BIN
      lib/slick-util-src.zip
  44. BIN
      lib/slick-util.jar
  45. 52
      src/mightypork/gamecore/backends/lwjgl/BufferHelper.java
  46. 38
      src/mightypork/gamecore/backends/lwjgl/InitTaskRedirectSlickLog.java
  47. 58
      src/mightypork/gamecore/backends/lwjgl/LwjglBackend.java
  48. 302
      src/mightypork/gamecore/backends/lwjgl/LwjglInputModule.java
  49. 75
      src/mightypork/gamecore/backends/lwjgl/SlickLogRedirector.java
  50. 146
      src/mightypork/gamecore/backends/lwjgl/audio/SlickAudio.java
  51. 75
      src/mightypork/gamecore/backends/lwjgl/audio/SlickAudioModule.java
  52. 84
      src/mightypork/gamecore/backends/lwjgl/graphics/AwtScreenshot.java
  53. 620
      src/mightypork/gamecore/backends/lwjgl/graphics/LwjglGraphicsModule.java
  54. 194
      src/mightypork/gamecore/backends/lwjgl/graphics/SlickTexture.java
  55. 143
      src/mightypork/gamecore/backends/lwjgl/graphics/font/DeferredLwjglFont.java
  56. 36
      src/mightypork/gamecore/backends/lwjgl/graphics/font/DeferredLwjglFontFromSystem.java
  57. 494
      src/mightypork/gamecore/backends/lwjgl/graphics/font/LwjglTextureBackedFont.java

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry combineaccessrules="false" kind="src" path="/GameCore"/>
<classpathentry combineaccessrules="false" kind="src" path="/MightyUtils"/>
<classpathentry kind="lib" path="lib/jogg-0.0.7.jar" sourcepath="lib/lwjgl-source-2.8.4.zip"/>
<classpathentry kind="lib" path="lib/jorbis-0.0.15.jar" sourcepath="lib/lwjgl-source-2.8.4.zip"/>
<classpathentry kind="lib" path="lib/lwjgl_util.jar" sourcepath="lib/lwjgl-source-2.8.4.zip"/>
<classpathentry kind="lib" path="lib/lwjgl.jar" sourcepath="lib/lwjgl-source-2.8.4.zip"/>
<classpathentry kind="lib" path="lib/slick-util.jar" sourcepath="lib/slick-util-src.zip"/>
<classpathentry kind="output" path="bin"/>
</classpath>

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>GameCore-LWJGL</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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

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

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

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

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

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

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

@ -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.<br>
* Screenshot, once taken, can be safely processed in separate thread.<br>
* 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.<br>
* 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.<br>
* 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);
}
}

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

@ -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.<br>
*
* @return height 0..1
*/
public float getHeight01()
{
if (!ensureLoaded()) return 0;
return backingTexture.getHeight();
}
/**
* Get the width of the texture, 0..1.<br>
*
* @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();
}
}

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

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

@ -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<Character, CharTile> 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<LoadedGlyph> glyphs = new ArrayList<>();
final List<Character> 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;
}
}
Loading…
Cancel
Save