33 Commits

Author SHA1 Message Date
ec98b05d61 free opengl resources 2025-04-28 18:23:29 +02:00
0fb24263e0 camel case 2025-04-28 18:08:10 +02:00
533e6260d5 refactor movePiece 2025-04-28 18:06:37 +02:00
0c6ab1df4b add 3d position cache 2025-04-28 17:52:29 +02:00
5f70daea91 board model add constants 2025-04-28 17:32:24 +02:00
dd043794d6 add comment 2025-04-28 17:31:46 +02:00
de4ed869ea feat: change board cells color 2025-04-28 11:04:41 +02:00
b57fa1482b add small example 2025-04-27 20:18:31 +02:00
cbbce43ede raycast cursor 2025-04-27 20:12:38 +02:00
1b61eca58b models: remove uv 2025-04-27 11:02:55 +02:00
9bc09cf812 add world interface 2025-04-27 11:00:14 +02:00
c488f3b4e0 rename PieceModel 2025-04-27 10:14:05 +02:00
9f35bd3c30 opengl package 2025-04-27 10:12:52 +02:00
6ca5d1294f better light 2025-04-26 19:24:52 +02:00
b62dcffcb1 refactor 2025-04-26 18:18:27 +02:00
65c904478f enable face culling 2025-04-26 17:26:27 +02:00
f8ae19fee8 set pieces color 2025-04-26 12:35:37 +02:00
1b22de17d8 change background color 2025-04-26 12:30:29 +02:00
24104fedf5 fix pieces rotation 2025-04-26 12:23:14 +02:00
5b6fce11bc refactor board model 2025-04-26 12:20:00 +02:00
ab309ae48a fix aspectRatio 2025-04-26 12:14:22 +02:00
7af9807127 omg ça marche 2025-04-26 12:00:48 +02:00
cff2d92070 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 2025-04-25 17:03:28 +02:00
098b605799 yodaaaaaa 2025-04-24 11:12:53 +02:00
ce0424ff8b osekour 2025-04-22 17:05:26 +02:00
4bd16026e1 begin 3d 2025-04-22 15:02:24 +02:00
2b5c2ad663 fix gradle input 2025-04-22 14:44:57 +02:00
Janet-Doe
0e2b07741d addo en passant test 2025-04-22 14:42:13 +02:00
b83726925e fix console 2025-04-18 10:33:10 +02:00
30eb12145d add hungry ai 2025-04-17 08:53:21 +02:00
17ef882d44 remove board from constructor 2025-04-16 19:29:31 +02:00
bee5e613bb refactor dumb ai castling 2025-04-16 19:25:04 +02:00
923ace22f1 add general ai 2025-04-16 19:21:47 +02:00
59 changed files with 1679 additions and 709 deletions

4
.gitattributes vendored
View File

@@ -3,7 +3,7 @@
# #
# Linux start script should use lf # Linux start script should use lf
/gradlew text eol=lf /gradlew text eol=lf
# These are Windows script files and should use crlf # These are Windows script files and should use crlf
*.bat text eol=crlf *.bat text eol=crlf
*.glb filter=lfs diff=lfs merge=lfs -text
*.fbx filter=lfs diff=lfs merge=lfs -text

View File

