605 lines
15 KiB
Java
605 lines
15 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.getMonitor() == null)
|
|
{
|
|
throw new IllegalStateException("Monitor or Video Mode is missing: "+videoMode);
|
|
}
|
|
Monitor monitor = videoMode.getMonitor();
|
|
windowId = GLFW.glfwCreateWindow(flags.isFlagSet(FLAG_FULL_SCREEN) ? videoMode.getWidth() : width, flags.isFlagSet(FLAG_FULL_SCREEN) ? videoMode.getHeight() : 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.getWidth() / 2) - (width / 2)) : monitor.getXOffset() + xPos;
|
|
yPos = stat.center ? (monitor.getYOffset() + (videoMode.getHeight() / 2) - (width / 2)) : monitor.getYOffset() + yPos;
|
|
GLFW.glfwSetWindowPos(windowId, xPos, yPos);
|
|
}
|
|
else
|
|
{
|
|
xPos = monitor.getXOffset() + (videoMode.getWidth() / 2) - (width / 2);
|
|
yPos = monitor.getYOffset() + (videoMode.getHeight() / 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++)
|
|
{
|
|
WindowCallback callback = callbacks.get(i);
|
|
callback.destroy();
|
|
callback.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.getMonitor() == getMonitor())
|
|
{
|
|
videoMode = mode;
|
|
if(flags.isAnyFlagSet(FLAG_FULL_SCREEN | FLAG_MAXIMIZED))
|
|
{
|
|
if(flags.isFlagSet(FLAG_FULL_SCREEN))
|
|
{
|
|
GLFW.glfwSetWindowMonitor(windowId, videoMode.getMonitor().getMonitorId(), 0, 0, videoMode.getWidth(), videoMode.getHeight(), videoMode.getRefreshRate());
|
|
}
|
|
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.getWidth(), videoMode.getHeight(), videoMode.getRefreshRate());
|
|
fetchWindowBounds();
|
|
}
|
|
else
|
|
{
|
|
if(flags.isFlagNotSet(FLAG_MAXIMIZED))
|
|
{
|
|
width = tempWidth;
|
|
height = tempHeight;
|
|
GLFW.glfwSetWindowMonitor(windowId, 0, xPos, yPos, width, height, videoMode.getRefreshRate());
|
|
}
|
|
else
|
|
{
|
|
GLFW.glfwSetWindowMonitor(windowId, 0, 0, 0, width, height, videoMode.getRefreshRate());
|
|
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.getWidth() : width, flags.isFlagSet(FLAG_FULL_SCREEN) ? videoMode.getHeight() : 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 void setClipboardString(String text)
|
|
{
|
|
GLFW.glfwSetClipboardString(windowId, text);
|
|
}
|
|
|
|
public void requestClose()
|
|
{
|
|
flags.setFlag(FLAG_CLOSE);
|
|
}
|
|
|
|
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 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);
|
|
for(WindowCallback back : callbacks)
|
|
{
|
|
back.destroy();
|
|
}
|
|
callbacks.clear();
|
|
}
|
|
|
|
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 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.reload(windowId);
|
|
}
|
|
return callback;
|
|
}
|
|
|
|
public void removeCallback(WindowCallback call)
|
|
{
|
|
if(callbacks.remove(call))
|
|
{
|
|
call.destroy();
|
|
}
|
|
}
|
|
|
|
private void createProjectionMatrix()
|
|
{
|
|
// float aspectRatio = (float)width / (float)height;
|
|
// float y_scale = (float)((1f / Math.tan(Math.toRadians(fieldOfView / 2f))) * aspectRatio);
|
|
// float x_scale = y_scale / aspectRatio;
|
|
// float frustum_length = far_plane - near_plane;
|
|
// projection.setIdentity();
|
|
// projection.set(0, x_scale);
|
|
// projection.set(5, y_scale);
|
|
// projection.set(10, -((far_plane + near_plane) / frustum_length));
|
|
// projection.set(11, -1);
|
|
// projection.set(14, -((2 * near_plane * far_plane) / frustum_length));
|
|
// projection.set(15, 0);
|
|
// invertedProjection.load(projection).invert();
|
|
//
|
|
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];
|
|
}
|
|
}
|