Migrate to BatchedQuads mesh

This commit is contained in:
2025-07-18 14:41:59 +02:00
parent f131f9ef7f
commit 1d1730f97b
29 changed files with 432 additions and 584 deletions

View File

@@ -1,288 +0,0 @@
package com.bartlomiejpluta.base.engine.core.gl.object.mesh;
import com.bartlomiejpluta.base.api.camera.Camera;
import com.bartlomiejpluta.base.api.screen.Screen;
import com.bartlomiejpluta.base.internal.gc.Disposable;
import com.bartlomiejpluta.base.internal.render.Renderable;
import com.bartlomiejpluta.base.internal.render.ShaderManager;
import lombok.Getter;
import org.lwjgl.opengl.GL15;
import org.lwjgl.system.MemoryStack;
import java.util.ArrayList;
import java.util.List;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL30.*;
public class BatchedQuads implements Renderable, Disposable {
// Texture: Quad:
// (0,1) ---- (1,1) 1 ----------- 2
// | | | |
// | | => | TEXTURE |
// | | | |
// (0,0) ---- (1,0) 0 ----------- 3
private static final float[] DEFAULT_TEXTURE_COORDINATES = new float[] {
0, 0, // 0 - bottom left
0, 1, // 1 - top left
1, 1, // 2 - top right
1, 0 // 3 - bottom right
};
private static final int POSITION_VECTOR_LAYOUT_INDEX = 0;
private static final int POSITION_VECTOR_LENGTH = 2;
private static final int TEXTURE_COORDINATES_VECTOR_LAYOUT_INDEX = 1;
private static final int TEXTURE_COORDINATES_VECTOR_LENGTH = 2;
private static final int VERTICES_PER_QUAD = 4;
private static final int INDICES_PER_QUAD = 6;
private final int maxQuads;
private final int vaoId;
private final List<Integer> vboIds = new ArrayList<>(3);
private final int maxVertices;
private final int maxIndices;
private final float[] vertexBuffer;
private final float[] texCoordBuffer;
private final int[] indexBuffer;
@Getter
private int currentQuadCount = 0;
public BatchedQuads(int maxQuads) {
this.maxQuads = maxQuads;
this.maxVertices = maxQuads * VERTICES_PER_QUAD * 2;
this.maxIndices = maxQuads * INDICES_PER_QUAD;
this.vertexBuffer = new float[maxVertices];
this.texCoordBuffer = new float[maxVertices];
this.indexBuffer = new int[maxIndices];
try (var stack = MemoryStack.stackPush()) {
vaoId = glGenVertexArrays();
glBindVertexArray(vaoId);
// Vertex buffer
var vertexVboId = glGenBuffers();
vboIds.add(vertexVboId);
glBindBuffer(GL_ARRAY_BUFFER, vertexVboId);
glBufferData(GL_ARRAY_BUFFER, maxVertices * Float.BYTES, GL_DYNAMIC_DRAW);
glVertexAttribPointer(POSITION_VECTOR_LAYOUT_INDEX, POSITION_VECTOR_LENGTH, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(0);
// Texture coordinates buffer
var texCoordVboId = glGenBuffers();
vboIds.add(texCoordVboId);
glBindBuffer(GL_ARRAY_BUFFER, texCoordVboId);
glBufferData(GL_ARRAY_BUFFER, maxVertices * Float.BYTES, GL_DYNAMIC_DRAW);
glVertexAttribPointer(TEXTURE_COORDINATES_VECTOR_LAYOUT_INDEX, TEXTURE_COORDINATES_VECTOR_LENGTH, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(1);
// Index buffer
var indexVboId = glGenBuffers();
vboIds.add(indexVboId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVboId);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, maxIndices * Integer.BYTES, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
}
public int addQuad(Quad quad, float x, float y) {
return addQuad(quad, x, y, DEFAULT_TEXTURE_COORDINATES);
}
public int addQuad(Quad quad, float x, float y, float[] textureCoordinates) {
if (currentQuadCount >= maxQuads) {
throw new RuntimeException("Batch is full!");
}
int quadId = currentQuadCount;
// Vertices
var localVertices = quad.getVertices();
int vertexStartIndex = currentQuadCount * VERTICES_PER_QUAD * 2;
for (int i = 0; i < VERTICES_PER_QUAD; i++) {
float localX = localVertices[i * 2];
float localY = localVertices[i * 2 + 1];
vertexBuffer[vertexStartIndex + i * 2] = x + localX;
vertexBuffer[vertexStartIndex + i * 2 + 1] = y + localY;
}
// Texture
var texCoordStartIndex = currentQuadCount * VERTICES_PER_QUAD * 2;
System.arraycopy(textureCoordinates, 0, texCoordBuffer, texCoordStartIndex, textureCoordinates.length);
// Indices
var indexStartIndex = currentQuadCount * INDICES_PER_QUAD;
var vertexOffset = currentQuadCount * VERTICES_PER_QUAD;
int[] quadIndices = {
// First triangle: indices 0-1-2
// 1 ----------- 2
// |XXXXXXXXXXX |
// |XXXXXXXX |
// |XXXXX |
// |XX |
// 0 ----------- 3
vertexOffset + 0, vertexOffset + 1, vertexOffset + 2,
// Second triangle: indices 2-3-0
// 1 ----------- 2
// | X|
// | XXXX|
// | XXXXXXX|
// | XXXXXXXXXX|
// 0 ----------- 3
vertexOffset + 2, vertexOffset + 3, vertexOffset + 0
};
System.arraycopy(quadIndices, 0, indexBuffer, indexStartIndex, quadIndices.length);
currentQuadCount++;
return quadId;
}
public void setQuadTextureCoordinates(int quadId, float[] textureCoordinates) {
if (quadId < 0 || quadId >= currentQuadCount) {
throw new IllegalArgumentException("Invalid quad ID: " + quadId);
}
var texCoordsIndex = quadId * VERTICES_PER_QUAD * 2;
System.arraycopy(textureCoordinates, 0, texCoordBuffer, texCoordsIndex, textureCoordinates.length);
}
public void clear() {
currentQuadCount = 0;
}
public void removeQuad(int quadId) {
if (quadId < 0 || quadId >= currentQuadCount) {
throw new IllegalArgumentException("Invalid quad ID: " + quadId);
}
int lastQuadId = currentQuadCount - 1;
if (quadId != lastQuadId) {
swapQuads(quadId, lastQuadId);
}
currentQuadCount--;
}
private void swapQuads(int quad1, int quad2) {
// Swap vertices
swapQuadData(vertexBuffer, quad1, quad2, VERTICES_PER_QUAD * 2);
// Swap texture coordinates
swapQuadData(texCoordBuffer, quad1, quad2, VERTICES_PER_QUAD * 2);
// Swap indices
swapQuadIndices(quad1, quad2);
}
private void swapQuadData(float[] buffer, int quad1, int quad2, int dataSize) {
var start1 = quad1 * dataSize;
var start2 = quad2 * dataSize;
for (int i = 0; i < dataSize; i++) {
var temp = buffer[start1 + i];
buffer[start1 + i] = buffer[start2 + i];
buffer[start2 + i] = temp;
}
}
private void swapQuadIndices(int quad1, int quad2) {
var start1 = quad1 * INDICES_PER_QUAD;
var start2 = quad2 * INDICES_PER_QUAD;
var vertexOffset1 = quad1 * VERTICES_PER_QUAD;
int[] newIndices1 = {
vertexOffset1 + 0, vertexOffset1 + 1, vertexOffset1 + 2,
vertexOffset1 + 2, vertexOffset1 + 3, vertexOffset1 + 0
};
var vertexOffset2 = quad2 * VERTICES_PER_QUAD;
int[] newIndices2 = {
vertexOffset2 + 0, vertexOffset2 + 1, vertexOffset2 + 2,
vertexOffset2 + 2, vertexOffset2 + 3, vertexOffset2 + 0
};
System.arraycopy(newIndices1, 0, indexBuffer, start1, INDICES_PER_QUAD);
System.arraycopy(newIndices2, 0, indexBuffer, start2, INDICES_PER_QUAD);
}
@Override
public void render(Screen screen, Camera camera, ShaderManager shaderManager) {
if (currentQuadCount == 0) {
return;
}
glBindVertexArray(vaoId);
try (var stack = MemoryStack.stackPush()) {
// Vertex buffer
glBindBuffer(GL_ARRAY_BUFFER, vboIds.get(0));
var vertexFloatBuffer = stack.mallocFloat(currentQuadCount * VERTICES_PER_QUAD * 2);
for (int i = 0; i < currentQuadCount * VERTICES_PER_QUAD * 2; i++) {
vertexFloatBuffer.put(vertexBuffer[i]);
}
vertexFloatBuffer.flip();
glBufferSubData(GL_ARRAY_BUFFER, 0, vertexFloatBuffer);
// Texture coordinate buffer
glBindBuffer(GL_ARRAY_BUFFER, vboIds.get(1));
var texCoordFloatBuffer = stack.mallocFloat(currentQuadCount * VERTICES_PER_QUAD * 2);
for (int i = 0; i < currentQuadCount * VERTICES_PER_QUAD * 2; i++) {
texCoordFloatBuffer.put(texCoordBuffer[i]);
}
texCoordFloatBuffer.flip();
glBufferSubData(GL_ARRAY_BUFFER, 0, texCoordFloatBuffer);
// Index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIds.get(2));
var indexIntBuffer = stack.mallocInt(currentQuadCount * INDICES_PER_QUAD);
for (int i = 0; i < currentQuadCount * INDICES_PER_QUAD; i++) {
indexIntBuffer.put(indexBuffer[i]);
}
indexIntBuffer.flip();
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indexIntBuffer);
}
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glDrawElements(GL_TRIANGLES, currentQuadCount * INDICES_PER_QUAD, GL_UNSIGNED_INT, 0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glBindVertexArray(0);
}
@Override
public void dispose() {
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
vboIds.forEach(GL15::glDeleteBuffers);
glBindVertexArray(0);
glDeleteVertexArrays(vaoId);
}
public boolean isFull() {
return currentQuadCount >= maxQuads;
}
public boolean isEmpty() {
return currentQuadCount == 0;
}
}

View File

@@ -6,110 +6,257 @@ import com.bartlomiejpluta.base.internal.gc.Disposable;
import com.bartlomiejpluta.base.internal.render.Renderable;
import com.bartlomiejpluta.base.internal.render.ShaderManager;
import lombok.Getter;
import org.joml.Vector2f;
import org.joml.Vector2fc;
import org.lwjgl.opengl.GL15;
import org.lwjgl.system.MemoryStack;
import java.util.ArrayList;
import java.util.List;
import static java.lang.Math.abs;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL30.*;
public class Mesh implements Renderable, Disposable {
// default.vs: layout(location=0) in vec2 position;
// ^ ^
// Texture: Quad:
// (0,1) ---- (1,1) 1 ----------- 2
// | | | |
// | | => | TEXTURE |
// | | | |
// (0,0) ---- (1,0) 0 ----------- 3
private static final float[] DEFAULT_TEXTURE_COORDINATES = new float[] {
0, 0, // 0 - bottom left
0, 1, // 1 - top left
1, 1, // 2 - top right
1, 0 // 3 - bottom right
};
private static final int POSITION_VECTOR_LAYOUT_INDEX = 0;
private static final int POSITION_VECTOR_LENGTH = 2;
private static final int TEXTURE_COORDINATES_VECTOR_LAYOUT_INDEX = 1;
private static final int TEXTURE_COORDINATES_VECTOR_LENGTH = 2;
// default.vs: layout(location=1) in vec2 texCoord;
// ^ ^
private static final int TEX_COORDS_VECTOR_LAYOUT_INDEX = 1;
private static final int TEX_COORDS_VECTOR_LENGTH = 2;
private static final int VERTICES_PER_QUAD = 4;
private static final int INDICES_PER_QUAD = 6;
private final int maxQuads;
private final int vaoId;
private final List<Integer> vboIds = new ArrayList<>(2);
private final int elementsCount;
private final List<Integer> vboIds = new ArrayList<>(3);
private final int maxVertices;
private final int maxIndices;
private final float[] vertexBuffer;
private final float[] texCoordBuffer;
private final int[] indexBuffer;
@Getter
private final Vector2fc farthestVertex;
private int currentQuadCount = 0;
@Getter
private final Vector2fc primarySize;
public Mesh(int maxQuads) {
this.maxQuads = maxQuads;
this.maxVertices = maxQuads * VERTICES_PER_QUAD * 2;
this.maxIndices = maxQuads * INDICES_PER_QUAD;
public Mesh(float[] vertices, float[] texCoords, int[] elements) {
this.elementsCount = elements.length;
this.vertexBuffer = new float[maxVertices];
this.texCoordBuffer = new float[maxVertices];
this.indexBuffer = new int[maxIndices];
var vboId = 0;
try (var stack = MemoryStack.stackPush()) {
vaoId = glGenVertexArrays();
glBindVertexArray(vaoId);
// Vertices VBO
vboId = glGenBuffers();
vboIds.add(vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, stack.mallocFloat(vertices.length).put(vertices).flip(), GL_STATIC_DRAW);
// Vertex buffer
var vertexVboId = glGenBuffers();
vboIds.add(vertexVboId);
glBindBuffer(GL_ARRAY_BUFFER, vertexVboId);
glBufferData(GL_ARRAY_BUFFER, maxVertices * Float.BYTES, GL_DYNAMIC_DRAW);
glVertexAttribPointer(POSITION_VECTOR_LAYOUT_INDEX, POSITION_VECTOR_LENGTH, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(0);
// Texture Coordinates VBO
vboId = glGenBuffers();
vboIds.add(vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, stack.mallocFloat(texCoords.length).put(texCoords).flip(), GL_STATIC_DRAW);
glVertexAttribPointer(TEX_COORDS_VECTOR_LAYOUT_INDEX, TEX_COORDS_VECTOR_LENGTH, GL_FLOAT, false, 0, 0);
// Texture coordinates buffer
var texCoordVboId = glGenBuffers();
vboIds.add(texCoordVboId);
glBindBuffer(GL_ARRAY_BUFFER, texCoordVboId);
glBufferData(GL_ARRAY_BUFFER, maxVertices * Float.BYTES, GL_DYNAMIC_DRAW);
glVertexAttribPointer(TEXTURE_COORDINATES_VECTOR_LAYOUT_INDEX, TEXTURE_COORDINATES_VECTOR_LENGTH, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(1);
// Elements VBO
vboId = glGenBuffers();
vboIds.add(vboId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboId);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, stack.mallocInt(elementsCount).put(elements).flip(), GL_STATIC_DRAW);
// Index buffer
var indexVboId = glGenBuffers();
vboIds.add(indexVboId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVboId);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, maxIndices * Integer.BYTES, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
}
var minX = Float.MAX_VALUE;
var minY = Float.MAX_VALUE;
var maxX = 0f;
var maxY = 0f;
public int addQuad(QuadTemplate template, float x, float y) {
return addQuad(template, x, y, DEFAULT_TEXTURE_COORDINATES);
}
for (int i = 0; i < vertices.length / 2; ++i) {
var x = vertices[2 * i];
var y = vertices[2 * i + 1];
if (x < minX) {
minX = x;
}
if (x > maxX) {
maxX = x;
}
if (y < minY) {
minY = y;
}
if (y > maxY) {
maxY = y;
}
public int addQuad(QuadTemplate template, float x, float y, float[] textureCoordinates) {
if (currentQuadCount >= maxQuads) {
throw new RuntimeException("Batch is full!");
}
farthestVertex = new Vector2f(abs(maxX) > abs(minX) ? maxX : minX, abs(maxY) > abs(minY) ? maxY : minY);
primarySize = new Vector2f(maxX - minX, maxY - minY);
int quadId = currentQuadCount;
// Vertices
var localVertices = template.getVertices();
int vertexStartIndex = currentQuadCount * VERTICES_PER_QUAD * 2;
for (int i = 0; i < VERTICES_PER_QUAD; i++) {
float localX = localVertices[i * 2];
float localY = localVertices[i * 2 + 1];
vertexBuffer[vertexStartIndex + i * 2] = x + localX;
vertexBuffer[vertexStartIndex + i * 2 + 1] = y + localY;
}
// Texture
var texCoordStartIndex = currentQuadCount * VERTICES_PER_QUAD * 2;
System.arraycopy(textureCoordinates, 0, texCoordBuffer, texCoordStartIndex, textureCoordinates.length);
// Indices
var indexStartIndex = currentQuadCount * INDICES_PER_QUAD;
var vertexOffset = currentQuadCount * VERTICES_PER_QUAD;
int[] quadIndices = {
// First triangle: indices 0-1-2
// 1 ----------- 2
// |XXXXXXXXXXX |
// |XXXXXXXX |
// |XXXXX |
// |XX |
// 0 ----------- 3
vertexOffset + 0, vertexOffset + 1, vertexOffset + 2,
// Second triangle: indices 2-3-0
// 1 ----------- 2
// | X|
// | XXXX|
// | XXXXXXX|
// | XXXXXXXXXX|
// 0 ----------- 3
vertexOffset + 2, vertexOffset + 3, vertexOffset + 0
};
System.arraycopy(quadIndices, 0, indexBuffer, indexStartIndex, quadIndices.length);
currentQuadCount++;
return quadId;
}
public void setQuadTextureCoordinates(int quadId, float[] textureCoordinates) {
if (quadId < 0 || quadId >= currentQuadCount) {
throw new IllegalArgumentException("Invalid quad ID: " + quadId);
}
var texCoordsIndex = quadId * VERTICES_PER_QUAD * 2;
System.arraycopy(textureCoordinates, 0, texCoordBuffer, texCoordsIndex, textureCoordinates.length);
}
public void clear() {
currentQuadCount = 0;
}
public void removeQuad(int quadId) {
if (quadId < 0 || quadId >= currentQuadCount) {
throw new IllegalArgumentException("Invalid quad ID: " + quadId);
}
int lastQuadId = currentQuadCount - 1;
if (quadId != lastQuadId) {
swapQuads(quadId, lastQuadId);
}
currentQuadCount--;
}
private void swapQuads(int quad1, int quad2) {
// Swap vertices
swapQuadData(vertexBuffer, quad1, quad2, VERTICES_PER_QUAD * 2);
// Swap texture coordinates
swapQuadData(texCoordBuffer, quad1, quad2, VERTICES_PER_QUAD * 2);
// Swap indices
swapQuadIndices(quad1, quad2);
}
private void swapQuadData(float[] buffer, int quad1, int quad2, int dataSize) {
var start1 = quad1 * dataSize;
var start2 = quad2 * dataSize;
for (int i = 0; i < dataSize; i++) {
var temp = buffer[start1 + i];
buffer[start1 + i] = buffer[start2 + i];
buffer[start2 + i] = temp;
}
}
private void swapQuadIndices(int quad1, int quad2) {
var start1 = quad1 * INDICES_PER_QUAD;
var start2 = quad2 * INDICES_PER_QUAD;
var vertexOffset1 = quad1 * VERTICES_PER_QUAD;
int[] newIndices1 = {
vertexOffset1 + 0, vertexOffset1 + 1, vertexOffset1 + 2,
vertexOffset1 + 2, vertexOffset1 + 3, vertexOffset1 + 0
};
var vertexOffset2 = quad2 * VERTICES_PER_QUAD;
int[] newIndices2 = {
vertexOffset2 + 0, vertexOffset2 + 1, vertexOffset2 + 2,
vertexOffset2 + 2, vertexOffset2 + 3, vertexOffset2 + 0
};
System.arraycopy(newIndices1, 0, indexBuffer, start1, INDICES_PER_QUAD);
System.arraycopy(newIndices2, 0, indexBuffer, start2, INDICES_PER_QUAD);
}
@Override
public void render(Screen screen, Camera camera, ShaderManager shaderManager) {
if (currentQuadCount == 0) {
return;
}
glBindVertexArray(vaoId);
try (var stack = MemoryStack.stackPush()) {
// Vertex buffer
glBindBuffer(GL_ARRAY_BUFFER, vboIds.get(0));
var vertexFloatBuffer = stack.mallocFloat(currentQuadCount * VERTICES_PER_QUAD * 2);
for (int i = 0; i < currentQuadCount * VERTICES_PER_QUAD * 2; i++) {
vertexFloatBuffer.put(vertexBuffer[i]);
}
vertexFloatBuffer.flip();
glBufferSubData(GL_ARRAY_BUFFER, 0, vertexFloatBuffer);
// Texture coordinate buffer
glBindBuffer(GL_ARRAY_BUFFER, vboIds.get(1));
var texCoordFloatBuffer = stack.mallocFloat(currentQuadCount * VERTICES_PER_QUAD * 2);
for (int i = 0; i < currentQuadCount * VERTICES_PER_QUAD * 2; i++) {
texCoordFloatBuffer.put(texCoordBuffer[i]);
}
texCoordFloatBuffer.flip();
glBufferSubData(GL_ARRAY_BUFFER, 0, texCoordFloatBuffer);
// Index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIds.get(2));
var indexIntBuffer = stack.mallocInt(currentQuadCount * INDICES_PER_QUAD);
for (int i = 0; i < currentQuadCount * INDICES_PER_QUAD; i++) {
indexIntBuffer.put(indexBuffer[i]);
}
indexIntBuffer.flip();
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indexIntBuffer);
}
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glDrawElements(GL_TRIANGLES, elementsCount, GL_UNSIGNED_INT, 0);
glDrawElements(GL_TRIANGLES, currentQuadCount * INDICES_PER_QUAD, GL_UNSIGNED_INT, 0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
@@ -131,55 +278,15 @@ public class Mesh implements Renderable, Disposable {
glDeleteVertexArrays(vaoId);
}
public static Mesh quad(float width, float height, float originX, float originY) {
// 1 ----------- 2
// | |
// | |
// | |
// | |
// | |
// 0 ----------- 3
var vertices = new float[]{
-originX, -originY, // 0 - bottom left
-originX, height - originY, // 1 - top left
width - originX, height - originY, // 2 - top right
width - originX, -originY // 3 - bottom right
};
public boolean isFull() {
return currentQuadCount >= maxQuads;
}
// Texture: Quad:
// (0,1) ---- (1,1) 1 ----------- 2
// | | | |
// | | => | TEKSTURA |
// | | | |
// (0,0) ---- (1,0) 0 ----------- 3
var texCoords = new float[]{
0, 0, // 0 - bottom left
0, 1, // 1 - top left
1, 1, // 2 - top right
1, 0 // 3 - bottom right
};
var elements = new int[]{
// First triangle: indices 0-1-2
// 1 ----------- 2
// |XXXXXXXXXXX |
// |XXXXXXXX |
// |XXXXX |
// |XX |
// 0 ----------- 3
0, 1, 2,
// Second triangle: indices 2-3-0
// 1 ----------- 2
// | X|
// | XXXX|
// | XXXXXXX|
// | XXXXXXXXXX|
// 0 ----------- 3
2, 3, 0
};
return new Mesh(vertices, texCoords, elements);
public boolean isEmpty() {
return currentQuadCount == 0;
}
public static QuadMesh quad(float width, float height, float originX, float originY) {
return new QuadMesh(width, height, originX, originY);
}
}

View File

@@ -0,0 +1,26 @@
package com.bartlomiejpluta.base.engine.core.gl.object.mesh;
import lombok.NonNull;
public class QuadMesh extends Mesh {
private final int quadId;
QuadMesh(float width, float height, float originX, float originY) {
this(new QuadTemplate(width, height, originX, originY));
}
QuadMesh(@NonNull QuadTemplate template) {
super(1);
this.quadId = addQuad(template, 0, 0);
}
@Override
public void removeQuad(int quadId) {
throw new UnsupportedOperationException();
}
public void setTextureCoordinates(float[] textureCoordinates) {
super.setQuadTextureCoordinates(quadId, textureCoordinates);
}
}

View File

@@ -3,14 +3,14 @@ package com.bartlomiejpluta.base.engine.core.gl.object.mesh;
import lombok.Getter;
@Getter
public class Quad {
public class QuadTemplate {
private final float width;
private final float height;
private final float originX;
private final float originY;
private final float[] vertices;
public Quad(float width, float height, float originX, float originY) {
public QuadTemplate(float width, float height, float originX, float originY) {
this.width = width;
this.height = height;
this.originX = originX;
@@ -30,33 +30,4 @@ public class Quad {
width - originX, -originY // 3 - bottom right
};
}
static float[] textureCoordinates = new float[]{
0, 0, // 0 - bottom left
0, 1, // 1 - top left
1, 1, // 2 - top right
1, 0 // 3 - bottom right
};
static int[] elements = new int[]{
// First triangle: indices 0-1-2
// 1 ----------- 2
// |XXXXXXXXXXX |
// |XXXXXXXX |
// |XXXXX |
// |XX |
// 0 ----------- 3
0, 1, 2,
// Second triangle: indices 2-3-0
// 1 ----------- 2
// | X|
// | XXXX|
// | XXXXXXX|
// | XXXXXXXXXX|
// 0 ----------- 3
2, 3, 0
};
}

View File

@@ -74,6 +74,16 @@ public class Texture implements Disposable {
}
}
public float[][] getTextureCoordinatesForAllFrames() {
var array = new float[rows*columns][];
for (var i=0; i<array.length; ++i) {
array[i] = getTextureCoordinates(i);
}
return array;
}
public float[] getTextureCoordinates(int id) {
return getTextureCoordinates(id % columns, id / columns);
}
@@ -93,7 +103,7 @@ public class Texture implements Disposable {
return new float[]{
normalizedX, normalizedY, // bottom left
normalizedX, yEnd, // top left
normalizedX, yEnd, // top left
xEnd, yEnd, // top right
xEnd, normalizedY // bottom right
};

View File

@@ -1,43 +1,15 @@
package com.bartlomiejpluta.base.engine.util.mesh;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.joml.Vector2f;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
// TODO
@Slf4j
@Component
public class DefaultMeshManager implements MeshManager {
private final Map<QuadDimension, Mesh> quads = new HashMap<>();
@Override
public Mesh createQuad(float width, float height, float originX, float originY) {
var dim = new QuadDimension(new Vector2f(width, height), new Vector2f(originX, originY));
var mesh = quads.get(dim);
if(mesh == null) {
log.info("Creating [w:{}, h:{} | O:{}, {}] mesh and putting it into the cache", width, height, originX, originY);
mesh = Mesh.quad(width, height, originX, originY);
quads.put(dim, mesh);
}
return mesh;
}
@Override
public void cleanUp() {
log.info("Disposing meshes");
quads.forEach((dim, mesh) -> mesh.dispose());
log.info("{} meshes have been disposed", quads.size());
log.info("Nothing to do for now (todo...)");
}
@Data
private static class QuadDimension {
private final Vector2f size;
private final Vector2f origin;
}
}

View File

@@ -1,8 +1,6 @@
package com.bartlomiejpluta.base.engine.util.mesh;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.internal.gc.Cleanable;
public interface MeshManager extends Cleanable {
Mesh createQuad(float width, float height, float originX, float originY);
}

View File

@@ -2,7 +2,6 @@ package com.bartlomiejpluta.base.engine.world.animation.manager;
import com.bartlomiejpluta.base.api.animation.Animation;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.core.gl.object.texture.TextureManager;
import com.bartlomiejpluta.base.engine.error.AppException;
import com.bartlomiejpluta.base.engine.project.config.ProjectConfiguration;
@@ -28,11 +27,10 @@ public class DefaultAnimationManager implements AnimationManager {
private final Map<String, AnimationAsset> assets = new HashMap<>();
private final Map<String, Vector2fc[]> frames = new HashMap<>();
private final ProjectConfiguration configuration;
private Mesh mesh;
@Override
public void init() {
mesh = meshManager.createQuad(1, 1, 0.5f, 0.5f);
// TODO mesh = meshManager.createQuad(1, 1, 0.5f, 0.5f);
}
@Override
@@ -62,7 +60,7 @@ public class DefaultAnimationManager implements AnimationManager {
var texture = textureManager.loadTexture(source, asset.getRows(), asset.getColumns());
var material = Material.textured(texture);
return new DefaultAnimation(mesh, material, animationFrames);
return new DefaultAnimation(material, animationFrames);
}
private Vector2fc[] createFrames(int rows, int columns) {

View File

@@ -3,7 +3,6 @@ package com.bartlomiejpluta.base.engine.world.animation.model;
import com.bartlomiejpluta.base.api.animation.Animated;
import com.bartlomiejpluta.base.engine.core.engine.DefaultGameEngine;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.world.object.Sprite;
import com.bartlomiejpluta.base.util.math.MathUtil;
import lombok.EqualsAndHashCode;
@@ -17,14 +16,18 @@ public abstract class AnimatedSprite extends Sprite implements Animated {
private int intervalInMilliseconds = 100;
protected int currentAnimationFrame;
public AnimatedSprite(Mesh mesh, Material material) {
super(mesh, material);
public AnimatedSprite(Material material) {
super(material);
}
protected abstract boolean shouldAnimate();
protected abstract Vector2fc[] getSpriteAnimationFramesPositions();
protected int[] getAvailableFrames() {
return null;
}
@Override
public void setAnimationSpeed(float speed) {
intervalInMilliseconds = (int) (1 / MathUtil.clamp(speed / DefaultGameEngine.TARGET_UPS, Float.MIN_VALUE, 1.0));
@@ -37,20 +40,24 @@ public abstract class AnimatedSprite extends Sprite implements Animated {
@Override
public void setAnimationFrame(int frame) {
var positions = getSpriteAnimationFramesPositions();
currentAnimationFrame = frame % positions.length;
var current = positions[currentAnimationFrame];
material.setSpritePosition(current);
var availableFrames = getAvailableFrames();
if (availableFrames == null) {
setFrame(frame % getTextureCoordinates().length);
return;
}
setFrame(availableFrames[frame % availableFrames.length]);
}
@Override
public void update(float dt) {
if (shouldAnimate()) {
time += dt * 1000;
var positions = getSpriteAnimationFramesPositions();
currentAnimationFrame = ((time % (positions.length * intervalInMilliseconds)) / intervalInMilliseconds);
var current = positions[currentAnimationFrame];
material.setSpritePosition(current);
setAnimationFrame(time / intervalInMilliseconds * intervalInMilliseconds);
// var maxFrames = getTextureCoordinates().length;
// currentAnimationFrame = ((time % (maxFrames * intervalInMilliseconds)) / intervalInMilliseconds);
// setSprite(currentAnimationFrame);
} else {
time = 0;
}

View File

@@ -7,7 +7,6 @@ import com.bartlomiejpluta.base.api.move.AnimationMovement;
import com.bartlomiejpluta.base.api.move.Direction;
import com.bartlomiejpluta.base.api.move.Movement;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.world.movement.MovableSprite;
import com.bartlomiejpluta.base.util.path.Path;
import com.bartlomiejpluta.base.util.path.PathExecutor;
@@ -51,8 +50,8 @@ public class DefaultAnimation extends MovableSprite implements Animation {
@Getter
private final CompletableFuture<Animation> future = new CompletableFuture<>();
public DefaultAnimation(Mesh mesh, Material material, @NonNull Vector2fc[] frames) {
super(mesh, material);
public DefaultAnimation(Material material, @NonNull Vector2fc[] frames) {
super(material);
this.frames = frames;
this.lastFrameIndex = frames.length - 1;

View File

@@ -1,6 +1,5 @@
package com.bartlomiejpluta.base.engine.world.autotile.manager;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.core.gl.object.texture.TextureManager;
import com.bartlomiejpluta.base.engine.error.AppException;
import com.bartlomiejpluta.base.engine.project.config.ProjectConfiguration;
@@ -24,11 +23,10 @@ public class DefaultAutoTileSetManager implements AutoTileManager {
private final Map<String, AutoTileSet> autoTiles = new HashMap<>();
private final Map<String, AutoTileSetAsset> assets = new HashMap<>();
private final ProjectConfiguration configuration;
private Mesh mesh;
@Override
public void init() {
this.mesh = meshManager.createQuad(1, 1, 0, 0);
// TODO: this.mesh = meshManager.createQuad(1, 1, 0, 0);
}
@Override
@@ -55,7 +53,7 @@ public class DefaultAutoTileSetManager implements AutoTileManager {
var source = configuration.projectFile("autotiles", asset.getSource());
var texture = textureManager.loadTexture(source, asset.getRows() * asset.getLayout().getRows() * 2, asset.getColumns() * asset.getLayout().getColumns() * 2);
autoTile = new AutoTileSet(texture, mesh, asset.getRows(), asset.getColumns(), asset.getLayout());
autoTile = new AutoTileSet(texture, asset.getRows(), asset.getColumns(), asset.getLayout());
log.info("Loading auto tile set from assets to cache under the key: [{}]", uid);
autoTiles.put(uid, autoTile);
}

View File

@@ -1,7 +1,6 @@
package com.bartlomiejpluta.base.engine.world.autotile.model;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.world.object.Sprite;
import lombok.Getter;
import lombok.NonNull;
@@ -14,16 +13,16 @@ public class AutoSubTile extends Sprite {
private final Vector2fc tileSpriteSize;
private final Vector2f tileScale = new Vector2f(1, 1);
public AutoSubTile(Mesh mesh, AutoTileSet autoTileSet) {
super(mesh, Material.textured(autoTileSet.getTexture()));
public AutoSubTile(AutoTileSet autoTileSet) {
super(Material.textured(autoTileSet.getTexture()));
tileSpriteSize = material.getTexture().getSpriteSize();
tileSpriteSize = getMaterial().getTexture().getSpriteSize();
super.setScale(tileSpriteSize.x() * tileScale.x, tileSpriteSize.y() * tileScale.y);
}
public void recalculate(@NonNull Vector2ic spritePosition) {
material.setSpritePosition(spritePosition.y(), spritePosition.x());
setFrame(spritePosition.x(), spritePosition.y());
}
@Override

View File

@@ -2,7 +2,6 @@ package com.bartlomiejpluta.base.engine.world.autotile.model;
import com.bartlomiejpluta.base.api.camera.Camera;
import com.bartlomiejpluta.base.api.screen.Screen;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.internal.render.Renderable;
import com.bartlomiejpluta.base.internal.render.ShaderManager;
import lombok.Getter;
@@ -18,11 +17,11 @@ public class AutoTile implements Renderable {
@Getter
private int setId;
public AutoTile(@NonNull Mesh mesh, @NonNull AutoTileSet autoTileSet, int setId) {
this.topLeftSubTile = new AutoSubTile(mesh, autoTileSet);
this.topRightSubTile = new AutoSubTile(mesh, autoTileSet);
this.bottomLeftSubTile = new AutoSubTile(mesh, autoTileSet);
this.bottomRightSubTile = new AutoSubTile(mesh, autoTileSet);
public AutoTile(@NonNull AutoTileSet autoTileSet, int setId) {
this.topLeftSubTile = new AutoSubTile(autoTileSet);
this.topRightSubTile = new AutoSubTile(autoTileSet);
this.bottomLeftSubTile = new AutoSubTile(autoTileSet);
this.bottomRightSubTile = new AutoSubTile(autoTileSet);
this.autoTileSet = autoTileSet;
this.setId = setId;
}

View File

@@ -1,6 +1,5 @@
package com.bartlomiejpluta.base.engine.world.autotile.model;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.core.gl.object.texture.Texture;
import lombok.Getter;
import lombok.NonNull;
@@ -45,7 +44,6 @@ public class AutoTileSet {
@Getter
private final Texture texture;
private final Mesh mesh;
@Getter
private final Vector2fc tileSize;
@@ -58,9 +56,8 @@ public class AutoTileSet {
@Getter
private final int setsCount;
public AutoTileSet(@NonNull Texture texture, @NonNull Mesh mesh, int rows, int columns, @NonNull AutoTileLayout layout) {
public AutoTileSet(@NonNull Texture texture, int rows, int columns, @NonNull AutoTileLayout layout) {
this.texture = texture;
this.mesh = mesh;
this.rows = rows;
this.columns = columns;
this.tileSize = texture.getSpriteSize();
@@ -202,6 +199,6 @@ public class AutoTileSet {
}
public AutoTile createTile(int setId) {
return new AutoTile(mesh, this, setId);
return new AutoTile(this, setId);
}
}

View File

@@ -2,7 +2,6 @@ package com.bartlomiejpluta.base.engine.world.character.manager;
import com.bartlomiejpluta.base.api.character.Character;
import com.bartlomiejpluta.base.api.move.Direction;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.util.mesh.MeshManager;
import com.bartlomiejpluta.base.engine.world.character.config.CharacterSpriteConfiguration;
import com.bartlomiejpluta.base.engine.world.character.model.DefaultCharacter;
@@ -27,8 +26,6 @@ public class DefaultCharacterManager implements CharacterManager {
private final Map<Direction, Integer> spriteDirectionRows;
private final Map<Direction, Vector2fc> spriteDefaultRows;
private Mesh mesh;
@Autowired
public DefaultCharacterManager(MeshManager meshManager, CharacterSetManager characterSetManager, CharacterSpriteConfiguration configuration) {
this.meshManager = meshManager;
@@ -45,12 +42,12 @@ public class DefaultCharacterManager implements CharacterManager {
@Override
public void init() {
mesh = meshManager.createQuad(1, 1, 0.5f, 1);
// TODO mesh = meshManager.createQuad(1, 1, 0.5f, 1);
}
@Override
public Character createCharacter(String characterSetUid) {
return new DefaultCharacter(mesh, characterSetManager, defaultSpriteColumn, spriteDirectionRows, spriteDefaultRows, characterSetUid);
return new DefaultCharacter(characterSetManager, defaultSpriteColumn, spriteDirectionRows, spriteDefaultRows, characterSetUid);
}
@Override

View File

@@ -8,7 +8,6 @@ import com.bartlomiejpluta.base.api.move.CharacterMovement;
import com.bartlomiejpluta.base.api.move.Direction;
import com.bartlomiejpluta.base.api.move.Movement;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.error.AppException;
import com.bartlomiejpluta.base.engine.world.character.manager.CharacterSetManager;
import com.bartlomiejpluta.base.engine.world.movement.MovableSprite;
@@ -29,6 +28,15 @@ import static java.util.Objects.requireNonNull;
@EqualsAndHashCode(callSuper = true)
public class DefaultCharacter extends MovableSprite implements Character {
private static final Map<Direction, int[]> CHARSET_FRAMES = Map.of(
Direction.DOWN, new int[]{0, 1, 2, 3},
Direction.LEFT, new int[]{4, 5, 6, 7},
Direction.RIGHT, new int[]{8, 9, 10, 11},
Direction.UP, new int[]{12, 13, 14, 15}
);
private static final int DEFAULT_CHARSET_FRAME_COLUMN = 0;
private final int defaultSpriteColumn;
private final CharacterSetManager characterSetManager;
private final Map<Direction, Integer> spriteDirectionRows;
@@ -56,19 +64,21 @@ public class DefaultCharacter extends MovableSprite implements Character {
private final Queue<CharacterInstantAnimation> instantAnimations = new LinkedList<>();
public DefaultCharacter(Mesh mesh, CharacterSetManager characterSetManager, int defaultSpriteColumn, Map<Direction, Integer> spriteDirectionRows, Map<Direction, Vector2fc> spriteDefaultRows, String characterSetUid) {
super(mesh, createMaterial(characterSetManager, characterSetUid));
public DefaultCharacter(CharacterSetManager characterSetManager, int defaultSpriteColumn, Map<Direction, Integer> spriteDirectionRows, Map<Direction, Vector2fc> spriteDefaultRows, String characterSetUid) {
super(createMaterial(characterSetManager, characterSetUid));
this.defaultSpriteColumn = defaultSpriteColumn;
this.characterSetManager = characterSetManager;
this.spriteDirectionRows = spriteDirectionRows;
this.faceDirection = Direction.DOWN;
this.spriteDefaultRows = spriteDefaultRows;
var texture = material.getTexture();
var texture = getMaterial().getTexture();
if (texture != null) {
this.characterSetSize = texture.getSpriteSize();
super.setScale(characterSetSize.x() * characterScale.x, characterSetSize.y() * characterScale.y);
}
setDefaultAnimationFrame();
}
private static Material createMaterial(CharacterSetManager characterSetManager, String characterSetUid) {
@@ -102,13 +112,13 @@ public class DefaultCharacter extends MovableSprite implements Character {
@Override
protected boolean shouldAnimate() {
return animationEnabled && material.getTexture() != null && (isMoving() || !instantAnimations.isEmpty());
return animationEnabled && getMaterial().getTexture() != null && (isMoving() || !instantAnimations.isEmpty());
}
@Override
protected Vector2fc[] getSpriteAnimationFramesPositions() {
var row = spriteDirectionRows.get(faceDirection);
var frames = material.getTexture().getColumns();
var frames = getMaterial().getTexture().getColumns();
var array = new Vector2f[frames];
for (int column = 0; column < frames; ++column) {
@@ -118,15 +128,21 @@ public class DefaultCharacter extends MovableSprite implements Character {
return array;
}
@Override
protected int[] getAvailableFrames() {
return CHARSET_FRAMES.get(faceDirection);
}
@Override
protected void setDefaultAnimationFrame() {
material.setSpritePosition(spriteDefaultRows.get(faceDirection));
// getMaterial().setSpritePosition(spriteDefaultRows.get(faceDirection));
setAnimationFrame(CHARSET_FRAMES.get(faceDirection)[DEFAULT_CHARSET_FRAME_COLUMN]);
}
@Override
public void changeCharacterSet(String characterSetUid) {
this.material = createMaterial(characterSetManager, characterSetUid);
var texture = this.material.getTexture();
setMaterial(createMaterial(characterSetManager, characterSetUid));
var texture = this.getMaterial().getTexture();
if (texture != null) {
this.characterSetSize = texture.getSpriteSize();

View File

@@ -1,7 +1,6 @@
package com.bartlomiejpluta.base.engine.world.icon.manager;
import com.bartlomiejpluta.base.api.icon.Icon;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.util.mesh.MeshManager;
import com.bartlomiejpluta.base.engine.world.icon.model.DefaultIcon;
import lombok.RequiredArgsConstructor;
@@ -15,16 +14,15 @@ import org.springframework.stereotype.Component;
public class DefaultIconManager implements IconManager {
private final MeshManager meshManager;
private final IconSetManager iconSetManager;
private Mesh mesh;
@Override
public void init() {
this.mesh = meshManager.createQuad(1, 1, 0.5f, 1);
// TODO: this.mesh = meshManager.createQuad(1, 1, 0.5f, 1);
}
@Override
public Icon createIcon(String iconSetUid, int row, int column) {
return new DefaultIcon(mesh, iconSetManager, iconSetUid, row, column);
return new DefaultIcon(iconSetManager, iconSetUid, row, column);
}
@Override

View File

@@ -4,7 +4,6 @@ import com.bartlomiejpluta.base.api.event.Event;
import com.bartlomiejpluta.base.api.event.EventType;
import com.bartlomiejpluta.base.api.icon.Icon;
import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.error.AppException;
import com.bartlomiejpluta.base.engine.world.icon.manager.IconSetManager;
import com.bartlomiejpluta.base.engine.world.object.Sprite;
@@ -42,12 +41,12 @@ public class DefaultIcon extends Sprite implements Icon {
@Getter
private int zIndex;
public DefaultIcon(Mesh mesh, IconSetManager iconSetManager, String iconSetUid, int row, int column) {
super(mesh, iconSetManager.loadObject(iconSetUid));
public DefaultIcon(IconSetManager iconSetManager, String iconSetUid, int row, int column) {
super(iconSetManager.loadObject(iconSetUid));
this.iconSetManager = iconSetManager;
material.setSpritePosition(column, row);
getMaterial().setSpritePosition(column, row);
var texture = material.getTexture();
var texture = getMaterial().getTexture();
if (texture != null) {
this.iconSetSize = texture.getSpriteSize();
super.setScale(iconSetSize.x() * iconScale.x, iconSetSize.y() * iconScale.y);
@@ -70,21 +69,21 @@ public class DefaultIcon extends Sprite implements Icon {
@Override
public void changeIcon(int row, int column) {
material.setSpritePosition(column, row);
getMaterial().setSpritePosition(column, row);
this.iconSetRow = row;
this.iconSetColumn = column;
}
@Override
public void changeIcon(String iconSetUid, int row, int column) {
this.material = iconSetManager.loadObject(iconSetUid);
material.setSpritePosition(column, row);
setMaterial(iconSetManager.loadObject(iconSetUid));
getMaterial().setSpritePosition(column, row);
this.iconSetUid = iconSetUid;
this.iconSetRow = row;
this.iconSetColumn = column;
var texture = material.getTexture();
var texture = getMaterial().getTexture();
if (texture != null) {
this.iconSetSize = texture.getSpriteSize();
super.setScale(iconSetSize.x() * iconScale.x, iconSetSize.y() * iconScale.y);

View File

@@ -2,7 +2,6 @@ package com.bartlomiejpluta.base.engine.world.image.manager;
import com.bartlomiejpluta.base.api.image.Image;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.core.gl.object.texture.TextureManager;
import com.bartlomiejpluta.base.engine.error.AppException;
import com.bartlomiejpluta.base.engine.project.config.ProjectConfiguration;
@@ -30,11 +29,10 @@ public class DefaultImageManager implements ImageManager {
private final Map<String, ImageAsset> assets = new HashMap<>();
private final Map<String, ByteBuffer> imageBuffers = new HashMap<>();
private final ProjectConfiguration configuration;
private Mesh mesh;
@Override
public void init() {
mesh = meshManager.createQuad(1, 1, 0, 0);
// TODO mesh = meshManager.createQuad(1, 1, 0, 0);
}
@Override
@@ -66,7 +64,7 @@ public class DefaultImageManager implements ImageManager {
var material = Material.textured(texture);
log.info("Creating new image on asset with UID: [{}]", uid);
return new DefaultImage(mesh, material, initialWidth, initialHeight, gcd);
return new DefaultImage(material, initialWidth, initialHeight, gcd);
}
@Override

View File

@@ -2,7 +2,6 @@ package com.bartlomiejpluta.base.engine.world.image.model;
import com.bartlomiejpluta.base.api.image.Image;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.world.object.Sprite;
import lombok.Getter;
import lombok.NonNull;
@@ -17,8 +16,8 @@ public class DefaultImage extends Sprite implements Image {
private final int width;
private final int height;
public DefaultImage(@NonNull Mesh mesh, @NonNull Material texture, int primaryWidth, int primaryHeight, int factor) {
super(mesh, texture);
public DefaultImage(@NonNull Material texture, int primaryWidth, int primaryHeight, int factor) {
super(texture);
this.primaryWidth = primaryWidth;
this.primaryHeight = primaryHeight;
this.factor = factor;
@@ -31,12 +30,12 @@ public class DefaultImage extends Sprite implements Image {
@Override
public void setOpacity(float opacity) {
material.setAlpha(opacity);
getMaterial().setAlpha(opacity);
}
@Override
public float getOpacity() {
return material.getColor().w();
return getMaterial().getColor().w();
}
@Override

View File

@@ -9,7 +9,6 @@ import com.bartlomiejpluta.base.engine.world.location.LocationableModel;
import com.bartlomiejpluta.base.internal.render.ShaderManager;
import lombok.Getter;
import lombok.Setter;
import org.joml.Vector2f;
import org.joml.Vector3f;
import org.joml.Vector3fc;

View File

@@ -144,7 +144,8 @@ public class DefaultColorLayer extends BaseLayer implements ColorLayer {
private static class Color extends Sprite {
public Color(@NonNull MeshManager meshManager, float red, float green, float blue, float alpha) {
super(meshManager.createQuad(1, 1, 0, 0), Material.colored(red, green, blue, alpha));
// TODO (Custom Mesh): super(meshManager.createQuad(1, 1, 0, 0), Material.colored(red, green, blue, alpha));
super(Material.colored(red, green, blue, alpha));
}
}
}

View File

@@ -2,8 +2,8 @@ package com.bartlomiejpluta.base.engine.world.map.layer.tile;
import com.bartlomiejpluta.base.api.camera.Camera;
import com.bartlomiejpluta.base.api.screen.Screen;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.BatchedQuads;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Quad;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.QuadTemplate;
import com.bartlomiejpluta.base.engine.core.gl.object.texture.Texture;
import com.bartlomiejpluta.base.engine.core.gl.shader.constant.Shader;
import com.bartlomiejpluta.base.engine.core.gl.shader.constant.UniformName;
@@ -16,9 +16,9 @@ import com.bartlomiejpluta.base.internal.render.ShaderManager;
public class TileChunk extends Model implements Placeable, Renderable, Disposable, BoundingBox {
private final Texture tileSet;
private final BatchedQuads mesh;
private final Mesh mesh;
private final int chunkSize;
private final Quad quad;
private final QuadTemplate template;
private final float originX;
private final float originY;
@@ -29,14 +29,14 @@ public class TileChunk extends Model implements Placeable, Renderable, Disposabl
public TileChunk(Texture tileSet, int chunkSize, float originX, float originY) {
this.tileSet = tileSet;
this.chunkSize = chunkSize;
this.mesh = new BatchedQuads(chunkSize * chunkSize);
this.quad = new Quad(tileSet.getSpriteSize().x(), tileSet.getSpriteSize().y(), originX, originY);
this.mesh = new Mesh(chunkSize * chunkSize);
this.template = new QuadTemplate(tileSet.getSpriteSize().x(), tileSet.getSpriteSize().y(), originX, originY);
this.originX = originX;
this.originY = originY;
}
public int addTile(int x, int y, int tileId) {
return mesh.addQuad(quad, x, y, tileSet.getTextureCoordinates(tileId));
return mesh.addQuad(template, x, y, tileSet.getTextureCoordinates(tileId));
}
public void removeTile(int quadId) {

View File

@@ -2,7 +2,6 @@ package com.bartlomiejpluta.base.engine.world.map.manager;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.map.handler.MapHandler;
import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer;
import com.bartlomiejpluta.base.engine.error.AppException;
import com.bartlomiejpluta.base.engine.project.config.ProjectConfiguration;
import com.bartlomiejpluta.base.engine.util.reflection.ClassLoader;

View File

@@ -4,7 +4,6 @@ import com.bartlomiejpluta.base.api.move.Movable;
import com.bartlomiejpluta.base.api.move.Movement;
import com.bartlomiejpluta.base.engine.core.engine.DefaultGameEngine;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.world.animation.model.AnimatedSprite;
import com.bartlomiejpluta.base.internal.program.Updatable;
import com.bartlomiejpluta.base.util.math.MathUtil;
@@ -12,9 +11,6 @@ import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.joml.Vector2f;
import static java.lang.Math.abs;
import static java.lang.Math.max;
@EqualsAndHashCode(callSuper = true)
public abstract class MovableSprite extends AnimatedSprite implements Movable, Updatable {
private int moveTime = 0;
@@ -24,8 +20,8 @@ public abstract class MovableSprite extends AnimatedSprite implements Movable, U
@Getter
private Movement movement;
public MovableSprite(Mesh mesh, Material material) {
super(mesh, material);
public MovableSprite(Material material) {
super(material);
}
@Override

View File

@@ -4,43 +4,103 @@ import com.bartlomiejpluta.base.api.camera.Camera;
import com.bartlomiejpluta.base.api.screen.Screen;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.QuadMesh;
import com.bartlomiejpluta.base.engine.core.gl.shader.constant.Shader;
import com.bartlomiejpluta.base.engine.core.gl.shader.constant.UniformName;
import com.bartlomiejpluta.base.engine.world.location.LocationableModel;
import com.bartlomiejpluta.base.internal.render.BoundingBox;
import com.bartlomiejpluta.base.internal.render.Renderable;
import com.bartlomiejpluta.base.internal.render.ShaderManager;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
@EqualsAndHashCode(callSuper = true)
public abstract class Sprite extends LocationableModel implements Renderable {
private final float farthestVertexDistance;
protected final Mesh mesh;
public abstract class Sprite extends LocationableModel implements Renderable, BoundingBox {
@NonNull
@Setter
@Getter
protected Material material;
private Material material;
public Sprite(Mesh mesh, Material material) {
this.mesh = mesh;
private final QuadMesh quad;
private final float width = 1f;
private final float height = 1f;
private final float originX = .5f;
private final float originY = 1f;
@Getter
private float[][] textureCoordinates;
public Sprite(Material material) {
this.material = material;
this.quad = Mesh.quad(width, height, originX, originY);
this.farthestVertexDistance = this.mesh.getFarthestVertex().lengthSquared();
updateTextureCoordinates();
}
private void updateTextureCoordinates() {
var texture = material.getTexture();
if (texture == null) {
return;
}
textureCoordinates = texture.getTextureCoordinatesForAllFrames();
}
public void setMaterial(@NonNull Material material) {
this.material = material;
updateTextureCoordinates();
}
public void setFrame(int row, int column) {
setFrame(row * material.getTexture().getColumns() + column);
}
public void setFrame(int id) {
quad.setTextureCoordinates(textureCoordinates[id]);
}
@Override
public float getMinX() {
float scaledOriginX = originX * scaleX;
return getPosition().x() - scaledOriginX;
}
@Override
public float getMaxX() {
float scaledOriginX = originX * scaleX;
float scaledChunkWidth = width * scaleX;
return getPosition().x() + scaledChunkWidth - scaledOriginX;
}
@Override
public float getMinY() {
float scaledOriginY = originY * scaleY;
return getPosition().y() - scaledOriginY;
}
@Override
public float getMaxY() {
float scaledOriginY = originY * scaleY;
float scaledChunkHeight = height * scaleY;
return getPosition().y() + scaledChunkHeight - scaledOriginY;
}
@Override
public void render(Screen screen, Camera camera, ShaderManager shaderManager) {
if (!camera.insideFrustum(position.x, position.y, farthestVertexDistance * (scaleX > scaleY ? scaleX : scaleY))) {
if (!camera.containsBox(this)) {
return;
}
material.getTexture().activate();
shaderManager.activateShader(Shader.BATCH.name);
shaderManager.setUniform(UniformName.UNI_PROJECTION_MATRIX, camera.getProjectionMatrix());
shaderManager.setUniform(UniformName.UNI_VIEW_MODEL_MATRIX, camera.computeViewModelMatrix(getModelMatrix()));
shaderManager.setUniform(UniformName.UNI_MODEL_MATRIX, getModelMatrix());
material.render(screen, camera, shaderManager);
mesh.render(screen, camera, shaderManager);
shaderManager.setUniform(UniformName.UNI_TEXTURE_SAMPLER, 0);
quad.render(screen, camera, shaderManager);
shaderManager.deactivateShader();
}
}

View File

@@ -1,6 +1,5 @@
package com.bartlomiejpluta.base.engine.world.tileset.manager;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.core.gl.object.texture.TextureManager;
import com.bartlomiejpluta.base.engine.error.AppException;
import com.bartlomiejpluta.base.engine.project.config.ProjectConfiguration;
@@ -24,11 +23,10 @@ public class DefaultTileSetManager implements TileSetManager {
private final Map<String, TileSet> tileSets = new HashMap<>();
private final Map<String, TileSetAsset> assets = new HashMap<>();
private final ProjectConfiguration configuration;
private Mesh mesh;
@Override
public void init() {
this.mesh = meshManager.createQuad(1, 1, 0, 0);
// TODO this.mesh = meshManager.createQuad(1, 1, 0, 0);
}
@Override
@@ -55,7 +53,7 @@ public class DefaultTileSetManager implements TileSetManager {
var source = configuration.projectFile("tilesets", asset.getSource());
var texture = textureManager.loadTexture(source, asset.getRows(), asset.getColumns());
tileset = new TileSet(texture, mesh);
tileset = new TileSet(texture);
log.info("Loading tile set from assets to cache under the key: [{}]", uid);
tileSets.put(uid, tileset);
}

View File

@@ -1,7 +1,6 @@
package com.bartlomiejpluta.base.engine.world.tileset.model;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.core.gl.object.texture.Texture;
import com.bartlomiejpluta.base.engine.world.object.Sprite;
import lombok.Getter;
@@ -17,28 +16,28 @@ public class Tile extends Sprite {
private final Vector2fc tileSpriteSize;
public Tile setLocation(int row, int column) {
var stepSize = material.getTexture().getSpriteSize();
var stepSize = getMaterial().getTexture().getSpriteSize();
setPosition(column * stepSize.x(), row * stepSize.y());
return this;
}
public Tile(Mesh mesh, Texture tileSet, int id) {
super(mesh, Material.textured(tileSet));
public Tile(Texture tileSet, int id) {
super(Material.textured(tileSet));
this.id = id;
this.tileSetRow = id / tileSet.getColumns();
this.tileSetColumn = id % tileSet.getColumns();
material.setSpritePosition(tileSetColumn, tileSetRow);
getMaterial().setSpritePosition(tileSetColumn, tileSetRow);
tileSpriteSize = tileSet.getSpriteSize();
super.setScale(tileSpriteSize.x() * tileScale.x, tileSpriteSize.y() * tileScale.y);
}
public Tile(Mesh mesh, Texture tileSet, int row, int column) {
super(mesh, Material.textured(tileSet));
public Tile(Texture tileSet, int row, int column) {
super(Material.textured(tileSet));
this.tileSetRow = row;
this.tileSetColumn = column;
this.id = row * tileSet.getColumns() + column;
material.setSpritePosition(tileSetColumn, tileSetRow);
getMaterial().setSpritePosition(tileSetColumn, tileSetRow);
tileSpriteSize = tileSet.getSpriteSize();
super.setScale(tileSpriteSize.x() * tileScale.x, tileSpriteSize.y() * tileScale.y);

View File

@@ -1,29 +1,25 @@
package com.bartlomiejpluta.base.engine.world.tileset.model;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.core.gl.object.texture.Texture;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@Getter
public class TileSet {
private final Texture tileSet;
private final Mesh mesh;
private final Material material;
public TileSet(@NonNull Texture tileSet, @NonNull Mesh mesh) {
public TileSet(@NonNull Texture tileSet) {
this.tileSet = tileSet;
this.mesh = mesh;
this.material = Material.textured(tileSet);
}
public Tile tileById(int id) {
return new Tile(mesh, tileSet, id);
return new Tile(tileSet, id);
}
public Tile tileAt(int row, int column) {
return new Tile(mesh, tileSet, row, column);
return new Tile(tileSet, row, column);
}
}