@@ -26,11 +26,13 @@ dependencies {
implementation "org.lwjgl:lwjgl:$lwjgl_version" implementation "org.lwjgl:lwjgl:$lwjgl_version"
implementation "org.lwjgl:lwjgl-opengl:$lwjgl_version" implementation "org.lwjgl:lwjgl-opengl:$lwjgl_version"
implementation "org.lwjgl:lwjgl-glfw:$lwjgl_version" implementation "org.lwjgl:lwjgl-glfw:$lwjgl_version"
implementation "org.lwjgl:lwjgl-assimp:$lwjgl_version"
implementation "org.joml:joml:1.10.8" implementation "org.joml:joml:1.10.8"
implementation "org.lwjgl:lwjgl::$lwjgl_natives" implementation "org.lwjgl:lwjgl::$lwjgl_natives"
implementation "org.lwjgl:lwjgl-opengl::$lwjgl_natives" implementation "org.lwjgl:lwjgl-opengl::$lwjgl_natives"
implementation "org.lwjgl:lwjgl-glfw::$lwjgl_natives" implementation "org.lwjgl:lwjgl-glfw::$lwjgl_natives"
implementation "org.lwjgl:lwjgl-assimp::$lwjgl_natives"
} }
application { application {
@@ -45,6 +47,10 @@ jar {
} }
} }
run {
standardInput = System.in
}
tasks.named('test') { tasks.named('test') {
// Use JUnit Platform for unit tests. // Use JUnit Platform for unit tests.
useJUnitPlatform() useJUnitPlatform()

View File

@@ -1,16 +1,17 @@
package chess; package chess;
import chess.view.consolerender.Colors;
import java.util.Scanner; import java.util.Scanner;
import chess.view.consolerender.Colors;
public class App { public class App {
public static void main(String[] args) { public static void main(String[] args) {
System.out.println(Colors.RED + "Credits: Grenier Lilas, Pribylski Simon." + Colors.RESET); System.out.println(Colors.RED + "Credits: Grenier Lilas, Pribylski Simon." + Colors.RESET);
System.out.println(""" System.out.println("""
Pick the version to use: Pick the version to use:
1 - Console 1 - Console
2 - Window."""); 2 - Window
3 - 3D.""");
switch (new Scanner(System.in).nextLine()) { switch (new Scanner(System.in).nextLine()) {
case "1", "Console", "console": case "1", "Console", "console":
ConsoleMain.main(args); ConsoleMain.main(args);
@@ -18,6 +19,9 @@ public class App {
case "2", "Window", "window": case "2", "Window", "window":
SwingMain.main(args); SwingMain.main(args);
break; break;
case "3", "3D", "3d":
OpenGLMain.main(args);
break;
default: default:
System.out.println("Invalid input"); System.out.println("Invalid input");
break; break;

View File

@@ -5,26 +5,17 @@ package chess;
import chess.controller.CommandExecutor; import chess.controller.CommandExecutor;
import chess.controller.commands.NewGameCommand; import chess.controller.commands.NewGameCommand;
import chess.model.ChessBoard;
import chess.model.Game; import chess.model.Game;
import chess.simulator.PromoteTest;
import chess.view.consolerender.Console; import chess.view.consolerender.Console;
public class ConsoleMain { public class ConsoleMain {
public static void main(String[] args) { public static void main(String[] args) {
Game game = new Game(new ChessBoard()); Game game = new Game();
CommandExecutor commandExecutor = new CommandExecutor(game); CommandExecutor commandExecutor = new CommandExecutor(game);
PromoteTest promoteTest = new PromoteTest(commandExecutor);
commandExecutor.addListener(promoteTest);
Console console = new Console(commandExecutor); Console console = new Console(commandExecutor);
commandExecutor.addListener(console); commandExecutor.addListener(console);
promoteTest.onComplete.connect(() -> {
console.setCaptureInput(true);
});
commandExecutor.executeCommand(new NewGameCommand()); commandExecutor.executeCommand(new NewGameCommand());
} }
} }

View File

@@ -1,33 +1,21 @@
package chess; package chess;
import chess.ai.DumbAI;
import chess.controller.CommandExecutor; import chess.controller.CommandExecutor;
import chess.controller.commands.NewGameCommand; import chess.controller.commands.NewGameCommand;
import chess.controller.event.GameAdaptator;
import chess.model.ChessBoard;
import chess.model.Color;
import chess.model.Game; import chess.model.Game;
import chess.pgn.PgnExport; import chess.view.DDDrender.DDDView;
import chess.view.render.Window;
public class OpenGLMain { public class OpenGLMain {
public static void main(String[] args) { public static void main(String[] args) {
Game game = new Game(new ChessBoard()); Game game = new Game();
CommandExecutor commandExecutor = new CommandExecutor(game); CommandExecutor commandExecutor = new CommandExecutor(game);
Window window = new Window(commandExecutor); DDDView ddd = new DDDView(commandExecutor);
commandExecutor.addListener(window); commandExecutor.addListener(ddd);
// DumbAI ai = new DumbAI(commandExecutor, Color.Black);
// commandExecutor.addListener(ai);
// DumbAI ai2 = new DumbAI(commandExecutor, Color.White);
// commandExecutor.addListener(ai2);
commandExecutor.executeCommand(new NewGameCommand()); commandExecutor.executeCommand(new NewGameCommand());
window.run(); ddd.run();
commandExecutor.close();
} }
} }

View File

@@ -1,10 +1,10 @@
package chess; package chess;
import chess.ai.DumbAI; import chess.ai.DumbAI;
import chess.ai.HungryAI;
import chess.controller.CommandExecutor; import chess.controller.CommandExecutor;
import chess.controller.commands.NewGameCommand; import chess.controller.commands.NewGameCommand;
import chess.controller.event.GameAdaptator; import chess.controller.event.GameAdaptator;
import chess.model.ChessBoard;
import chess.model.Color; import chess.model.Color;
import chess.model.Game; import chess.model.Game;
import chess.pgn.PgnExport; import chess.pgn.PgnExport;
@@ -12,7 +12,7 @@ import chess.view.simplerender.Window;
public class SwingMain { public class SwingMain {
public static void main(String[] args) { public static void main(String[] args) {
Game game = new Game(new ChessBoard()); Game game = new Game();
CommandExecutor commandExecutor = new CommandExecutor(game); CommandExecutor commandExecutor = new CommandExecutor(game);
Window window = new Window(commandExecutor, false); Window window = new Window(commandExecutor, false);
@@ -21,7 +21,7 @@ public class SwingMain {
DumbAI ai = new DumbAI(commandExecutor, Color.Black); DumbAI ai = new DumbAI(commandExecutor, Color.Black);
commandExecutor.addListener(ai); commandExecutor.addListener(ai);
DumbAI ai2 = new DumbAI(commandExecutor, Color.White); HungryAI ai2 = new HungryAI(commandExecutor, Color.White);
commandExecutor.addListener(ai2); commandExecutor.addListener(ai2);
commandExecutor.addListener(new GameAdaptator(){ commandExecutor.addListener(new GameAdaptator(){

View File

@@ -0,0 +1,68 @@
package chess.ai;
import java.util.List;
import chess.controller.Command;
import chess.controller.CommandExecutor;
import chess.controller.commands.GetPieceAtCommand;
import chess.controller.commands.GetPlayerMovesCommand;
import chess.controller.commands.GetAllowedCastlingsCommand;
import chess.controller.commands.GetAllowedCastlingsCommand.CastlingResult;
import chess.controller.event.GameAdaptator;
import chess.model.Color;
import chess.model.Coordinate;
import chess.model.Move;
import chess.model.Piece;
public abstract class AI extends GameAdaptator{
protected final CommandExecutor commandExecutor;
protected final Color color;
public AI(CommandExecutor commandExecutor, Color color) {
this.commandExecutor = commandExecutor;
this.color = color;
}
protected abstract void play();
protected abstract void promote(Coordinate pawnCoords);
@Override
public void onPlayerTurn(Color color, boolean undone) {
if (this.color != color || undone)
return;
play();
}
@Override
public void onPromotePawn(Coordinate pieceCoords) {
Piece pawn = pieceAt(pieceCoords);
if (pawn.getColor() != this.color)
return;
promote(pieceCoords);
}
protected Piece pieceAt(Coordinate coordinate) {
GetPieceAtCommand command = new GetPieceAtCommand(coordinate);
sendCommand(command);
return command.getPiece();
}
protected List<Move> getAllowedMoves() {
GetPlayerMovesCommand cmd = new GetPlayerMovesCommand();
sendCommand(cmd);
return cmd.getMoves();
}
protected CastlingResult getAllowedCastlings() {
GetAllowedCastlingsCommand cmd2 = new GetAllowedCastlingsCommand();
sendCommand(cmd2);
return cmd2.getCastlingResult();
}
protected void sendCommand(Command command) {
this.commandExecutor.executeCommand(command);
}
}

View File

@@ -3,46 +3,28 @@ package chess.ai;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import chess.controller.Command;
import chess.controller.CommandExecutor; import chess.controller.CommandExecutor;
import chess.controller.commands.CastlingCommand; import chess.controller.commands.CastlingCommand;
import chess.controller.commands.GetAllowedCastlingsCommand; import chess.controller.commands.GetAllowedCastlingsCommand.CastlingResult;
import chess.controller.commands.GetPieceAtCommand;
import chess.controller.commands.GetPlayerMovesCommand;
import chess.controller.commands.MoveCommand; import chess.controller.commands.MoveCommand;
import chess.controller.commands.PromoteCommand; import chess.controller.commands.PromoteCommand;
import chess.controller.commands.GetAllowedCastlingsCommand.CastlingResult;
import chess.controller.commands.PromoteCommand.PromoteType; import chess.controller.commands.PromoteCommand.PromoteType;
import chess.controller.event.GameAdaptator;
import chess.model.Color; import chess.model.Color;
import chess.model.Coordinate; import chess.model.Coordinate;
import chess.model.Move; import chess.model.Move;
import chess.model.Piece;
public class DumbAI extends GameAdaptator { public class DumbAI extends AI {
private final Color player;
private final CommandExecutor commandExecutor;
private final Random random = new Random(); private final Random random = new Random();
public DumbAI(CommandExecutor commandExecutor, Color color) { public DumbAI(CommandExecutor commandExecutor, Color color) {
this.player = color; super(commandExecutor, color);
this.commandExecutor = commandExecutor;
} }
@Override @Override
public void onPlayerTurn(Color color) { protected void play() {
if (color != player) CastlingResult castlings = getAllowedCastlings();
return; List<Move> moves = getAllowedMoves();
GetPlayerMovesCommand cmd = new GetPlayerMovesCommand();
sendCommand(cmd);
GetAllowedCastlingsCommand cmd2 = new GetAllowedCastlingsCommand();
sendCommand(cmd2);
CastlingResult castlings = cmd2.getCastlingResult();
List<Move> moves = cmd.getMoves();
switch (castlings) { switch (castlings) {
case Both: { case Both: {
@@ -53,20 +35,12 @@ public class DumbAI extends GameAdaptator {
return; return;
} }
case Small: { case Small:
int randomMove = this.random.nextInt(moves.size() + 1);
if (randomMove != moves.size())
break;
this.commandExecutor.executeCommand(new CastlingCommand(false));
return;
}
case Big: { case Big: {
int randomMove = this.random.nextInt(moves.size() + 1); int randomMove = this.random.nextInt(moves.size() + 1);
if (randomMove != moves.size()) if (randomMove != moves.size())
break; break;
this.commandExecutor.executeCommand(new CastlingCommand(true)); this.commandExecutor.executeCommand(new CastlingCommand(castlings == CastlingResult.Big));
return; return;
} }
@@ -76,27 +50,12 @@ public class DumbAI extends GameAdaptator {
int randomMove = this.random.nextInt(moves.size()); int randomMove = this.random.nextInt(moves.size());
this.commandExecutor.executeCommand(new MoveCommand(moves.get(randomMove))); this.commandExecutor.executeCommand(new MoveCommand(moves.get(randomMove)));
} }
@Override @Override
public void onPromotePawn(Coordinate pieceCoords) { protected void promote(Coordinate pawnCoords) {
Piece pawn = pieceAt(pieceCoords);
if (pawn.getColor() != this.player)
return;
int promote = this.random.nextInt(PromoteType.values().length); int promote = this.random.nextInt(PromoteType.values().length);
this.commandExecutor.executeCommand(new PromoteCommand(PromoteType.values()[promote])); this.commandExecutor.executeCommand(new PromoteCommand(PromoteType.values()[promote]));
} }
private Piece pieceAt(Coordinate coordinate) {
GetPieceAtCommand command = new GetPieceAtCommand(coordinate);
sendCommand(command);
return command.getPiece();
}
private void sendCommand(Command command) {
this.commandExecutor.executeCommand(command);
}
} }

View File

@@ -0,0 +1,61 @@
package chess.ai;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import chess.controller.CommandExecutor;
import chess.controller.commands.MoveCommand;
import chess.controller.commands.PromoteCommand;
import chess.controller.commands.PromoteCommand.PromoteType;
import chess.model.Color;
import chess.model.Coordinate;
import chess.model.Move;
import chess.model.Piece;
public class HungryAI extends AI {
private final PieceCost pieceCost;
private final Random random;
public HungryAI(CommandExecutor commandExecutor, Color color) {
super(commandExecutor, color);
this.pieceCost = new PieceCost(color);
this.random = new Random();
}
private int getMoveCost(Move move) {
Piece piece = pieceAt(move.getDeadPieceCoords());
return pieceCost.getCost(piece);
}
private List<Move> getBestMoves() {
List<Move> moves = getAllowedMoves();
List<Move> bestMoves = new ArrayList<>();
int bestCost = 0;
for (Move move : moves) {
int moveCost = getMoveCost(move);
if (moveCost == bestCost) {
bestMoves.add(move);
} else if (moveCost > bestCost) {
bestMoves.clear();
bestMoves.add(move);
bestCost = moveCost;
}
}
return bestMoves;
}
@Override
protected void play() {
List<Move> bestMoves = getBestMoves();
int randomMove = this.random.nextInt(bestMoves.size());
this.commandExecutor.executeCommand(new MoveCommand(bestMoves.get(randomMove)));
}
@Override
protected void promote(Coordinate pawnCoords) {
sendCommand(new PromoteCommand(PromoteType.Queen));
}
}

View File

@@ -0,0 +1,60 @@
package chess.ai;
import chess.model.Color;
import chess.model.Piece;
import chess.model.PieceVisitor;
import chess.model.pieces.Bishop;
import chess.model.pieces.King;
import chess.model.pieces.Knight;
import chess.model.pieces.Pawn;
import chess.model.pieces.Queen;
import chess.model.pieces.Rook;
public class PieceCost implements PieceVisitor<Integer> {
private final Color player;
public PieceCost(Color color) {
this.player = color;
}
public int getCost(Piece piece) {
if (piece == null)
return 0;
int cost = visit(piece);
if (piece.getColor() == player)
cost = -cost;
return cost;
}
@Override
public Integer visitPiece(Bishop bishop) {
return 3;
}
@Override
public Integer visitPiece(King king) {
return 90;
}
@Override
public Integer visitPiece(Knight knight) {
return 3;
}
@Override
public Integer visitPiece(Pawn pawn) {
return 1;
}
@Override
public Integer visitPiece(Queen queen) {
return 9;
}
@Override
public Integer visitPiece(Rook rook) {
return 5;
}
}

View File

@@ -57,14 +57,14 @@ public class CommandExecutor {
this.dispatcher.onGameEnd(); this.dispatcher.onGameEnd();
return; return;
} }
switchPlayerTurn(); switchPlayerTurn(command instanceof UndoCommand);
return; return;
} }
} }
private void switchPlayerTurn() { private void switchPlayerTurn(boolean undone) {
this.game.switchPlayerTurn(); this.game.switchPlayerTurn();
this.dispatcher.onPlayerTurn(this.game.getPlayerTurn()); this.dispatcher.onPlayerTurn(this.game.getPlayerTurn(), undone);
} }
/** /**

View File

@@ -51,7 +51,7 @@ public class NewGameCommand extends Command {
game.reset(); game.reset();
outputSystem.onGameStart(); outputSystem.onGameStart();
outputSystem.onPlayerTurn(game.getPlayerTurn()); outputSystem.onPlayerTurn(game.getPlayerTurn(), false);
return CommandResult.NotMoved; return CommandResult.NotMoved;
} }

View File

@@ -43,7 +43,7 @@ public class EmptyGameDispatcher extends GameDispatcher {
} }
@Override @Override
public void onPlayerTurn(Color color) { public void onPlayerTurn(Color color, boolean undone) {
} }
@Override @Override

View File

@@ -7,7 +7,7 @@ import chess.model.Move;
public abstract class GameAdaptator implements GameListener { public abstract class GameAdaptator implements GameListener {
@Override @Override
public void onPlayerTurn(Color color) {} public void onPlayerTurn(Color color, boolean undone) {}
@Override @Override
public void onWin(Color color) {} public void onWin(Color color) {}

View File

@@ -29,8 +29,8 @@ public class GameDispatcher implements GameListener {
} }
@Override @Override
public void onPlayerTurn(Color color) { public void onPlayerTurn(Color color, boolean undone) {
asyncForEachCall((l) -> l.onPlayerTurn(color)); asyncForEachCall((l) -> l.onPlayerTurn(color, undone));
} }
@Override @Override

View File

@@ -56,8 +56,9 @@ public interface GameListener {
/** /**
* Invoked when it's the player turn * Invoked when it's the player turn
* @param color the color of the player who should play * @param color the color of the player who should play
* @param undone true if it's a result of an undo command
*/ */
void onPlayerTurn(Color color); void onPlayerTurn(Color color, boolean undone);
/** /**
* Invoked when a pawn should be promoted * Invoked when a pawn should be promoted

View File

@@ -20,8 +20,8 @@ public class Game {
Draw, Check, CheckMate, OnGoing, Pat; Draw, Check, CheckMate, OnGoing, Pat;
} }
public Game(ChessBoard board) { public Game() {
this.board = board; this.board = new ChessBoard();
this.movesHistory = new Stack<>(); this.movesHistory = new Stack<>();
this.traitsPos = new HashMap<>(); this.traitsPos = new HashMap<>();
} }

View File

@@ -11,7 +11,6 @@ import chess.controller.commands.MoveCommand;
import chess.controller.commands.NewGameCommand; import chess.controller.commands.NewGameCommand;
import chess.controller.commands.PromoteCommand; import chess.controller.commands.PromoteCommand;
import chess.controller.event.EmptyGameDispatcher; import chess.controller.event.EmptyGameDispatcher;
import chess.model.ChessBoard;
import chess.model.Color; import chess.model.Color;
import chess.model.Coordinate; import chess.model.Coordinate;
import chess.model.Game; import chess.model.Game;
@@ -22,7 +21,7 @@ import chess.model.pieces.Pawn;
public class PgnExport { public class PgnExport {
// public static void main(String[] args) { // public static void main(String[] args) {
// final Game game = new Game(new ChessBoard()); // final Game game = new Game();
// final CommandExecutor commandExecutor = new CommandExecutor(game); // final CommandExecutor commandExecutor = new CommandExecutor(game);
// DumbAI ai1 = new DumbAI(commandExecutor, Color.White); // DumbAI ai1 = new DumbAI(commandExecutor, Color.White);
@@ -190,8 +189,7 @@ public class PgnExport {
public static String exportGame(Game game) { public static String exportGame(Game game) {
ChessBoard board = new ChessBoard(); Game virtualGame = new Game();
Game virtualGame = new Game(board);
CommandExecutor executor = new CommandExecutor(virtualGame, new EmptyGameDispatcher()); CommandExecutor executor = new CommandExecutor(virtualGame, new EmptyGameDispatcher());
executor.executeCommand(new NewGameCommand()); executor.executeCommand(new NewGameCommand());

View File

@@ -0,0 +1,29 @@
package chess.simulator;
import chess.controller.CommandExecutor;
import chess.model.Coordinate;
import chess.model.Move;
import java.util.Arrays;
import java.util.List;
public class EnPassantTest extends Simulator{
public EnPassantTest(CommandExecutor commandExecutor) {
super(commandExecutor);
}
@Override
protected List<Move> getMoves() {
return Arrays.asList(
// white pawn
new Move(new Coordinate(4, 6), new Coordinate(4, 4)),
// black pawn 1
new Move(new Coordinate(4, 1), new Coordinate(4, 2)),
// white pawn
new Move(new Coordinate(4, 4), new Coordinate(4, 3)),
// black pawn #2
new Move(new Coordinate(3, 1), new Coordinate(3, 3)));
}
}

View File

@@ -0,0 +1,102 @@
package chess.view.DDDrender;
import org.joml.Matrix4f;
import org.joml.Vector2f;
import org.joml.Vector3f;
import org.joml.Vector4f;
public class Camera {
public static final float fov = 70.0f;
public static final float zNear = 0.01f;
public static final float zFar = 1000.0f;
private static final Vector3f up = new Vector3f(0.0f, 1.0f, 0.0f);
private static final Vector3f center = new Vector3f(0.0f, 0.0f, 0.0f);
private final float distance = 1.5f;
private final float camHeight = 1.5f;
private float aspectRatio;
private float angle;
private Vector3f pos;
public Camera() {
this.pos = new Vector3f(0.0f, camHeight, 0.0f);
setRotateAngle(0.0f);
}
public void move(float x, float y) {
this.pos.x += x;
this.pos.y += y;
}
public void setRotateAngle(float angle) {
this.angle = angle;
updatePostion();
}
private void updatePostion() {
final float finalX = (float) Math.sin(angle);
final float finalZ = (float) -Math.cos(angle);
this.pos.set(distance * finalX, this.pos.get(1), distance * finalZ);
}
public float getRotateAngle() {
return angle;
}
public Vector3f getPos() {
return pos;
}
public float getFov() {
return fov;
}
public void setX(float x) {
this.pos.x = x;
}
public void setY(float y) {
this.pos.y = y;
}
public void setZ(float z) {
this.pos.z = z;
}
public void setPosition(Vector3f pos) {
this.pos = pos;
}
public void setAspectRatio(float aspectRatio) {
this.aspectRatio = aspectRatio;
}
public Matrix4f getPerspectiveMatrix() {
return new Matrix4f().perspective((float) (Math.toRadians(fov)), aspectRatio, zNear, zFar);
}
public Matrix4f getViewMatrix() {
return new Matrix4f().lookAt(pos, center, up);
}
public Vector2f getCursorWorldFloorPos(Vector2f screenPos, int windowWidth, int windowHeight) {
float relativeX = (screenPos.x / (float) windowWidth * 2.0f) - 1.0f;
float relativeY = 1.0f - (screenPos.y / (float) windowHeight * 2.0f);
Vector4f rayClip = new Vector4f(relativeX, relativeY, -1.0f, 1.0f);
Vector4f rayEye = getPerspectiveMatrix().invert().transform(rayClip);
rayEye = new Vector4f(rayEye.x, rayEye.y, -1.0f, 0.0f);
Vector4f rayWorld = getViewMatrix().invert().transform(rayEye);
float lambda = -this.pos.y / rayWorld.y;
return new Vector2f(lambda * rayWorld.x + this.pos.x, lambda *
rayWorld.z + this.pos.z);
}
}

View File

@@ -0,0 +1,27 @@
package chess.view.DDDrender;
import java.io.Closeable;
import java.io.IOException;
import java.util.List;
import chess.view.DDDrender.opengl.VertexArray;
public class DDDModel implements Closeable {
private final List<VertexArray> vaos;
public DDDModel(List<VertexArray> vaos) {
this.vaos = vaos;
}
public List<VertexArray> getVaos() {
return vaos;
}
@Override
public void close() throws IOException {
for (VertexArray vertexArray : vaos) {
vertexArray.close();
}
}
}

View File

@@ -0,0 +1,21 @@
package chess.view.DDDrender;
import org.joml.Vector2f;
import chess.model.Coordinate;
class DDDPlacement {
public static Vector2f coordinatesToVector(Coordinate coo) {
return coordinatesToVector(coo.getX(), coo.getY());
}
public static Vector2f coordinatesToVector(float x, float y) {
return new Vector2f(1.0f - 0.125f - x * 0.250f, 1.0f - 0.125f - y * 0.250f);
}
public static Coordinate vectorToCoordinates(Vector2f pos) {
int x = (int) ((1.0f - pos.x) * 4.0f);
int y = (int) ((1.0f - pos.y) * 4.0f);
return new Coordinate(x, y);
}
}

View File

@@ -0,0 +1,125 @@
package chess.view.DDDrender;
import java.io.IOException;
import org.joml.Vector2f;
import org.joml.Vector3f;
import chess.controller.CommandExecutor;
import chess.controller.commands.GetPieceAtCommand;
import chess.controller.event.GameAdaptator;
import chess.model.Color;
import chess.model.Coordinate;
import chess.model.Move;
import chess.model.Piece;
import chess.view.DDDrender.world.BoardEntity;
import chess.view.DDDrender.world.PieceEntity;
import chess.view.DDDrender.world.World;
public class DDDView extends GameAdaptator {
private static final Vector3f BLACK = new Vector3f(0.3f, 0.3f, 0.3f);
private static final Vector3f WHITE = new Vector3f(1.0f, 1.0f, 1.0f);
private final CommandExecutor commandExecutor;
private final Window window;
private final World world;
private BoardEntity boardEntity;
public DDDView(CommandExecutor commandExecutor) {
this.commandExecutor = commandExecutor;
this.world = new World();
this.window = new Window(new Renderer(), this.world, new Camera());
}
// Invoked when a cell is clicked
private void onCellClick(Coordinate coordinate) {
System.out.println("Cell clicked : " + coordinate);
}
// Invoked when a cell is hovered
private void onCellEnter(Coordinate coordinate) {
// small test turning a cell red when hovered
this.boardEntity.setCellColor(coordinate, new Vector3f(1, 0, 0));
Piece p = pieceAt(coordinate);
if (p == null)
return;
this.world.getPiece(coordinate).setColor(new Vector3f(1, 0, 0));
}
// Invoked when a cell is not hovered anymore
private void onCellExit(Coordinate coordinate) {
this.boardEntity.resetCellColor(coordinate);
Piece p = pieceAt(coordinate);
if (p == null)
return;
this.world.getPiece(coordinate).setColor(p.getColor() == Color.White ? WHITE : BLACK);
}
private Piece pieceAt(Coordinate pos) {
GetPieceAtCommand cmd = new GetPieceAtCommand(pos);
this.commandExecutor.executeCommand(cmd);
return cmd.getPiece();
}
@Override
public void onGameStart() {
this.window.scheduleTask(() -> {
try {
initBoard();
} catch (IOException e) {
e.printStackTrace();
}
// start listening to mouse events
this.window.OnCellClick.connect(this::onCellClick);
this.window.OnCellEnter.connect(this::onCellEnter);
this.window.OnCellExit.connect(this::onCellExit);
});
}
private void initBoard() throws IOException {
for (int i = 0; i < Coordinate.VALUE_MAX; i++) {
for (int j = 0; j < Coordinate.VALUE_MAX; j++) {
Coordinate pos = new Coordinate(i, j);
Piece piece = pieceAt(pos);
if (piece == null)
continue;
Vector2f pieceBoardPos = DDDPlacement.coordinatesToVector(pos);
Vector3f pieceWorldPos = new Vector3f(pieceBoardPos.x(), 0, pieceBoardPos.y());
PieceEntity entity = new PieceEntity(piece, pieceWorldPos,
piece.getColor() == Color.White ? WHITE : BLACK,
piece.getColor() == Color.White ? 0.0f : (float) Math.PI);
this.world.addPiece(entity, pos);
}
}
this.boardEntity = new BoardEntity();
this.world.addEntity(this.boardEntity);
}
@Override
public void onMove(Move move) {
// update world internal positions
this.world.movePiece(this.world.getPiece(move.getStart()), move);
}
public void run() {
// this.window.addRegularTask((delta) -> {
// final float angle = 1f;
// final Camera cam = this.world.getCamera();
// cam.setRotateAngle(cam.getRotateAngle() + angle * delta);
// });
this.window.run();
// free OpenGL resources
try {
this.window.close();
this.world.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,68 @@
package chess.view.DDDrender;
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT;
import java.io.Closeable;
import java.io.IOException;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.lwjgl.opengl.GL30;
import chess.view.DDDrender.opengl.VertexArray;
import chess.view.DDDrender.shader.BoardShader;
import chess.view.DDDrender.shader.PieceShader;
import chess.view.DDDrender.shader.ShaderProgram;
public class Renderer implements Closeable{
private BoardShader boardShader;
private PieceShader pieceShader;
public Renderer() {
this.boardShader = new BoardShader();
this.pieceShader = new PieceShader();
}
public void Init() {
boardShader.LoadShader();
pieceShader.LoadShader();
}
public void Update(Camera cam) {
this.boardShader.Start();
this.boardShader.SetCamMatrix(cam);
this.pieceShader.Start();
this.pieceShader.SetCamMatrix(cam);
}
public void Render(DDDModel model, Vector3f color, Vector3f position, float rotation) {
this.pieceShader.Start();
this.pieceShader.setModelColor(color);
this.pieceShader.setModelTransform(new Matrix4f().translate(position).rotate(rotation, new Vector3f(0, 1, 0)));
Render(model);
}
public void Render(DDDModel model) {
for (int i = 0; i < model.getVaos().size(); i++) {
VertexArray vao = model.getVaos().get(i);
RenderVao(this.pieceShader, vao);
}
}
public void RenderVao(ShaderProgram shader, VertexArray vertexArray) {
shader.Start();
vertexArray.Bind();
GL30.glDrawElements(GL30.GL_TRIANGLES, vertexArray.GetVertexCount(), GL_UNSIGNED_INT, 0);
vertexArray.Unbind();
}
public BoardShader getBoardShader() {
return boardShader;
}
@Override
public void close() throws IOException {
this.boardShader.close();
this.pieceShader.close();
}
}

View File

@@ -0,0 +1,235 @@
package chess.view.DDDrender;
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.world.Entity;
import chess.view.DDDrender.world.World;
import common.Signal1;
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 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 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 void addRegularTask(Consumer<Float> task) {
this.regularTasks.add(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();
}
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);
} // the stack frame is popped automatically
// Make the OpenGL context current
glfwMakeContextCurrent(window);
// Enable v-sync
glfwSwapInterval(1);
// Make the window visible
glfwShowWindow(window);
}
private void render(float delta, float aspectRatio) {
cam.setAspectRatio(aspectRatio);
renderer.Update(cam);
renderWorld();
}
private void renderWorld() {
for (Entity entity : this.world.getEntites()) {
entity.render(this.renderer);
}
}
private 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(int windowWidth, int windowHeight) {
double x[] = new double[1];
double y[] = new double[1];
glfwGetCursorPos(this.window, x, y);
Vector2f cursorPos = this.cam.getCursorWorldFloorPos(new Vector2f((float) x[0], (float) y[0]), 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;
}
glfwSetMouseButtonCallback(this.window, (window, button, action, mods) -> {
if (button == GLFW_MOUSE_BUTTON_1 && action == GLFW_PRESS) {
if (this.lastCell.isValid())
this.OnCellClick.emit(this.lastCell);
}
});
}
private void loop() {
// This line is critical for LWJGL's interoperation with GLFW's
// OpenGL context, or any context that is managed externally.
// LWJGL detects the context that is current in the current thread,
// creates the GLCapabilities instance and makes the OpenGL
// bindings available for use.
GL.createCapabilities();
renderer.Init();
// 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);
double lastTime = glfwGetTime();
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)) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
double currentTime = glfwGetTime();
float deltaTime = (float) (currentTime - lastTime);
render(deltaTime, (float) width[0] / (float) height[0]);
lastTime = glfwGetTime();
glfwSwapBuffers(window); // swap the color buffers
// Poll for window events. The key callback above will only be
// invoked during this call.
glfwPollEvents();
checkCursor(width[0], height[0]);
executeTasks(deltaTime);
glfwGetWindowSize(window, width, height);
glViewport(0, 0, width[0], height[0]);
}
}
@Override
public void close() throws IOException {
this.renderer.close();
}
}

View File

@@ -0,0 +1,98 @@
package chess.view.DDDrender.loader;
import org.joml.Vector3f;
import chess.view.DDDrender.opengl.ElementBuffer;
import chess.view.DDDrender.opengl.VertexArray;
import chess.view.DDDrender.opengl.VertexBuffer;
public class BoardModelLoader {
private static final int BOARD_WIDTH = 8;
private static final int BOARD_HEIGHT = 8;
private static final int BOARD_SIZE = BOARD_WIDTH * BOARD_HEIGHT;
private static final int SQUARE_VERTEX_COUNT = 4;
private static final Vector3f WHITE = new Vector3f(1, 1, 1);
private static final Vector3f BLACK = new Vector3f(0, 0, 0);
private static float[] GetBoardPositions() {
float[] positions = new float[BOARD_SIZE * SQUARE_VERTEX_COUNT * 3];
for (int i = 0; i < BOARD_WIDTH; i++) {
for (int j = 0; j < BOARD_HEIGHT; j++) {
float x = i / (float) BOARD_WIDTH;
float dx = (i + 1) / (float) BOARD_WIDTH;
float z = j / (float) BOARD_HEIGHT;
float dz = (j + 1) / (float) BOARD_HEIGHT;
float trueX = 2 * x - 1;
float trueZ = 2 * z - 1;
float trueDX = 2 * dx - 1;
float trueDZ = 2 * dz - 1;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3] = trueX;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 1] = 0.0f;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 2] = trueZ;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 3] = trueDX;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 4] = 0.0f;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 5] = trueZ;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 6] = trueX;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 7] = 0.0f;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 8] = trueDZ;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 9] = trueDX;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 10] = 0.0f;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 11] = trueDZ;
}
}
return positions;
}
private static float[] GetBoardColors() {
float[] colors = new float[BOARD_SIZE * SQUARE_VERTEX_COUNT * 3];
for (int i = 0; i < BOARD_WIDTH; i++) {
for (int j = 0; j < BOARD_HEIGHT; j++) {
Vector3f color = (i + j) % 2 == 0 ? WHITE : BLACK;
int squareIndex = i * BOARD_WIDTH + j;
for (int k = 0; k < SQUARE_VERTEX_COUNT; k++) {
colors[squareIndex * SQUARE_VERTEX_COUNT * 3 + k * 3] = color.x;
colors[squareIndex * SQUARE_VERTEX_COUNT * 3 + k * 3 + 1] = color.y;
colors[squareIndex * SQUARE_VERTEX_COUNT * 3 + k * 3 + 2] = color.z;
}
}
}
return colors;
}
private static int[] GetBoardIndicies() {
int[] indices = new int[BOARD_SIZE * 6];
for (int i = 0; i < BOARD_SIZE; i++) {
indices[i * 6] = i * 4;
indices[i * 6 + 1] = i * 4 + 2;
indices[i * 6 + 2] = i * 4 + 1;
indices[i * 6 + 3] = i * 4 + 1;
indices[i * 6 + 4] = i * 4 + 2;
indices[i * 6 + 5] = i * 4 + 3;
}
return indices;
}
public static VertexArray GetBoardModel() {
ElementBuffer eBuffer = new ElementBuffer(GetBoardIndicies());
VertexArray vao = new VertexArray(eBuffer);
VertexBuffer positionBuffer = new VertexBuffer(GetBoardPositions(), 3);
positionBuffer.AddVertexAttribPointer(0, 3, 0);
VertexBuffer colorBuffer = new VertexBuffer(GetBoardColors(), 3);
colorBuffer.AddVertexAttribPointer(1, 3, 0);
vao.Bind();
vao.BindVertexBuffer(positionBuffer);
vao.BindVertexBuffer(colorBuffer);
vao.Unbind();
return vao;
}
}

View File

@@ -0,0 +1,130 @@
package chess.view.DDDrender.loader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;
import org.lwjgl.assimp.AIFace.Buffer;
import org.lwjgl.assimp.AIMesh;
import org.lwjgl.assimp.AINode;
import org.lwjgl.assimp.AIScene;
import org.lwjgl.assimp.AIVector3D;
import org.lwjgl.assimp.Assimp;
import org.lwjgl.system.MemoryUtil;
import chess.view.AssetManager;
import chess.view.DDDrender.DDDModel;
import chess.view.DDDrender.opengl.ElementBuffer;
import chess.view.DDDrender.opengl.VertexArray;
import chess.view.DDDrender.opengl.VertexBuffer;
public class ModelLoader {
private static final int VERTEX_SIZE = 3;
private static final int VERTEX_POSITION_INDEX = 0;
private static final int VERTEX_NORMAL_INDEX = 1;
private static float[] toFloatArray(List<Float> list) {
float[] result = new float[list.size()];
for (int i = 0; i < list.size(); i++) {
result[i] = list.get(i);
}
return result;
}
private static int[] toIntArray(List<Integer> list) {
int[] result = new int[list.size()];
for (int i = 0; i < list.size(); i++) {
result[i] = list.get(i);
}
return result;
}
private static VertexArray processMesh(AIMesh mesh, AIScene scene) {
List<Float> positions = new ArrayList<>();
List<Float> normals = new ArrayList<>();
List<Integer> indicies = new ArrayList<>();
Buffer faces = mesh.mFaces();
int faceNumber = mesh.mNumFaces();
for (int i = 0; i < faceNumber; i++) {
IntBuffer faceIndicies = faces.get(i).mIndices();
for (int j = 0; j < faceIndicies.capacity(); j++) {
indicies.add(faceIndicies.get(j));
}
}
int vertNumber = mesh.mNumVertices();
org.lwjgl.assimp.AIVector3D.Buffer vertecies = mesh.mVertices();
for (int i = 0; i < vertNumber; i++) {
AIVector3D vertex = vertecies.get(i);
positions.add(vertex.x());
positions.add(vertex.y());
positions.add(vertex.z());
}
org.lwjgl.assimp.AIVector3D.Buffer vertexNormals = mesh.mNormals();
for (int i = 0; i < vertNumber; i++) {
AIVector3D normal = vertexNormals.get(i);
normals.add(normal.x());
normals.add(normal.y());
normals.add(normal.z());
}
VertexBuffer positionVBO = new VertexBuffer(toFloatArray(positions), VERTEX_SIZE);
positionVBO.AddVertexAttribPointer(VERTEX_POSITION_INDEX, VERTEX_SIZE, 0);
VertexBuffer normalVBO = new VertexBuffer(toFloatArray(normals), VERTEX_SIZE);
normalVBO.AddVertexAttribPointer(VERTEX_NORMAL_INDEX, VERTEX_SIZE, 0);
VertexArray vao = new VertexArray(new ElementBuffer(toIntArray(indicies)));
vao.Bind();
vao.BindVertexBuffer(positionVBO);
vao.BindVertexBuffer(normalVBO);
vao.Unbind();
return vao;
}
private static void processNode(AINode node, AIScene scene, List<VertexArray> meshes) {
for (int i = 0; i < node.mNumChildren(); i++) {
AINode child = AINode.create(node.mChildren().get(i));
processNode(child, scene, meshes);
}
for (int i = 0; i < node.mNumMeshes(); i++) {
AIMesh mesh = AIMesh.create(scene.mMeshes().get(node.mMeshes().get(i)));
meshes.add(processMesh(mesh, scene));
}
}
public static DDDModel loadModel(String filename) throws IOException {
InputStream input = AssetManager.getResource(filename);
byte[] buffer = input.readAllBytes();
ByteBuffer data = MemoryUtil.memCalloc(buffer.length);
data.put(buffer);
data.flip();
AIScene scene = Assimp.aiImportFileFromMemory(
data,
Assimp.aiProcess_Triangulate | Assimp.aiProcess_PreTransformVertices | Assimp.aiProcess_GlobalScale
| Assimp.aiProcess_ValidateDataStructure,
"");
if (scene == null)
System.err.println(Assimp.aiGetErrorString());
List<VertexArray> vertecies = new ArrayList<>();
processNode(scene.mRootNode(), scene, vertecies);
MemoryUtil.memFree(data);
return new DDDModel(vertecies);
}
}

View File

@@ -0,0 +1,75 @@
package chess.view.DDDrender.loader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import chess.model.Color;
import chess.model.Piece;
import chess.model.PieceVisitor;
import chess.model.pieces.Bishop;
import chess.model.pieces.King;
import chess.model.pieces.Knight;
import chess.model.pieces.Pawn;
import chess.model.pieces.Queen;
import chess.model.pieces.Rook;
import chess.view.DDDrender.DDDModel;
public class Piece3DModel implements PieceVisitor<String> {
private static final String basePath = "3d/";
private static final Map<String, DDDModel> cache = new HashMap<>();
public DDDModel getModel(Piece piece) throws IOException {
if (piece == null)
return null;
String path = basePath + colorToString(piece.getColor()) + "-" + visit(piece) + ".fbx";
return getModel(path);
}
private DDDModel getModel(String path) throws IOException {
DDDModel model = cache.get(path);
if (model != null)
return model;
model = ModelLoader.loadModel(path);
cache.put(path, model);
return model;
}
private String colorToString(Color color) {
return color == Color.Black ? "black" : "white";
}
@Override
public String visitPiece(Bishop bishop) {
return "bishop";
}
@Override
public String visitPiece(King king) {
return "king";
}
@Override
public String visitPiece(Knight knight) {
return "knight";
}
@Override
public String visitPiece(Pawn pawn) {
return "pawn";
}
@Override
public String visitPiece(Queen queen) {
return "queen";
}
@Override
public String visitPiece(Rook rook) {
return "rook";
}
}

View File

@@ -1,10 +1,13 @@
package chess.view.render; package chess.view.DDDrender.opengl;
import java.io.Closeable;
import java.io.IOException;
import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GL30;
public class ElementBuffer { public class ElementBuffer implements Closeable {
private int id; private final int id;
private int indiciesCount; private final int indiciesCount;
public ElementBuffer(int[] indicies) { public ElementBuffer(int[] indicies) {
this.indiciesCount = indicies.length; this.indiciesCount = indicies.length;
@@ -15,10 +18,6 @@ public class ElementBuffer {
Unbind(); Unbind();
} }
public void Destroy() {
GL30.glDeleteBuffers(this.id);
}
public void Bind() { public void Bind() {
GL30.glBindBuffer(GL30.GL_ELEMENT_ARRAY_BUFFER, this.id); GL30.glBindBuffer(GL30.GL_ELEMENT_ARRAY_BUFFER, this.id);
} }
@@ -30,4 +29,9 @@ public class ElementBuffer {
public int GetIndiciesCount() { public int GetIndiciesCount() {
return this.indiciesCount; return this.indiciesCount;
} }
@Override
public void close() throws IOException {
GL30.glDeleteBuffers(this.id);
}
} }

View File

@@ -1,14 +1,17 @@
package chess.view.render; package chess.view.DDDrender.opengl;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GL30;
public class VertexArray { public class VertexArray implements Closeable {
private int id;
private ElementBuffer elementBuffer; private final int id;
private List<VertexBuffer> vertexBuffers; private final ElementBuffer elementBuffer;
private final List<VertexBuffer> vertexBuffers;
public VertexArray(ElementBuffer elementBuffer) { public VertexArray(ElementBuffer elementBuffer) {
this.id = GL30.glGenVertexArrays(); this.id = GL30.glGenVertexArrays();
@@ -19,10 +22,6 @@ public class VertexArray {
Unbind(); Unbind();
} }
public void Destroy() {
GL30.glDeleteBuffers(this.id);
}
public int GetVertexCount() { public int GetVertexCount() {
return this.elementBuffer.GetIndiciesCount(); return this.elementBuffer.GetIndiciesCount();
} }
@@ -44,4 +43,17 @@ public class VertexArray {
private void BindElementArrayBuffer() { private void BindElementArrayBuffer() {
this.elementBuffer.Bind(); this.elementBuffer.Bind();
} }
public List<VertexBuffer> getVertexBuffers() {
return vertexBuffers;
}
@Override
public void close() throws IOException {
GL30.glDeleteBuffers(this.id);
for (VertexBuffer vertexBuffer : vertexBuffers) {
vertexBuffer.close();
}
elementBuffer.close();
}
} }

View File

@@ -1,4 +1,4 @@
package chess.view.render; package chess.view.DDDrender.opengl;
public record VertexAttribPointer(int index, int size, int offset) { public record VertexAttribPointer(int index, int size, int offset) {

View File

@@ -1,16 +1,18 @@
package chess.view.render; package chess.view.DDDrender.opengl;
import static org.lwjgl.opengl.GL11.GL_FLOAT; import static org.lwjgl.opengl.GL11.GL_FLOAT;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GL30;
public class VertexBuffer { public class VertexBuffer implements Closeable {
private int id; private final int id;
private int dataStride; private final int dataStride;
List<VertexAttribPointer> vertexAttribs; private final List<VertexAttribPointer> vertexAttribs;
public VertexBuffer(float[] data, int stride) { public VertexBuffer(float[] data, int stride) {
this.id = GL30.glGenBuffers(); this.id = GL30.glGenBuffers();
@@ -22,8 +24,10 @@ public class VertexBuffer {
Unbind(); Unbind();
} }
public void Destroy() { public void UpdateData(int offset, float[] data) {
GL30.glDeleteBuffers(id); Bind();
GL30.glBufferSubData(GL30.GL_ARRAY_BUFFER, offset, data);
Unbind();
} }
public void Bind() { public void Bind() {
@@ -46,4 +50,9 @@ public class VertexBuffer {
this.dataStride * 4, vertexAttribPointer.offset()); this.dataStride * 4, vertexAttribPointer.offset());
} }
} }
@Override
public void close() throws IOException {
GL30.glDeleteBuffers(id);
}
} }

View File

@@ -0,0 +1,102 @@
package chess.view.DDDrender.shader;
import chess.view.DDDrender.Camera;
public class BoardShader extends ShaderProgram {
private static String vertexShader = """
#version 330
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 color;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform vec3 lightPosition = vec3(0, 10, 0);
flat out vec3 pass_color;
out vec3 toLightVector;
out vec3 toCameraVector;
out vec3 surfaceNormal;
void main(void){
const vec4 normal = vec4(0.0, 1.0, 0.0, 1.0);
gl_Position = projectionMatrix * viewMatrix * vec4(position, 1.0);
vec3 camPos = (inverse(viewMatrix) * vec4(0.0, 0.0, 0.0, 1.0)).xyz;
toLightVector = lightPosition - position;
toCameraVector = camPos - position;
surfaceNormal = (normal).xyz;
pass_color = color;
}
""";
private static String fragmentShader = """
#version 330
flat in vec3 pass_color;
in vec3 toLightVector;
in vec3 toCameraVector;
in vec3 surfaceNormal;
out vec4 out_color;
void main(void){
const float shineDamper = 10.0;
const float reflectivity = 1.0;
float lightDistance = length(toLightVector);
const vec3 attenuation = vec3(0.2, 0.1, 0.0);
float attenuationFactor = attenuation.x + attenuation.y * lightDistance + attenuation.z * lightDistance * lightDistance;
vec3 unitNormal = normalize(surfaceNormal);
vec3 unitLightVector = normalize(toLightVector);
vec3 unitCamVector = normalize(toCameraVector);
vec3 lightDirection = -unitLightVector;
vec3 reflectedLightDirection = reflect(lightDirection, unitNormal);
float diffuse = max(0.2, dot(unitNormal, unitLightVector));
float specularFactor = max(0.0, dot(reflectedLightDirection, unitCamVector));
float specular = pow(specularFactor, shineDamper) * reflectivity;
float brightness = (diffuse + specular) / attenuationFactor;
out_color = brightness * vec4(pass_color, 1.0);
out_color.w = 1.0;
}
""";
private int location_ProjectionMatrix = 0;
private int location_ViewMatrix = 0;
public BoardShader() {
}
public void LoadShader() {
super.LoadProgram(vertexShader, fragmentShader);
}
@Override
protected void GetAllUniformLocation() {
location_ProjectionMatrix = GetUniformLocation("projectionMatrix");
location_ViewMatrix = GetUniformLocation("viewMatrix");
}
public void SetCamMatrix(Camera camera) {
LoadMat4(location_ProjectionMatrix, camera.getPerspectiveMatrix());
LoadMat4(location_ViewMatrix, camera.getViewMatrix());
}
}

View File

@@ -0,0 +1,116 @@
package chess.view.DDDrender.shader;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import chess.view.DDDrender.Camera;
public class PieceShader extends ShaderProgram {
private static String vertexShader = """
#version 330
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelTransform;
uniform vec3 lightPosition = vec3(0, 10, 0);
out vec3 toLightVector;
out vec3 toCameraVector;
out vec3 surfaceNormal;
void main(void){
vec4 modelPos = modelTransform * vec4(position, 1.0);
vec4 globalNormal = modelTransform * vec4(normal, 1.0);
gl_Position = projectionMatrix * viewMatrix * modelPos;
vec3 camPos = (inverse(viewMatrix) * vec4(0.0, 0.0, 0.0, 1.0)).xyz;
toLightVector = lightPosition - modelPos.xyz;
toCameraVector = camPos - position;
surfaceNormal = normalize(globalNormal.xyz);
}
""";
private static String fragmentShader = """
#version 330
in vec3 toLightVector;
in vec3 toCameraVector;
in vec3 surfaceNormal;
uniform vec3 modelColor;
out vec4 out_color;
void main(void){
const float shineDamper = 10.0;
const float reflectivity = 1.0;
float lightDistance = length(toLightVector);
const vec3 attenuation = vec3(0.2, 0.1, 0.0);
float attenuationFactor = attenuation.x + attenuation.y * lightDistance + attenuation.z * lightDistance * lightDistance;
vec3 unitNormal = normalize(surfaceNormal);
vec3 unitLightVector = normalize(toLightVector);
vec3 unitCamVector = normalize(toCameraVector);
vec3 lightDirection = -unitLightVector;
vec3 reflectedLightDirection = reflect(lightDirection, unitNormal);
float diffuse = max(0.2, dot(unitNormal, unitLightVector));
float specularFactor = max(0.0, dot(reflectedLightDirection, unitCamVector));
float specular = pow(specularFactor, shineDamper) * reflectivity;
float brightness = (diffuse + specular) / attenuationFactor;
out_color = brightness * vec4(modelColor, 1.0);
out_color.w = 1.0;
}
""";
private int location_ProjectionMatrix = 0;
private int location_ViewMatrix = 0;
private int location_ModelTransform = 0;
private int location_ModelColor = 0;
public PieceShader() {
}
public void LoadShader() {
super.LoadProgram(vertexShader, fragmentShader);
}
@Override
protected void GetAllUniformLocation() {
location_ProjectionMatrix = GetUniformLocation("projectionMatrix");
location_ViewMatrix = GetUniformLocation("viewMatrix");
location_ModelTransform = GetUniformLocation("modelTransform");
location_ModelColor = GetUniformLocation("modelColor");
}
public void SetCamMatrix(Camera camera) {
LoadMat4(location_ProjectionMatrix, camera.getPerspectiveMatrix());
LoadMat4(location_ViewMatrix, camera.getViewMatrix());
}
public void setModelTransform(Matrix4f mat) {
LoadMat4(location_ModelTransform, mat);
}
public void setModelColor(Vector3f color) {
LoadVector(location_ModelColor, color);
}
}

View File

@@ -1,5 +1,7 @@
package chess.view.render.shader; package chess.view.DDDrender.shader;
import java.io.Closeable;
import java.io.IOException;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
@@ -9,7 +11,7 @@ import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GL30;
import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryStack;
public abstract class ShaderProgram { public abstract class ShaderProgram implements Closeable {
private int programId; private int programId;
private int vertexShaderId; private int vertexShaderId;
private int fragmentShaderId; private int fragmentShaderId;
@@ -56,8 +58,8 @@ public abstract class ShaderProgram {
if (compileSuccesful.get() != 1) { if (compileSuccesful.get() != 1) {
System.out.println("Shader did not compile !"); System.out.println("Shader did not compile !");
System.err.println(GL30.glGetShaderInfoLog(shaderId));
return -1; return -1;
} }
return shaderId; return shaderId;
@@ -68,7 +70,7 @@ public abstract class ShaderProgram {
protected int GetUniformLocation(String uniformName) { protected int GetUniformLocation(String uniformName) {
int location = GL30.glGetUniformLocation(programId, uniformName); int location = GL30.glGetUniformLocation(programId, uniformName);
if (location == -1) { if (location == -1) {
System.out.println("Uniform value not found !"); System.out.println("Uniform value \"" + uniformName + "\" not found !");
} }
return location; return location;
} }
@@ -91,4 +93,11 @@ public abstract class ShaderProgram {
GL30.glUniformMatrix4fv(location, false, buffer); GL30.glUniformMatrix4fv(location, false, buffer);
} }
} }
@Override
public void close() throws IOException {
GL30.glDeleteShader(vertexShaderId);
GL30.glDeleteShader(fragmentShaderId);
GL30.glDeleteProgram(programId);
}
} }

View File

@@ -0,0 +1,48 @@
package chess.view.DDDrender.world;
import java.io.IOException;
import org.joml.Vector3f;
import chess.model.Coordinate;
import chess.view.DDDrender.Renderer;
import chess.view.DDDrender.loader.BoardModelLoader;
import chess.view.DDDrender.opengl.VertexArray;
import chess.view.DDDrender.opengl.VertexBuffer;
public class BoardEntity extends Entity {
private final VertexArray vao;
private final VertexBuffer colorVbo;
private static final Vector3f WHITE = new Vector3f(1, 1, 1);
private static final Vector3f BLACK = new Vector3f(0, 0, 0);
public BoardEntity() {
this.vao = BoardModelLoader.GetBoardModel();
this.colorVbo = this.vao.getVertexBuffers().get(1);
}
public void setCellColor(Coordinate coord, Vector3f color) {
float[] data = { color.x, color.y, color.z, color.x, color.y, color.z, color.x, color.y, color.z, color.x,
color.y, color.z};
int cellNumber = (Coordinate.VALUE_MAX - 1 - coord.getX()) * Coordinate.VALUE_MAX + Coordinate.VALUE_MAX - 1 - coord.getY();
int offset = cellNumber * 4 * 4 * 3;
this.colorVbo.UpdateData(offset, data);
}
public void resetCellColor(Coordinate coord) {
setCellColor(coord, (coord.getX() + coord.getY()) % 2 == 0 ? WHITE : BLACK);
}
@Override
public void render(Renderer renderer) {
renderer.RenderVao(renderer.getBoardShader(), vao);
}
@Override
public void close() throws IOException {
vao.close();
}
}

View File

@@ -0,0 +1,11 @@
package chess.view.DDDrender.world;
import java.io.Closeable;
import chess.view.DDDrender.Renderer;
public abstract class Entity implements Closeable{
public abstract void render(Renderer renderer);
}

View File

@@ -0,0 +1,46 @@
package chess.view.DDDrender.world;
import java.io.IOException;
import org.joml.Vector3f;
import chess.view.DDDrender.DDDModel;
import chess.view.DDDrender.Renderer;
public class ModelEntity extends Entity {
protected Vector3f position;
protected Vector3f color;
protected float rotation;
protected DDDModel model;
public ModelEntity(DDDModel model, Vector3f position, Vector3f color, float rotation) {
this.position = position;
this.color = color;
this.rotation = rotation;
this.model = model;
}
@Override
public void render(Renderer renderer) {
renderer.Render(model, color, position, rotation);
}
public void setPosition(Vector3f position) {
this.position = position;
}
public void setColor(Vector3f color) {
this.color = color;
}
public void setRotation(float rotation) {
this.rotation = rotation;
}
@Override
public void close() throws IOException {
this.model.close();
}
}

View File

@@ -0,0 +1,25 @@
package chess.view.DDDrender.world;
import java.io.IOException;
import org.joml.Vector3f;
import chess.model.Piece;
import chess.view.DDDrender.loader.Piece3DModel;
public class PieceEntity extends ModelEntity {
private static final Piece3DModel modelLoader = new Piece3DModel();
private final Piece piece;
public PieceEntity(Piece piece, Vector3f position, Vector3f color, float rotation) throws IOException {
super(modelLoader.getModel(piece), position, color, rotation);
this.piece = piece;
}
public Piece getPiece() {
return piece;
}
}

View File

@@ -0,0 +1,67 @@
package chess.view.DDDrender.world;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import chess.model.Coordinate;
import chess.model.Move;
import chess.model.Piece;
public class World implements Closeable{
/** Renderable entity list */
private final List<Entity> entites;
/** provides fast access to 3d pieces */
private final PieceEntity[] pieces;
public World() {
this.entites = new ArrayList<>();
this.pieces = new PieceEntity[Coordinate.VALUE_MAX * Coordinate.VALUE_MAX];
}
public PieceEntity getEntity(Piece piece) {
for (Entity entity : entites) {
if (entity instanceof PieceEntity p) {
if (p.getPiece() == piece)
return p;
}
}
return null;
}
public void addPiece(PieceEntity entity, Coordinate coordinate) {
addEntity(entity);
setPieceCoords(entity, coordinate);
}
private void setPieceCoords(PieceEntity entity, Coordinate coordinate) {
pieces[coordinate.toIndex()] = entity;
}
public void movePiece(PieceEntity entity, Move move) {
setPieceCoords(entity, move.getFinish());
setPieceCoords(null, move.getStart());
}
public PieceEntity getPiece(Coordinate coordinate) {
return pieces[coordinate.toIndex()];
}
public void addEntity(Entity entity) {
this.entites.add(entity);
}
public List<Entity> getEntites() {
return entites;
}
@Override
public void close() throws IOException {
for (Entity entity : entites) {
entity.close();
}
}
}

View File

@@ -20,12 +20,17 @@ public class Console implements GameListener {
private final Scanner scanner = new Scanner(System.in); private final Scanner scanner = new Scanner(System.in);
private final CommandExecutor commandExecutor; private final CommandExecutor commandExecutor;
private final ConsolePieceName consolePieceName = new ConsolePieceName(); private final ConsolePieceName consolePieceName = new ConsolePieceName();
private boolean captureInput = false; private boolean captureInput;
private final ExecutorService executor; private final ExecutorService executor;
public Console(CommandExecutor commandExecutor) { public Console(CommandExecutor commandExecutor, boolean captureInput) {
this.commandExecutor = commandExecutor; this.commandExecutor = commandExecutor;
this.executor = Executors.newSingleThreadExecutor(); this.executor = Executors.newSingleThreadExecutor();
this.captureInput = captureInput;
}
public Console(CommandExecutor commandExecutor) {
this(commandExecutor, true);
} }
private Piece pieceAt(int x, int y) { private Piece pieceAt(int x, int y) {
@@ -61,7 +66,7 @@ public class Console implements GameListener {
} }
@Override @Override
public void onPlayerTurn(Color color) { public void onPlayerTurn(Color color, boolean undone) {
if (!captureInput) if (!captureInput)
return; return;
System.out.println(Colors.RED + "Player turn: " + color + Colors.RESET); System.out.println(Colors.RED + "Player turn: " + color + Colors.RESET);

View File

@@ -1,128 +0,0 @@
package chess.view.render;
import org.joml.Matrix4f;
import org.joml.Vector3f;
public class Camera {
public static final float fov = 70.0f;
// should be changed to match screen
public static final float aspect = 1.0f;
public static final float zNear = 0.01f;
public static final float zFar = 100.0f;
private Vector3f pos;
private float yaw = 0.0f;
private float pitch = 0.0f;
public Camera() {
this.pos = new Vector3f(0, 2.0f, 0);
setRotation(0.0f, -3.14150f / 2.0f);
}
public void move(float x, float y) {
this.pos.x += x;
this.pos.y += y;
}
public void rotate(float yaw, float pitch) {
this.yaw += yaw;
this.pitch += pitch;
}
public Vector3f getPos() {
return pos;
}
public float getYaw() {
return yaw;
}
public float getPitch() {
return pitch;
}
public float getFov() {
return fov;
}
public void setX(float x) {
this.pos.x = x;
}
public void setY(float y) {
this.pos.y = y;
}
public void setZ(float z) {
this.pos.z = z;
}
public void setYaw(float yaw) {
this.yaw = yaw;
}
public void setPitch(float pitch) {
this.pitch = pitch;
}
public void reset() {
resetPosition();
resetRotation();
}
public void resetPosition() {
pos = new Vector3f(0.0f, 0.0f, 0.0f);
}
public void resetRotation() {
yaw = 0.0f;
pitch = 0.0f;
}
public void moveForward(float distance) {
pos.x += distance * (float) Math.cos(yaw);
pos.y += distance * (float) Math.sin(yaw);
}
public void moveRight(float distance) {
pos.x += distance * (float) Math.cos(yaw);
pos.y += distance * (float) Math.sin(yaw);
}
public void moveUp(float distance) {
pos.z += distance;
}
public void moveDown(float distance) {
pos.z -= distance;
}
public void addYaw(float angle) {
yaw += angle;
}
public void addPitch(float angle) {
pitch += angle;
}
public void setPosition(Vector3f pos) {
this.pos = pos;
}
public void setRotation(float yaw, float pitch) {
this.yaw = yaw;
this.pitch = pitch;
}
public Matrix4f getMatrix() {
Vector3f forward = new Vector3f(
(float) (Math.cos(yaw) * Math.cos(pitch)),
(float) (Math.sin(pitch)),
(float) (Math.sin(yaw) * Math.cos(pitch)));
return new Matrix4f()
.perspective((float) (Math.toRadians(fov)), aspect, zNear, zFar)
.lookAt(pos, forward, new Vector3f(0.0f, 1.0f, 0.0f));
}
}

View File

@@ -1,157 +0,0 @@
package chess.view.render;
import org.joml.Vector3f;
import org.lwjgl.opengl.*;
import chess.model.Coordinate;
import chess.view.render.shader.BoardShader;
import static org.lwjgl.opengl.GL30.*;
public class Renderer {
private BoardShader shader;
private VertexArray vao;
private static int BOARD_WIDTH = 8;
private static int BOARD_HEIGHT = 8;
private static int BOARD_SIZE = BOARD_WIDTH * BOARD_HEIGHT;
private static int SQUARE_VERTEX_COUNT = 4;
public Renderer() {
this.shader = new BoardShader();
}
public void Init() {
shader.LoadShader();
InitBoard();
}
private float[] GetBoardPositions() {
float[] positions = new float[BOARD_SIZE * SQUARE_VERTEX_COUNT * 3];
for (int i = 0; i < BOARD_WIDTH; i++) {
for (int j = 0; j < BOARD_HEIGHT; j++) {
float x = i / (float) BOARD_WIDTH;
float dx = (i + 1) / (float) BOARD_WIDTH;
float z = j / (float) BOARD_HEIGHT;
float dz = (j + 1) / (float) BOARD_HEIGHT;
float trueX = 2 * x - 1;
float trueZ = 2 * z - 1;
float trueDX = 2 * dx - 1;
float trueDZ = 2 * dz - 1;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3] = trueX;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 1] = 0.0f;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 2] = trueZ;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 3] = trueDX;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 4] = 0.0f;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 5] = trueZ;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 6] = trueX;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 7] = 0.0f;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 8] = trueDZ;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 9] = trueDX;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 10] = 0.0f;
positions[(BOARD_WIDTH * i + j) * SQUARE_VERTEX_COUNT * 3 + 11] = trueDZ;
}
}
return positions;
}
private Coordinate GetCellFromColor(Vector3f color) {
int offset = 1;
if (color.x > 0.5) {
color = new Vector3f(1.0f, 1.0f, 1.0f).sub(color);
offset = 0;
}
int r = (int) (color.x * 255.0f);
int g = (int) (color.y * 255.0f);
int b = (int) (color.z * 255.0f);
int index = (r + g + b) * 2 + offset;
return Coordinate.fromIndex(index);
}
private Vector3f GetCellColor(int x, int y) {
float index = (y * BOARD_WIDTH + x) / 2.0f;
float r = (int) (index / 3) / 255.0f;
float g = (int) ((index + 1) / 3) / 255.0f;
float b = (int) ((index + 2) / 3) / 255.0f;
if ((x + y) % 2 != 0) {
System.out.println(GetCellFromColor(new Vector3f(1.0f - r - 1.0f / 255.0f, 1.0f - g - 1.0f / 255.0f, 1.0f - b - 1.0f / 255.0f)));
return new Vector3f(1.0f - r - 1.0f / 255.0f, 1.0f - g - 1.0f / 255.0f, 1.0f - b - 1.0f / 255.0f);
} else {
System.out.println(GetCellFromColor(new Vector3f(r, g, b)));
return new Vector3f(r, g, b);
}
}
private float[] GetBoardColors() {
float[] colors = new float[BOARD_SIZE * SQUARE_VERTEX_COUNT * 3];
for (int j = 0; j < BOARD_HEIGHT; j++) {
for (int i = 0; i < BOARD_WIDTH; i++) {
Vector3f color = GetCellColor(i, j);
int squareIndex = i * BOARD_WIDTH + j;
for (int k = 0; k < SQUARE_VERTEX_COUNT; k++) {
colors[squareIndex * SQUARE_VERTEX_COUNT * 3 + k * 3] = color.x;
colors[squareIndex * SQUARE_VERTEX_COUNT * 3 + k * 3 + 1] = color.y;
colors[squareIndex * SQUARE_VERTEX_COUNT * 3 + k * 3 + 2] = color.z;
}
}
}
return colors;
}
private int[] GetBoardIndicies() {
int[] indices = new int[BOARD_SIZE * 6];
for (int i = 0; i < BOARD_SIZE; i++) {
indices[i * 6] = i * 4;
indices[i * 6 + 1] = i * 4 + 1;
indices[i * 6 + 2] = i * 4 + 2;
indices[i * 6 + 3] = i * 4 + 1;
indices[i * 6 + 4] = i * 4 + 2;
indices[i * 6 + 5] = i * 4 + 3;
}
return indices;
}
private void InitBoard() {
ElementBuffer eBuffer = new ElementBuffer(GetBoardIndicies());
this.vao = new VertexArray(eBuffer);
VertexBuffer positionBuffer = new VertexBuffer(GetBoardPositions(), 3);
positionBuffer.AddVertexAttribPointer(0, 3, 0);
VertexBuffer colorBuffer = new VertexBuffer(GetBoardColors(), 3);
colorBuffer.AddVertexAttribPointer(1, 3, 0);
this.vao.Bind();
this.vao.BindVertexBuffer(positionBuffer);
this.vao.BindVertexBuffer(colorBuffer);
this.vao.Unbind();
}
public Coordinate GetSelectedCell() {
float pixels[] = new float[3];
GL30.glReadPixels(500, 500, 1, 1, GL_RGB, GL_FLOAT, pixels);
return GetCellFromColor(new Vector3f(pixels[0], pixels[1], pixels[2]));
}
public void Render(Camera cam) {
this.shader.Start();
this.shader.SetCamMatrix(cam.getMatrix());
RenderVao(vao);
}
public void RenderVao(VertexArray vertexArray) {
this.shader.Start();
vertexArray.Bind();
GL30.glDrawElements(GL30.GL_TRIANGLES, vertexArray.GetVertexCount(), GL_UNSIGNED_INT, 0);
vertexArray.Unbind();
}
}

View File

@@ -1,224 +0,0 @@
package chess.view.render;
import org.lwjgl.*;
import org.lwjgl.glfw.*;
import org.lwjgl.opengl.*;
import org.lwjgl.system.*;
import chess.controller.CommandExecutor;
import chess.controller.event.GameListener;
import chess.model.Color;
import chess.model.Coordinate;
import chess.model.Move;
import java.nio.*;
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 GameListener{
// The window handle
private long window;
private final CommandExecutor commandExecutor;
private Renderer renderer;
private Camera cam;
public Window(CommandExecutor commandExecutor) {
this.renderer = new Renderer();
this.cam = new Camera();
this.commandExecutor = new CommandExecutor();
}
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();
}
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);
} // the stack frame is popped automatically
// Make the OpenGL context current
glfwMakeContextCurrent(window);
// Enable v-sync
glfwSwapInterval(1);
// Make the window visible
glfwShowWindow(window);
}
private void render() {
cam.rotate(0.01f, 0.01f);
renderer.Render(cam);
}
private void loop() {
// This line is critical for LWJGL's interoperation with GLFW's
// OpenGL context, or any context that is managed externally.
// LWJGL detects the context that is current in the current thread,
// creates the GLCapabilities instance and makes the OpenGL
// bindings available for use.
GL.createCapabilities();
renderer.Init();
// Set the clear color
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
// Run the rendering loop until the user has attempted to close
// the window or has pressed the ESCAPE key.
while (!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
render();
System.out.println(this.renderer.GetSelectedCell());
glfwSwapBuffers(window); // swap the color buffers
// Poll for window events. The key callback above will only be
// invoked during this call.
glfwPollEvents();
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);
glViewport(0, 0, pWidth.get(), pHeight.get());
} // the stack frame is popped automatically
}
}
@Override
public void onBoardUpdate() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'onBoardUpdate'");
}
@Override
public void onDraw() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'onDraw'");
}
@Override
public void onGameEnd() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'onGameEnd'");
}
@Override
public void onGameStart() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'onGameStart'");
}
@Override
public void onKingInCheck() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'onKingInCheck'");
}
@Override
public void onKingInMat() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'onKingInMat'");
}
@Override
public void onMove(Move move) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'onMove'");
}
@Override
public void onMoveNotAllowed(Move move) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'onMoveNotAllowed'");
}
@Override
public void onPatSituation() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'onPatSituation'");
}
@Override
public void onPlayerTurn(Color color) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'onPlayerTurn'");
}
@Override
public void onPromotePawn(Coordinate pieceCoords) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'onPromotePawn'");
}
@Override
public void onSurrender(Color coward) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'onSurrender'");
}
@Override
public void onWin(Color winner) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'onWin'");
}
}

