/**
* Copyright 2008 - 2012
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* @project loon
* @author cping
* @email:javachenpeng@yahoo.com
* @version 0.3.3
*/
package loon;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import loon.LSetting.Listener;
import loon.action.ActionControl;
import loon.core.Assets;
import loon.core.event.Updateable;
import loon.core.geom.RectBox;
import loon.core.graphics.Screen;
import loon.core.graphics.device.LColor;
import loon.core.graphics.device.LFont;
import loon.core.graphics.device.LImage;
import loon.core.graphics.opengl.GL;
import loon.core.graphics.opengl.GLEx;
import loon.core.graphics.opengl.LSTRFont;
import loon.core.graphics.opengl.LTexture;
import loon.core.graphics.opengl.LTextures;
import loon.core.graphics.opengl.ScreenUtils;
import loon.core.graphics.opengl.LTexture.Format;
import loon.core.processes.RealtimeProcessManager;
import loon.core.timer.LTimerContext;
import loon.core.timer.SystemTimer;
import loon.utils.MathUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.PixelFormat;
public class LGame extends JavaSEApp {
public static LGame register(LSetting setting,
Class<? extends Screen> clazz, Object... args) {
LGame game = new LGame(setting.title, setting.width, setting.height);
game._listener = setting.listener;
game._AWT_Canvas = setting.javaCanvas;
game._x = setting.appX;
game._y = setting.appY;
game._resizable = setting.resizable;
game.setShowFPS(setting.showFPS);
game.setShowMemory(setting.showMemory);
game.setShowLogo(setting.showLogo);
game.setFPS(setting.fps);
if (clazz != null) {
if (args != null) {
try {
final int funs = args.length;
if (funs == 0) {
game.setScreen(clazz.newInstance());
game.showScreen();
} else {
Class<?>[] functions = new Class<?>[funs];
for (int i = 0; i < funs; i++) {
functions[i] = getType(args[i]);
}
java.lang.reflect.Constructor<?> constructor = Class
.forName(clazz.getName()).getConstructor(
functions);
Object o = constructor.newInstance(args);
if (o != null && (o instanceof Screen)) {
game.setScreen((Screen) o);
game.showScreen();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
return game;
}
private final ExecutorService _exec = Executors.newFixedThreadPool(4);
private LSTRFont fpsFont;
private long maxFrames = LSystem.DEFAULT_MAX_FPS, frameRate;
private OpenGLThread mainLoop;
private boolean isRunning, isFull, isFPS, isMemory;
private DisplayMode displayMode;
private int _lastWidth, _lastHeight, drawPriority;
private int _x = -1, _y = -1;
private boolean _resizable, _isAWTCanvas;
private GLEx gl;
private GLMode glMode = GLMode.Default;
private String windowTitle;
private RectBox bounds = new RectBox();
private boolean fullscreen = true;
private LTexture logo;
private static int updateWidth = 0, updateHeight = 0;
private static boolean updateScreen = false;
private Listener _listener;
public static void updateSize(int w, int h) {
LGame.updateWidth = w;
LGame.updateHeight = h;
LGame.updateScreen = true;
}
public LGame() {
this(null, LSystem.MAX_SCREEN_WIDTH, LSystem.MAX_SCREEN_HEIGHT);
}
public LGame(String titleName, int width, int height) {
if (width < 1 || height < 1) {
throw new RuntimeException("Width and Height must be positive !");
}
if (LSystem.isWindows()) {
System.setProperty("sun.java2d.translaccel", "true");
System.setProperty("sun.java2d.ddforcevram", "true");
} else if (LSystem.isMacOS()) {
System.setProperty("apple.awt.showGrowBox", "false");
System.setProperty("apple.awt.graphics.EnableQ2DX", "true");
System.setProperty("apple.awt.graphics.EnableLazyDrawing", "true");
System.setProperty(
"apple.awt.window.position.forceSafeUserPositioning",
"true");
System.setProperty("apple.awt.window.position.forceSafeCreation",
"true");
System.setProperty("com.apple.hwaccel", "true");
System.setProperty("com.apple.forcehwaccel", "true");
System.setProperty("com.apple.macos.smallTabs", "true");
System.setProperty("com.apple.macos.use-file-dialog-packages",
"true");
} else {
System.setProperty("sun.java2d.opengl", "true");
}
if (LSystem.screenRect == null) {
LSystem.screenRect = new RectBox(0, 0, width, height);
} else {
LSystem.screenRect.setBounds(0, 0, width, height);
}
this._lastWidth = width;
this._lastHeight = height;
LSystem.screenProcess = new LProcess(this, width, height);
setFPS(LSystem.DEFAULT_MAX_FPS);
setTitle(titleName);
setSize(width, height);
}
protected void setTitle(String titleName) {
this.windowTitle = titleName;
}
protected void setGLMode(GLMode renderMode) {
this.glMode = renderMode;
}
protected GLEx getGraphics() {
return gl;
}
protected void setScreen(Screen screen) {
LSystem.screenProcess.setScreen(screen);
}
public int getHeight() {
return bounds.height;
}
public int getWidth() {
return bounds.width;
}
protected void setBounds(int x, int y, int width, int height) {
bounds.setBounds(x, y, width, height);
}
public RectBox getBounds() {
return bounds;
}
public void setViewPort(int x, int y, int width, int height) {
setBounds(x, y, width, height);
gl.setViewPort(x, y, width, height);
}
public void setViewPort(RectBox vPort) {
setViewPort((int) vPort.x, (int) vPort.y, vPort.width, vPort.height);
}
public RectBox getViewPort() {
return gl.getViewPort();
}
final private class OpenGLThread extends Thread {
private long before, lastTimeMicros, currTimeMicros, goalTimeMicros,
elapsedTimeMicros, remainderMicros, elapsedTime, frameCount,
frames;
public OpenGLThread() {
isRunning = true;
setName("OpenGLThread");
}
/**
* 显示游戏logo
*/
private void showLogo() {
int number = 0;
try {
long elapsed;
int cx = 0, cy = 0;
double delay;
if (logo == null) {
logo = LTextures.loadTexture(LSystem.FRAMEWORK_IMG_NAME
+ "logo.png", Format.BILINEAR);
}
cx = (int) (getWidth() * LSystem.scaleWidth) / 2
- logo.getWidth() / 2;
cy = (int) (getHeight() * LSystem.scaleHeight) / 2
- logo.getHeight() / 2;
float alpha = 0.0f;
boolean firstTime = true;
elapsed = innerClock();
while (alpha < 1.0f) {
gl.drawClear();
gl.setAlpha(alpha);
gl.drawTexture(logo, cx, cy);
if (firstTime) {
firstTime = false;
}
elapsed = innerClock();
delay = 0.00065 * elapsed;
if (delay > 0.22) {
delay = 0.22 + (delay / 6);
}
alpha += delay;
Display.update();
}
while (number < 3000) {
number += innerClock();
Display.update();
}
alpha = 1.0f;
while (alpha > 0.0f) {
gl.drawClear();
gl.setAlpha(alpha);
gl.drawTexture(logo, cx, cy);
elapsed = innerClock();
delay = 0.00055 * elapsed;
if (delay > 0.15) {
delay = 0.15 + ((delay - 0.04) / 2);
}
alpha -= delay;
Display.update();
}
gl.setAlpha(1.0f);
gl.setColor(LColor.white);
} catch (Throwable e) {
} finally {
logo.dispose();
logo = null;
gl.setBlendMode(GL.MODE_NORMAL);
LSystem.isLogo = false;
}
}
private long innerClock() {
long now = System.currentTimeMillis();
long ret = now - before;
before = now;
return ret;
}
/**
* 游戏窗体主循环
*/
@Override
public void run() {
createScreen();
if (LSystem.isLogo) {
showLogo();
}
boolean wasActive = Display.isActive();
final LTimerContext timerContext = new LTimerContext();
final SystemTimer timer = LSystem.getSystemTimer();
final LProcess process = LSystem.screenProcess;
Thread currentThread = Thread.currentThread();
process.begin();
{
process.resize(LSystem.screenRect.width,
LSystem.screenRect.height);
for (; isRunning && !Display.isCloseRequested()
&& mainLoop == currentThread;) {
Display.processMessages();
if (wasActive != Display.isActive()) {
if (wasActive) {
LSystem.isPaused = true;
Assets.onPause();
onPause();
if (process != null) {
process.onPause();
}
if (_listener != null) {
_listener.onPause();
}
pause(500);
LSystem.gc(1000, 1);
lastTimeMicros = timer.getTimeMicros();
elapsedTime = 0;
remainderMicros = 0;
Display.update();
} else {
LSystem.isPaused = false;
Assets.onResume();
onResume();
if (process != null) {
process.onResume();
}
if (_listener != null) {
_listener.onResume();
}
}
wasActive = Display.isActive();
continue;
}
boolean lockedRender = false;
if (_isAWTCanvas) {
int width = _AWT_Canvas.getWidth();
int height = _AWT_Canvas.getHeight();
if (width != _lastWidth || height != _lastHeight) {
LSystem.scaleWidth = ((float) width)
/ LSystem.screenRect.width;
LSystem.scaleHeight = ((float) height)
/ LSystem.screenRect.height;
if (gl != null && !gl.isClose()) {
gl.setViewPort(0, 0, width, height);
}
if (process != null) {
process.resize(width, height);
}
_lastWidth = width;
_lastHeight = height;
lockedRender = true;
}
} else if (_resizable) {
int width = Display.getWidth();
int height = Display.getHeight();
if (Display.wasResized() || width != _lastWidth
|| height != _lastHeight) {
LSystem.scaleWidth = ((float) width)
/ LSystem.screenRect.width;
LSystem.scaleHeight = ((float) height)
/ LSystem.screenRect.height;
if (gl != null && !gl.isClose()) {
gl.setViewPort(0, 0, width, height);
}
if (process != null) {
process.resize(width, height);
}
_lastWidth = width;
_lastHeight = height;
requestRendering();
}
}
_queue.execute();
synchronized (gl) {
if (!process.next()) {
continue;
}
process.load();
process.calls();
if (!isRunning) {
break;
}
goalTimeMicros = lastTimeMicros + 1000000L / maxFrames;
currTimeMicros = timer.sleepTimeMicros(goalTimeMicros);
elapsedTimeMicros = currTimeMicros - lastTimeMicros
+ remainderMicros;
elapsedTime = MathUtils.max(0,
(elapsedTimeMicros / 1000));
remainderMicros = elapsedTimeMicros - elapsedTime
* 1000;
lastTimeMicros = currTimeMicros;
timerContext.millisSleepTime = remainderMicros;
timerContext.timeSinceLastUpdate = elapsedTime;
lockedRender |= shouldRender();
RealtimeProcessManager.get().tick(elapsedTime);
ActionControl.update(elapsedTime);
process.runTimer(timerContext);
if (LSystem.AUTO_REPAINT && lockedRender) {
int repaintMode = process.getRepaintMode();
switch (repaintMode) {
case Screen.SCREEN_BITMAP_REPAINT:
gl.reset(true);
if (process.getX() == 0 && process.getY() == 0) {
gl.drawTexture(process.getBackground(), 0,
0);
} else {
gl.drawTexture(process.getBackground(),
process.getX(), process.getY());
}
break;
case Screen.SCREEN_COLOR_REPAINT:
gl.reset(true);
LColor c = process.getColor();
if (c != null) {
gl.drawClear(c);
}
break;
case Screen.SCREEN_CANVAS_REPAINT:
gl.reset(true);
break;
case Screen.SCREEN_NOT_REPAINT:
gl.reset(true);
break;
default:
gl.reset(true);
if (process.getX() == 0 && process.getY() == 0) {
gl.drawTexture(
process.getBackground(),
repaintMode
/ 2
- LSystem.random
.nextInt(repaintMode),
repaintMode
/ 2
- LSystem.random
.nextInt(repaintMode));
} else {
gl.drawTexture(
process.getBackground(),
process.getX()
+ repaintMode
/ 2
- LSystem.random
.nextInt(repaintMode),
process.getY()
+ repaintMode
/ 2
- LSystem.random
.nextInt(repaintMode));
}
break;
}
gl.resetFont();
process.draw(gl);
process.drawable(elapsedTime);
if (isFPS) {
tickFrames();
fpsFont.drawString("FPS:" + frameRate, 5, 5, 0,
LColor.white);
}
if (isMemory) {
Runtime runtime = Runtime.getRuntime();
long totalMemory = runtime.totalMemory();
long currentMemory = totalMemory
- runtime.freeMemory();
String memory = ((float) ((currentMemory * 10) >> 20) / 10)
+ " of "
+ ((float) ((totalMemory * 10) >> 20) / 10)
+ " MB";
fpsFont.drawString("MEMORY:" + memory, 5, 25,
0, LColor.white);
}
process.drawEmulator(gl);
process.unload();
// 刷新游戏画面
Display.update();
} else {
Display.sync(60);
}
}
// 此版将F12设定为全屏
if (Keyboard.isKeyDown(Keyboard.KEY_F12) && !isFull) {
isFull = true;
updateFullScreen();
} else if (!Keyboard.isKeyDown(Keyboard.KEY_F12)) {
if (updateScreen) {
updateFullScreen(updateWidth, updateHeight, true);
updateScreen = false;
} else {
isFull = false;
}
}
}
}
process.end();
exit();
}
private final void pause(long sleep) {
try {
Thread.sleep(sleep);
} catch (InterruptedException ex) {
}
}
private void tickFrames() {
long time = System.currentTimeMillis();
if (time - frameCount > 1000L) {
frameRate = Math.min(maxFrames, frames);
frames = 0;
frameCount = time;
}
frames++;
}
}
volatile boolean _isContinuous = true;
volatile boolean _requestRendering = false;
public void setContinuousRendering(boolean isContinuous) {
this._isContinuous = isContinuous;
}
public boolean isContinuousRendering() {
return _isContinuous;
}
public void requestRendering() {
synchronized (this) {
_requestRendering = true;
}
}
public boolean shouldRender() {
synchronized (this) {
boolean rq = _requestRendering;
_requestRendering = false;
return rq || _isContinuous || Display.isDirty();
}
}
@Override
public void exit() {
isRunning = false;
synchronized (this) {
if (LSystem.screenProcess != null) {
LSystem.screenProcess.onDestroy();
}
if (gl != null) {
gl.dispose();
}
ActionControl.getInstance().stopAll();
Assets.onDestroy();
LSystem.destroy();
LSystem.gc();
if (displayMode != null) {
Mouse.destroy();
Keyboard.destroy();
Display.destroy();
}
notifyAll();
}
onExit();
if (_listener != null) {
_listener.onExit();
}
try {
_exec.shutdown();
_exec.awaitTermination(1, TimeUnit.SECONDS);
} catch (InterruptedException ie) {
}
System.exit(0);
}
protected void showScreen() {
if (!isRunning) {
isRunning = true;
if (mainLoop == null) {
if (_resizable) {
Display.setResizable(_resizable);
}
this.mainLoop = new OpenGLThread();
this.drawPriority = Thread.NORM_PRIORITY;
this.mainLoop.setPriority(drawPriority);
this.mainLoop.start();
}
}
}
private void setSize(int w, int h) {
LSystem.screenRect.setSize(w, h);
}
private void createScreen() {
try {
DisplayMode[] ds;
ds = Display.getAvailableDisplayModes();
for (int i = 0; i < ds.length; i++) {
if (ds[i].getWidth() == LSystem.screenRect.width
&& ds[i].getHeight() == LSystem.screenRect.height
&& ds[i].getBitsPerPixel() == 32) {
displayMode = ds[i];
break;
}
}
if (displayMode == null) {
displayMode = new DisplayMode(LSystem.screenRect.width,
LSystem.screenRect.height);
}
if (_AWT_Canvas != null) {
Display.setParent(_AWT_Canvas);
_isAWTCanvas = true;
} else {
Display.setDisplayMode(displayMode);
_isAWTCanvas = false;
}
Display.setTitle(windowTitle);
Display.setInitialBackground(0, 0, 0);
if (_x != -1 && _y != -1) {
Display.setLocation(_x, _y);
}
int samples = 0;
try {
Display.create(new PixelFormat(8, 8, 0, samples));
} catch (Exception e) {
Display.destroy();
try {
Display.create(new PixelFormat(8, 8, 0));
} catch (Exception ex) {
Display.destroy();
try {
Display.create(new PixelFormat());
} catch (Exception exc) {
if (exc.getMessage().contains(
"Pixel format not accelerated"))
throw new RuntimeException(
"not supported by the OpenGL driver.", exc);
}
}
}
updateScreen();
boolean support = GLEx.checkVBO();
if (glMode == GLMode.VBO) {
if (support) {
GLEx.setVbo(true);
} else {
GLEx.setVbo(false);
setGLMode(GLMode.Default);
}
} else {
GLEx.setVbo(false);
setGLMode(GLMode.Default);
}
this.gl = new GLEx(LSystem.screenRect.width,
LSystem.screenRect.height);
this.setViewPort(getBounds());
this.gl.update();
} catch (LWJGLException ex) {
ex.printStackTrace();
}
}
protected void setIcon(String path) {
setIcon(new LImage(path));
}
protected void setIcon(LImage icon) {
Display.setIcon(new java.nio.ByteBuffer[] { (java.nio.ByteBuffer) icon
.getByteBuffer() });
}
public boolean isClosed() {
if (displayMode != null) {
return Display.isCloseRequested();
}
return true;
}
public boolean isActive() {
if (displayMode != null) {
return Display.isActive();
}
return false;
}
public int getScreenWidth() {
return java.awt.Toolkit.getDefaultToolkit().getScreenSize().width;
}
public int getScreenHeight() {
return java.awt.Toolkit.getDefaultToolkit().getScreenSize().height;
}
protected void updateScreen() {
int w = 0;
int h = 0;
if (_AWT_Canvas == null) {
DisplayMode dm = Display.getDisplayMode();
w = dm.getWidth();
h = dm.getHeight();
} else {
w = _AWT_Canvas.getWidth();
h = _AWT_Canvas.getHeight();
}
LSystem.scaleWidth = ((float) w) / LSystem.screenRect.width;
LSystem.scaleHeight = ((float) h) / LSystem.screenRect.height;
this.setBounds(0, 0, w, h);
}
protected void updateFullScreen() {
updateFullScreen(getScreenWidth(), getScreenHeight(), true);
}
protected void updateFullScreen(int w, int h) {
updateFullScreen(w, h, false);
}
private void updateFullScreen(int w, int h, boolean limit) {
this.fullscreen = !fullscreen;
if (!fullscreen) {
try {
if (limit) {
if (Display.isFullscreen()) {
return;
}
java.awt.DisplayMode useDisplayMode = ScreenUtils
.searchFullScreenModeDisplay(w, h);
if (useDisplayMode == null) {
return;
}
}
DisplayMode d = new DisplayMode(w, h);
if (gl != null && !gl.isClose()) {
gl.setViewPort(0, 0, w, h);
}
Display.setDisplayModeAndFullscreen(d);
} catch (Exception e) {
}
} else {
try {
if (gl != null && !gl.isClose()) {
gl.setViewPort(0, 0, displayMode.getWidth(),
displayMode.getHeight());
}
Display.setDisplayMode(displayMode);
} catch (Exception e) {
}
}
updateScreen();
}
public LTexture getLogo() {
return logo;
}
public void setLogo(LTexture img) {
logo = img;
}
public void setLogo(String path) {
setLogo(LTextures.loadTexture(path));
}
protected void setShowLogo(boolean showLogo) {
LSystem.isLogo = showLogo;
}
@Override
public void invokeAsync(final Updateable act) {
Runnable run = new Runnable() {
@Override
public void run() {
act.action(null);
}
};
_exec.execute(run);
}
private final String pFontString = " MEORYFPSB0123456789:.of";
protected void setShowFPS(boolean showFps) {
this.isFPS = showFps;
if (showFps && fpsFont == null) {
this.fpsFont = new LSTRFont(LFont.getDefaultFont(), pFontString);
}
}
protected void setShowMemory(boolean showMemory) {
this.isMemory = showMemory;
if (showMemory && fpsFont == null) {
this.fpsFont = new LSTRFont(LFont.getDefaultFont(), pFontString);
}
}
public int getAppX() {
return _x;
}
public void seAppX(int x) {
this._x = x;
}
public int getAppY() {
return _y;
}
public void setAppY(int y) {
this._y = y;
}
public boolean isResizable() {
return _resizable;
}
protected void setResizable(boolean r) {
this._resizable = r;
}
protected void setFPS(long frames) {
this.maxFrames = frames;
}
public long getMaxFPS() {
return this.maxFrames;
}
public long getCurrentFPS() {
return this.frameRate;
}
public float getScalex() {
return LSystem.scaleWidth;
}
public float getScaley() {
return LSystem.scaleHeight;
}
@Override
public void onPause() {
}
@Override
public void onResume() {
}
@Override
public void onExit() {
}
}