diff --git a/src/java/ru/dmitriymx/lwjgl/tools/Tessellator.java b/src/java/ru/dmitriymx/lwjgl/tools/Tessellator.java new file mode 100644 index 0000000..64fa7ca --- /dev/null +++ b/src/java/ru/dmitriymx/lwjgl/tools/Tessellator.java @@ -0,0 +1,275 @@ +package ru.dmitriymx.lwjgl.tools; + +import java.nio.FloatBuffer; + +import org.lwjgl.BufferUtils; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL15.*; + +public class Tessellator { + private static Tessellator instance = new Tessellator(); + private int draw_mode; + private float[] float_buffer; + private int float_buffer_position; + private boolean use_dl, use_vbo, use_va; + private int vertex_count; + private boolean use_color, use_texture; + private int display_list_id; + + private Tessellator() { + float_buffer = new float[0x200000]; + } + + public Tessellator getInstance() { + return instance; + } + + public void reset() { + use_vbo = false; + draw_mode = 0; + vertex_count = 0; + float_buffer_position = 0; + use_color = false; + use_texture = false; + } + + public void addVertex(float x, float y, float z) { + if (use_dl) { + dl_add_vertex(x, y, z); + } else if (use_vbo) { + vbo_add_vertex(x, y, z); + } else if (use_va) { + va_add_vertex(x, y, z); + } + } + + public Tessellator setTexture(float u, float v) { + if (use_dl) { + dl_set_texture(u, v); + } else if (use_vbo) { + vbo_set_texture(u, v); + } else if (use_va) { + va_set_texture(u, v); + } + + return this; + } + + public Tessellator setColor(float red, float green, float blue, float alpha) { + if (use_dl) { + dl_set_color(red, green, blue, alpha); + } else if (use_vbo) { + vbo_set_color(red, green, blue, alpha); + } else if (use_va) { + va_set_color(red, green, blue, alpha); + } + + return this; + } + + public Tessellator setColor(float red, float green, float blue) { + return setColor(red, green, blue, 1f); + } + + // DISPLAY LIST (DL) ====================================================== + + public void startDrawingUseDL(int drawMode) { + use_dl = true; + display_list_id = glGenLists(1); + glNewList(display_list_id, GL_COMPILE); + glBegin(drawMode); + } + + private void dl_add_vertex(float x, float y, float z) { + glVertex3f(x, y, z); + } + + private void dl_set_color(float red, float green, float blue, float alpha) { + glColor4f(red, green, blue, alpha); + } + + private void dl_set_texture(float u, float v) { + glTexCoord2f(u, v); + } + + public int createDL() { + if (use_dl) { + glEnd(); + glEndList(); + int result = display_list_id; + reset(); + return result; + } else { + throw new IllegalAccessError("Drawing with display list not started"); + } + } + + public void removeDL(int displayListId){ + glDeleteLists(displayListId, 1); + } + + public void drawDL(int displayListId) { + glCallList(displayListId); + } + + // VERTEX BUFFER OBJECT (VBO) ============================================= + + public void startDrawingUseVBO(int drawMode) { + draw_mode = drawMode; + use_vbo = true; + } + + private void vbo_add_vertex(float x, float y, float z) { + float_buffer[float_buffer_position] = x; + float_buffer[float_buffer_position + 1] = y; + float_buffer[float_buffer_position + 2] = z; + + float_buffer_position += 9; + vertex_count++; + } + + private void vbo_set_color(float red, float green, float blue, float alpha) { + float_buffer[float_buffer_position + 5] = red; + float_buffer[float_buffer_position + 6] = green; + float_buffer[float_buffer_position + 7] = blue; + float_buffer[float_buffer_position + 8] = alpha; + + use_color = true; + } + + private void vbo_set_texture(float u, float v) { + float_buffer[float_buffer_position + 3] = u; + float_buffer[float_buffer_position + 4] = v; + + use_texture = true; + } + + public int createVBO() { + if (use_vbo) { + if (vertex_count > 0) { + FloatBuffer vbo_buffer = BufferUtils.createFloatBuffer(float_buffer_position); + vbo_buffer.put(float_buffer, 0, float_buffer_position); + vbo_buffer.flip(); + + int vboId = glGenBuffers(); + glBindBuffer(GL_ARRAY_BUFFER, vboId); + glBufferData(GL_ARRAY_BUFFER, vbo_buffer, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + int r2 = vboId | (draw_mode << 8) | (vertex_count << 16) | ((use_color ? 1 : 0) << 24) | ((use_texture ? 1 : 0) << 32); + reset(); + + return r2; + } else { + throw new IllegalAccessError("Vertex count < 1"); + } + } else { + throw new IllegalAccessError("Drawing with vertex buffer object not started"); + } + } + + public void removeVBO(long vboData){ + glDeleteBuffers((int) (0b11111111 & vboData)); + } + + public void drawVBO(int vboData) { + glBindBuffer(GL_ARRAY_BUFFER, (int) (0b11111111 & vboData)); + boolean has_color = (0b11111111 & vboData >>> 24) == 1; + boolean has_texture = (0b11111111 & vboData >>> 32) == 1; + + + if (has_color) { + glEnableClientState(GL_COLOR_ARRAY); + // 12L -> 3 << 2 + glColorPointer(4, GL_FLOAT, 9 << 2, 5 << 2); + } + if (has_texture) { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + // X << 3 -> (X * 2) << 2 + glTexCoordPointer(2, GL_FLOAT, 9 << 2, 3 << 2); + } + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, 9 << 2, 0); + + glDrawArrays((int) (0b11111111 & vboData >>> 8), 0, (int) (0b11111111 & vboData >>> 16)); + + if (has_texture) { + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + if (has_color) { + glDisableClientState(GL_COLOR_ARRAY); + } + glDisableClientState(GL_VERTEX_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + // VERTEX ARRAY (VA) ====================================================== + + public void startDrawingUseVA(int drawMode) { + use_va = true; + draw_mode = drawMode; + use_color = false; //TODO а надоли? есть же reset() + use_texture = false; + } + + private void va_add_vertex(float x, float y, float z) { + float_buffer[float_buffer_position] = x; + float_buffer[float_buffer_position + 1] = y; + float_buffer[float_buffer_position + 2] = z; + + float_buffer_position += 9; + vertex_count++; + } + + private void va_set_color(float red, float green, float blue, float alpha) { + float_buffer[float_buffer_position + 5] = red; + float_buffer[float_buffer_position + 6] = green; + float_buffer[float_buffer_position + 7] = blue; + float_buffer[float_buffer_position + 8] = alpha; + + use_color = true; + } + + private void va_set_texture(float u, float v) { + float_buffer[float_buffer_position + 3] = u; + float_buffer[float_buffer_position + 4] = v; + + use_texture = true; + } + + public void drawVA() { + if (use_va) { + if (vertex_count > 0) { + FloatBuffer va_buffer = BufferUtils.createFloatBuffer(float_buffer_position); + va_buffer.put(float_buffer, 0, float_buffer_position); + va_buffer.flip(); + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, 9 << 2, va_buffer); + // 1 float = 32 bits = 4 bytes + // => sizeOf(float) * 9 = (4 bytes) * 9 = _36 bytes_ = (9 << 2) + + if (use_color) { + va_buffer.position(5); + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(4, 9 << 2, va_buffer); + } + if (use_texture) { + va_buffer.position(3); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, 9 << 2, va_buffer); + } + + glDrawArrays(draw_mode, 0, vertex_count); + + if (use_texture) glDisableClientState(GL_TEXTURE_COORD_ARRAY); + if (use_color) glDisableClientState(GL_COLOR_ARRAY); + + glDisableClientState(GL_VERTEX_ARRAY); + + reset(); + } + } + } +}