View File

@@ -1,53 +0,0 @@
package chess.view.render.shader;
import org.joml.Matrix4f;
public class BoardShader extends ShaderProgram {
private static String vertexShader = """
#version 330
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 color;
uniform mat4 camMatrix;
flat out vec3 pass_color;
void main(void){
gl_Position = camMatrix * vec4(position, 1.0);
pass_color = color;
}
""";
private static String fragmentShader = """
#version 330
flat in vec3 pass_color;
out vec4 out_color;
void main(void){
out_color = vec4(pass_color, 1.0);
}
""";
private int location_CamMatrix = 0;
public BoardShader() {
}
public void LoadShader() {
super.LoadProgram(vertexShader, fragmentShader);
}
@Override
protected void GetAllUniformLocation() {
location_CamMatrix = GetUniformLocation("camMatrix");
}
public void SetCamMatrix(Matrix4f mat) {
LoadMat4(location_CamMatrix, mat);
}
}

View File

@@ -213,7 +213,7 @@ public class Window extends JFrame implements GameListener {
} }
@Override @Override
public void onPlayerTurn(chess.model.Color color) { public void onPlayerTurn(chess.model.Color color, boolean undone) {
this.displayText.setText("Current turn: " + color); this.displayText.setText("Current turn: " + color);
updateButtons(); updateButtons();
} }

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.

