312 lines
8.8 KiB
Java
312 lines
8.8 KiB
Java
package chess.view.DDDrender;
|
|
|
|
import common.AssetManager;
|
|
import common.Signal0;
|
|
import org.joml.Vector2f;
|
|
import org.lwjgl.*;
|
|
import org.lwjgl.glfw.*;
|
|
import org.lwjgl.opengl.*;
|
|
import org.lwjgl.system.*;
|
|
|
|
import chess.model.Coordinate;
|
|
import chess.view.DDDrender.opengl.FrameBuffer;
|
|
import chess.view.DDDrender.world.Entity;
|
|
import chess.view.DDDrender.world.World;
|
|
import common.Signal1;
|
|
import imgui.ImFontConfig;
|
|
import imgui.ImGui;
|
|
import imgui.ImVec2;
|
|
import imgui.flag.ImGuiWindowFlags;
|
|
import imgui.gl3.ImGuiImplGl3;
|
|
import imgui.glfw.ImGuiImplGlfw;
|
|
import imgui.type.ImBoolean;
|
|
import imgui.type.ImInt;
|
|
|
|
import java.io.Closeable;
|
|
import java.io.IOException;
|
|
import java.nio.*;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Queue;
|
|
import java.util.concurrent.ConcurrentLinkedDeque;
|
|
import java.util.function.Consumer;
|
|
|
|
import static org.lwjgl.glfw.Callbacks.*;
|
|
import static org.lwjgl.glfw.GLFW.*;
|
|
import static org.lwjgl.opengl.GL11.*;
|
|
import static org.lwjgl.system.MemoryStack.*;
|
|
import static org.lwjgl.system.MemoryUtil.*;
|
|
|
|
public class Window implements Closeable {
|
|
|
|
// The window handle
|
|
private long window;
|
|
|
|
private final ImGuiImplGl3 implGl3 = new ImGuiImplGl3();
|
|
private final ImGuiImplGlfw implGlfw = new ImGuiImplGlfw();
|
|
|
|
private Renderer renderer;
|
|
private final Camera cam;
|
|
private final World world;
|
|
|
|
private final Queue<Runnable> tasks;
|
|
private final List<Consumer<Float>> regularTasks;
|
|
|
|
private Coordinate lastCell = null;
|
|
|
|
public final Signal1<Coordinate> OnCellClick = new Signal1<>();
|
|
public final Signal1<Coordinate> OnCellEnter = new Signal1<>();
|
|
public final Signal1<Coordinate> OnCellExit = new Signal1<>();
|
|
|
|
public final Signal0 OnImGuiTopRender = new Signal0();
|
|
public final Signal0 OnImGuiBottomRender = new Signal0();
|
|
|
|
private ImInt detailLevel = new ImInt(10);
|
|
private ImBoolean pixelatedFrame = new ImBoolean(true);
|
|
|
|
public Window(Renderer renderer, World world, Camera camera) {
|
|
this.renderer = new Renderer();
|
|
this.cam = camera;
|
|
this.tasks = new ConcurrentLinkedDeque<>();
|
|
this.world = world;
|
|
this.regularTasks = new ArrayList<>();
|
|
}
|
|
|
|
public synchronized void addRegularTask(Consumer<Float> task) {
|
|
this.regularTasks.add(task);
|
|
}
|
|
|
|
public synchronized void removeRegularTask(Consumer<Float> task) {this.regularTasks.remove(task);}
|
|
|
|
public synchronized void scheduleTask(Runnable runnable) {
|
|
this.tasks.add(runnable);
|
|
}
|
|
|
|
public synchronized Runnable getNextTask() {
|
|
return this.tasks.poll();
|
|
}
|
|
|
|
public void run() {
|
|
System.out.println("LWJGL " + Version.getVersion() + "!");
|
|
|
|
init();
|
|
loop();
|
|
|
|
// Free the window callbacks and destroy the window
|
|
glfwFreeCallbacks(window);
|
|
glfwDestroyWindow(window);
|
|
|
|
// Terminate GLFW and free the error callback
|
|
glfwTerminate();
|
|
glfwSetErrorCallback(null).free();
|
|
|
|
implGl3.shutdown();
|
|
implGlfw.shutdown();
|
|
|
|
ImGui.destroyContext();
|
|
}
|
|
|
|
private void initImGui() {
|
|
ImGui.setCurrentContext(ImGui.createContext());
|
|
|
|
implGl3.init("#version 330");
|
|
implGlfw.init(window, true);
|
|
|
|
ImFontConfig config = new ImFontConfig();
|
|
config.setFontDataOwnedByAtlas(false);
|
|
try {
|
|
ImGui.getIO().getFonts().addFontFromMemoryTTF(AssetManager.getResource("fonts/comic.ttf").readAllBytes(),
|
|
50.0f, config);
|
|
} catch (IOException e) {
|
|
// TODO Auto-generated catch block
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
private void init() {
|
|
// Setup an error callback. The default implementation
|
|
// will print the error message in System.err.
|
|
GLFWErrorCallback.createPrint(System.err).set();
|
|
|
|
// Initialize GLFW. Most GLFW functions will not work before doing this.
|
|
if (!glfwInit())
|
|
throw new IllegalStateException("Unable to initialize GLFW");
|
|
|
|
// Configure GLFW
|
|
glfwDefaultWindowHints(); // optional, the current window hints are already the default
|
|
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation
|
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable
|
|
|
|
// Create the window
|
|
window = glfwCreateWindow(1000, 1000, "3DChess", NULL, NULL);
|
|
if (window == NULL)
|
|
throw new RuntimeException("Failed to create the GLFW window");
|
|
|
|
// Get the thread stack and push a new frame
|
|
try (MemoryStack stack = stackPush()) {
|
|
IntBuffer pWidth = stack.mallocInt(1); // int*
|
|
IntBuffer pHeight = stack.mallocInt(1); // int*
|
|
|
|
// Get the window size passed to glfwCreateWindow
|
|
glfwGetWindowSize(window, pWidth, pHeight);
|
|
|
|
// Get the resolution of the primary monitor
|
|
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
|
|
|
// Center the window
|
|
glfwSetWindowPos(
|
|
window,
|
|
(vidmode.width() - pWidth.get(0)) / 2,
|
|
(vidmode.height() - pHeight.get(0)) / 2);
|
|
|
|
glfwSetWindowSize(window, vidmode.width(), vidmode.height());
|
|
|
|
// Make the OpenGL context current
|
|
glfwMakeContextCurrent(window);
|
|
|
|
GL.createCapabilities();
|
|
|
|
initImGui();
|
|
|
|
renderer.Init(vidmode.width(), vidmode.height());
|
|
} // the stack frame is popped automatically
|
|
|
|
// Enable v-sync
|
|
glfwSwapInterval(1);
|
|
|
|
// Make the window visible
|
|
glfwShowWindow(window);
|
|
}
|
|
|
|
private void render(float delta, float aspectRatio) {
|
|
final FrameBuffer frameBuffer = this.renderer.getFrameBuffer();
|
|
|
|
cam.setAspectRatio(frameBuffer.getAspectRatio());
|
|
frameBuffer.Bind();
|
|
frameBuffer.Clear();
|
|
renderer.Update(cam);
|
|
renderWorld();
|
|
frameBuffer.Unbind();
|
|
}
|
|
|
|
private void renderWorld() {
|
|
for (Entity entity : this.world.getEntites()) {
|
|
entity.render(this.renderer);
|
|
}
|
|
}
|
|
|
|
private synchronized void executeTasks(float delta) {
|
|
Runnable task = getNextTask();
|
|
while (task != null) {
|
|
task.run();
|
|
task = getNextTask();
|
|
}
|
|
for (Consumer<Float> consumer : regularTasks) {
|
|
consumer.accept(delta);
|
|
}
|
|
}
|
|
|
|
private void checkCursor(float cursorPosX, float cursorPosY, int windowWidth, int windowHeight) {
|
|
Vector2f cursorPos = this.cam.getCursorWorldFloorPos(new Vector2f(cursorPosX, cursorPosY), windowWidth,
|
|
windowHeight);
|
|
Coordinate selectedCell = DDDPlacement.vectorToCoordinates(cursorPos);
|
|
if (this.lastCell == null) {
|
|
this.lastCell = selectedCell;
|
|
if (selectedCell.isValid())
|
|
this.OnCellEnter.emit(selectedCell);
|
|
} else if (!this.lastCell.equals(selectedCell)) {
|
|
if (this.lastCell.isValid())
|
|
this.OnCellExit.emit(this.lastCell);
|
|
if (selectedCell.isValid())
|
|
this.OnCellEnter.emit(selectedCell);
|
|
this.lastCell = selectedCell;
|
|
}
|
|
|
|
if (ImGui.getIO().getMouseClicked(0)) {
|
|
if (this.lastCell != null && this.lastCell.isValid())
|
|
this.OnCellClick.emit(this.lastCell);
|
|
}
|
|
}
|
|
|
|
private void newFrame() {
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
|
|
|
|
implGlfw.newFrame();
|
|
implGl3.newFrame();
|
|
ImGui.newFrame();
|
|
}
|
|
|
|
private void renderWindow() {
|
|
ImGui.showDemoWindow();
|
|
|
|
final FrameBuffer frameBuffer = this.renderer.getFrameBuffer();
|
|
final int frameWidth = (int) ImGui.getIO().getDisplaySizeX();
|
|
final int frameHeight = (int) (ImGui.getIO().getDisplaySizeY() * 8.0f / 10.0f);
|
|
|
|
ImGui.setNextWindowSize(ImGui.getIO().getDisplaySize());
|
|
ImGui.setNextWindowPos(new ImVec2());
|
|
ImGui.begin("Main Window", ImGuiWindowFlags.NoDecoration);
|
|
this.OnImGuiTopRender.emit();
|
|
ImVec2 mousePos = ImGui.getIO().getMousePos();
|
|
ImVec2 framePos = ImGui.getCursorScreenPos();
|
|
checkCursor(mousePos.x - framePos.x, frameHeight - (mousePos.y - framePos.y), frameWidth, frameHeight);
|
|
ImGui.image(frameBuffer.getRenderTexture(), new ImVec2(frameWidth, frameHeight));
|
|
this.OnImGuiBottomRender.emit();
|
|
if(ImGui.sliderInt("Niveau de détail", detailLevel.getData(), 1, 10)){
|
|
frameBuffer.Resize((int) ((float) frameWidth * detailLevel.get() / 10.0f), (int) ((float) frameHeight * detailLevel.get() / 10.0f));
|
|
}
|
|
if (ImGui.checkbox("Minecraft", pixelatedFrame)) {
|
|
frameBuffer.SetPixelScaling(pixelatedFrame.get());
|
|
}
|
|
ImGui.end();
|
|
}
|
|
|
|
private void loop() {
|
|
|
|
// Set the clear color
|
|
glClearColor(0.4f, 0.4f, 0.6f, 1.0f);
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glEnable(GL_CULL_FACE);
|
|
glCullFace(GL_FRONT);
|
|
glFrontFace(GL_CW);
|
|
|
|
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
|
|
|
|
int width[] = new int[1];
|
|
int height[] = new int[1];
|
|
glfwGetWindowSize(window, width, height);
|
|
|
|
// Run the rendering loop until the user has attempted to close
|
|
// the window or has pressed the ESCAPE key.
|
|
while (!glfwWindowShouldClose(window)) {
|
|
|
|
newFrame();
|
|
|
|
render(ImGui.getIO().getDeltaTime(), (float) width[0] / (float) height[0]);
|
|
|
|
renderWindow();
|
|
ImGui.render();
|
|
implGl3.renderDrawData(ImGui.getDrawData());
|
|
|
|
glfwSwapBuffers(window); // swap the color buffers
|
|
|
|
// Poll for window events. The key callback above will only be
|
|
// invoked during this call.
|
|
glfwPollEvents();
|
|
|
|
executeTasks(ImGui.getIO().getDeltaTime());
|
|
|
|
glfwGetWindowSize(window, width, height);
|
|
glViewport(0, 0, width[0], height[0]);
|
|
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void close() throws IOException {
|
|
this.renderer.close();
|
|
}
|
|
|
|
} |