SimpleJavaEngine/src/main/java/speiger/src/coreengine/rendering/input/window/Window.java

415 lines
14 KiB
Java

package speiger.src.coreengine.rendering.input.window;
import java.util.List;
import java.util.function.LongFunction;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GLCapabilities;
import org.lwjgl.system.Callback;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.math.MathUtils;
import speiger.src.coreengine.math.vector.matrix.Matrix4f;
import speiger.src.coreengine.rendering.input.window.WindowProvider.WindowStats;
import speiger.src.coreengine.rendering.utils.GLUtils;
import speiger.src.coreengine.utils.collections.FlagHolder;
public class Window
{
static final int FLAG_VISIBLE = 1;
static final int FLAG_VSYNC = 2;
static final int FLAG_FOCUS = 4;
static final int FLAG_CPU_FPS_CAP = 8;
static final int FLAG_FULL_SCREEN = 16;
static final int FLAG_MAXIMIZED = 32;
static final int FLAG_BORDERLESS = 64;
static final int FLAG_CLOSE = 128;
static final int FLAG_WINDOW_CHANGE = 256;
static final int FLAG_FLOATING = 512;
WindowProvider owner;
long windowId;
VideoMode videoMode;
FlagHolder flags = new FlagHolder();
String title = "";
int xPos;
int yPos;
int tempWidth;
int tempHeight;
int width;
int height;
int aaLevel;
int uiScale;
ScaledResolution ui_frame = new ScaledResolution(this);
float fieldOfView = 70F;
final float near_plane = 0.1F;
final float far_plane = 2000F;
Matrix4f projection = new Matrix4f();
Matrix4f invertedProjection = new Matrix4f();
List<WindowCallback> callbacks = new ObjectArrayList<>();
List<IWindowListener> listeners = new ObjectArrayList<>();
GLCapabilities capabilities;
public Window() {
GLFW.glfwDefaultWindowHints();
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, 0); // 0 = False, 1 = true
GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, 1);
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 4);
GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 0);
GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE);//GLFW.GLFW_OPENGL_COMPAT_PROFILE);
GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_FORWARD_COMPAT, 1);
}
protected void initWindowListeners() {
addCallback(T -> GLFW.glfwSetFramebufferSizeCallback(T, this::onResize));
addCallback(T -> GLFW.glfwSetWindowMaximizeCallback(T, (K, V) -> flags.setFlag(FLAG_WINDOW_CHANGE)));
addCallback(T -> GLFW.glfwSetWindowPosCallback(T, (W, X, Y) -> setPosition(X, Y)));
addCallback(T -> GLFW.glfwSetWindowFocusCallback(T, (K, V) -> flags.setFlag(FLAG_FOCUS, V)));
}
Window createWindow(WindowStats stat) {
owner = stat.provider;
title = stat.name;
width = stat.width;
height = stat.height;
aaLevel = stat.antiAlis;
videoMode = stat.videoMode;
flags.setFlag(FLAG_BORDERLESS, stat.borderless);
flags.setFlag(FLAG_FULL_SCREEN, stat.fullScreen);
flags.setFlag(FLAG_FLOATING, stat.floating);
flags.setFlag(FLAG_VSYNC, stat.vsync);
flags.setFlag(FLAG_CPU_FPS_CAP, stat.fpsCap);
GLFW.glfwWindowHint(GLFW.GLFW_SAMPLES, aaLevel);
GLFW.glfwWindowHint(GLFW.GLFW_DECORATED, flags.isFlagNotSet(FLAG_FULL_SCREEN) && flags.isFlagSet(FLAG_BORDERLESS) ? 0 : 1);
GLFW.glfwWindowHint(GLFW.GLFW_FLOATING, flags.isFlagNotSet(FLAG_FULL_SCREEN) && flags.isFlagSet(FLAG_FLOATING) ? 1 : 0);
if(videoMode == null || videoMode.monitor() == null) throw new IllegalStateException("Monitor or Video Mode is missing: "+videoMode);
Monitor monitor = videoMode.monitor();
windowId = GLFW.glfwCreateWindow(flags.isFlagSet(FLAG_FULL_SCREEN) ? videoMode.width() : width, flags.isFlagSet(FLAG_FULL_SCREEN) ? videoMode.height() : height, title, flags.isFlagSet(FLAG_FULL_SCREEN) ? monitor.getMonitorId() : 0L, stat.parent == null ? 0 : stat.parent.windowId);
if(windowId == 0) throw new IllegalStateException("Window Couldn't be Created");
initWindowListeners();
GLFW.glfwMakeContextCurrent(windowId);
if(capabilities == null) capabilities = GL.createCapabilities();
if(flags.isFlagNotSet(FLAG_FULL_SCREEN)) {
xPos = stat.center ? (monitor.getXOffset() + (videoMode.width() / 2) - (width / 2)) : monitor.getXOffset() + xPos;
yPos = stat.center ? (monitor.getYOffset() + (videoMode.height() / 2) - (width / 2)) : monitor.getYOffset() + yPos;
GLFW.glfwSetWindowPos(windowId, xPos, yPos);
}
else {
xPos = monitor.getXOffset() + (videoMode.width() / 2) - (width / 2);
yPos = monitor.getYOffset() + (videoMode.height() / 2) - (width / 2);
tempWidth = width;
tempHeight = height;
}
GLFW.glfwSwapInterval(flags.isFlagSet(FLAG_VSYNC) ? 1 : 0);
fetchWindowBounds();
updateViewPort();
return this;
}
protected void onResize(long window, int newWidth, int newHeight) {
if(flags.isFlagNotSet(FLAG_FULL_SCREEN | FLAG_MAXIMIZED) && window == windowId && (width != newWidth || height != newHeight)) {
width = newWidth;
height = newHeight;
flags.setFlag(FLAG_WINDOW_CHANGE);
}
}
protected void updateCallbacks() {
for(int i = 0,m=callbacks.size();i<m;i++) {
callbacks.get(i).reload(windowId);
}
GLUtils.reapplyState();
}
protected void updateViewPort() {
flags.clearFlag(FLAG_WINDOW_CHANGE);
GLUtils.VIEW_PORT.setDefault(0, 0, width, height);
createProjectionMatrix();
ui_frame.updateScale();
for(int i = 0,m=listeners.size();i<m;i++) {
listeners.get(i).onWindowChanged(this);
}
}
public void setVideoMode(VideoMode mode) {
if(videoMode != mode && mode.monitor() == getMonitor()) {
videoMode = mode;
if(flags.isAnyFlagSet(FLAG_FULL_SCREEN | FLAG_MAXIMIZED)) {
if(flags.isFlagSet(FLAG_FULL_SCREEN)) {
GLFW.glfwSetWindowMonitor(windowId, videoMode.monitor().getMonitorId(), 0, 0, videoMode.width(), videoMode.height(), videoMode.refreshrate());
}
else if(flags.isFlagSet(FLAG_MAXIMIZED)) {
GLFW.glfwRestoreWindow(windowId);
GLFW.glfwMaximizeWindow(windowId);
}
fetchWindowBounds();
updateViewPort();
}
}
}
public boolean setTitle(String title) {
if(title != null && !this.title.equals(title)) {
this.title = title;
GLFW.glfwSetWindowTitle(windowId, title);
return true;
}
return false;
}
public boolean setVsync(boolean value) {
if(flags.setFlag(FLAG_VSYNC, value)) {
GLFW.glfwSwapInterval(value ? 1 : 0);
return true;
}
return false;
}
public boolean setFPSLimit(boolean value) {
return flags.setFlag(FLAG_CPU_FPS_CAP, value);
}
public boolean setFullscreen(boolean value) {
if(flags.setFlag(FLAG_FULL_SCREEN, value)) {
if(value) {
if(flags.isFlagNotSet(FLAG_MAXIMIZED)) {
tempWidth = width;
tempHeight = height;
}
Monitor monitor = getMonitor();
if(!monitor.contains(videoMode)) videoMode = monitor.getDefaultMode();
GLFW.glfwSetWindowMonitor(windowId, monitor.getMonitorId(), 0, 0, videoMode.width(), videoMode.height(), videoMode.refreshrate());
fetchWindowBounds();
}
else {
if(flags.isFlagNotSet(FLAG_MAXIMIZED)) {
width = tempWidth;
height = tempHeight;
GLFW.glfwSetWindowMonitor(windowId, 0, xPos, yPos, width, height, videoMode.refreshrate());
}
else {
GLFW.glfwSetWindowMonitor(windowId, 0, 0, 0, width, height, videoMode.refreshrate());
GLFW.glfwMaximizeWindow(windowId);
fetchWindowBounds();
}
if(flags.isFlagSet(FLAG_BORDERLESS)) {
GLFW.glfwSetWindowAttrib(windowId, GLFW.GLFW_DECORATED, 0);
GLFW.glfwRestoreWindow(windowId);
GLFW.glfwMaximizeWindow(windowId);
}
else {
GLFW.glfwSetWindowAttrib(windowId, GLFW.GLFW_DECORATED, 1);
}
}
updateViewPort();
GLFW.glfwSwapInterval(flags.isFlagSet(FLAG_VSYNC) ? 1 : 0);
return true;
}
return false;
}
public boolean setBorderless(boolean value) {
if(flags.isFlagNotSet(FLAG_FULL_SCREEN) && flags.setFlag(FLAG_BORDERLESS, value)) {
GLFW.glfwSetWindowAttrib(windowId, GLFW.GLFW_DECORATED, value ? 0 : 1);
if(flags.isFlagSet(FLAG_MAXIMIZED)) {
GLFW.glfwRestoreWindow(windowId);
GLFW.glfwMaximizeWindow(windowId);
fetchWindowBounds();
}
return true;
}
return false;
}
public boolean setFloating(boolean value) {
if(flags.isFlagNotSet(FLAG_FULL_SCREEN) && flags.setFlag(FLAG_FLOATING, value)) {
GLFW.glfwSetWindowAttrib(windowId, GLFW.GLFW_FLOATING, value ? 1 : 0);
return true;
}
return false;
}
public boolean setMaximized(boolean value) {
if(flags.isFlagNotSet(FLAG_FULL_SCREEN) && flags.setFlag(FLAG_MAXIMIZED, value)) {
if(value) {
tempWidth = width;
tempHeight = height;
GLFW.glfwMaximizeWindow(windowId);
fetchWindowBounds();
}
else {
width = tempWidth;
height = tempHeight;
GLFW.glfwRestoreWindow(windowId);
}
return true;
}
return false;
}
public boolean setAntiAliasing(int level) {
if(aaLevel != level) {
aaLevel = level;
long last = windowId;
GLFW.glfwDefaultWindowHints();
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, 1); // 0 = False, 1 = true
GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, 1);
GLFW.glfwWindowHint(GLFW.GLFW_SAMPLES, aaLevel);
GLFW.glfwWindowHint(GLFW.GLFW_DECORATED, flags.isFlagNotSet(FLAG_FULL_SCREEN) && flags.isFlagSet(FLAG_BORDERLESS) ? 0 : 1);
Monitor monitor = getMonitor();
if(!monitor.contains(videoMode)) videoMode = monitor.getDefaultMode();
windowId = GLFW.glfwCreateWindow(flags.isFlagSet(FLAG_FULL_SCREEN) ? videoMode.width() : width, flags.isFlagSet(FLAG_FULL_SCREEN) ? videoMode.height() : height, title, flags.isFlagSet(FLAG_FULL_SCREEN) ? monitor.getMonitorId() : 0L, windowId);
if(windowId == 0) throw new IllegalStateException("Window Couldn't be Created");
GLFW.glfwMakeContextCurrent(windowId);
GLFW.glfwDestroyWindow(last);
GLFW.glfwSwapInterval(flags.isFlagSet(FLAG_VSYNC) ? 1 : 0);
GLFW.glfwSetWindowPos(windowId, xPos, yPos);
updateCallbacks();
updateViewPort();
return true;
}
return false;
}
public boolean setWindowSize(int newWidth, int newHeight) {
if(flags.isFlagNotSet(FLAG_MAXIMIZED | FLAG_FULL_SCREEN) && (width != newWidth || height != newWidth)) {
width = newWidth;
height = newHeight;
flags.setFlag(FLAG_WINDOW_CHANGE);
return true;
}
return false;
}
public boolean setPosition(int xNewPos, int yNewPos) {
if(flags.isFlagNotSet(FLAG_MAXIMIZED | FLAG_FULL_SCREEN) && (xPos != xNewPos || yPos != yNewPos)) {
xPos = xNewPos;
yPos = yNewPos;
return true;
}
return false;
}
public boolean setVisible(boolean value) {
if(flags.setFlag(FLAG_VISIBLE, value)) {
if(value) GLFW.glfwShowWindow(windowId);
else GLFW.glfwHideWindow(windowId);
return true;
}
return false;
}
public void setUIScale(int newScale) {
int lastScale = uiScale;
uiScale = MathUtils.clamp(0, 100, newScale);
if(lastScale != uiScale) flags.setFlag(FLAG_WINDOW_CHANGE);
}
public void editScale(int change) {
if(change != 0) {
uiScale = MathUtils.clamp(0, 100, uiScale + change);
flags.setFlag(FLAG_WINDOW_CHANGE);
}
}
public boolean isChanged() { return flags.isFlagSet(FLAG_WINDOW_CHANGE); }
public boolean isFullscreen() { return flags.isFlagSet(FLAG_FULL_SCREEN); }
public boolean isMaximized() { return flags.isFlagSet(FLAG_MAXIMIZED); }
public boolean isBorderless() { return flags.isFlagSet(FLAG_BORDERLESS); }
public boolean isCPULimited() { return flags.isFlagSet(FLAG_CPU_FPS_CAP); }
public int getAntiAliasingLevel() { return aaLevel; }
public long getId() { return windowId; }
public int getWidth() { return width; }
public int getHeight() { return height; }
int getActualWidth() { return flags.isAnyFlagSet(FLAG_FULL_SCREEN | FLAG_MAXIMIZED) ? tempWidth : width; }
int getActualHeight() { return flags.isAnyFlagSet(FLAG_FULL_SCREEN | FLAG_MAXIMIZED) ? tempHeight : height; }
int getXPos() { return xPos; }
int getYPos() { return yPos; }
public Monitor getMonitor() { return owner.getMonitorFromWindow(this); }
public String getClipboardString() { return GLFW.glfwGetClipboardString(windowId); }
public void setClipboardString(String text) { GLFW.glfwSetClipboardString(windowId, text); }
public void requestClose() { flags.setFlag(FLAG_CLOSE); }
public boolean shouldRun() { return flags.isFlagNotSet(FLAG_CLOSE) && !GLFW.glfwWindowShouldClose(windowId); }
public void finishWindow() {
setVisible(true);
GLFW.glfwShowWindow(windowId);
updateViewPort();
}
public void update() {
if(flags.isFlagSet(FLAG_WINDOW_CHANGE)) updateViewPort();
}
public void finishFrame() {
GLFW.glfwSwapBuffers(windowId);
}
public void gatherInputs() {
GLFW.glfwPollEvents();
}
public void destroy() {
GLFW.glfwDestroyWindow(windowId);
callbacks.forEach(WindowCallback::destroy);
callbacks.clear();
}
public Matrix4f getProjectionMatrix() {
return projection;
}
public Matrix4f getInvertedProjectionMatrix() {
return invertedProjection;
}
public ScaledResolution getUIFrame() {
return ui_frame;
}
public void addListener(IWindowListener listener, boolean notify) {
if(listener != null) {
listeners.add(listener);
if(notify) listener.onWindowChanged(this);
}
}
public void removeListener(IWindowListener listener) {
listeners.remove(listener);
}
public WindowCallback addCallback(LongFunction<Callback> provider) {
if(provider == null) return null;
WindowCallback callback = new WindowCallback(provider);
callbacks.add(callback);
if(windowId != 0) {
callback.load(windowId);
}
return callback;
}
public void removeCallback(WindowCallback call) {
if(callbacks.remove(call)) {
call.destroy();
}
}
private void createProjectionMatrix() {
projection.setIdentity().setPerspective((float)Math.toRadians(fieldOfView), (float)width / (float)height, near_plane, far_plane);
invertedProjection.load(projection).invert();
}
protected void fetchWindowBounds() {
int[] width = new int[1];
int[] height = new int[1];
GLFW.glfwGetWindowSize(windowId, width, height);
this.width = width[0];
this.height = height[0];
}
}