View File

@@ -3,24 +3,20 @@
*/ */
package chess; package chess;
import org.junit.jupiter.api.Test;
import chess.ai.DumbAI; import chess.ai.DumbAI;
import chess.controller.Command; import chess.controller.Command;
import chess.controller.CommandExecutor; import chess.controller.CommandExecutor;
import chess.controller.commands.NewGameCommand; import chess.controller.commands.NewGameCommand;
import chess.controller.commands.UndoCommand; import chess.controller.commands.UndoCommand;
import chess.controller.event.GameAdaptator; import chess.controller.event.GameAdaptator;
import chess.model.*; import chess.model.Color;
import chess.model.pieces.*; import chess.model.Game;
import chess.simulator.Simulator;
import chess.view.simplerender.Window;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Objects;
class AppTest { class AppTest {
@Test void functionalRollback(){ @Test void functionalRollback(){
Game game = new Game(new ChessBoard()); Game game = new Game();
CommandExecutor commandExecutor = new CommandExecutor(game); CommandExecutor commandExecutor = new CommandExecutor(game);
DumbAI ai = new DumbAI(commandExecutor, Color.Black); DumbAI ai = new DumbAI(commandExecutor, Color.Black);
@@ -38,9 +34,9 @@ class AppTest {
result = commandExecutor.executeCommand(new UndoCommand()); result = commandExecutor.executeCommand(new UndoCommand());
} while (result != Command.CommandResult.NotAllowed); } while (result != Command.CommandResult.NotAllowed);
Game initialGame = new Game(new ChessBoard()); Game initialGame = new Game();
CommandExecutor initialCommandExecutor = new CommandExecutor(initialGame); CommandExecutor initialCommandExecutor = new CommandExecutor(initialGame);
commandExecutor.executeCommand(new NewGameCommand()); initialCommandExecutor.executeCommand(new NewGameCommand());
assert(game.getBoard().equals(initialGame.getBoard())); assert(game.getBoard().equals(initialGame.getBoard()));