diff --git a/build.gradle b/build.gradle index b683771bd4..2c7a6ac25d 100644 --- a/build.gradle +++ b/build.gradle @@ -62,6 +62,9 @@ subprojects { into sourceSets.test.output.classesDir } processTestResources.dependsOn copyTestResources + + compileJava.options.encoding = "UTF-8" + compileTestJava.options.encoding = "UTF-8" } plugins.withType(ApplicationPlugin).whenPluginAdded { startScripts { diff --git a/go.graphics.android/src/main/java/go/graphics/android/AndroidTextDrawer.java b/go.graphics.android/src/main/java/go/graphics/android/AndroidTextDrawer.java index 544b2d7ab3..f11142408d 100644 --- a/go.graphics.android/src/main/java/go/graphics/android/AndroidTextDrawer.java +++ b/go.graphics.android/src/main/java/go/graphics/android/AndroidTextDrawer.java @@ -1,339 +1,87 @@ -/******************************************************************************* - * Copyright (c) 2015 - 2017 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - *******************************************************************************/ package go.graphics.android; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Arrays; - -import go.graphics.AbstractColor; -import go.graphics.EGeometryFormatType; -import go.graphics.EGeometryType; -import go.graphics.GeometryHandle; -import go.graphics.IllegalBufferException; -import go.graphics.TextureHandle; -import go.graphics.text.EFontSize; -import go.graphics.text.TextDrawer; - import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Paint; -import android.util.TypedValue; -import android.widget.TextView; - -public class AndroidTextDrawer implements TextDrawer { - - private static final int TEXTURE_HEIGHT = 512; - private static final int TEXTURE_WIDTH = 512; - - private static AndroidTextDrawer[] instances = new AndroidTextDrawer[EFontSize.values().length]; - - private final EFontSize size; - private final GLES11DrawContext context; - private TextureHandle texture = null; - /** - * The number of lines we use on our texture. - */ - private int lines; - /** - * The current string starting in line i. - *

- */ - private String[] linestrings; - - /** - * The width of line i. This width can be higher than TEXTURE_WIDTH. Then the string is split to multiple lines. - */ - private int[] linewidths; - - /** - * An index of the next tile if the width of the current line is bigger than TEXTURE_WIDTH. This forms an linked list. -1 means no next tile. - */ - private int[] nextTile; - - private int lineheight; - - /** - * Data to do LRU - */ - private int lastUsedCount = 0; - private int[] lastused; - - private TextView renderer; - private float pixelScale; - private GeometryHandle texturepos; - - private static final float[] textureposarray = { - // top left - 0, - 0, - 0, - 0, - - // bottom left - 0, - 0, - 0, - 0, - // bottom right - TEXTURE_WIDTH, - 0, - 1, - 0, - - // top right - TEXTURE_WIDTH, - 0, - 1, - 0, - }; +import go.graphics.text.AbstractTextDrawer; +import go.graphics.text.EFontSize; - private AndroidTextDrawer(EFontSize size, GLES11DrawContext context) { - this.size = size; - this.context = context; - pixelScale = context.getAndroidContext().getResources().getDisplayMetrics().scaledDensity; - initGeometry(); - } +import static android.opengl.GLES20.*; - private final ByteBuffer updateBfr = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder()); +public class AndroidTextDrawer extends AbstractTextDrawer { - private void updateTexturePos(int pos, float value) throws IllegalBufferException { - updateBfr.rewind(); - updateBfr.putFloat(value); - context.updateGeometryAt(texturepos, pos*4, updateBfr); - } - - private void checkInvariants() { - boolean[] isNextTile = new boolean[lines]; - for (int i = 0; i < lines; i++) { - int next = nextTile[i]; - if (next >= 0) { - if (isNextTile[next]) { - System.err.println("WARNING: The line " + next + " is linked multiple times as next line."); - } - isNextTile[next] = true; - } - } - for (int i = 0; i < lines; i++) { - if (isNextTile[i]) { - if (linestrings[i] != null) { - System.out.println("Linestring should be null for line " + i); - } - if (lastused[i] != Integer.MAX_VALUE) { - System.out.println("Last used should not be set for line " + i); - } - } - } + public AndroidTextDrawer(GLESDrawContext gl) { + super(gl, 0); } @Override - public void renderCentered(float cx, float cy, String text) { - // TODO: we may want to optimize this. - drawString(cx - getWidth(text) / 2, cy - getHeight(text) / 2, text); + protected float calculateScalingFactor() { + return drawContext.getAndroidContext().getResources().getDisplayMetrics().density; } - @Override - public void drawString(float x, float y, String string) { - initialize(); - - int line = findLineFor(string); - - for (; line >= 0; line = nextTile[line], x += TEXTURE_WIDTH) { - // texture mirrored - float bottom = (float) ((line + 1) * lineheight) / TEXTURE_HEIGHT; - float top = (float) (line * lineheight) / TEXTURE_HEIGHT; - try { - updateTexturePos(3, top); - updateTexturePos(7, bottom); - updateTexturePos(11, bottom); - updateTexturePos(15, top); - } catch (IllegalBufferException e) { - e.printStackTrace(); - } - - context.draw2D(texturepos, texture, EGeometryType.Quad, 0, 4, x, y, 0f, 1f, 1f, 1f, color, 1); - } - } + private Paint paint; - private int findExistingString(String string) { - int length = lines; - for (int i = 0; i < length; i++) { - if (string.equals(linestrings[i])) { - lastused[i] = lastUsedCount++; - return i; - } - } - return -1; - } + @Override + protected int init() { + paint = new Paint(); + paint.setTextSize(TEXTURE_GENERATION_SIZE); - private int findLineToUse() { - int unnededline = 0; - int unnededrating = Integer.MAX_VALUE; + float[] float_char_widths = new float[CHARACTER_COUNT]; + paint.getTextWidths(CHARACTERS, float_char_widths); + for(int i = 0;i != CHARACTER_COUNT; i++) char_widths[i] = (int)float_char_widths[i]; - for (int i = 0; i < lines; i++) { - if (lastused[i] < unnededrating) { - unnededline = i; - unnededrating = lastused[i]; - } - } + Paint.FontMetricsInt fm = paint.getFontMetricsInt(); + gentex_line_height = fm.leading-fm.ascent+fm.descent; - // now free the next lines - for (int next = unnededline; next > -1; next = nextTile[next]) { - nextTile[next] = -1; - lastused[next] = 0; - linestrings[next] = null; - } - return unnededline; - } + Paint sizedFont = new Paint(paint); - private int findLineFor(String string) { - int line = findExistingString(string); - if (line >= 0) { - return line; - } + EFontSize[] values = EFontSize.values(); + for(int i = 0; i != values.length; i++) { + sizedFont.setTextSize(values[i].getSize()); - int width = (int) Math.ceil(computeWidth(string) + 25); - renderer = new TextView(context.getAndroidContext()); - renderer.setTextColor(Color.WHITE); - renderer.setSingleLine(true); - renderer.setTextSize(TypedValue.COMPLEX_UNIT_PX, getScaledSize()); - renderer.setText(string); + Paint.FontMetricsInt sized_fm = sizedFont.getFontMetricsInt(); - int firstLine = findLineToUse(); - // System.out.println("string cache miss for " + string + - // ", allocating new line: " + firstLine); - int lastLine = firstLine; - - for (int x = 0; x < width; x += TEXTURE_WIDTH) { - if (x == 0) { - line = firstLine; - } else { - line = findLineToUse(); - nextTile[lastLine] = line; - linestrings[line] = null; - linewidths[line] = -1; - } - // important to not allow cycles. - lastused[line] = Integer.MAX_VALUE; - // just to be sure. - nextTile[line] = -1; - - // render the new text to that line. - Bitmap bitmap = Bitmap.createBitmap(TEXTURE_WIDTH, lineheight, Bitmap.Config.ALPHA_8); - Canvas canvas = new Canvas(bitmap); - renderer.layout(0, 0, width, lineheight); - canvas.translate(-x, 0); - renderer.draw(canvas); - // canvas.translate(50, .8f * lineheight); - int points = lineheight * TEXTURE_WIDTH; - ByteBuffer alpha8 = ByteBuffer.allocateDirect(points); - bitmap.copyPixelsToBuffer(alpha8); - ByteBuffer updateBuffer; - if(context instanceof GLES20DrawContext) { - updateBuffer = ByteBuffer.allocateDirect(points*4); - for(int i = 0;i != points;i++) { - updateBuffer.putInt(0xFFFFFF00|alpha8.get(i)); - } - } else { - updateBuffer = alpha8; - } - updateBuffer.rewind(); - context.updateFontTexture(texture, 0, line*lineheight, TEXTURE_WIDTH, lineheight, updateBuffer); - lastLine = line; + heightPerSize[i] = (sized_fm.leading-sized_fm.ascent+sized_fm.descent); } - lastused[firstLine] = lastUsedCount++; - linestrings[firstLine] = string; - linewidths[firstLine] = width; - - checkInvariants(); - return firstLine; - } - private void initGeometry() { - if(texturepos == null || !texturepos.isValid()) texturepos = context.storeGeometry(textureposarray, EGeometryFormatType.Texture2D, true, "android-textdrawer" + size.getSize()); + return fm.descent; } - private void initialize() { - if (texture == null || !texture.isValid()) { - texture = context.generateFontTexture(TEXTURE_WIDTH, TEXTURE_HEIGHT); - lineheight = (int) (getScaledSize() * 1.3); - lines = TEXTURE_HEIGHT / lineheight; - linestrings = new String[lines]; - linewidths = new int[lines]; - lastused = new int[lines]; - nextTile = new int[lines]; - Arrays.fill(nextTile, -1); - - try { - updateTexturePos(1, lineheight); - updateTexturePos(13, lineheight); - } catch (IllegalBufferException e) { - e.printStackTrace(); - } - - } - initGeometry(); - } + private Bitmap pre_render; + private Canvas canvas; @Override - public float getWidth(String string) { - int index = findExistingString(string); - if (index < 0) { - return computeWidth(string); - } else { - return linewidths[index]; - } - } - - private float computeWidth(String string) { - Paint paint = new Paint(); - paint.setTextSize(getScaledSize()); - return paint.measureText(string); + protected void setupBitmapDraw() { + pre_render = Bitmap.createBitmap(tex_width, tex_height, Bitmap.Config.ALPHA_8); + canvas = new Canvas(pre_render); + paint.setColor(0); + canvas.drawPaint(paint); + paint.setColor(0xFFFFFFFF); } @Override - public float getHeight(String string) { - return getScaledSize(); - } - - private float getScaledSize() { - return size.getSize() * pixelScale; + protected void drawChar(char[] character, int x, int y) { + canvas.drawText(character, 0, 1, x, y, paint); } - private AbstractColor color; - @Override - public void setColor(AbstractColor color) { - this.color = color; + protected int[] getRGB() { + int[] pixels = new int[tex_width*tex_height]; + pre_render.getPixels(pixels, 0, tex_width, 0, 0, tex_width, tex_height); + return pixels; } - public static TextDrawer getInstance(EFontSize size, GLES11DrawContext context) { - int ordinal = size.ordinal(); - if (instances[ordinal] == null) { - instances[ordinal] = new AndroidTextDrawer(size, context); - } - return instances[ordinal]; + @Override + protected void endDraw() { + pre_render = null; + canvas = null; } - public static void invalidateTextures() { - for (int i = 0; i < instances.length; i++) { - instances[i] = null; - } + @Override + protected void setTexParams() { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } - } diff --git a/go.graphics.android/src/main/java/go/graphics/android/GLES11DrawContext.java b/go.graphics.android/src/main/java/go/graphics/android/GLES11DrawContext.java deleted file mode 100644 index 2c065f87e5..0000000000 --- a/go.graphics.android/src/main/java/go/graphics/android/GLES11DrawContext.java +++ /dev/null @@ -1,327 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - *******************************************************************************/ -package go.graphics.android; - -import android.content.Context; -import android.opengl.GLES11; -import android.opengl.GLES20; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.ShortBuffer; - -import go.graphics.AbstractColor; -import go.graphics.EGeometryFormatType; -import go.graphics.GLDrawContext; -import go.graphics.GeometryHandle; -import go.graphics.TextureHandle; -import go.graphics.text.EFontSize; -import go.graphics.text.TextDrawer; - -public class GLES11DrawContext implements GLDrawContext { - - private final Context context; - - private GeometryHandle lastGeometry = null; - private TextureHandle lastTexture = null; - private boolean tex_coord_on = false; - - public GLES11DrawContext(Context context) { - this.context = context; - GLES11.glClearColor(0, 0, 0, 1); - GLES11.glPixelStorei(GLES11.GL_UNPACK_ALIGNMENT, 1); - GLES11.glEnable(GLES11.GL_BLEND); - GLES11.glBlendFunc(GLES11.GL_SRC_ALPHA, GLES11.GL_ONE_MINUS_SRC_ALPHA); - - GLES11.glDepthFunc(GLES11.GL_LEQUAL); - GLES11.glEnable(GLES11.GL_DEPTH_TEST); - - init(); - } - - private float lr, lg, lb, la = -1; - private float lx, ly, lz = -2; - private float lsx, lsy, lsz = -1; - - public void draw2D(GeometryHandle geometry, TextureHandle texture, int primitive, int offset, int vertices, float x, float y, float z, float sx, float sy, float sz, AbstractColor color, float intensity) { - if(lx != x || ly != y || lz != z || lsx != sx || lsy != sy || lsz != sz) { - if(lsz != -1) GLES11.glPopMatrix(); - GLES11.glPushMatrix(); - if(x != 0 || y != 0 || z != 0) GLES11.glTranslatef(x, y, z); - if(sx != 1 || sy != 1 || sz != 1) GLES11.glScalef(sx, sy, sz); - lx = x; lsx = sx; - ly = y; lsy = sy; - lz = z; lsz = sz; - } - - if(color != null) { - float r = color.red*intensity; - float g = color.green*intensity; - float b = color.blue*intensity; - float a = color.alpha; - if(lr != r || lg != g || lb != b || la != a) GLES11.glColor4f(lr=r, lg=g, lb=b, la=a); - } else { - if(lr != lg || lr != lb || lr != intensity || la != 1) GLES11.glColor4f(intensity, intensity, intensity, 1); - lr = lg = lb = intensity; - la = 1; - } - - bindTexture(texture); - bindGeometry(geometry); - EGeometryFormatType format = geometry.getFormat(); - - if(format.getTexCoordPos() == -1) GLES11.glDisableClientState(GLES11.GL_TEXTURE_COORD_ARRAY); - - specifyFormat(format); - GLES11.glDrawArrays(primitive, offset * vertices, vertices); - - if(format.getTexCoordPos() == -1) GLES11.glEnableClientState(GLES11.GL_TEXTURE_COORD_ARRAY); - } - - protected void specifyFormat(EGeometryFormatType format) { - if (format.getTexCoordPos() == -1) { - if(tex_coord_on) GLES11.glDisableClientState(GLES11.GL_TEXTURE_COORD_ARRAY); - GLES11.glVertexPointer(2, GLES11.GL_FLOAT, 0, 0); - } else { - if(!tex_coord_on) GLES11.glEnableClientState(GLES11.GL_TEXTURE_COORD_ARRAY); - int stride = format.getBytesPerVertexSize(); - GLES11.glVertexPointer(2, GLES11.GL_FLOAT, stride, 0); - GLES11.glTexCoordPointer(2, GLES11.GL_FLOAT, stride, format.getTexCoordPos()); - } - - tex_coord_on = format.getTexCoordPos() != -1; - } - - protected void bindTexture(TextureHandle texture) { - if (texture != lastTexture) { - int id = 0; - if (texture != null) { - id = texture.getInternalId(); - } - GLES11.glBindTexture(GLES11.GL_TEXTURE_2D, id); - lastTexture = texture; - } - } - - public void setGlobalAttributes(float x, float y, float z, float sx, float sy, float sz) { - // reset matrix stack - if(lsz != -1) GLES11.glPopMatrix(); - lsz = -1; - - GLES11.glLoadIdentity(); - GLES11.glTranslatef(x, y, z); - GLES11.glScalef(sx, sy, sz); - } - - protected void bindGeometry(GeometryHandle geometry) { - if(geometry != lastGeometry) { - int id = 0; - if(geometry != null) { - id = geometry.getInternalId(); - } - GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, id); - lastGeometry = geometry; - } - } - - @Override - public TextureHandle generateTexture(int width, int height, ShortBuffer data, String name) { - TextureHandle texture = genTextureIndex(); - - bindTexture(texture); - GLES11.glTexImage2D(GLES11.GL_TEXTURE_2D, 0, GLES11.GL_RGBA, width, - height, 0, GLES11.GL_RGBA, GLES11.GL_UNSIGNED_SHORT_4_4_4_4, data); - setTextureParameters(); - return texture; - } - - private TextureHandle genTextureIndex() { - int[] textureIndexes = new int[1]; - GLES11.glGenTextures(1, textureIndexes, 0); - return new TextureHandle(this, textureIndexes[0]); - } - - /** - * Sets the texture parameters, assuming that the texture was just created and is bound. - */ - private static void setTextureParameters() { - GLES11.glTexParameterf(GLES11.GL_TEXTURE_2D, - GLES11.GL_TEXTURE_MAG_FILTER, GLES11.GL_NEAREST); - GLES11.glTexParameterf(GLES11.GL_TEXTURE_2D, - GLES11.GL_TEXTURE_MIN_FILTER, GLES11.GL_NEAREST); - GLES11.glTexParameterf(GLES11.GL_TEXTURE_2D, GLES11.GL_TEXTURE_WRAP_S, - GLES11.GL_REPEAT); - GLES11.glTexParameterf(GLES11.GL_TEXTURE_2D, GLES11.GL_TEXTURE_WRAP_T, - GLES11.GL_REPEAT); - } - - @Override - public void updateTexture(TextureHandle textureIndex, int left, int bottom, - int width, int height, ShortBuffer data) { - bindTexture(textureIndex); - GLES11.glTexSubImage2D(GLES11.GL_TEXTURE_2D, 0, left, bottom, width, height, - GLES11.GL_RGBA, GLES11.GL_UNSIGNED_SHORT_4_4_4_4, data); - } - - public TextureHandle generateFontTexture(int width, int height) { - TextureHandle texture = genTextureIndex(); - - ByteBuffer data = ByteBuffer.allocateDirect(width * height * 4); - while (data.hasRemaining()) { - data.put((byte) 0); - } - data.rewind(); - - bindTexture(texture); - if(this instanceof GLES20DrawContext) { - GLES11.glTexImage2D(GLES11.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, - height, 0, GLES11.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, data); - } else { - GLES11.glTexImage2D(GLES11.GL_TEXTURE_2D, 0, GLES11.GL_ALPHA, width, - height, 0, GLES11.GL_ALPHA, GLES11.GL_UNSIGNED_BYTE, data); - } - - setTextureParameters(); - return texture; - } - - public void updateFontTexture(TextureHandle textureIndex, int left, int bottom, - int width, int height, ByteBuffer data) { - bindTexture(textureIndex); - if(this instanceof GLES20DrawContext) { - GLES11.glTexSubImage2D(GLES11.GL_TEXTURE_2D, 0, left, bottom, width, - height, GLES11.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, data); - } else { - GLES11.glTexSubImage2D(GLES11.GL_TEXTURE_2D, 0, left, bottom, width, - height, GLES11.GL_ALPHA, GLES11.GL_UNSIGNED_BYTE, data); - } - } - - private float[] heightMatrix; - - @Override - public void setHeightMatrix(float[] matrix) { - heightMatrix = matrix; - } - - @Override - public TextDrawer getTextDrawer(EFontSize size) { - return AndroidTextDrawer.getInstance(size, this); - } - - public void reinit(int width, int height) { - GLES11.glViewport(0, 0, width, height); - GLES11.glMatrixMode(GLES11.GL_PROJECTION); - GLES11.glLoadIdentity(); - GLES11.glOrthof(0, width, 0, height, -1, 1); - GLES11.glMatrixMode(GLES11.GL_MODELVIEW); - } - - public void init() { - GLES11.glEnableClientState(GLES11.GL_VERTEX_ARRAY); - GLES11.glEnableClientState(GLES11.GL_TEXTURE_COORD_ARRAY); - - GLES11.glAlphaFunc(GLES11.GL_GREATER, 0.1f); - GLES11.glEnable(GLES11.GL_ALPHA_TEST); - - GLES11.glEnable(GLES11.GL_TEXTURE_2D); - } - - @Override - public GeometryHandle generateGeometry(int vertices, EGeometryFormatType format, boolean writable, String name) { - GeometryHandle geometry = allocateVBO(format); - - bindGeometry(geometry); - GLES11.glBufferData(GLES11.GL_ARRAY_BUFFER, vertices*format.getBytesPerVertexSize(), null, - writable ? GLES11.GL_DYNAMIC_DRAW : GLES11.GL_STATIC_DRAW); - return geometry; - } - - public void drawTrianglesWithTextureColored(TextureHandle textureid, GeometryHandle vertexHandle, GeometryHandle colorHandle, int offset, int lines, int width, int stride, float x, float y) { - bindTexture(textureid); - int starti = offset < 0 ? (int)Math.ceil(-offset/(float)stride) : 0; - - if(lsz != -1) GLES11.glPopMatrix(); - GLES11.glPushMatrix(); - GLES11.glTranslatef(x, y, -.1f); - GLES11.glScalef(1, 1, 0); - GLES11.glMultMatrixf(heightMatrix, 0); - - if(!tex_coord_on) { - GLES11.glEnableClientState(GLES11.GL_TEXTURE_COORD_ARRAY); - tex_coord_on = true; - } - - bindGeometry(vertexHandle); - GLES11.glVertexPointer(3, GLES11.GL_FLOAT, 5 * 4, 0); - GLES11.glTexCoordPointer(2, GLES11.GL_FLOAT, 5 * 4, 3 * 4); - - bindGeometry(colorHandle); - GLES11.glColorPointer(4, GLES11.GL_UNSIGNED_BYTE, 0, 0); - - GLES11.glEnableClientState(GLES11.GL_COLOR_ARRAY); - for (int i = starti; i != lines; i++) { - GLES11.glDrawArrays(GLES11.GL_TRIANGLES, (offset + stride * i) * 3, width * 3); - } - GLES11.glDisableClientState(GLES11.GL_COLOR_ARRAY); - - GLES11.glPopMatrix(); - lsz = -1; - } - - @Override - public GeometryHandle storeGeometry(float[] geometry, EGeometryFormatType format, boolean writable, String name) { - GeometryHandle vertexBufferId = allocateVBO(format); - ByteBuffer bfr = ByteBuffer.allocateDirect(4*geometry.length).order(ByteOrder.nativeOrder()); - bfr.asFloatBuffer().put(geometry); - GLES11.glBufferData(GLES11.GL_ARRAY_BUFFER, 4*geometry.length, bfr, writable ? GLES11.GL_DYNAMIC_DRAW : GLES11.GL_STATIC_DRAW); - - return vertexBufferId; - } - - GeometryHandle allocateVBO(EGeometryFormatType type) { - int[] vbos = new int[] {0}; - GLES11.glGenBuffers(1, vbos, 0); - GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, vbos[0]); - return lastGeometry = new GeometryHandle(this, vbos[0], 0, type); - } - - @Override - public void updateGeometryAt(GeometryHandle handle, int pos, ByteBuffer data) { - bindGeometry(handle); - data.rewind(); - GLES11.glBufferSubData(GLES11.GL_ARRAY_BUFFER, pos, data.limit(), data); - } - - public Context getAndroidContext() { - return context; - } - - public void invalidateContext() { - valid = false; - } - - @Override - public void deleteTexture(TextureHandle texture) { - GLES11.glDeleteTextures(1, new int[] {texture.getInternalId()}, 0); - } - - private boolean valid = true; - - @Override - public boolean isValid() { - return valid; - } -} diff --git a/go.graphics.android/src/main/java/go/graphics/android/GLES20DrawContext.java b/go.graphics.android/src/main/java/go/graphics/android/GLES20DrawContext.java deleted file mode 100644 index a3c4dae579..0000000000 --- a/go.graphics.android/src/main/java/go/graphics/android/GLES20DrawContext.java +++ /dev/null @@ -1,360 +0,0 @@ -package go.graphics.android; - -import android.content.Context; -import android.opengl.GLES20; -import android.opengl.GLES30; -import android.opengl.Matrix; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.ArrayList; - -import go.graphics.AbstractColor; -import go.graphics.EGeometryFormatType; -import go.graphics.GL2DrawContext; -import go.graphics.GeometryHandle; -import go.graphics.TextureHandle; - -public class GLES20DrawContext extends GLES11DrawContext implements GL2DrawContext { - public GLES20DrawContext(Context ctx, boolean gles3) { - super(ctx); - this.gles3 = gles3; - Matrix.setIdentityM(global, 0); - } - - private String[] uniform_names; - private ArrayList shaders; - - private final float[] global = new float[16]; - private final float[] mat = new float[16]; - private boolean gles3; - - @Override - public void init() { - uniform_names = new String[] {"projection", "globalTransform", "transform", "texHandle", "color", "height", "uni_info"}; - shaders = new ArrayList<>(); - - prog_background = new ShaderProgram("background"); - prog_unified = new ShaderProgram("tex-unified"); - prog_color = new ShaderProgram("color"); - prog_tex = new ShaderProgram("tex"); - - for(ShaderProgram shader : shaders) { - useProgram(shader); - if(shader.ufs[TEX] != -1) GLES20.glUniform1i(shader.ufs[TEX], 0); - } - } - - private ShaderProgram lastProgram = null; - private void useProgram(ShaderProgram id) { - if(id != lastProgram) { - GLES20.glUseProgram(id.program); - lastProgram = id; - } - } - - private ShaderProgram prog_background; - private ShaderProgram prog_unified; - private ShaderProgram prog_color; - private ShaderProgram prog_tex; - - private float clr, clg, clb, cla, tlr, tlg, tlb, tla; - - @Override - public void draw2D(GeometryHandle geometry, TextureHandle texture, int primitive, int offset, int vertices, float x, float y, float z, float sx, float sy, float sz, AbstractColor color, float intensity) { - boolean changeColor = false; - - float r, g, b, a; - if(color != null) { - r = color.red*intensity; - g = color.green*intensity; - b = color.blue*intensity; - a = color.alpha; - } else { - r = g = b = intensity; - a = 1; - } - - if(texture == null) { - useProgram(prog_color); - if(clr != r || clg != g || clb != b || cla != a) { - clr = r; - clg = g; - clb = b; - cla = a; - changeColor = true; - } - } else { - bindTexture(texture); - useProgram(prog_tex); - if(tlr != r || tlg != g || tlb != b || tla != a) { - tlr = r; - tlg = g; - tlb = b; - tla = a; - changeColor = true; - } - } - - GLES20.glUniform3fv(lastProgram.ufs[TRANS], 2, new float[] {x, y, z, sx, sy, sz}, 0); - - if(changeColor) { - GLES20.glUniform4f(lastProgram.ufs[COLOR], r, g, b, a); - } - - if(gles3) { - bindFormat(geometry.getInternalFormatId()); - } else { - bindGeometry(geometry); - specifyFormat(geometry.getFormat()); - } - GLES20.glDrawArrays(primitive, offset*vertices, vertices); - } - - private float ulr, ulg, ulb, ula, uli; - private boolean ulim, ulsh; - - @Override - public void drawUnified2D(GeometryHandle geometry, TextureHandle texture, int primitive, int offset, int vertices, boolean image, boolean shadow, float x, float y, float z, float sx, float sy, float sz, AbstractColor color, float intensity) { - useProgram(prog_unified); - bindTexture(texture); - - if(image) { - float r, g, b, a; - if (color != null) { - r = color.red * intensity; - g = color.green * intensity; - b = color.blue * intensity; - a = color.alpha; - } else { - r = g = b = intensity; - a = 1; - } - - if(ulr != r || ulg != g || ulb != b || ula != a) { - ulr = r; - ulg = g; - ulb = b; - ula = a; - GLES20.glUniform4f(prog_unified.ufs[COLOR], r, g, b, a); - } - } - - if(ulim != image || ulsh != shadow || uli != intensity) { - GLES20.glUniform3f(prog_unified.ufs[UNI_INFO], image?1:0, shadow?1:0, intensity); - ulim = image; - ulsh = shadow; - uli = intensity; - } - - GLES20.glUniform3fv(lastProgram.ufs[TRANS], 2, new float[] {x, y, z, sx, sy, sz}, 0); - - if(gles3) { - bindFormat(geometry.getInternalFormatId()); - } else { - bindGeometry(geometry); - specifyFormat(geometry.getFormat()); - } - GLES20.glDrawArrays(primitive, offset*vertices, vertices); - } - - private int lastFormat = 0; - protected void bindFormat(int format) { - if(format != lastFormat) { - GLES30.glBindVertexArray(format); - lastFormat = format; - } - } - - @Override - protected void specifyFormat(EGeometryFormatType format) { - GLES20.glEnableVertexAttribArray(0); - - if (format.getTexCoordPos() == -1) { - GLES20.glVertexAttribPointer(0, 2, GLES20.GL_FLOAT, false, 0, 0); - } else { - GLES20.glEnableVertexAttribArray(1); - int stride = format.getBytesPerVertexSize(); - GLES20.glVertexAttribPointer(0, 2, GLES20.GL_FLOAT, false, stride, 0); - GLES20.glVertexAttribPointer(1, 2, GLES20.GL_FLOAT, false, stride, format.getTexCoordPos()); - } - } - - @Override - GeometryHandle allocateVBO(EGeometryFormatType type) { - GeometryHandle geometry = super.allocateVBO(type); - if (gles3 && type.isSingleBuffer()) { - int[] vaos = new int[] {0}; - GLES30.glGenVertexArrays(1, vaos, 0); - geometry.setInternalFormatId(vaos[0]); - bindFormat(vaos[0]); - - specifyFormat(type); - } - - return geometry; - } - - @Override - public void setGlobalAttributes(float x, float y, float z, float sx, float sy, float sz) { - Matrix.setIdentityM(global, 0); - Matrix.translateM(global, 0, x, y, z); - Matrix.scaleM(global, 0, sx, sy, sz); - - for(ShaderProgram shader : shaders) { - useProgram(shader); - GLES20.glUniformMatrix4fv(shader.ufs[GLOBAL], 1, false, global, 0); - } - } - - @Override - public void reinit(int width, int height) { - GLES20.glViewport(0, 0, width, height); - - Matrix.setIdentityM(mat, 0); - Matrix.orthoM(mat, 0, 0, width, 0, height, -1, 1); - - for(ShaderProgram shader : shaders) { - useProgram(shader); - GLES20.glUniformMatrix4fv(shader.ufs[PROJ], 1, false, mat, 0); - } - } - - @Override - public void setHeightMatrix(float[] matrix) { - useProgram(prog_background); - GLES20.glUniformMatrix4fv(prog_background.ufs[HEIGHT], 1, false, matrix, 0); - } - - private int[] backgroundVAO = new int[] {-1}; - - @Override - public void drawTrianglesWithTextureColored(TextureHandle textureid, GeometryHandle shapeHandle, GeometryHandle colorHandle, int offset, int lines, int width, int stride, float x, float y) { - bindTexture(textureid); - - if(backgroundVAO[0] == -1) { - if(gles3) { - GLES30.glGenVertexArrays(1, backgroundVAO, 0); - bindFormat(backgroundVAO[0]); - } - GLES20.glEnableVertexAttribArray(0); - GLES20.glEnableVertexAttribArray(1); - GLES20.glEnableVertexAttribArray(2); - - bindGeometry(shapeHandle); - GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false, 5 * 4, 0); - GLES20.glVertexAttribPointer(1, 2, GLES20.GL_FLOAT, false, 5 * 4, 3 * 4); - - bindGeometry(colorHandle); - GLES20.glVertexAttribPointer(2, 1, GLES20.GL_FLOAT, false, 0, 0); - } - int starti = offset < 0 ? (int)Math.ceil(-offset/(float)stride) : 0; - - useProgram(prog_background); - - - GLES20.glUniform2f(prog_background.ufs[TRANS], x, y); - - bindFormat(backgroundVAO[0]); - for (int i = starti; i != lines; i++) { - GLES20.glDrawArrays(GLES20.GL_TRIANGLES, (offset + stride * i) * 3, width * 3); - } - } - private static final int PROJ = 0; - private static final int GLOBAL = 1; - private static final int TRANS = 2; - private static final int TEX = 3; - private static final int COLOR = 4; - private static final int HEIGHT = 5; - private static final int UNI_INFO = 6; - - - private class ShaderProgram { - public final int program; - public final int[] ufs = new int[7]; - - private ShaderProgram(String name) { - int vertexShader = -1; - int fragmentShader; - - String vname = name; - if(name.contains("-")) vname = name.split("-")[0]; - - try { - vertexShader = createShader(vname+".vert", GLES20.GL_VERTEX_SHADER); - fragmentShader = createShader(name+".frag", GLES20.GL_FRAGMENT_SHADER); - } catch (IOException e) { - e.printStackTrace(); - - if(vertexShader != -1) GLES20.glDeleteShader(vertexShader); - throw new Error("could not read shader files", e); - } - - program = GLES20.glCreateProgram(); - - GLES20.glAttachShader(program, vertexShader); - GLES20.glAttachShader(program, fragmentShader); - - GLES20.glBindAttribLocation(program, 0, "vertex"); - GLES20.glBindAttribLocation(program, 1, "texcoord"); - GLES20.glBindAttribLocation(program, 2, "color"); - - GLES20.glLinkProgram(program); - GLES20.glValidateProgram(program); - - //GLES20.glDetachShader(program, vertexShader); - //GLES20.glDetachShader(program, fragmentShader); - GLES20.glDeleteShader(vertexShader); - GLES20.glDeleteShader(fragmentShader); - - String log = GLES20.glGetProgramInfoLog(program); - if(!log.isEmpty()) System.out.print("info log of " + name + "=====\n" + log + "==== end\n"); - - int[] link_status = new int[1]; - GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, link_status, 0); - if(link_status[0] == 0) { - GLES20.glDeleteProgram(program); - throw new Error("Could not link " + name); - } - - for(int i = 0;i != ufs.length;i++) { - int uf = GLES20.glGetUniformLocation(program, uniform_names[i]); - ufs[i] = uf; - } - shaders.add(this); - } - - private int createShader(String name, int type) throws IOException { - int shader = GLES20.glCreateShader(type); - - BufferedReader is = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("/"+name))); - StringBuilder source = new StringBuilder(); - String line; - - while((line = is.readLine()) != null) { - source.append(line); - if(line.startsWith("#version")) { - //source.append(" es"); - } - source.append("\n"); - } - - GLES20.glShaderSource(shader, source.toString()); - GLES20.glCompileShader(shader); - - - String log = GLES20.glGetShaderInfoLog(shader); - if(!log.isEmpty()) System.out.print("info log of " + name + "=====\n" + log + "==== end\n"); - - int[] compile_status = new int[1]; - GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compile_status, 0); - if(compile_status[0] == 0) { - GLES20.glDeleteShader(shader); - throw new Error("Could not compile " + name); - } - - return shader; - } - } -} diff --git a/go.graphics.android/src/main/java/go/graphics/android/GLESDrawContext.java b/go.graphics.android/src/main/java/go/graphics/android/GLESDrawContext.java new file mode 100644 index 0000000000..586ccefa7f --- /dev/null +++ b/go.graphics.android/src/main/java/go/graphics/android/GLESDrawContext.java @@ -0,0 +1,606 @@ +/******************************************************************************* + * Copyright (c) 2019 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *******************************************************************************/ +package go.graphics.android; + +import android.content.Context; +import android.opengl.GLES30; +import android.opengl.Matrix; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; +import java.util.ArrayList; +import java.util.Arrays; + +import go.graphics.AbstractColor; +import go.graphics.BackgroundDrawHandle; +import go.graphics.GLDrawContext; +import go.graphics.BufferHandle; +import go.graphics.ManagedHandle; +import go.graphics.MultiDrawHandle; +import go.graphics.TextureHandle; +import go.graphics.UnifiedDrawHandle; + +import static android.opengl.GLES20.*; + +public class GLESDrawContext extends GLDrawContext { + private final Context context; + private BufferHandle lastGeometry = null; + private TextureHandle lastTexture = null; + + GLESDrawContext(Context ctx, boolean gles3) { + this.context = ctx; + this.gles3 = gles3; + shaders = new ArrayList<>(); + + glClearColor(0, 0, 0, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glDepthFunc(GL_LEQUAL); + glEnable(GL_DEPTH_TEST); + + if(gles3) { + prog_unified_multi = new ShaderProgram("unified-multi"); + prog_unified_array = new ShaderProgram("unified-array"); + } + prog_background = new ShaderProgram("background"); + prog_unified = new ShaderProgram("unified"); + Matrix.setIdentityM(global, 0); + + textDrawer = new AndroidTextDrawer(this); + } + + private ArrayList shaders; + + private final float[] global = new float[16]; + private final float[] mat = new float[16]; + private final boolean gles3; + + private ShaderProgram lastProgram = null; + private void useProgram(ShaderProgram id) { + if(id != lastProgram) { + glUseProgram(id.program); + lastProgram = id; + } + } + + private ShaderProgram prog_unified_multi = null; + private ShaderProgram prog_unified_array = null; + private ShaderProgram prog_background; + private ShaderProgram prog_unified; + + private float ulr, ulg, ulb, ula, uli; + private int ulm; + + /** + * Returns a texture id which is positive or 0. It returns a negative number on error. + * + * @param width + * The width of the image. + * @param height + * The height of the image. + * @param data + * The data as array. It needs to have a length of width * height and each element is a color with: 4 bits red, 4 bits green, 4 bits + * blue and 4 bits alpha. + * @return The id of the generated texture. + */ + public TextureHandle generateTexture(int width, int height, ShortBuffer data, String name) { + int[] textureIndexes = new int[1]; + glGenTextures(1, textureIndexes, 0); + TextureHandle texture = new TextureHandle(this, textureIndexes[0]); + + bindTexture(texture); + resizeTexture(texture, width, height, data); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + return texture; + } + + public void resizeTexture(TextureHandle textureIndex, int width, int height, ShortBuffer data) { + bindTexture(textureIndex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); + } + + public void updateTexture(TextureHandle textureIndex, int left, int bottom, + int width, int height, ShortBuffer data) { + bindTexture(textureIndex); + glTexSubImage2D(GL_TEXTURE_2D, 0, left, bottom, width, height, + GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); + } + + private void bindTexture(TextureHandle texture) { + if (texture != lastTexture) { + int id = 0; + if (texture != null) { + id = texture.getTextureId(); + } + glBindTexture(GL_TEXTURE_2D, id); + lastTexture = texture; + } + } + + private void bindGeometry(BufferHandle geometry) { + if(geometry != lastGeometry) { + int id = 0; + if(geometry != null) { + id = geometry.getBufferId(); + } + glBindBuffer(GL_ARRAY_BUFFER, id); + lastGeometry = geometry; + } + } + + private int lastFormat = 0; + private void bindFormat(int format) { + if(format != lastFormat) { + GLES30.glBindVertexArray(format); + lastFormat = format; + } + } + + public void updateBufferAt(BufferHandle handle, int pos, ByteBuffer data) { + bindGeometry(handle); + glBufferSubData(GL_ARRAY_BUFFER, pos, data.remaining(), data); + } + + public void setGlobalAttributes(float x, float y, float z, float sx, float sy, float sz) { + finishFrame(); + + Matrix.setIdentityM(global, 0); + Matrix.scaleM(global, 0, sx, sy, sz); + Matrix.translateM(global, 0, x, y, z); + + for(ShaderProgram shader : shaders) { + useProgram(shader); + glUniformMatrix4fv(shader.global, 1, false, global, 0); + } + } + + void resize(int width, int height) { + glViewport(0, 0, width, height); + + Matrix.setIdentityM(mat, 0); + Matrix.orthoM(mat, 0, 0, width, 0, height, -1, 1); + + for(ShaderProgram shader : shaders) { + useProgram(shader); + glUniformMatrix4fv(shader.proj, 1, false, mat, 0); + } + } + + @Override + public void setShadowDepthOffset(float depth) { + for(ShaderProgram shader : shaders) { + if(shader.shadow_depth != -1) { + useProgram(shader); + glUniform1f(shader.shadow_depth, depth); + + } + } + } + + public void setHeightMatrix(float[] matrix) { + useProgram(prog_background); + glUniformMatrix4fv(prog_background.height, 1, false, matrix, 0); + } + + Context getAndroidContext() { + return context; + } + + private int genBuffer() { + int[] buffer = new int[1]; + glGenBuffers(1, buffer, 0); + return buffer[0]; + } + + private int genVertexArray() { + int[] vertexArray = new int[1]; + GLES30.glGenVertexArrays(1, vertexArray, 0); + return vertexArray[0]; + } + + @Override + public BackgroundDrawHandle createBackgroundDrawCall(int vertices, TextureHandle texture) { + int vao = -1; + + if(gles3) vao = genVertexArray(); + + BufferHandle vertexBuffer = new BufferHandle(this, genBuffer()); + BufferHandle colorBuffer = new BufferHandle(this, genBuffer()); + + bindGeometry(vertexBuffer); + glBufferData(GL_ARRAY_BUFFER, (vertices*5*4), null, GL_DYNAMIC_DRAW); + bindGeometry(colorBuffer); + glBufferData(GL_ARRAY_BUFFER, (vertices*4), null, GL_DYNAMIC_DRAW); + + BackgroundDrawHandle handle = new BackgroundDrawHandle(this, vao, texture, vertexBuffer, colorBuffer); + + if(gles3) { + bindFormat(vao); + fillBackgroundFormat(handle); + } + + return handle; + } + + @Override + public UnifiedDrawHandle createUnifiedDrawCall(int vertices, String name, TextureHandle texture, float[] data) { + int vao = -1; + + if(gles3) vao = genVertexArray(); + + BufferHandle vertexBuffer = new BufferHandle(this, genBuffer()); + + bindGeometry(vertexBuffer); + if(data != null) { + glBufferData(GL_ARRAY_BUFFER, data.length*4, FloatBuffer.wrap(data), GL_STATIC_DRAW); + } else { + glBufferData(GL_ARRAY_BUFFER, vertices*(texture!=null?4:2)*4, null, GL_DYNAMIC_DRAW); + } + + UnifiedDrawHandle handle = new UnifiedDrawHandle(this, vao, 0, vertices, texture, vertexBuffer); + + if(gles3) { + bindFormat(vao); + fillUnifiedFormat(handle); + } + + return handle; + } + + @Override + protected MultiDrawHandle createMultiDrawCall(String name, ManagedHandle source) { + if(prog_unified_multi == null) return null; + int vao = genVertexArray(); + + BufferHandle drawCalls = new BufferHandle(this, genBuffer()); + + bindGeometry(drawCalls); + glBufferData(GL_ARRAY_BUFFER, MultiDrawHandle.MAX_CACHE_ENTRIES*12*4, null, GL_STREAM_DRAW); + + MultiDrawHandle handle = new MultiDrawHandle(this, vao, MultiDrawHandle.MAX_CACHE_ENTRIES, source, drawCalls); + + bindFormat(vao); + fillMultiFormat(handle); + + return handle; + } + + private void fillBackgroundFormat(BackgroundDrawHandle dh) { + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + + bindGeometry(dh.vertices); + glVertexAttribPointer(0, 3, GL_FLOAT, false, 5 * 4, 0); + glVertexAttribPointer(1, 2, GL_FLOAT, false, 5 * 4, 3 * 4); + + bindGeometry(dh.colors); + glVertexAttribPointer(2, 1, GL_FLOAT, false, 0, 0); + } + + private void fillUnifiedFormat(UnifiedDrawHandle uh) { + bindGeometry(uh.vertices); + glEnableVertexAttribArray(0); + + if(uh.texture!=null) { + glEnableVertexAttribArray(1); + + glVertexAttribPointer(0, 2, GL_FLOAT, false, 4 * 4, 0); + glVertexAttribPointer(1, 2, GL_FLOAT, false, 4 * 4, 2 * 4); + } else { + glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0); + } + } + + private void fillMultiFormat(MultiDrawHandle mh) { + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + + GLES30.glVertexAttribDivisor(0, 1); + GLES30.glVertexAttribDivisor(1, 1); + GLES30.glVertexAttribDivisor(2, 1); + GLES30.glVertexAttribDivisor(3, 1); + + bindGeometry(mh.drawCalls); + glVertexAttribPointer(0, 3, GL_FLOAT, false, 12*4, 0); + glVertexAttribPointer(1, 2, GL_FLOAT, false, 12*4, 3*4); + glVertexAttribPointer(2, 4, GL_FLOAT, false, 12*4, 5*4); + glVertexAttribPointer(3, 3, GL_FLOAT, false, 12*4, 9*4); + } + + private boolean[] vertArrays = new boolean[3]; + + private void enableVertArrays(boolean... vertArrays) { + for(int i = 0;i != vertArrays.length; i++) { + if(vertArrays[i] != this.vertArrays[i]) { + if(vertArrays[i]) { + glEnableVertexAttribArray(i); + } else { + glDisableVertexAttribArray(i); + } + } + } + + this.vertArrays = vertArrays; + } + protected void drawMulti(MultiDrawHandle call) { + bindTexture(call.sourceQuads.texture); + bindFormat(call.getVertexArrayId()); + + useProgram(prog_unified_multi); + + GLES30.glBindBufferBase(GLES30.GL_UNIFORM_BUFFER, 0, call.sourceQuads.vertices.getBufferId()); + + GLES30.glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, call.used); + } + + public void drawUnifiedArray(UnifiedDrawHandle call, int primitive, int vertexCount, float[] trans, float[] colors, int array_len) { + if(call.texture != null) bindTexture(call.texture); + + if(call.getVertexArrayId() != -1) { + bindFormat(call.getVertexArrayId()); + } else { + enableVertArrays(true, call.texture!=null, false); + fillUnifiedFormat(call); + } + + if(prog_unified_array != null) { + useProgram(prog_unified_array); + + glUniform4fv(prog_unified_array.color, array_len, colors, 0); + glUniform4fv(prog_unified_array.trans, array_len, trans, 0); + + GLES30.glDrawArraysInstanced(primitive, call.offset, vertexCount, array_len); + } else { + useProgram(prog_unified); + + for (int i = 0; i != array_len; i++) { + + float int_mode = trans[i*4+3]/10; + int mode = (int) Math.floor(int_mode); + float intensity = (int_mode-mode)*10-1; + + glUniform1i(prog_unified.mode, mode); + glUniform1fv(prog_unified.color, 4, new float[] {colors[i*4], colors[i*4+1], colors[i*4+2], colors[i*4+3], intensity}, i * 4); + glUniform3fv(prog_unified.trans, 2, new float[] {trans[i*4], trans[i*4+1], trans[i*4+2], 1, 1, 0}, 0); + + glDrawArrays(primitive, call.offset, vertexCount); + } + + ulr = -1; + ulm = -1; + } + } + + @Override + public void drawUnified(UnifiedDrawHandle call, int primitive, int count, int mode, float x, float y, float z, float sx, float sy, AbstractColor color, float intensity) { + if(call.texture != null) bindTexture(call.texture); + useProgram(prog_unified); + + if(call.getVertexArrayId() != -1) { + bindFormat(call.getVertexArrayId()); + } else { + enableVertArrays(true, call.texture!=null, false); + fillUnifiedFormat(call); + } + + float r, g, b, a; + if (color != null) { + r = color.red; + g = color.green; + b = color.blue; + a = color.alpha; + } else { + r = g = b = a = 1; + } + + if(ulr != r || ulg != g || ulb != b || ula != a || uli != intensity) { + ulr = r; + ulg = g; + ulb = b; + ula = a; + uli = intensity; + glUniform1fv(prog_unified.color, 5, new float[] {r, g, b, a, intensity}, 0); + } + + if(ulm != mode) { + ulm = mode; + glUniform1i(prog_unified.mode, mode); + } + + glUniform3fv(prog_unified.trans, 2, new float[] {x, y, z, sx, sy, 0}, 0); + + glDrawArrays(primitive, call.offset, count); + } + + public void drawBackground(BackgroundDrawHandle handle) { + bindTexture(handle.texture); + useProgram(prog_background); + if(handle.getVertexArrayId() != -1) { + bindFormat(handle.getVertexArrayId()); + } else { + enableVertArrays(true, true, true); + fillBackgroundFormat(handle); + } + + int starti = handle.offset < 0 ? (int)Math.ceil(-handle.offset/(float)handle.stride) : 0; + int draw_lines = handle.lines-starti; + + int[] firsts = new int[draw_lines]; + int[] counts = new int[draw_lines]; + for (int i = 0; i != draw_lines; i++) { + firsts[i] = (handle.offset + handle.stride * (i+starti)) * 3; + } + Arrays.fill(counts, handle.width*3); + + for(int i = 0; i != draw_lines; i++) { + glDrawArrays(GL_TRIANGLES, firsts[i], counts[i]); + } + } + + @SuppressWarnings("WeakerAccess") + protected class ShaderProgram { + public final int program; + + public final int proj; + public final int global; + public final int trans; + public final int tex; + public final int color; + public final int height; + public final int mode; + public final int shadow_depth; + public final int geometry_data; + + ShaderProgram(String name) { + int vertexShader = -1; + int fragmentShader; + + try { + vertexShader = createShader(name+".vert", GL_VERTEX_SHADER); + fragmentShader = createShader(name+".frag", GL_FRAGMENT_SHADER); + } catch (IOException e) { + e.printStackTrace(); + + if(vertexShader != -1) glDeleteShader(vertexShader); + throw new Error("could not read shader files", e); + } + + program = glCreateProgram(); + + glAttachShader(program, vertexShader); + glAttachShader(program, fragmentShader); + + for(int i = 0; i != attributes.size(); i++) { + glBindAttribLocation(program, i, attributes.get(i)); + } + + glLinkProgram(program); + glValidateProgram(program); + + glDetachShader(program, vertexShader); + glDetachShader(program, fragmentShader); + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + + String log = glGetProgramInfoLog(program); + if(!log.isEmpty()) System.out.print("info log of " + name + "=====\n" + log + "==== end\n"); + + int[] link_status = new int[1]; + glGetProgramiv(program, GL_LINK_STATUS, link_status, 0); + if(link_status[0] == 0) { + glDeleteProgram(program); + throw new Error("Could not link " + name); + } + + proj = glGetUniformLocation(program, "projection"); + global = glGetUniformLocation(program, "globalTransform"); + trans = glGetUniformLocation(program, "transform"); + tex = glGetUniformLocation(program, "texHandle"); + color = glGetUniformLocation(program, "color"); + height = glGetUniformLocation(program, "height"); + mode = glGetUniformLocation(program, "mode"); + shadow_depth = glGetUniformLocation(program, "shadow_depth"); + + if(gles3) { + geometry_data = GLES30.glGetUniformBlockIndex(program, "geometryDataBuffer"); + if (geometry_data != -1) GLES30.glUniformBlockBinding(program, geometry_data, 0); + } else { + geometry_data = -1; + } + + useProgram(this); + if(tex != -1) glUniform1i(tex, 0); + + shaders.add(this); + } + + private ArrayList attributes = new ArrayList<>(); + + private final String vendor_id = "//VENDOR=" + glGetString(GL_VENDOR) + " "; + + private int createShader(String name, int type) throws IOException { + StringBuilder source = new StringBuilder(); + try(InputStream shaderFile = getClass().getResourceAsStream("/"+name)) { + if (shaderFile == null) return -1; + BufferedReader is = new BufferedReader(new InputStreamReader(shaderFile)); + + String line; + while ((line = is.readLine()) != null) { + if (line.startsWith("attribute") || line.endsWith("//attribute")) { + attributes.add(line.split(" ")[2].replaceAll(";", "")); + } + + int vendor_index = line.indexOf(vendor_id); + if (vendor_index != -1) { + String remaining = line.substring(vendor_index + vendor_id.length()); + String[] replace = remaining.split("="); + line = line.substring(0, vendor_index).replaceFirst(replace[0], replace[1]); + } + + source.append(line); + if (line.startsWith("#version")) { + //source.append(" es"); + } + source.append("\n"); + } + } + + + int shader = glCreateShader(type); + if (shader == 0) return -1; + glShaderSource(shader, source.toString()); + glCompileShader(shader); + + + String log = glGetShaderInfoLog(shader); + if(!log.isEmpty()) System.out.print("info log of " + name + "=====\n" + log + "==== end\n"); + + int[] compile_status = new int[1]; + glGetShaderiv(shader, GL_COMPILE_STATUS, compile_status, 0); + if(compile_status[0] == 0) { + glDeleteShader(shader); + throw new Error("Could not compile " + name); + } + + return shader; + } + } + + public void clearDepthBuffer() { + finishFrame(); + glClear(GL_DEPTH_BUFFER_BIT); + } + + @Override + public void startFrame() { + super.startFrame(); + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + } +} diff --git a/go.graphics.android/src/main/java/go/graphics/android/GOSurfaceView.java b/go.graphics.android/src/main/java/go/graphics/android/GOSurfaceView.java index 95d7f9ea3c..ea4484852d 100644 --- a/go.graphics.android/src/main/java/go/graphics/android/GOSurfaceView.java +++ b/go.graphics.android/src/main/java/go/graphics/android/GOSurfaceView.java @@ -17,7 +17,6 @@ import android.content.Context; import android.opengl.EGL14; import android.opengl.EGLExt; -import android.opengl.GLES10; import android.opengl.GLSurfaceView; import android.os.Vibrator; import android.util.Log; @@ -47,7 +46,7 @@ public class GOSurfaceView extends GLSurfaceView implements RedrawListener, GOEv private final ActionAdapter actionAdapter = new ActionAdapter(getContext(), this); - private GLES11DrawContext drawcontext; + private GLESDrawContext drawcontext; private IContextDestroyedListener contextDestroyedListener = null; @@ -226,26 +225,35 @@ private Renderer(Context aContext) { @Override public void onDrawFrame(GL10 gl) { - GLES10.glClear(GLES10.GL_DEPTH_BUFFER_BIT | GLES10.GL_COLOR_BUFFER_BIT); + drawcontext.startFrame(); area.drawArea(drawcontext); + drawcontext.finishFrame(); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { area.setWidth(width); area.setHeight(height); - drawcontext.reinit(width, height); + drawcontext.resize(width, height); + } + + private GLESDrawContext createContext(GL10 gl) { + String version = gl.glGetString(GL10.GL_VERSION).split(" ")[2]; + int major = version.charAt(0)-'0'; + int minor = version.charAt(2)-'0'; + + if(major >= 2) { + try { + return new GLESDrawContext(ctx, major == 3); + } catch(Throwable thrown) {thrown.printStackTrace();}; + } + + return null; } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { - String version = gl.glGetString(GL10.GL_VERSION); - int major = version.split(" ")[2].charAt(0)-'0'; - if(major == 1) { - drawcontext = new GLES11DrawContext(ctx); - } else { - drawcontext = new GLES20DrawContext(ctx, major >= 3); - } + drawcontext = createContext(gl); } } @@ -260,9 +268,6 @@ public EGLContext createContext(EGL10 arg0, EGLDisplay display, EGLConfig config {EGLExt.EGL_CONTEXT_MAJOR_VERSION_KHR, 3, EGLExt.EGL_CONTEXT_MINOR_VERSION_KHR, 1, EGL10.EGL_NONE}, //3.1 {EGLExt.EGL_CONTEXT_MAJOR_VERSION_KHR, 3, EGLExt.EGL_CONTEXT_MINOR_VERSION_KHR, 0, EGL10.EGL_NONE}, //3.0 {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE}, // highest available version - {EGLExt.EGL_CONTEXT_MAJOR_VERSION_KHR, 1, EGLExt.EGL_CONTEXT_MINOR_VERSION_KHR, 1, EGL10.EGL_NONE}, // 1.1 - {EGL14.EGL_CONTEXT_CLIENT_VERSION, 1, EGL10.EGL_NONE}, // 1.x - {EGL10.EGL_NONE}, // lowest available version }; while(newCtx == null && attrs.length >= i) { @@ -279,8 +284,7 @@ public EGLContext createContext(EGL10 arg0, EGLDisplay display, EGLConfig config @Override public void destroyContext(EGL10 arg0, EGLDisplay arg1, EGLContext arg2) { Log.w("gl", "Invalidating texture context"); - if(drawcontext != null) drawcontext.invalidateContext(); - AndroidTextDrawer.invalidateTextures(); + if(drawcontext != null) drawcontext.invalidate(); IContextDestroyedListener listener = contextDestroyedListener; if (listener != null) { listener.glContextDestroyed(); diff --git a/go.graphics.android/src/main/resources/unified-multi.frag b/go.graphics.android/src/main/resources/unified-multi.frag new file mode 100644 index 0000000000..e9c4a36e6d --- /dev/null +++ b/go.graphics.android/src/main/resources/unified-multi.frag @@ -0,0 +1,49 @@ +#version 300 es + +#extension GL_NV_fragdepth : enable + +precision mediump float; + +in vec4 frag_color; +flat in int frag_mode; +in float frag_intensity; +in vec2 frag_texCoord; + +uniform sampler2D texHandle; +uniform float shadow_depth; + +out vec4 fragColor; + +void main() { + float fragDepth = gl_FragCoord.z; + fragColor = frag_color; + + bool textured = frag_mode!=0; + + if(textured) { + vec4 tex_color = texture(texHandle, frag_texCoord); + + bool image_fence = frag_mode>0; + bool torso_fence = frag_mode>1; + bool shadow_fence = abs(float(frag_mode))>2.0; + + if(torso_fence && tex_color.a < 0.1 && tex_color.r > 0.1) { // torso pixel + fragColor.rgb *= tex_color.b; + } else if(shadow_fence && tex_color.a < 0.1 && tex_color.g > 0.1) { // shadow pixel + fragColor.rgba = tex_color.aaag; + fragDepth += shadow_depth; + } else if(image_fence) { // image pixel + if(!torso_fence && !shadow_fence) { + fragColor *= tex_color; + } else { + fragColor = tex_color; + } + } + } + + if(fragColor.a < 0.5) discard; + + fragColor.rgb *= frag_intensity; + + gl_FragDepth = fragDepth; +} diff --git a/go.graphics.android/src/main/resources/unified-multi.vert b/go.graphics.android/src/main/resources/unified-multi.vert new file mode 100644 index 0000000000..0a04625a17 --- /dev/null +++ b/go.graphics.android/src/main/resources/unified-multi.vert @@ -0,0 +1,30 @@ +#version 300 es + +precision mediump float; + +in vec3 position; //attribute +in vec2 scale; //attribute +in vec4 color; //attribute +in vec3 additional; //attribute + +uniform mat4 globalTransform; +uniform mat4 projection; + +layout(std140) uniform geometryDataBuffer { + vec4 geometryData[4*1000]; +}; + +out vec4 frag_color; +flat out int frag_mode; +out float frag_intensity; +out vec2 frag_texCoord; + +void main() { + frag_mode = int(additional.z); + frag_color = color; + frag_intensity = additional.x; + int index = int(additional.y)+gl_VertexID; + + gl_Position = projection * globalTransform * vec4(position+vec3(scale*geometryData[index].xy, 0.f), 1.f); + frag_texCoord = geometryData[index].zw; +} diff --git a/go.graphics.swing/build.gradle b/go.graphics.swing/build.gradle index f11a64397a..fe8a5e2340 100644 --- a/go.graphics.swing/build.gradle +++ b/go.graphics.swing/build.gradle @@ -1,6 +1,6 @@ apply plugin: 'java' -def lwjgl_version="3.1.6" +def lwjgl_version="3.2.3" dependencies { implementation project(':go.graphics') @@ -10,6 +10,7 @@ dependencies { compile "org.lwjgl:lwjgl-glfw:"+lwjgl_version compile "org.lwjgl:lwjgl-egl:"+lwjgl_version compile "org.lwjgl:lwjgl-jawt:"+lwjgl_version + compile "org.joml:joml:1.9.17" compile "org.lwjgl:lwjgl:"+lwjgl_version+":natives-linux" compile "org.lwjgl:lwjgl:"+lwjgl_version+":natives-macos" @@ -29,5 +30,4 @@ dependencies { compile "org.jogamp.jogl:jogl-all:2.3.2" runtime "org.jogamp.gluegen:gluegen-rt:2.3.2:natives-macosx-universal" runtime "org.jogamp.jogl:jogl-all:2.3.2:natives-macosx-universal" - compile 'org.joml:joml:1.9.11' } diff --git a/go.graphics.swing/src/main/java/go/graphics/swing/AreaContainer.java b/go.graphics.swing/src/main/java/go/graphics/swing/AreaContainer.java index 43872408e7..d4e16c9d0c 100644 --- a/go.graphics.swing/src/main/java/go/graphics/swing/AreaContainer.java +++ b/go.graphics.swing/src/main/java/go/graphics/swing/AreaContainer.java @@ -15,11 +15,14 @@ package go.graphics.swing; import java.awt.BorderLayout; +import java.awt.Color; + import go.graphics.DrawmodeListener; import go.graphics.RedrawListener; import go.graphics.area.Area; import go.graphics.event.GOEvent; import go.graphics.swing.contextcreator.EBackendType; +import go.graphics.swing.contextcreator.GLContextException; /** * This class lets you embed areas into swing components. @@ -42,30 +45,32 @@ public class AreaContainer extends GLContainer implements RedrawListener { * The area to display */ public AreaContainer(Area area) { - this(area, EBackendType.DEFAULT, false); + this(area, EBackendType.DEFAULT, false, 0); } - public AreaContainer(Area area, EBackendType backend, boolean debug) { + public AreaContainer(Area area, EBackendType backend, boolean debug, float guiScale) { super(backend, new BorderLayout(), debug); this.area = area; + this.guiScale = guiScale; if(cc instanceof DrawmodeListener) { area.setDrawmodeListener((DrawmodeListener) cc); } + setBackground(Color.BLACK); area.addRedrawListener(this); } - public void resize_gl(int width, int height) { - super.resize_gl(width, height); + public void resizeContext(int width, int height) throws GLContextException { + super.resizeContext(width, height); area.setWidth(width); area.setHeight(height); } - public void draw() { + public void draw() throws GLContextException { super.draw(); area.drawArea(context); } @@ -74,4 +79,8 @@ public void draw() { public void handleEvent(GOEvent event) { area.handleEvent(event); } + + public void notifyResize() { + cc.componentResized(null); + } } diff --git a/go.graphics.swing/src/main/java/go/graphics/swing/GLContainer.java b/go.graphics.swing/src/main/java/go/graphics/swing/GLContainer.java index 78637a923d..95f021fc60 100644 --- a/go.graphics.swing/src/main/java/go/graphics/swing/GLContainer.java +++ b/go.graphics.swing/src/main/java/go/graphics/swing/GLContainer.java @@ -1,7 +1,6 @@ package go.graphics.swing; import org.lwjgl.opengl.GL; -import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GLCapabilities; import java.awt.Component; @@ -16,15 +15,16 @@ import go.graphics.swing.contextcreator.ContextCreator; import go.graphics.swing.contextcreator.EBackendType; import go.graphics.swing.contextcreator.JAWTContextCreator; -import go.graphics.swing.opengl.LWJGL15DrawContext; -import go.graphics.swing.opengl.LWJGL20DrawContext; +import go.graphics.swing.contextcreator.GLContextException; +import go.graphics.swing.opengl.LWJGLDrawContext; public abstract class GLContainer extends JPanel implements GOEventHandlerProvider { protected ContextCreator cc; - protected LWJGL15DrawContext context; + protected LWJGLDrawContext context; private boolean debug; + protected float guiScale = 0; public GLContainer(EBackendType backend, LayoutManager layout, boolean debug) { setLayout(layout); @@ -35,36 +35,46 @@ public GLContainer(EBackendType backend, LayoutManager layout, boolean debug) { cc.init(); } catch (Exception ex) { ex.printStackTrace(); - JOptionPane.showMessageDialog(null, "Could not create opengl context through " + backend.cc_name + "\nPress ok to exit"); - System.exit(1); + fatal("Could not create opengl context through " + backend.cc_name); } } - public void resize_gl(int width, int height) { + public void fatal(String message) { + SwingUtilities.invokeLater(() -> { + JOptionPane.showMessageDialog(null, message+ "\nPress ok to exit", "Error", JOptionPane.ERROR_MESSAGE); + System.exit(1); + }); + System.err.println(message); + } + + public void resizeContext(int width, int height) throws GLContextException { + if(context == null) throw new GLContextException(); context.resize(width, height); } + public void finishFrame() { + context.finishFrame(); + } + public void wrapNewContext() { if(cc instanceof JAWTContextCreator) ((JAWTContextCreator)cc).makeCurrent(true); - if(context != null) context.disposeAll(); + if(context != null) context.invalidate(); GLCapabilities caps = GL.createCapabilities(); - if(caps.OpenGL20) { - context = new LWJGL20DrawContext(caps, debug); - } else if(caps.OpenGL15 && caps.GL_ARB_texture_non_power_of_two) { - context = new LWJGL15DrawContext(caps, debug); - } else { - context = null; - errorGLVersion(); + try { + if(caps.OpenGL20) { + context = new LWJGLDrawContext(caps, cc::getScale, debug, guiScale); + } else { + errorGLVersion(); + } + } catch(Throwable thrown) { + fatal(thrown.getLocalizedMessage()); } } private void errorGLVersion() { - SwingUtilities.invokeLater(() -> { - JOptionPane.showMessageDialog(null, "JSettlers needs at least OpenGL 1.5 with GL_ARB_texture_non_power_of_two\nPress ok to exit"); - System.exit(1); - }); + fatal("JSettlers needs at least OpenGL 2.0"); } /** @@ -73,17 +83,18 @@ private void errorGLVersion() { public void disposeAll() { cc.stop(); if (context != null) { - context.disposeAll(); + context.invalidate(); } context = null; } - public void draw() { - GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); + public void draw() throws GLContextException { + if(context == null) throw new GLContextException(); + context.startFrame(); } public void requestRedraw() { - cc.repaint(); + if(cc != null) cc.repaint(); } /** @@ -97,4 +108,8 @@ public void requestFocus() { public void addCanvas(Component canvas) { add(canvas); } + + public void updateFPSLimit(int fpsLimit) { + if(cc != null) cc.updateFPSLimit(fpsLimit); + } } diff --git a/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/AsyncContextCreator.java b/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/AsyncContextCreator.java index b471e6cda9..4eeb241629 100644 --- a/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/AsyncContextCreator.java +++ b/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/AsyncContextCreator.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018 + * Copyright (c) 2018 - 2019 * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, @@ -15,8 +15,6 @@ package go.graphics.swing.contextcreator; import org.lwjgl.BufferUtils; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL12; import java.awt.Graphics; import java.awt.image.BufferedImage; @@ -26,20 +24,22 @@ import javax.swing.SwingUtilities; import go.graphics.DrawmodeListener; +import go.graphics.FramerateComputer; import go.graphics.swing.GLContainer; import go.graphics.swing.event.swingInterpreter.GOSwingEventConverter; +import static org.lwjgl.opengl.GL20C.*; + public abstract class AsyncContextCreator extends ContextCreator implements Runnable,DrawmodeListener { private boolean offscreen = true; private boolean clear_offscreen = true; private boolean continue_run = true; - protected boolean ignore_resize = false; - protected BufferedImage bi = null; - protected IntBuffer pixels; + private BufferedImage bi = null; + private IntBuffer pixels; - private Thread render_thread; + private Thread render_thread = new Thread(this, "AsyncRenderer"); public AsyncContextCreator(GLContainer container, boolean debug) { super(container, debug); @@ -52,7 +52,7 @@ public void stop() { @Override public void initSpecific() { - JPanel panel = new JPanel() { + canvas = new JPanel() { public void paintComponent(Graphics graphics) { super.paintComponent(graphics); @@ -69,24 +69,12 @@ public void paintComponent(Graphics graphics) { } else { graphics.drawString("Press m to enable offscreen transfer", width/3, height/2); } + + if(fpsLimit == 0) repaint(); } }; - canvas = panel; - render_thread = new Thread(this); render_thread.start(); - - - } - - @Override - public void repaint() { - canvas.repaint(); - } - - @Override - public void requestFocus() { - canvas.requestFocus(); } public abstract void async_init(); @@ -101,47 +89,55 @@ public void requestFocus() { @Override public void run() { + synchronized (wnd_lock) { + width = new_width; + height = new_height; + } async_init(); - parent.wrapNewContext(); + FramerateComputer fpsComputer = new FramerateComputer(); while(continue_run) { - if (change_res) { - if(!ignore_resize) { - width = new_width; - height = new_height; - async_set_size(width, height); + try { + if (change_res) { + synchronized (wnd_lock) { + width = new_width; + height = new_height; + async_set_size(width, height); - parent.resize_gl(width, height); + parent.resizeContext(width, height); - bi = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); - pixels = BufferUtils.createIntBuffer(width * height); + bi = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); + pixels = BufferUtils.createIntBuffer(width * height); + } + change_res = false; } - ignore_resize = false; - change_res = false; - } + async_refresh(); - async_refresh(); + parent.draw(); + parent.finishFrame(); - parent.draw(); - - if (offscreen) { - synchronized (wnd_lock) { - GL11.glReadPixels(0, 0, width, height, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, pixels); - for (int x = 0; x != width; x++) { - for (int y = 0; y != height; y++) { - bi.setRGB(x, height - y - 1, pixels.get(y * width + x)); + if (offscreen) { + synchronized (wnd_lock) { + glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels); + for (int x = 0; x != width; x++) { + for (int y = 0; y != height; y++) { + bi.setRGB(x, height - y - 1, pixels.get(y * width + x)); + } } } } - } - if(!offscreen || clear_offscreen ){ - if(clear_offscreen) { - GL11.glClear(GL11.GL_COLOR_BUFFER_BIT); - clear_offscreen = false; + if (!offscreen || clear_offscreen) { + if (clear_offscreen) { + glClear(GL_COLOR_BUFFER_BIT); + clear_offscreen = false; + } + async_swapbuffers(); + if (fpsLimit != 0) fpsComputer.nextFrame(fpsLimit); } - async_swapbuffers(); + } catch(Throwable thrown) { + thrown.printStackTrace(); } } diff --git a/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/BackendSelector.java b/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/BackendSelector.java index b4809fbacf..0dea063790 100644 --- a/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/BackendSelector.java +++ b/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/BackendSelector.java @@ -35,8 +35,14 @@ public class BackendSelector extends JComboBox { public void actionPerformed(ActionEvent actionEvent) { super.actionPerformed(actionEvent); - if(actionEvent.getActionCommand() == "comboBoxChanged") { - EBackendType bi = (EBackendType) getSelectedItem(); + if(actionEvent.getActionCommand().equals("comboBoxChanged")) { + Object item = getSelectedItem(); + if(item == null || item instanceof String) { + setSelectedItem(current_item); + return; + } + + EBackendType bi = (EBackendType) item; if (bi.platform != null && bi.platform != Platform.get()) { setSelectedItem(current_item); BackendSelector.this.hidePopup(); @@ -49,11 +55,10 @@ public void actionPerformed(ActionEvent actionEvent) { } public BackendSelector() { + super(availableBackends().toArray(EBackendType[]::new)); setEditable(false); addActionListener(this); - - availableBackends().forEach(backend -> addItem(backend)); } private static Stream availableBackends() { @@ -61,7 +66,6 @@ private static Stream availableBackends() { } public static EBackendType getBackendByName(String name) { - // matching and matching and suitable backends return availableBackends().filter(backend -> backend.cc_name.equalsIgnoreCase(name)).findFirst().orElse(EBackendType.DEFAULT); } diff --git a/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/ContextCreator.java b/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/ContextCreator.java index 3f6c6276ac..a19ef310d6 100644 --- a/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/ContextCreator.java +++ b/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/ContextCreator.java @@ -18,9 +18,11 @@ import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; +import javax.swing.SwingUtilities; + import go.graphics.swing.GLContainer; -public abstract class ContextCreator implements ComponentListener{ +public abstract class ContextCreator implements ComponentListener{ public ContextCreator(GLContainer ac, boolean debug) { parent = ac; @@ -32,7 +34,8 @@ public ContextCreator(GLContainer ac, boolean debug) { protected boolean change_res = true; protected final Object wnd_lock = new Object(); protected boolean first_draw = true; - protected Component canvas; + protected int fpsLimit = 0; + protected T canvas; protected GLContainer parent; protected boolean debug; @@ -40,11 +43,20 @@ public ContextCreator(GLContainer ac, boolean debug) { public abstract void stop(); public abstract void initSpecific(); - public abstract void repaint(); + public void repaint() { + canvas.repaint(); + } - public abstract void requestFocus(); + public void requestFocus() { + canvas.requestFocus(); + } + protected void error(String message) throws GLContextException { + parent.fatal(message); + throw new GLContextException(); + } + public void init() { initSpecific(); @@ -55,10 +67,10 @@ public void init() { @Override public void componentResized(ComponentEvent componentEvent) { - Component cmp = componentEvent.getComponent(); + if(!SwingUtilities.windowForComponent(canvas).isFocused()) return; synchronized (wnd_lock) { - new_width = cmp.getWidth(); - new_height = cmp.getHeight(); + new_width = canvas.getWidth(); + new_height = canvas.getHeight(); change_res = true; if(new_width == 0) new_width = 1; @@ -74,4 +86,12 @@ public void componentMoved(ComponentEvent componentEvent) {} @Override public void componentShown(ComponentEvent componentEvent) {} + + public void updateFPSLimit(int fpsLimit) { + this.fpsLimit = fpsLimit; + } + + public float getScale() { + return 1; + } } diff --git a/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/EGLContextCreator.java b/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/EGLContextCreator.java index 7373ac7f4b..260a819fed 100644 --- a/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/EGLContextCreator.java +++ b/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/EGLContextCreator.java @@ -1,3 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2019 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *******************************************************************************/ package go.graphics.swing.contextcreator; import org.lwjgl.BufferUtils; @@ -14,9 +28,11 @@ import org.lwjgl.egl.KHRDebug; import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryUtil; + import java.nio.IntBuffer; import go.graphics.swing.GLContainer; +import javax.swing.SwingUtilities; public class EGLContextCreator extends JAWTContextCreator { @@ -49,38 +65,31 @@ public void makeCurrent(boolean draw) { } } - private static final int[][] ctx_attrs = new int[][] { - { // GL2.0 with debugging - EGL15.EGL_CONTEXT_MAJOR_VERSION, 2, - EGL15.EGL_CONTEXT_MINOR_VERSION, 0, - EGL15.EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL15.EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + private static final int[][][] ctx_attrs = new int[][][] { + { + {// GL3.2+ with debugging + EGL15.EGL_CONTEXT_MAJOR_VERSION, 3, + EGL15.EGL_CONTEXT_MINOR_VERSION, 2, + EGL15.EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL15.EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, KHRCreateContext.EGL_CONTEXT_FLAGS_KHR, KHRCreateContext.EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, - EGL10.EGL_NONE - }, - {// GL1.5 with debugging - EGL15.EGL_CONTEXT_MAJOR_VERSION, 1, - EGL15.EGL_CONTEXT_MINOR_VERSION, 5, - KHRCreateContext.EGL_CONTEXT_FLAGS_KHR, KHRCreateContext.EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, - EGL10.EGL_NONE - }, - {// GL1.1+ with debugging - KHRCreateContext.EGL_CONTEXT_FLAGS_KHR, KHRCreateContext.EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, - EGL10.EGL_NONE + EGL10.EGL_NONE + }, + {// GL3.2+ + EGL15.EGL_CONTEXT_MAJOR_VERSION, 3, + EGL15.EGL_CONTEXT_MINOR_VERSION, 2, + EGL15.EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL15.EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + EGL10.EGL_NONE + } }, - { // GL2.0 - EGL15.EGL_CONTEXT_MAJOR_VERSION, 2, - EGL15.EGL_CONTEXT_MINOR_VERSION, 0, - EGL15.EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL15.EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, - EGL10.EGL_NONE - }, - {// GL1.5 - EGL15.EGL_CONTEXT_MAJOR_VERSION, 1, - EGL15.EGL_CONTEXT_MINOR_VERSION, 5, - EGL10.EGL_NONE - }, - {// GL1.1+ - EGL10.EGL_NONE + { + {// GL1.1+ with debugging + KHRCreateContext.EGL_CONTEXT_FLAGS_KHR, KHRCreateContext.EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, + EGL10.EGL_NONE + }, + {// GL1.1+ + EGL10.EGL_NONE + } }, }; @@ -91,38 +100,39 @@ private void setEGLDebugFunction(boolean info, boolean warning, boolean error_ar KHRDebug.EGL_DEBUG_MSG_CRITICAL_KHR, critical ? EGL10.EGL_TRUE : EGL10.EGL_FALSE, KHRDebug.EGL_DEBUG_MSG_ERROR_KHR, error_arg ? EGL10.EGL_TRUE : EGL10.EGL_FALSE, KHRDebug.EGL_DEBUG_MSG_WARN_KHR, warning ? EGL10.EGL_TRUE : EGL10.EGL_FALSE, - KHRDebug.EGL_DEBUG_MSG_INFO_KHR, info ? EGL10.EGL_TRUE : EGL10.EGL_FALSE + KHRDebug.EGL_DEBUG_MSG_INFO_KHR, info ? EGL10.EGL_TRUE : EGL10.EGL_FALSE, + EGL10.EGL_NONE ); - PointerBuffer bfr = stack.pointers(MemoryUtil.memAddress(debug)); + PointerBuffer bfr = stack.pointers(MemoryUtil.memAddress(debug), EGL10.EGL_NONE); - KHRDebug.eglDebugMessageControlKHR( - (error, command, messageType, threadLabel, objectLabel, message) -> { - String command_str = EGLDebugMessageKHRCallback.getCommand(command); - String message_str = EGLDebugMessageKHRCallback.getMessage(message); - System.out.println("[EGL] Debug Message"); - System.out.println(" error: " + error); - System.out.println(" command: " + command_str); - System.out.println(" messageType: " + messageType); - System.out.println(" threadLabel: " + threadLabel); - System.out.println(" objectLabel: " + objectLabel); - System.out.println(" message: " + message_str); - }, bfr); + try { + KHRDebug.eglDebugMessageControlKHR( + (error, command, messageType, threadLabel, objectLabel, message) -> { + String command_str = EGLDebugMessageKHRCallback.getCommand(command); + String message_str = EGLDebugMessageKHRCallback.getMessage(message); + System.out.println("[EGL] Debug Message"); + System.out.println(" error: " + error); + System.out.println(" command: " + command_str); + System.out.println(" messageType: " + messageType); + System.out.println(" threadLabel: " + threadLabel); + System.out.println(" objectLabel: " + objectLabel); + System.out.println(" message: " + message_str); + }, bfr); + } catch(Throwable thrown) {} } - System.out.println("egl error: " + EGL10.eglGetError()); } - protected void initStatic() { + private void initStatic() { if(egl_display != 0) return; + if(debug) setEGLDebugFunction(true, true, true, true); + egl_display = EGL10.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); EGL10.eglInitialize(egl_display, new int[] {1}, new int[] {1}); EGLCapabilities caps = EGL.createDisplayCapabilities(egl_display); - - if(debug && caps.EGL_KHR_debug) setEGLDebugFunction(true, true, true, true); - if(!caps.EGL14 || !EGL12.eglBindAPI(EGL14.EGL_OPENGL_API)) throw new Error("could not bind OpenGL"); int[] attrs = {EGL13.EGL_CONFORMANT, EGL14.EGL_OPENGL_BIT, @@ -135,26 +145,35 @@ protected void initStatic() { if(num_config[0] == 0) throw new Error("could not found egl configs!"); egl_config = cfgs.get(0); - int i = debug ? 0 : 4; - while(egl_context == 0 && ctx_attrs.length > i) { - int[] current_ctx_attrs = ctx_attrs[i]; + } - egl_context = EGL10.eglCreateContext(egl_display, egl_config, 0, current_ctx_attrs); - i++; + @Override + protected void onInit() throws GLContextException { + int i = 0; + while(egl_context == 0 && ctx_attrs.length > i) { + egl_context = EGL10.eglCreateContext(egl_display, egl_config, 0, ctx_attrs[i++][debug?0:1]); if(egl_context != 0 && EGL10.eglGetError() != EGL10.EGL_SUCCESS) { EGL10.eglDestroyContext(egl_display, egl_context); egl_context = 0; } } - } + if(egl_context == 0) error("could not create context"); - @Override - protected void onInit() { parent.wrapNewContext(); } @Override - protected void onNewDrawable() { + protected void onNewDrawable() throws GLContextException { egl_surface = EGL10.eglCreateWindowSurface(egl_display, egl_config, windowDrawable, (IntBuffer)null); + if(EGL10.eglGetError() != EGL10.EGL_SUCCESS) error("could not create new drawable"); + } + + @Override + public float getScale() { + int[] re = new int[1]; + EGL10.eglQuerySurface(egl_display, egl_surface, EGL10.EGL_WIDTH, re); + int surfaceWidth = re[0]; + + return surfaceWidth/(float)SwingUtilities.windowForComponent(canvas).getWidth(); } } diff --git a/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/GLContextException.java b/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/GLContextException.java new file mode 100644 index 0000000000..1174639165 --- /dev/null +++ b/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/GLContextException.java @@ -0,0 +1,7 @@ +package go.graphics.swing.contextcreator; + +/** + * This exception is intentionally checked to force the user to handle it appropriately which means ignore it because the error has already been reported. + */ +public class GLContextException extends Exception { +} diff --git a/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/GLFWContextCreator.java b/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/GLFWContextCreator.java index 5ea03ec7d3..0dfc13c3b9 100644 --- a/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/GLFWContextCreator.java +++ b/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/GLFWContextCreator.java @@ -23,7 +23,7 @@ import org.lwjgl.glfw.GLFWScrollCallback; import org.lwjgl.glfw.GLFWWindowSizeCallback; -import java.awt.Dimension; +import java.awt.Window; import java.util.HashMap; import javax.swing.SwingUtilities; @@ -54,20 +54,38 @@ public void async_init() { GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_DEBUG_CONTEXT, debug ? GLFW.GLFW_TRUE : GLFW.GLFW_DONT_CARE); GLFW.glfwWindowHint(GLFW.GLFW_STENCIL_BITS, 1); - glfw_wnd = GLFW.glfwCreateWindow(width + 1, width + 1, "lwjgl-offscreen", 0, 0); - GLFW.glfwMakeContextCurrent(glfw_wnd); - GLFW.glfwSwapInterval(0); + synchronized (wnd_lock) { + glfw_wnd = GLFW.glfwCreateWindow(width, height, "lwjgl-offscreen", 0, 0); + GLFW.glfwMakeContextCurrent(glfw_wnd); + GLFW.glfwSwapInterval(0); + parent.wrapNewContext(); + try { + parent.resizeContext(width, height); + } catch (GLContextException ignored) {} + } event_converter.registerCallbacks(); } public void async_set_size(int width, int height) { GLFW.glfwSetWindowSize(glfw_wnd, width, height); - } + private long glfw_resize_time = -1; + private int glfw_width, glfw_height; public void async_refresh() { + if(glfw_resize_time != -1) { + if(glfw_resize_time+10 <= System.currentTimeMillis()) { + Window wnd = SwingUtilities.windowForComponent(canvas); + int dw = wnd.getWidth()-canvas.getWidth(); + int dh = wnd.getHeight()-canvas.getHeight(); + + wnd.setSize(glfw_width+dw, glfw_height+dh); + + glfw_resize_time = -1; + } + } GLFW.glfwPollEvents(); } @@ -198,9 +216,16 @@ public void invoke(long window, double xoffset, double yoffset) { private GLFWWindowSizeCallback size_callback = new GLFWWindowSizeCallback() { @Override public void invoke(long window, int width, int height) { - Dimension size = parent.getSize(); - if(size.width != width || size.height != height) SwingUtilities.windowForComponent(canvas).setSize(width, height); - ignore_resize = true; + synchronized (wnd_lock) { + if(GLFWContextCreator.this.width == width && GLFWContextCreator.this.height == height) { + glfw_resize_time = -1; + return; + } + + glfw_resize_time = System.currentTimeMillis(); + glfw_width = width; + glfw_height = height; + } } }; diff --git a/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/GLXContextCreator.java b/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/GLXContextCreator.java index f3162ee68e..3ae281c5ee 100644 --- a/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/GLXContextCreator.java +++ b/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/GLXContextCreator.java @@ -36,43 +36,36 @@ public GLXContextCreator(GLContainer container, boolean debug) { X11.getLibrary().getName(); } - private static final int[][] ctx_attrs = new int[][] { - { // GL2.0 with debugging - GLXARBCreateContext.GLX_CONTEXT_MAJOR_VERSION_ARB, 2, - GLXARBCreateContext.GLX_CONTEXT_MINOR_VERSION_ARB, 0, - GLXARBCreateContextProfile.GLX_CONTEXT_PROFILE_MASK_ARB, GLXARBCreateContextProfile.GLX_CONTEXT_CORE_PROFILE_BIT_ARB, - GLXARBCreateContext.GLX_CONTEXT_FLAGS_ARB, GLXARBCreateContext.GLX_CONTEXT_DEBUG_BIT_ARB, - 0 - }, - {// GL1.5 with debugging - GLXARBCreateContext.GLX_CONTEXT_MAJOR_VERSION_ARB, 1, - GLXARBCreateContext.GLX_CONTEXT_MINOR_VERSION_ARB, 5, - GLXARBCreateContext.GLX_CONTEXT_FLAGS_ARB, GLXARBCreateContext.GLX_CONTEXT_DEBUG_BIT_ARB, - 0 - }, - {// GL1.1+ with debugging - GLXARBCreateContext.GLX_CONTEXT_FLAGS_ARB, GLXARBCreateContext.GLX_CONTEXT_DEBUG_BIT_ARB, - 0 + private static final int[][][] ctx_attrs = new int[][][] { + { + {// GL3.2+ with debugging + GLXARBCreateContext.GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLXARBCreateContext.GLX_CONTEXT_MINOR_VERSION_ARB, 2, + GLXARBCreateContextProfile.GLX_CONTEXT_PROFILE_MASK_ARB, GLXARBCreateContextProfile.GLX_CONTEXT_CORE_PROFILE_BIT_ARB, + GLXARBCreateContext.GLX_CONTEXT_FLAGS_ARB, GLXARBCreateContext.GLX_CONTEXT_DEBUG_BIT_ARB, + 0, }, + {// GL3.2+ + GLXARBCreateContext.GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLXARBCreateContext.GLX_CONTEXT_MINOR_VERSION_ARB, 2, + GLXARBCreateContextProfile.GLX_CONTEXT_PROFILE_MASK_ARB, GLXARBCreateContextProfile.GLX_CONTEXT_CORE_PROFILE_BIT_ARB, + 0, + } + }, - { // GL2.0 - GLXARBCreateContext.GLX_CONTEXT_MAJOR_VERSION_ARB, 2, - GLXARBCreateContext.GLX_CONTEXT_MINOR_VERSION_ARB, 0, - GLXARBCreateContextProfile.GLX_CONTEXT_PROFILE_MASK_ARB, GLXARBCreateContextProfile.GLX_CONTEXT_CORE_PROFILE_BIT_ARB, - 0, - }, - {// GL1.5 - GLXARBCreateContext.GLX_CONTEXT_MAJOR_VERSION_ARB, 1, - GLXARBCreateContext.GLX_CONTEXT_MINOR_VERSION_ARB, 5, - 0 + { + {// GL1.1+ with debugging + GLXARBCreateContext.GLX_CONTEXT_FLAGS_ARB, GLXARBCreateContext.GLX_CONTEXT_DEBUG_BIT_ARB, + 0 }, {// GL1.1+ - 0 - }, + 0 + } + }, }; @Override - protected void onInit() { + protected void onInit() throws GLContextException { int screen = X11.XDefaultScreen(windowConnection); int[] xvi_attrs = new int[]{ @@ -84,19 +77,19 @@ protected void onInit() { GLXCapabilities glxcaps = GL.createCapabilitiesGLX(windowConnection, screen); if(glxcaps.GLX13 && glxcaps.GLX_ARB_create_context && glxcaps.GLX_ARB_create_context_profile) { PointerBuffer fbc = GLX13.glXChooseFBConfig(windowConnection, screen, new int[] {0}); - if(fbc == null || fbc.capacity() < 1) throw new Error("GLX could not find any FBConfig!"); + if(fbc == null || fbc.capacity() < 1) error("GLX could not find any FBConfig!"); - int i = debug ? 0 : 3; + int i = 0; while(context == 0 && ctx_attrs.length > i) { - context = GLXARBCreateContext.glXCreateContextAttribsARB(windowConnection, fbc.get(), 0, true, ctx_attrs[i]); + context = GLXARBCreateContext.glXCreateContextAttribsARB(windowConnection, fbc.get(), 0, true, ctx_attrs[i++][debug?0:1]); } } else { - if(debug) throw new Error("GLX could not create a debug context!"); + if(debug) error("GLX could not create a debug context!"); XVisualInfo xvi = GLX.glXChooseVisual(windowConnection, screen, xvi_attrs); context = GLX.glXCreateContext(windowConnection, xvi, 0, true); } - if (context == 0) throw new Error("Could not create GLX context!"); + if (context == 0) error("Could not create GLX context!"); parent.wrapNewContext(); } diff --git a/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/JAWTContextCreator.java b/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/JAWTContextCreator.java index 9ff97f7105..2b32742957 100644 --- a/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/JAWTContextCreator.java +++ b/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/JAWTContextCreator.java @@ -48,7 +48,7 @@ public JAWTContextCreator(GLContainer container, boolean debug) { @Override public abstract void stop(); - private void regenerateWindowInfo() { + private void regenerateWindowInfo() throws GLContextException { long oldWindowConnection = windowConnection; long oldWindowDrawable = windowDrawable; if(currentPlatform == Platform.LINUX) { @@ -65,10 +65,10 @@ private void regenerateWindowInfo() { if(windowConnection != oldWindowConnection) onNewConnection(); } - protected void onNewConnection() {} - protected void onNewDrawable() {} + protected void onNewConnection() throws GLContextException {} + protected void onNewDrawable() throws GLContextException {} - protected void onInit() {} + protected void onInit() throws GLContextException {} @Override public void initSpecific() { @@ -79,38 +79,45 @@ public void update(Graphics graphics) { } public void paint(Graphics graphics) { - surface = JAWTFunctions.JAWT_GetDrawingSurface(jawt.GetDrawingSurface(), canvas); + surface = JAWTFunctions.JAWT_GetDrawingSurface(canvas, jawt.GetDrawingSurface()); + JAWTFunctions.JAWT_DrawingSurface_Lock(surface, surface.Lock()); + surfaceinfo = JAWTFunctions.JAWT_DrawingSurface_GetDrawingSurfaceInfo(surface, surface.GetDrawingSurfaceInfo()); + try { + regenerateWindowInfo(); - JAWTFunctions.JAWT_DrawingSurface_Lock(surface.Lock(), surface); - surfaceinfo = JAWTFunctions.JAWT_DrawingSurface_GetDrawingSurfaceInfo(surface.GetDrawingSurfaceInfo(), surface); - regenerateWindowInfo(); + if (first_draw) { + first_draw = false; + new GOSwingEventConverter(this, parent); - if (first_draw) { - first_draw = false; - new GOSwingEventConverter(this, parent); - - onInit(); - } - makeCurrent(true); + onInit(); + } + makeCurrent(true); - synchronized (wnd_lock) { - if (change_res) { - width = new_width; - height = new_height; + synchronized (wnd_lock) { + if (change_res) { + width = new_width; + height = new_height; - parent.resize_gl(width, height); - change_res = false; + parent.resizeContext(width, height); + change_res = false; + } } - } - parent.draw(); + parent.draw(); + parent.finishFrame(); - swapBuffers(); - makeCurrent(false); - JAWTFunctions.JAWT_DrawingSurface_Unlock(surface.Unlock(), surface); + swapBuffers(); + makeCurrent(false); + } catch(GLContextException ignored) { + } catch (Throwable thrown) { + thrown.printStackTrace(); + } + + if (fpsLimit == 0) repaint(); + JAWTFunctions.JAWT_DrawingSurface_Unlock(surface, surface.Unlock()); - JAWTFunctions.JAWT_DrawingSurface_FreeDrawingSurfaceInfo(surface.FreeDrawingSurfaceInfo(), surfaceinfo); - JAWTFunctions.JAWT_FreeDrawingSurface(jawt.FreeDrawingSurface(), surface); + JAWTFunctions.JAWT_DrawingSurface_FreeDrawingSurfaceInfo(surfaceinfo, surface.FreeDrawingSurfaceInfo()); + JAWTFunctions.JAWT_FreeDrawingSurface(surface, jawt.FreeDrawingSurface()); } }; } @@ -118,15 +125,4 @@ public void paint(Graphics graphics) { protected abstract void swapBuffers(); public abstract void makeCurrent(boolean draw); - - @Override - public void repaint() { - canvas.repaint(); - } - - @Override - public void requestFocus() { - canvas.requestFocus(); - - } } diff --git a/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/JOGLContextCreator.java b/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/JOGLContextCreator.java index 88e006d19f..f4294f0974 100644 --- a/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/JOGLContextCreator.java +++ b/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/JOGLContextCreator.java @@ -17,7 +17,6 @@ import com.jogamp.opengl.GL4; import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLCapabilities; -import com.jogamp.opengl.GLContext; import com.jogamp.opengl.GLEventListener; import com.jogamp.opengl.GLProfile; import com.jogamp.opengl.awt.GLJPanel; @@ -25,7 +24,7 @@ import go.graphics.swing.GLContainer; import go.graphics.swing.event.swingInterpreter.GOSwingEventConverter; -public class JOGLContextCreator extends ContextCreator implements GLEventListener{ +public class JOGLContextCreator extends ContextCreator implements GLEventListener { public JOGLContextCreator(GLContainer container, boolean debug) { super(container, debug); @@ -42,21 +41,11 @@ public void initSpecific() { caps.setStencilBits(1); canvas = new GLJPanel(caps); - ((GLJPanel)canvas).addGLEventListener(this); + canvas.addGLEventListener(this); new GOSwingEventConverter(canvas, parent); } - @Override - public void repaint() { - canvas.repaint(); - } - - @Override - public void requestFocus() { - canvas.requestFocus(); - } - @Override public void init(GLAutoDrawable drawable) { drawable.getGL().setSwapInterval(0); @@ -71,11 +60,17 @@ public void dispose(GLAutoDrawable drawable) { @Override public void display(GLAutoDrawable drawable) { - parent.draw(); + try { + parent.draw(); + parent.finishFrame(); + if(fpsLimit == 0) repaint(); + } catch(GLContextException ignored) {} } @Override public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { - parent.resize_gl(width, height); + try { + parent.resizeContext(width, height); + } catch(GLContextException ignored) {} } } diff --git a/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/WGLContextCreator.java b/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/WGLContextCreator.java index 6949956ccf..9551cef626 100644 --- a/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/WGLContextCreator.java +++ b/go.graphics.swing/src/main/java/go/graphics/swing/contextcreator/WGLContextCreator.java @@ -15,7 +15,6 @@ package go.graphics.swing.contextcreator; import org.lwjgl.opengl.GL; -import org.lwjgl.opengl.GLXARBCreateContext; import org.lwjgl.opengl.WGL; import org.lwjgl.opengl.WGLARBCreateContext; import org.lwjgl.opengl.WGLARBCreateContextProfile; @@ -35,39 +34,32 @@ public WGLContextCreator(GLContainer container, boolean debug) { } - private static final int[][] ctx_attrs = new int[][] { - { // GL2.0 with debugging - WGLARBCreateContext.WGL_CONTEXT_MAJOR_VERSION_ARB, 2, - WGLARBCreateContext.WGL_CONTEXT_MINOR_VERSION_ARB, 0, + private static final int[][][] ctx_attrs = new int[][][]{ + { + { // GL3.2+ with debugging + WGLARBCreateContext.WGL_CONTEXT_MAJOR_VERSION_ARB, 3, + WGLARBCreateContext.WGL_CONTEXT_MINOR_VERSION_ARB, 2, WGLARBCreateContextProfile.WGL_CONTEXT_PROFILE_MASK_ARB, WGLARBCreateContextProfile.WGL_CONTEXT_CORE_PROFILE_BIT_ARB, WGLARBCreateContext.WGL_CONTEXT_FLAGS_ARB, WGLARBCreateContext.WGL_CONTEXT_DEBUG_BIT_ARB, 0 }, - {// GL1.5 with debugging - WGLARBCreateContext.WGL_CONTEXT_MAJOR_VERSION_ARB, 1, - WGLARBCreateContext.WGL_CONTEXT_MINOR_VERSION_ARB, 5, - WGLARBCreateContext.WGL_CONTEXT_FLAGS_ARB, WGLARBCreateContext.WGL_CONTEXT_DEBUG_BIT_ARB, - 0 - }, - {// GL1.1+ with debugging - WGLARBCreateContext.WGL_CONTEXT_FLAGS_ARB, WGLARBCreateContext.WGL_CONTEXT_DEBUG_BIT_ARB, - 0 - }, - - { // GL2.0 - WGLARBCreateContext.WGL_CONTEXT_MAJOR_VERSION_ARB, 2, - WGLARBCreateContext.WGL_CONTEXT_MINOR_VERSION_ARB, 0, + { // GL3.2+ + WGLARBCreateContext.WGL_CONTEXT_MAJOR_VERSION_ARB, 3, + WGLARBCreateContext.WGL_CONTEXT_MINOR_VERSION_ARB, 2, WGLARBCreateContextProfile.WGL_CONTEXT_PROFILE_MASK_ARB, WGLARBCreateContextProfile.WGL_CONTEXT_CORE_PROFILE_BIT_ARB, 0 - }, - {// GL1.5 - WGLARBCreateContext.WGL_CONTEXT_MAJOR_VERSION_ARB, 1, - WGLARBCreateContext.WGL_CONTEXT_MINOR_VERSION_ARB, 5, - 0 + } + }, + + { + {// GL1.1+ with debugging + WGLARBCreateContext.WGL_CONTEXT_FLAGS_ARB, WGLARBCreateContext.WGL_CONTEXT_DEBUG_BIT_ARB, + 0 }, {// GL1.1+ - 0 - }, + 0 + } + }, }; @Override @@ -90,7 +82,7 @@ public void makeCurrent(boolean draw) { } @Override - protected void onNewConnection() { + protected void onNewConnection() throws GLContextException { PIXELFORMATDESCRIPTOR pfd = PIXELFORMATDESCRIPTOR.calloc(); pfd.dwFlags(GDI32.PFD_DRAW_TO_WINDOW | GDI32.PFD_SUPPORT_OPENGL | GDI32.PFD_DOUBLEBUFFER); pfd.iPixelType(GDI32.PFD_TYPE_RGBA); @@ -100,7 +92,7 @@ protected void onNewConnection() { pfd.cDepthBits((byte) 24); int pixel_format = GDI32.ChoosePixelFormat(windowDrawable, pfd); - if(pixel_format == 0) throw new Error("Could not find pixel format!"); + if(pixel_format == 0) error("Could not find pixel format!"); GDI32.SetPixelFormat(windowDrawable, pixel_format, pfd); pfd.free(); @@ -113,18 +105,16 @@ protected void onNewConnection() { WGL.wglDeleteContext(context); context = 0; - int i = debug ? 0 : 3; + int i = 0; while(context == 0 && ctx_attrs.length > i) { - context = WGLARBCreateContext.wglCreateContextAttribsARB(windowDrawable, 0, ctx_attrs[i]); - } - } else { - if(debug) { - WGL.wglDeleteContext(context); - throw new Error("WGL could not create a debug context!"); + context = WGLARBCreateContext.wglCreateContextAttribsARB(windowDrawable, 0, ctx_attrs[i++][debug?0:1]); } + } else if(debug) { + WGL.wglDeleteContext(context); + error("WGL could not create a debug context!"); } - if(context == 0) throw new Error("Could not create WGL context!"); + if(context == 0) error("Could not create WGL context!"); parent.wrapNewContext(); } } diff --git a/go.graphics.swing/src/main/java/go/graphics/swing/event/swingInterpreter/GOSwingEventConverter.java b/go.graphics.swing/src/main/java/go/graphics/swing/event/swingInterpreter/GOSwingEventConverter.java index 9c73659674..a291e1cd8b 100644 --- a/go.graphics.swing/src/main/java/go/graphics/swing/event/swingInterpreter/GOSwingEventConverter.java +++ b/go.graphics.swing/src/main/java/go/graphics/swing/event/swingInterpreter/GOSwingEventConverter.java @@ -103,7 +103,8 @@ private void updateScaleFactor(Component component) { } catch (NoSuchFieldException exception) { // if there is no Field scale then we have a scale factor of 1 // this is expected for Oracle JRE < 1.7.0_u40 - } catch (Exception exception) { + } catch (Throwable exception) { + // this is an illegal reflective operation but only modern java will actually check exception.printStackTrace(); } } diff --git a/go.graphics.swing/src/main/java/go/graphics/swing/opengl/LWJGL15DrawContext.java b/go.graphics.swing/src/main/java/go/graphics/swing/opengl/LWJGL15DrawContext.java deleted file mode 100644 index 4687844f90..0000000000 --- a/go.graphics.swing/src/main/java/go/graphics/swing/opengl/LWJGL15DrawContext.java +++ /dev/null @@ -1,347 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015-2018 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - *******************************************************************************/ -package go.graphics.swing.opengl; - -import org.lwjgl.BufferUtils; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL12; -import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GLCapabilities; - -import java.nio.ByteBuffer; -import java.nio.ShortBuffer; - -import go.graphics.AbstractColor; -import go.graphics.EGeometryFormatType; -import go.graphics.GLDrawContext; -import go.graphics.GeometryHandle; -import go.graphics.TextureHandle; -import go.graphics.swing.text.LWJGLTextDrawer; -import go.graphics.text.EFontSize; -import go.graphics.text.TextDrawer; - -import org.lwjgl.opengl.KHRDebug; -import org.lwjgl.system.MemoryStack; - -/** - * This is the draw context implementation for LWJGL. OpenGL draw calles are mapped to the corresponding LWJGL calls. - * - * @author Michael Zangl - * @author paul - * - */ -public class LWJGL15DrawContext implements GLDrawContext { - - private TextDrawer[] sizedTextDrawers = new TextDrawer[EFontSize.values().length]; - private LWJGLTextDrawer textDrawer = null; - protected LWJGLDebugOutput debugOutput = null; - - public final GLCapabilities glcaps; - - private GeometryHandle lastGeometry = null; - private TextureHandle lastTexture = null; - public LWJGL15DrawContext(GLCapabilities glcaps, boolean debug) { - this.glcaps = glcaps; - - if(debug) debugOutput = new LWJGLDebugOutput(this); - - GL11.glEnable(GL11.GL_BLEND); - GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - - GL11.glEnable(GL11.GL_DEPTH_TEST); - GL11.glDepthFunc(GL11.GL_LEQUAL); - - GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); - - init(); - } - - void init() { - GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY); - - GL11.glEnable(GL11.GL_ALPHA_TEST); - GL11.glAlphaFunc(GL11.GL_GREATER, 0.5f); - - GL11.glEnable(GL11.GL_TEXTURE_2D); - } - - private float lr, lg, lb, la = -1; - private float lx, ly, lz = -2; - private float lsx, lsy, lsz = -1; - - public void draw2D(GeometryHandle geometry, TextureHandle texture, int primitive, int offset, int vertices, float x, float y, float z, float sx, float sy, float sz, AbstractColor color, float intensity) { - if(lx != x || ly != y || lz != z || lsx != sx || lsy != sy || lsz != sz) { - if(lsz != -1) GL11.glPopMatrix(); - GL11.glPushMatrix(); - if(x != 0 || y != 0 || z != 0) GL11.glTranslatef(x, y, z); - if(sx != 1 || sy != 1 || sz != 1) GL11.glScalef(sx, sy, sz); - lx = x; lsx = sx; - ly = y; lsy = sy; - lz = z; lsz = sz; - } - - if(color != null) { - float r = color.red*intensity; - float g = color.green*intensity; - float b = color.blue*intensity; - float a = color.alpha; - if(lr != r || lg != g || lb != b || la != a) GL11.glColor4f(lr=r, lg=g, lb=b, la=a); - } else { - if(lr != lg || lr != lb || lr != intensity || la != 1) GL11.glColor4f(intensity, intensity, intensity, 1); - lr = lg = lb = intensity; - la = 1; - } - - bindTexture(texture); - bindGeometry(geometry); - EGeometryFormatType format = geometry.getFormat(); - - if(format.getTexCoordPos() == -1) GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY); - - specifyFormat(format); - GL11.glDrawArrays(primitive, offset * vertices, vertices); - - if(format.getTexCoordPos() == -1) GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY); - } - - private boolean tex_coord_on = false; - - protected void specifyFormat(EGeometryFormatType format) { - if (format.getTexCoordPos() == -1) { - if(tex_coord_on) GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY); - GL11.glVertexPointer(2, GL11.GL_FLOAT, 0, 0); - } else { - if(!tex_coord_on) GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY); - int stride = format.getBytesPerVertexSize(); - GL11.glVertexPointer(2, GL11.GL_FLOAT, stride, 0); - GL11.glTexCoordPointer(2, GL11.GL_FLOAT, stride, format.getTexCoordPos()); - } - - tex_coord_on = format.getTexCoordPos() != -1; - } - - /** - * The global context valid flag. As soon as this is set to false, the context is not valid any more. - */ - private boolean contextValid = true; - - public void setGlobalAttributes(float x, float y, float z, float sx, float sy, float sz) { - // reset matrix stack - if(lsz != -1) GL11.glPopMatrix(); - lsz = -1; - - GL11.glLoadIdentity(); - GL11.glTranslatef(x, y, z); - GL11.glScalef(sx, sy, sz); - } - - @Override - public TextureHandle generateTexture(int width, int height, ShortBuffer data, String name) { - int texture = GL11.glGenTextures(); - if (texture == 0) { - return null; - } - - //fix strange alpha test problem (minimap and landscape are unaffected) - ShortBuffer bfr = BufferUtils.createShortBuffer(data.capacity()); - int cap = data.capacity(); - for(int i = 0;i != cap;i++) bfr.put(i, data.get(i)); - - TextureHandle textureHandle = new TextureHandle(this, texture); - bindTexture(textureHandle); - GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, - GL11.GL_RGBA, GL12.GL_UNSIGNED_SHORT_4_4_4_4, bfr); - setTextureParameters(); - - setObjectLabel(GL11.GL_TEXTURE, texture, name + "-tex"); - - return textureHandle; - } - - /** - * Sets the texture parameters, assuming that the texture was just created and is bound. - */ - private void setTextureParameters() { - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, - GL11.GL_CLAMP); - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, - GL11.GL_CLAMP); - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, - GL11.GL_NEAREST); - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, - GL11.GL_NEAREST); - } - - @Override - public void updateTexture(TextureHandle texture, int left, int bottom, - int width, int height, ShortBuffer data) { - bindTexture(texture); - GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, left, bottom, width, height, - GL11.GL_RGBA, GL12.GL_UNSIGNED_SHORT_4_4_4_4, data); - } - - protected void bindTexture(TextureHandle texture) { - if(lastTexture != texture) { - int id = 0; - if (texture != null) { - id = texture.getInternalId(); - } - GL11.glBindTexture(GL11.GL_TEXTURE_2D, id); - lastTexture = texture; - } - } - - protected void bindGeometry(GeometryHandle geometry) { - if(lastGeometry != geometry) { - int id = 0; - if (geometry != null) { - id = geometry.getInternalId(); - } - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, id); - lastGeometry = geometry; - } - } - - private float[] heightMatrix; - - @Override - public void setHeightMatrix(float[] matrix) { - heightMatrix = matrix; - } - - /** - * Gets a text drawer for the given text size. - * - * @param size - * The size for the drawer. - * @return An instance of a drawer for that size. - */ - @Override - public TextDrawer getTextDrawer(EFontSize size) { - if(textDrawer == null) textDrawer = new LWJGLTextDrawer(this); - - if (sizedTextDrawers[size.ordinal()] == null) { - sizedTextDrawers[size.ordinal()] = textDrawer.derive(size); - } - return sizedTextDrawers[size.ordinal()]; - } - - public void drawTrianglesWithTextureColored(TextureHandle textureid, GeometryHandle shapeHandle, GeometryHandle colorHandle, int offset, int lines, int width, int stride, float x, float y) { - bindTexture(textureid); - int starti = offset < 0 ? (int)Math.ceil(-offset/(float)stride) : 0; - - if(lsz != -1) GL11.glPopMatrix(); - GL11.glPushMatrix(); - GL11.glTranslatef(x, y, -.1f); - GL11.glScalef(1, 1, 0); - GL11.glMultMatrixf(heightMatrix); - - if(!tex_coord_on) { - GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY); - tex_coord_on = true; - } - - bindGeometry(shapeHandle); - GL11.glVertexPointer(3, GL11.GL_FLOAT, 5 * 4, 0); - GL11.glTexCoordPointer(2, GL11.GL_FLOAT, 5 * 4, 3 * 4); - - bindGeometry(colorHandle); - GL11.glColorPointer(4, GL11.GL_UNSIGNED_BYTE, 0, 0); - - GL11.glEnableClientState(GL11.GL_COLOR_ARRAY); - for (int i = starti; i != lines; i++) { - GL11.glDrawArrays(GL11.GL_TRIANGLES, (offset + stride * i) * 3, width * 3); - } - GL11.glDisableClientState(GL11.GL_COLOR_ARRAY); - - GL11.glPopMatrix(); - lsz = -1; - } - - @Override - public void updateGeometryAt(GeometryHandle handle, int pos, ByteBuffer data) { - bindGeometry(handle); - data.rewind(); - GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, pos, data); - } - - @Override - public GeometryHandle storeGeometry(float[] geometry, EGeometryFormatType type, boolean writable, String name) { - GeometryHandle geometryBuffer = allocateVBO(type, name); - - bindGeometry(geometryBuffer); - try(MemoryStack stack = MemoryStack.stackPush()) { - ByteBuffer bfr = stack.malloc(4*geometry.length); - bfr.asFloatBuffer().put(geometry); - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, bfr, writable ? GL15.GL_DYNAMIC_DRAW : GL15.GL_STATIC_DRAW); - setObjectLabel(KHRDebug.GL_BUFFER, geometryBuffer.getInternalId(), name + "-vertices"); - } - - return geometryBuffer; - } - - @Override - public GeometryHandle generateGeometry(int vertices, EGeometryFormatType type, boolean writable, String name) { - GeometryHandle vertexBufferId = allocateVBO(type, name); - - bindGeometry(vertexBufferId); - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertices*type.getBytesPerVertexSize(), writable ? GL15.GL_DYNAMIC_DRAW : GL15.GL_STATIC_DRAW); - setObjectLabel(KHRDebug.GL_BUFFER, vertexBufferId.getInternalId(), name + "-vertices"); - return vertexBufferId; - } - - GeometryHandle allocateVBO(EGeometryFormatType type, String name) { - int vbo = GL15.glGenBuffers(); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); - - return lastGeometry = new GeometryHandle(this, vbo, 0, type); - } - - protected void setObjectLabel(int type, int id, String name) { - if(debugOutput == null) return; - - if(glcaps.GL_KHR_debug) { - KHRDebug.glObjectLabel(type, id, name); - } - } - - @Override - public void deleteTexture(TextureHandle textureHandle) { - GL11.glDeleteTextures(textureHandle.getInternalId()); - } - - /** - * Called whenever we should dispose all buffers associated with this context. - */ - public void disposeAll() { - contextValid = false; - } - - public boolean isValid() { - return contextValid; - } - - public void resize(int width, int height) { - GL11.glMatrixMode(GL11.GL_PROJECTION); - GL11.glLoadIdentity(); - // coordinate system origin at lower left with width and height same as - // the window - GL11.glOrtho(0, width, 0, height, -1, 1); - - GL11.glMatrixMode(GL11.GL_MODELVIEW); - GL11.glLoadIdentity(); - GL11.glViewport(0, 0, width, height); - } -} diff --git a/go.graphics.swing/src/main/java/go/graphics/swing/opengl/LWJGL20DrawContext.java b/go.graphics.swing/src/main/java/go/graphics/swing/opengl/LWJGL20DrawContext.java deleted file mode 100644 index 5f11d97812..0000000000 --- a/go.graphics.swing/src/main/java/go/graphics/swing/opengl/LWJGL20DrawContext.java +++ /dev/null @@ -1,366 +0,0 @@ -package go.graphics.swing.opengl; - -import org.joml.Matrix4f; -import org.lwjgl.BufferUtils; -import org.lwjgl.opengl.ARBVertexArrayObject; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL20; -import org.lwjgl.opengl.GLCapabilities; -import org.lwjgl.opengl.KHRDebug; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.FloatBuffer; -import java.util.ArrayList; - -import go.graphics.AbstractColor; -import go.graphics.EGeometryFormatType; -import go.graphics.GL2DrawContext; -import go.graphics.GeometryHandle; -import go.graphics.TextureHandle; - -public class LWJGL20DrawContext extends LWJGL15DrawContext implements GL2DrawContext{ - public LWJGL20DrawContext(GLCapabilities glcaps, boolean debug) { - super(glcaps, debug); - global.identity(); - - } - - private String[] uniform_names; - private ArrayList shaders; - - private final Matrix4f global = new Matrix4f(); - private final Matrix4f mat = new Matrix4f(); - private final FloatBuffer matBfr = BufferUtils.createFloatBuffer(16); - - @Override - void init() { - uniform_names = new String[] {"projection", "globalTransform", "transform", "texHandle", "color", "height", "uni_info"}; - shaders = new ArrayList<>(); - - prog_background = new ShaderProgram("background"); - prog_unified = new ShaderProgram("tex-unified"); - prog_color = new ShaderProgram("color"); - prog_tex = new ShaderProgram("tex"); - - for(ShaderProgram shader : shaders) { - useProgram(shader); - if(shader.ufs[TEX] != -1) GL20.glUniform1i(shader.ufs[TEX], 0); - } - } - - private ShaderProgram lastProgram = null; - private void useProgram(ShaderProgram id) { - if(id != lastProgram) { - GL20.glUseProgram(id.program); - lastProgram = id; - } - } - - private ShaderProgram prog_background; - private ShaderProgram prog_unified; - private ShaderProgram prog_color; - private ShaderProgram prog_tex; - - private float clr, clg, clb, cla, tlr, tlg, tlb, tla; - - @Override - public void draw2D(GeometryHandle geometry, TextureHandle texture, int primitive, int offset, int vertices, float x, float y, float z, float sx, float sy, float sz, AbstractColor color, float intensity){ - boolean changeColor = false; - - float r, g, b, a; - if(color != null) { - r = color.red*intensity; - g = color.green*intensity; - b = color.blue*intensity; - a = color.alpha; - } else { - r = g = b = intensity; - a = 1; - } - - if(texture == null) { - useProgram(prog_color); - if(clr != r || clg != g || clb != b || cla != a) { - clr = r; - clg = g; - clb = b; - cla = a; - changeColor = true; - } - } else { - bindTexture(texture); - useProgram(prog_tex); - if(tlr != r || tlg != g || tlb != b || tla != a) { - tlr = r; - tlg = g; - tlb = b; - tla = a; - changeColor = true; - } - } - - GL20.glUniform3fv(lastProgram.ufs[TRANS], new float[] {x, y, z, sx, sy, sz}); - - if(changeColor) { - GL20.glUniform4f(lastProgram.ufs[COLOR], r, g, b, a); - } - - if(glcaps.GL_ARB_vertex_array_object) { - bindFormat(geometry.getInternalFormatId()); - } else { - bindGeometry(geometry); - specifyFormat(geometry.getFormat()); - } - GL11.glDrawArrays(primitive, offset*vertices, vertices); - } - - private float ulr, ulg, ulb, ula, uli; - private boolean ulim, ulsh; - - @Override - public void drawUnified2D(GeometryHandle geometry, TextureHandle texture, int primitive, int offset, int vertices, boolean image, boolean shadow, float x, float y, float z, float sx, float sy, float sz, AbstractColor color, float intensity) { - useProgram(prog_unified); - bindTexture(texture); - - if(image) { - float r, g, b, a; - if (color != null) { - r = color.red * intensity; - g = color.green * intensity; - b = color.blue * intensity; - a = color.alpha; - } else { - r = g = b = intensity; - a = 1; - } - - if(ulr != r || ulg != g || ulb != b || ula != a) { - ulr = r; - ulg = g; - ulb = b; - ula = a; - GL20.glUniform4f(prog_unified.ufs[COLOR], r, g, b, a); - } - } - - if(ulim != image || ulsh != shadow || uli != intensity) { - GL20.glUniform3f(prog_unified.ufs[UNI_INFO], image?1:0, shadow?1:0, intensity); - ulim = image; - ulsh = shadow; - uli = intensity; - } - - GL20.glUniform3fv(lastProgram.ufs[TRANS], new float[] {x, y, z, sx, sy, sz}); - - if(glcaps.GL_ARB_vertex_array_object) { - bindFormat(geometry.getInternalFormatId()); - } else { - bindGeometry(geometry); - specifyFormat(geometry.getFormat()); - } - GL11.glDrawArrays(primitive, offset*vertices, vertices); - } - - @Override - protected void specifyFormat(EGeometryFormatType format) { - GL20.glEnableVertexAttribArray(0); - - if (format.getTexCoordPos() == -1) { - GL20.glVertexAttribPointer(0, 2, GL11.GL_FLOAT, false, 0, 0); - } else { - GL20.glEnableVertexAttribArray(1); - int stride = format.getBytesPerVertexSize(); - GL20.glVertexAttribPointer(0, 2, GL11.GL_FLOAT, false, stride, 0); - GL20.glVertexAttribPointer(1, 2, GL11.GL_FLOAT, false, stride, format.getTexCoordPos()); - } - } - - private int lastFormat = 0; - protected void bindFormat(int format) { - if(format != lastFormat) { - ARBVertexArrayObject.glBindVertexArray(format); - lastFormat = format; - } - } - - @Override - GeometryHandle allocateVBO(EGeometryFormatType type, String name) { - GeometryHandle geometry = super.allocateVBO(type, name); - if (glcaps.GL_ARB_vertex_array_object && type.isSingleBuffer()) { - geometry.setInternalFormatId(ARBVertexArrayObject.glGenVertexArrays()); - bindFormat(geometry.getInternalFormatId()); - - specifyFormat(type); - } - - if(type.isSingleBuffer()) { - setObjectLabel(GL11.GL_VERTEX_ARRAY, geometry.getInternalFormatId(), name + "-vao"); - } - - return geometry; - } - - @Override - public void setGlobalAttributes(float x, float y, float z, float sx, float sy, float sz) { - global.identity(); - global.translate(x, y, z); - global.scale(sx, sy, sz); - global.get(matBfr); - - for(ShaderProgram shader : shaders) { - useProgram(shader); - GL20.glUniformMatrix4fv(shader.ufs[GLOBAL], false, matBfr); - } - } - - @Override - public void resize(int width, int height) { - GL11.glViewport(0, 0, width, height); - - mat.identity(); - mat.ortho(0, width, 0, height, -1, 1); - mat.get(matBfr); - - for(ShaderProgram shader : shaders) { - useProgram(shader); - GL20.glUniformMatrix4fv(shader.ufs[PROJ], false, matBfr); - } - } - - @Override - public void setHeightMatrix(float[] matrix) { - useProgram(prog_background); - GL20.glUniformMatrix4fv(prog_background.ufs[HEIGHT], false, matrix); - } - - private int backgroundVAO = -1; - - @Override - public void drawTrianglesWithTextureColored(TextureHandle textureid, GeometryHandle shapeHandle, GeometryHandle colorHandle, int offset, int lines, int width, int stride, float x, float y) { - bindTexture(textureid); - - if(backgroundVAO == -1) { - if(glcaps.GL_ARB_vertex_array_object) { - backgroundVAO = ARBVertexArrayObject.glGenVertexArrays(); - bindFormat(backgroundVAO); - } - GL20.glEnableVertexAttribArray(0); - GL20.glEnableVertexAttribArray(1); - GL20.glEnableVertexAttribArray(2); - - bindGeometry(shapeHandle); - GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 5 * 4, 0); - GL20.glVertexAttribPointer(1, 2, GL11.GL_FLOAT, false, 5 * 4, 3 * 4); - - bindGeometry(colorHandle); - GL20.glVertexAttribPointer(2, 1, GL11.GL_FLOAT, false, 0, 0); - - setObjectLabel(GL11.GL_VERTEX_ARRAY, backgroundVAO, "background-vao"); - setObjectLabel(KHRDebug.GL_BUFFER, shapeHandle.getInternalId(), "background-shape"); - setObjectLabel(KHRDebug.GL_BUFFER, colorHandle.getInternalId(), "background-color"); - } - int starti = offset < 0 ? (int)Math.ceil(-offset/(float)stride) : 0; - - useProgram(prog_background); - - GL20.glUniform2f(prog_background.ufs[TRANS], x, y); - - bindFormat(backgroundVAO); - for (int i = starti; i != lines; i++) { - GL11.glDrawArrays(GL11.GL_TRIANGLES, (offset + stride * i) * 3, width * 3); - } - } - private static final int PROJ = 0; - private static final int GLOBAL = 1; - private static final int TRANS = 2; - private static final int TEX = 3; - private static final int COLOR = 4; - private static final int HEIGHT = 5; - private static final int UNI_INFO = 6; - - private class ShaderProgram { - public final int program; - public final int[] ufs = new int[7]; - - private ShaderProgram(String name) { - int vertexShader = -1; - int fragmentShader; - - String vname = name; - if(name.contains("-")) vname = name.split("-")[0]; - - try { - vertexShader = createShader(vname+".vert", GL20.GL_VERTEX_SHADER); - fragmentShader = createShader(name+".frag", GL20.GL_FRAGMENT_SHADER); - } catch (IOException e) { - e.printStackTrace(); - - if(vertexShader != -1) GL20.glDeleteShader(vertexShader); - throw new Error("could not read shader files", e); - } - - program = GL20.glCreateProgram(); - setObjectLabel(KHRDebug.GL_PROGRAM, program, name); - - GL20.glAttachShader(program, vertexShader); - GL20.glAttachShader(program, fragmentShader); - - GL20.glBindAttribLocation(program, 0, "vertex"); - GL20.glBindAttribLocation(program, 1, "texcoord"); - GL20.glBindAttribLocation(program, 2, "color"); - - GL20.glLinkProgram(program); - GL20.glValidateProgram(program); - - GL20.glDetachShader(program, vertexShader); - GL20.glDetachShader(program, fragmentShader); - GL20.glDeleteShader(vertexShader); - GL20.glDeleteShader(fragmentShader); - - String log = GL20.glGetProgramInfoLog(program); - if(debugOutput != null && !log.isEmpty()) System.out.print("info log of " + name + "=====\n" + log + "==== end\n"); - - if(GL20.glGetProgrami(program, GL20.GL_LINK_STATUS) == 0) { - - GL20.glDeleteProgram(program); - throw new Error("Could not link " + name); - } - - for(int i = 0;i != ufs.length;i++) { - int uf = GL20.glGetUniformLocation(program, uniform_names[i]); - ufs[i] = uf; - } - shaders.add(this); - } - - private int createShader(String name, int type) throws IOException { - int shader = GL20.glCreateShader(type); - setObjectLabel(KHRDebug.GL_SHADER, shader, name); - - BufferedReader is = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("/"+name))); - StringBuilder source = new StringBuilder(); - String line; - - while((line = is.readLine()) != null) { - source.append(line).append("\n"); - } - - GL20.glShaderSource(shader, source); - GL20.glCompileShader(shader); - - - String log = GL20.glGetShaderInfoLog(shader); - if(debugOutput != null && !log.isEmpty()) System.out.print("info log of " + name + "=====\n" + log + "==== end\n"); - - if(GL20.glGetShaderi(shader, GL20.GL_COMPILE_STATUS) == 0) { - - GL20.glDeleteShader(shader); - throw new Error("Could not compile " + name); - } - - return shader; - } - } -} diff --git a/go.graphics.swing/src/main/java/go/graphics/swing/opengl/LWJGLDebugOutput.java b/go.graphics.swing/src/main/java/go/graphics/swing/opengl/LWJGLDebugOutput.java index 183db37726..3aa8240ca2 100644 --- a/go.graphics.swing/src/main/java/go/graphics/swing/opengl/LWJGLDebugOutput.java +++ b/go.graphics.swing/src/main/java/go/graphics/swing/opengl/LWJGLDebugOutput.java @@ -1,23 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2019 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *******************************************************************************/ package go.graphics.swing.opengl; -import org.lwjgl.opengl.ARBDebugOutput; -import org.lwjgl.opengl.GL11; +import static org.lwjgl.opengl.GL20C.*; +import static org.lwjgl.opengl.ARBDebugOutput.*; +import static org.lwjgl.opengl.KHRDebug.*; import org.lwjgl.opengl.GLDebugMessageARBCallback; import org.lwjgl.opengl.GLDebugMessageARBCallbackI; import org.lwjgl.opengl.GLDebugMessageCallbackI; -import org.lwjgl.opengl.KHRDebug; import java.util.HashMap; public class LWJGLDebugOutput { - LWJGLDebugOutput(LWJGL15DrawContext dc) { + LWJGLDebugOutput(LWJGLDrawContext dc) { + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); if(dc.glcaps.GL_KHR_debug) { - GL11.glEnable(KHRDebug.GL_DEBUG_OUTPUT_SYNCHRONOUS); - KHRDebug.glDebugMessageCallback(debugCallback, 0); + glDebugMessageCallback(debugCallback, 0); } else if(dc.glcaps.GL_ARB_debug_output) { - GL11.glEnable(ARBDebugOutput.GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); - ARBDebugOutput.glDebugMessageCallbackARB(debugCallbackARB, 0); + glDebugMessageCallbackARB(debugCallbackARB, 0); } } @@ -44,7 +57,7 @@ private static void writeMessage(String msg) { private static void debugMessage(int source, int type, int id, int severity, int length, long message) { String msg = GLDebugMessageARBCallback.getMessage(length, message); - if(lastId == id && lastType == type && lastSource == source && lastSeverity == severity && lastMessageCount != Long.MAX_VALUE) { + if(lastId == id && lastType == type && lastSource == source && lastSeverity == severity && lastMessageCount != Long.MAX_VALUE && lastHeader!=null) { if(lastMessageCount < MAX_PRINT_MESSAGES) writeMessage(msg); lastMessageCount++; } else { @@ -64,20 +77,20 @@ private static void debugMessage(int source, int type, int id, int severity, int private static final HashMap debugEnum = new HashMap<>(); static { - debugEnum.put(KHRDebug.GL_DEBUG_TYPE_ERROR, "ERROR"); - debugEnum.put(KHRDebug.GL_DEBUG_TYPE_OTHER, "OTHER"); - debugEnum.put(KHRDebug.GL_DEBUG_TYPE_PERFORMANCE, "PERF "); - debugEnum.put(KHRDebug.GL_DEBUG_TYPE_PORTABILITY, "PORT "); - debugEnum.put(KHRDebug.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR, "NDEF "); - debugEnum.put(KHRDebug.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, "DEPRE"); + debugEnum.put(GL_DEBUG_TYPE_ERROR, "ERROR"); + debugEnum.put(GL_DEBUG_TYPE_OTHER, "OTHER"); + debugEnum.put(GL_DEBUG_TYPE_PERFORMANCE, "PERF "); + debugEnum.put(GL_DEBUG_TYPE_PORTABILITY, "PORT "); + debugEnum.put(GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR, "NDEF "); + debugEnum.put(GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, "DEPRE"); - debugEnum.put(KHRDebug.GL_DEBUG_SOURCE_API, "API"); - debugEnum.put(KHRDebug.GL_DEBUG_SOURCE_SHADER_COMPILER, "SHADER_COMPILER"); - debugEnum.put(KHRDebug.GL_DEBUG_SOURCE_WINDOW_SYSTEM, "WINDOW_SYSTEM"); - debugEnum.put(KHRDebug.GL_DEBUG_SEVERITY_HIGH, "HIGH"); - debugEnum.put(KHRDebug.GL_DEBUG_SEVERITY_MEDIUM, "MEDIUM"); - debugEnum.put(KHRDebug.GL_DEBUG_SEVERITY_LOW, "LOW"); - debugEnum.put(KHRDebug.GL_DEBUG_SEVERITY_NOTIFICATION, "NOTIFICATION"); + debugEnum.put(GL_DEBUG_SOURCE_API, "API"); + debugEnum.put(GL_DEBUG_SOURCE_SHADER_COMPILER, "SHADER_COMPILER"); + debugEnum.put(GL_DEBUG_SOURCE_WINDOW_SYSTEM, "WINDOW_SYSTEM"); + debugEnum.put(GL_DEBUG_SEVERITY_HIGH, "HIGH"); + debugEnum.put(GL_DEBUG_SEVERITY_MEDIUM, "MEDIUM"); + debugEnum.put(GL_DEBUG_SEVERITY_LOW, "LOW"); + debugEnum.put(GL_DEBUG_SEVERITY_NOTIFICATION, "NOTIFICATION"); } private static String S(int type) { diff --git a/go.graphics.swing/src/main/java/go/graphics/swing/opengl/LWJGLDrawContext.java b/go.graphics.swing/src/main/java/go/graphics/swing/opengl/LWJGLDrawContext.java new file mode 100644 index 0000000000..0858372a54 --- /dev/null +++ b/go.graphics.swing/src/main/java/go/graphics/swing/opengl/LWJGLDrawContext.java @@ -0,0 +1,598 @@ +package go.graphics.swing.opengl; + +import org.joml.Matrix4f; +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.ARBInstancedArrays; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GLCapabilities; +import org.lwjgl.opengl.KHRDebug; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.ShortBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java8.util.function.Supplier; + +import go.graphics.AbstractColor; +import go.graphics.BackgroundDrawHandle; +import go.graphics.GLDrawContext; +import go.graphics.BufferHandle; +import go.graphics.ManagedHandle; +import go.graphics.MultiDrawHandle; +import go.graphics.TextureHandle; +import go.graphics.UnifiedDrawHandle; +import go.graphics.swing.text.LWJGLTextDrawer; + +import static org.lwjgl.opengl.ARBDrawInstanced.*; +import static org.lwjgl.opengl.ARBVertexArrayObject.*; +import static org.lwjgl.opengl.ARBUniformBufferObject.*; +import static org.lwjgl.opengl.GL20C.*; + +public class LWJGLDrawContext extends GLDrawContext { + + private Supplier nativeScale; + + public LWJGLDrawContext(GLCapabilities glcaps, Supplier nativeScale, boolean debug, float guiScale) { + this.nativeScale = nativeScale; + this.glcaps = glcaps; + shaders = new ArrayList<>(); + + if(debug) debugOutput = new LWJGLDebugOutput(this); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + if(glcaps.GL_ARB_instanced_arrays && glcaps.GL_ARB_uniform_buffer_object) { + prog_unified_multi = new ShaderProgram("unified-multi"); + } + if(glcaps.GL_EXT_draw_instanced) prog_unified_array = new ShaderProgram("unified-array"); + prog_background = new ShaderProgram("background"); + prog_unified = new ShaderProgram("unified"); + + textDrawer = new LWJGLTextDrawer(this, guiScale); + } + + private ArrayList shaders; + + private final Matrix4f global = new Matrix4f(); + private final Matrix4f mat = new Matrix4f(); + private final FloatBuffer matBfr = BufferUtils.createFloatBuffer(16); + private LWJGLDebugOutput debugOutput = null; + + final GLCapabilities glcaps; + + private BufferHandle lastGeometry = null; + private TextureHandle lastTexture = null; + + private ShaderProgram lastProgram = null; + private void useProgram(ShaderProgram id) { + if(id != lastProgram) { + glUseProgram(id.program); + lastProgram = id; + } + } + + private ShaderProgram prog_unified_multi = null; + private ShaderProgram prog_unified_array = null; + private ShaderProgram prog_background; + private ShaderProgram prog_unified; + + private float ulr, ulg, ulb, ula, uli; + private float ulm; + + + public TextureHandle generateTexture(int width, int height, ShortBuffer data, String name) { + int texture = glGenTextures(); + if (texture == 0) { + return null; + } + + TextureHandle textureHandle = new TextureHandle(this, texture); + resizeTexture(textureHandle, width, height, data); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + setObjectLabel(GL11.GL_TEXTURE, texture, name + "-tex"); + + return textureHandle; + } + + public void resizeTexture(TextureHandle textureIndex, int width, int height, ShortBuffer data) { + bindTexture(textureIndex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); + } + + public void updateTexture(TextureHandle texture, int left, int bottom, + int width, int height, ShortBuffer data) { + bindTexture(texture); + glTexSubImage2D(GL_TEXTURE_2D, 0, left, bottom, width, height, + GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); + } + + private void bindTexture(TextureHandle texture) { + if(lastTexture != texture) { + int id = 0; + if (texture != null) { + id = texture.getTextureId(); + } + glBindTexture(GL_TEXTURE_2D, id); + lastTexture = texture; + } + } + + private void bindGeometry(BufferHandle geometry) { + if(lastGeometry != geometry) { + int id = 0; + if (geometry != null) { + id = geometry.getBufferId(); + } + glBindBuffer(GL_ARRAY_BUFFER, id); + lastGeometry = geometry; + } + } + + private int lastFormat = 0; + private void bindFormat(int format) { + if(format != lastFormat) { + glBindVertexArray(format); + lastFormat = format; + } + } + + public void updateBufferAt(BufferHandle handle, int pos, ByteBuffer data) { + bindGeometry(handle); + glBufferSubData(GL_ARRAY_BUFFER, pos, data); + } + + private void setObjectLabel(int type, int id, String name) { + if(debugOutput != null && glcaps.GL_KHR_debug) { + KHRDebug.glObjectLabel(type, id, name); + } + } + + public void setGlobalAttributes(float x, float y, float z, float sx, float sy, float sz) { + finishFrame(); + + global.identity(); + global.scale(sx, sy, sz); + global.translate(x, y, z); + global.get(matBfr); + + for(ShaderProgram shader : shaders) { + useProgram(shader); + glUniformMatrix4fv(shader.global, false, matBfr); + } + } + + public void resize(int width, int height) { + float scale = nativeScale.get(); + + glViewport(0, 0, (int)(width*scale), (int)(height*scale)); + mat.setOrtho(0, width, 0, height, -1, 1); + mat.get(matBfr); + + for(ShaderProgram shader : shaders) { + useProgram(shader); + glUniformMatrix4fv(shader.proj, false, matBfr); + } + } + + + public void setShadowDepthOffset(float depth) { + for(ShaderProgram shader : shaders) { + if(shader.shadow_depth != -1) { + useProgram(shader); + glUniform1f(shader.shadow_depth, depth); + + } + } + } + + + public void setHeightMatrix(float[] matrix) { + useProgram(prog_background); + glUniformMatrix4fv(prog_background.height, false, matrix); + } + + @Override + public BackgroundDrawHandle createBackgroundDrawCall(int vertices, TextureHandle texture) { + int vao = -1; + + if(glcaps.GL_ARB_vertex_array_object) vao = glGenVertexArrays(); + + BufferHandle vertexBuffer = new BufferHandle(this, glGenBuffers()); + BufferHandle colorBuffer = new BufferHandle(this, glGenBuffers()); + + bindGeometry(vertexBuffer); + setObjectLabel(KHRDebug.GL_BUFFER, vertexBuffer.getBufferId(), "background-shape"); + glBufferData(GL_ARRAY_BUFFER, vertices*5*4, GL_DYNAMIC_DRAW); + bindGeometry(colorBuffer); + setObjectLabel(KHRDebug.GL_BUFFER, colorBuffer.getBufferId(), "background-color"); + glBufferData(GL_ARRAY_BUFFER, vertices*4, GL_DYNAMIC_DRAW); + + BackgroundDrawHandle handle = new BackgroundDrawHandle(this, vao, texture, vertexBuffer, colorBuffer); + + if(glcaps.GL_ARB_vertex_array_object) { + bindFormat(vao); + setObjectLabel(GL_VERTEX_ARRAY, vao, "background-vao"); + fillBackgroundFormat(handle); + } + + return handle; + } + + @Override + public UnifiedDrawHandle createUnifiedDrawCall(int vertices, String name, TextureHandle texture, float[] data) { + int vao = -1; + + if(glcaps.GL_ARB_vertex_array_object) vao = glGenVertexArrays(); + + BufferHandle vertexBuffer = new BufferHandle(this, glGenBuffers()); + + bindGeometry(vertexBuffer); + setObjectLabel(KHRDebug.GL_BUFFER, vertexBuffer.getBufferId(), name + "-vertices"); + if(data != null) { + glBufferData(GL_ARRAY_BUFFER, data, GL_STATIC_DRAW); + } else { + glBufferData(GL_ARRAY_BUFFER, vertices*(texture!=null?4:2)*4, GL_DYNAMIC_DRAW); + } + + UnifiedDrawHandle handle = new UnifiedDrawHandle(this, vao, 0, vertices, texture, vertexBuffer); + + if(glcaps.GL_ARB_vertex_array_object) { + bindFormat(vao); + setObjectLabel(GL_VERTEX_ARRAY, vao, name + "-vao"); + fillUnifiedFormat(handle); + } + + return handle; + } + + @Override + protected MultiDrawHandle createMultiDrawCall(String name, ManagedHandle source) { + if(prog_unified_multi == null) return null; + + int vao = -1; + + if(glcaps.GL_ARB_vertex_array_object) vao = glGenVertexArrays(); + + BufferHandle drawCalls = new BufferHandle(this, glGenBuffers()); + + bindGeometry(drawCalls); + setObjectLabel(KHRDebug.GL_BUFFER, drawCalls.getBufferId(), name + "-drawcalls"); + glBufferData(GL_ARRAY_BUFFER, MultiDrawHandle.MAX_CACHE_ENTRIES*12*4, GL_STREAM_DRAW); + + MultiDrawHandle handle = new MultiDrawHandle(this, vao, MultiDrawHandle.MAX_CACHE_ENTRIES, source, drawCalls); + + if(glcaps.GL_ARB_vertex_array_object) { + bindFormat(vao); + setObjectLabel(GL_VERTEX_ARRAY, vao, name + "-vao"); + fillMultiFormat(handle); + } + + return handle; + } + + private void fillBackgroundFormat(BackgroundDrawHandle dh) { + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + + bindGeometry(dh.vertices); + glVertexAttribPointer(0, 3, GL_FLOAT, false, 5 * 4, 0); + glVertexAttribPointer(1, 2, GL_FLOAT, false, 5 * 4, 3 * 4); + + bindGeometry(dh.colors); + glVertexAttribPointer(2, 1, GL_FLOAT, false, 0, 0); + } + + private void fillUnifiedFormat(UnifiedDrawHandle uh) { + bindGeometry(uh.vertices); + glEnableVertexAttribArray(0); + + if(uh.texture!=null) { + glEnableVertexAttribArray(1); + + glVertexAttribPointer(0, 2, GL_FLOAT, false, 4 * 4, 0); + glVertexAttribPointer(1, 2, GL_FLOAT, false, 4 * 4, 2 * 4); + } else { + glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0); + } + } + + private void fillMultiFormat(MultiDrawHandle mh) { + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + + ARBInstancedArrays.glVertexAttribDivisorARB(0, 1); + ARBInstancedArrays.glVertexAttribDivisorARB(1, 1); + ARBInstancedArrays.glVertexAttribDivisorARB(2, 1); + ARBInstancedArrays.glVertexAttribDivisorARB(3, 1); + + bindGeometry(mh.drawCalls); + glVertexAttribPointer(0, 3, GL_FLOAT, false, 12*4, 0); + glVertexAttribPointer(1, 2, GL_FLOAT, false, 12*4, 3*4); + glVertexAttribPointer(2, 4, GL_FLOAT, false, 12*4, 5*4); + glVertexAttribPointer(3, 3, GL_FLOAT, false, 12*4, 9*4); + } + + private boolean[] vertArrays = new boolean[4]; + + private void enableVertArrays(boolean... vertArrays) { + for(int i = 0;i != vertArrays.length; i++) { + if(vertArrays[i] != this.vertArrays[i]) { + if(vertArrays[i]) { + glEnableVertexAttribArray(i); + } else { + glDisableVertexAttribArray(i); + } + } + } + + this.vertArrays = vertArrays; + } + + protected void drawMulti(MultiDrawHandle call) { + bindTexture(call.sourceQuads.texture); + + if(call.getVertexArrayId() != -1) { + bindFormat(call.getVertexArrayId()); + } else { + enableVertArrays(true, true, true, true); + fillMultiFormat(call); + } + + useProgram(prog_unified_multi); + + glBindBufferBase(GL_UNIFORM_BUFFER, 0, call.sourceQuads.vertices.getBufferId()); + + glDrawArraysInstancedARB(GL_TRIANGLE_FAN, 0, 4, call.used); + } + + public void drawUnifiedArray(UnifiedDrawHandle call, int primitive, int vertexCount, float[] trans, float[] colors, int array_len) { + if(call.texture != null) bindTexture(call.texture); + + if(call.getVertexArrayId() != -1) { + bindFormat(call.getVertexArrayId()); + } else { + enableVertArrays(true, call.texture!=null, false, false); + fillUnifiedFormat(call); + } + + if(prog_unified_array != null) { + useProgram(prog_unified_array); + + glUniform4fv(prog_unified_array.color, colors); + glUniform4fv(prog_unified_array.trans, trans); + + glDrawArraysInstancedARB(primitive, call.offset, vertexCount, array_len); + } else { + useProgram(prog_unified); + + for (int i = 0; i != array_len; i++) { + + float int_mode = trans[i*4+3]/10; + int mode = (int) Math.floor(int_mode); + float intensity = (int_mode-mode)*10-1; + + glUniform1i(prog_unified.mode, mode); + glUniform1fv(prog_unified.color, new float[] {colors[i*4], colors[i*4+1], colors[i*4+2], colors[i*4+3], intensity}); + glUniform3fv(prog_unified.trans, new float[] {trans[i*4], trans[i*4+1], trans[i*4+2], 1, 1, 0}); + + glDrawArrays(primitive, call.offset, vertexCount); + } + + ulr = -1; + ulm = -1; + } + } + + @Override + public void drawUnified(UnifiedDrawHandle call, int primitive, int count, int mode, float x, float y, float z, float sx, float sy, AbstractColor color, float intensity) { + if(call.texture != null) bindTexture(call.texture); + useProgram(prog_unified); + + if(call.getVertexArrayId() != -1) { + bindFormat(call.getVertexArrayId()); + } else { + enableVertArrays(true, call.texture!=null, false, false); + fillUnifiedFormat(call); + } + + float r, g, b, a; + if (color != null) { + r = color.red; + g = color.green; + b = color.blue; + a = color.alpha; + } else { + r = g = b = a = 1; + } + + if(ulr != r || ulg != g || ulb != b || ula != a || uli != intensity) { + ulr = r; + ulg = g; + ulb = b; + ula = a; + uli = intensity; + glUniform1fv(prog_unified.color, new float[] {r, g, b, a, intensity}); + } + + + if(ulm != mode) { + ulm = mode; + glUniform1i(prog_unified.mode, mode); + } + + glUniform3fv(prog_unified.trans, new float[] {x, y, z, sx, sy, 0}); + + glDrawArrays(primitive, call.offset, count); + } + + public void drawBackground(BackgroundDrawHandle handle) { + bindTexture(handle.texture); + useProgram(prog_background); + if(handle.getVertexArrayId() != -1) { + bindFormat(handle.getVertexArrayId()); + } else { + enableVertArrays(true, true, true, false); + fillBackgroundFormat(handle); + } + + int starti = handle.offset < 0 ? (int)Math.ceil(-handle.offset/(float)handle.stride) : 0; + int draw_lines = handle.lines-starti; + + int[] firsts = new int[draw_lines]; + int[] counts = new int[draw_lines]; + for (int i = 0; i != draw_lines; i++) { + firsts[i] = (handle.offset + handle.stride * (i+starti)) * 3; + } + Arrays.fill(counts, handle.width*3); + + glMultiDrawArrays(GL_TRIANGLES, firsts, counts); + } + + @SuppressWarnings("WeakerAccess") + protected class ShaderProgram { + public final int program; + + public final int proj; + public final int global; + public final int trans; + public final int tex; + public final int color; + public final int height; + public final int mode; + public final int shadow_depth; + public final int geometry_data; + + + protected ShaderProgram(String name) { + int vertexShader = -1; + int fragmentShader; + + + try { + vertexShader = createShader(name+".vert", GL_VERTEX_SHADER); + fragmentShader = createShader(name+".frag", GL_FRAGMENT_SHADER); + } catch (IOException e) { + e.printStackTrace(); + + if(vertexShader != -1) glDeleteShader(vertexShader); + throw new Error("could not read shader files", e); + } + + program = glCreateProgram(); + setObjectLabel(KHRDebug.GL_PROGRAM, program, name); + + glAttachShader(program, vertexShader); + glAttachShader(program, fragmentShader); + + for(int i = 0; i != attributes.size(); i++) { + glBindAttribLocation(program, i, attributes.get(i)); + } + + glLinkProgram(program); + glValidateProgram(program); + + glDetachShader(program, vertexShader); + glDetachShader(program, fragmentShader); + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + + String log = glGetProgramInfoLog(program); + if(debugOutput != null && !log.isEmpty()) System.out.print("info log of " + name + "=====\n" + log + "==== end\n"); + + if(glGetProgrami(program, GL_LINK_STATUS) == 0) { + + glDeleteProgram(program); + throw new Error("Could not link " + name); + } + + proj = glGetUniformLocation(program, "projection"); + global = glGetUniformLocation(program, "globalTransform"); + trans = glGetUniformLocation(program, "transform"); + tex = glGetUniformLocation(program, "texHandle"); + color = glGetUniformLocation(program, "color"); + height = glGetUniformLocation(program, "height"); + mode = glGetUniformLocation(program, "mode"); + shadow_depth = glGetUniformLocation(program, "shadow_depth"); + + if(glcaps.GL_ARB_uniform_buffer_object) { + geometry_data = glGetUniformBlockIndex(program, "geometryDataBuffer"); + if (geometry_data != -1) glUniformBlockBinding(program, geometry_data, 0); + } else { + geometry_data = -1; + } + + useProgram(this); + if(tex != -1) glUniform1i(tex, 0); + + shaders.add(this); + } + + private ArrayList attributes = new ArrayList<>(); + + private int createShader(String name, int type) throws IOException { + StringBuilder source = new StringBuilder(); + try(InputStream shaderFile = getClass().getResourceAsStream("/"+name)) { + if (shaderFile == null) return -1; + BufferedReader is = new BufferedReader(new InputStreamReader(shaderFile)); + + String line; + while ((line = is.readLine()) != null) { + if (line.startsWith("attribute") || line.endsWith("//attribute")) { + attributes.add(line.split(" ")[2].replaceAll(";", "")); + } + + source.append(line).append("\n"); + } + } + + int shader = glCreateShader(type); + if (shader == 0) return -1; + setObjectLabel(KHRDebug.GL_SHADER, shader, name); + glShaderSource(shader, source); + glCompileShader(shader); + + + String log = glGetShaderInfoLog(shader); + if(debugOutput != null && !log.isEmpty()) System.out.print("info log of " + name + "=====\n" + log + "==== end\n"); + + if(glGetShaderi(shader, GL_COMPILE_STATUS) == 0) { + + glDeleteShader(shader); + throw new Error("Could not compile " + name); + } + + return shader; + } + } + + + public void clearDepthBuffer() { + finishFrame(); + glClear(GL_DEPTH_BUFFER_BIT); + } + + @Override + public void startFrame() { + super.startFrame(); + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + } +} diff --git a/go.graphics.swing/src/main/java/go/graphics/swing/text/LWJGLTextDrawer.java b/go.graphics.swing/src/main/java/go/graphics/swing/text/LWJGLTextDrawer.java index 3943545bae..1975094df3 100644 --- a/go.graphics.swing/src/main/java/go/graphics/swing/text/LWJGLTextDrawer.java +++ b/go.graphics.swing/src/main/java/go/graphics/swing/text/LWJGLTextDrawer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015 - 2018 + * Copyright (c) 2015 - 2019 * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, @@ -14,17 +14,9 @@ *******************************************************************************/ package go.graphics.swing.text; -import org.lwjgl.opengl.GL11; - -import go.graphics.AbstractColor; -import go.graphics.EGeometryFormatType; -import go.graphics.EGeometryType; -import go.graphics.GeometryHandle; -import go.graphics.SharedGeometry; -import go.graphics.TextureHandle; -import go.graphics.swing.opengl.LWJGL15DrawContext; +import go.graphics.swing.opengl.LWJGLDrawContext; +import go.graphics.text.AbstractTextDrawer; import go.graphics.text.EFontSize; -import go.graphics.text.TextDrawer; import java.awt.Color; import java.awt.Font; @@ -33,223 +25,78 @@ import java.awt.Graphics2D; import java.awt.Toolkit; import java.awt.image.BufferedImage; -import java.nio.ShortBuffer; - -/** - * This class is a text drawer used to wrap the text renderer. - * - * @author michael - * @author paul - */ -public final class LWJGLTextDrawer { - - private static final String FONTNAME = "Arial"; - private static final int TEXTURE_GENERATION_SIZE = 30; - private static final int DEFAULT_DPI = 96; - private static final float SCALING_FACTOR = calculateScalingFactor(); +import static org.lwjgl.opengl.GL20C.*; - private GeometryHandle geometry; - private TextureHandle font_tex; - private final int gentex_line_height; - private int tex_height; - private int tex_width; - private final int[] char_widths; - - private final static int char_spacing = 2; // spacing between two characters (otherwise j and f would overlap with the next character) - - private final LWJGL15DrawContext drawContext; - - private static float calculateScalingFactor() { - int screenDPI = Toolkit.getDefaultToolkit().getScreenResolution(); - return Math.max((float) (screenDPI / DEFAULT_DPI), 1); - } - - private final Font font; +public final class LWJGLTextDrawer extends AbstractTextDrawer { + private static final int DEFAULT_DPI = 96; + private static final Font FONT = new Font("Arial", Font.PLAIN, TEXTURE_GENERATION_SIZE); /** * Creates a new text drawer. * */ - public LWJGLTextDrawer(LWJGL15DrawContext drawContext) { - this.drawContext = drawContext; - font = new Font(FONTNAME, Font.PLAIN, TEXTURE_GENERATION_SIZE); + public LWJGLTextDrawer(LWJGLDrawContext drawContext, float guiScale) { + super(drawContext, guiScale); + } + + @Override + protected float calculateScalingFactor() { + int screenDPI = Toolkit.getDefaultToolkit().getScreenResolution(); + return Math.max((float) (screenDPI / DEFAULT_DPI), 1); + } + @Override + protected int init() { BufferedImage tmp_bi = new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR); Graphics tmp_graph = tmp_bi.getGraphics(); - tmp_graph.setFont(font); + tmp_graph.setFont(FONT); FontMetrics fm = tmp_graph.getFontMetrics(); - char_widths = fm.getWidths(); + for(int i = 0;i != CHARACTER_COUNT; i++) char_widths[i] = fm.charWidth(CHARACTERS.charAt(i)); gentex_line_height = fm.getHeight(); - tmp_graph.dispose(); - if(char_widths.length != 256) { - throw new IndexOutOfBoundsException("we only support 256 characters (256!="+char_widths.length); + EFontSize[] values = EFontSize.values(); + for(int i = 0; i != values.length; i++) { + tmp_graph.setFont(FONT.deriveFont(values[i].getSize())); + heightPerSize[i] = tmp_graph.getFontMetrics().getHeight(); } - generateTexture(); - generateGeometry(fm.getDescent()); - } + tmp_graph.dispose(); - private int getMaxLen() { - int max_len = 0; - for(int l = 0;l != 16;l++) { - int current_len = 0; - for(int c = 0;c != 16;c++) { - current_len += char_widths[l*16+c]+char_spacing; - max_len = Math.max(max_len, current_len); - } - } - return max_len; + return fm.getDescent(); } - private void generateTexture() { - int max_len = getMaxLen(); + @Override + protected int[] getRGB() { + return pre_render.getRGB(0, 0, tex_width, tex_height, null, 0, tex_width); + } - tex_width = max_len; - tex_height = gentex_line_height*16; + private BufferedImage pre_render; + private Graphics2D graph; - BufferedImage pre_render = new BufferedImage(tex_width, tex_height, BufferedImage.TYPE_INT_ARGB); - Graphics2D graph = pre_render.createGraphics(); + @Override + protected void setupBitmapDraw() { + pre_render = new BufferedImage(tex_width, tex_height, BufferedImage.TYPE_INT_ARGB); + graph = pre_render.createGraphics(); graph.setColor(Color.WHITE); - graph.setFont(font); - - for(int l = 0;l != 16;l++) { - int line_offset = 0; - for (int c = 0; c != 16; c++) { - graph.drawChars(new char[]{(char) (l * 16 + c)}, 0, 1, line_offset, l * gentex_line_height); - line_offset += char_widths[l*16+c]+char_spacing; - } - } - graph.dispose(); - - short[] short_tex_data = new short[tex_width*tex_height]; - - final short alpha_channel = 0b1111; - final short alpha_white = ~alpha_channel; - for(int x = 0;x != tex_width;x++) { - for (int y = 0; y != tex_height; y++) { - int pixel = pre_render.getRGB(x, tex_height-y-1); - - short a = (short) ((pixel >> 24) != 0 ? alpha_channel : 0); - short_tex_data[y*tex_width+x] = (short) (a | alpha_white); - } - } - ShortBuffer bfr = ShortBuffer.wrap(short_tex_data); - - font_tex = drawContext.generateTexture(max_len, tex_height, bfr, font.getName()); - - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, - GL11.GL_LINEAR); - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, - GL11.GL_LINEAR); + graph.setFont(FONT); } - private void generateGeometry(int descent) { - float[] geodata = new float[256*4*4]; - for(int l = 0;l != 16;l++) { - int line_offset = 0; - for (int c = 0; c != 16; c++) { - - float dx = line_offset; - float dy = tex_height-(l*gentex_line_height+descent); - - float dw = char_widths[l*16+c]; - float dh = gentex_line_height; - - float[] data = SharedGeometry.createQuadGeometry(0, 0,dw/(float)gentex_line_height, 1, dx/tex_width, dy/tex_height, (dx+dw)/tex_width, (dy+dh)/tex_height); - System.arraycopy(data, 0, geodata, (l*16+c)*4*4, 4*4); - - line_offset += char_widths[l*16+c]+char_spacing; - } - } - geometry = drawContext.storeGeometry(geodata, EGeometryFormatType.Texture2D, false, font.getName()); + @Override + protected void drawChar(char[] character, int x, int y) { + graph.drawChars(character, 0, 1, x, y); } - public TextDrawer derive(EFontSize size) { - return new SizedLWJGLTextDrawer(size); + @Override + protected void endDraw() { + graph.dispose(); + graph = null; + pre_render = null; } - - private class SizedLWJGLTextDrawer implements TextDrawer { - - private final float widthFactor; - private final float line_height; - private final Font sizedFont; - private AbstractColor color = null; - - private SizedLWJGLTextDrawer(EFontSize size) { - sizedFont = font.deriveFont(size.getSize()); - - BufferedImage tmp_bi = new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR); - Graphics tmp_graph = tmp_bi.getGraphics(); - tmp_graph.setFont(sizedFont); - FontMetrics fm = tmp_graph.getFontMetrics(); - line_height = fm.getHeight()*SCALING_FACTOR; - widthFactor = line_height/(float)gentex_line_height; - } - - /* - * (non-Javadoc) - * - * @see go.graphics.swing.text.TextDrawer#renderCentered(int, int, java.lang.String) - */ - @Override - public void renderCentered(float cx, float cy, String text) { - drawString(cx-(getWidth(text)/2), cy-(getHeight(text)/2), text); - } - - /** - * TODO: we should remove this. - */ - public void setColor(AbstractColor color) { - this.color = color; - } - - public void drawChar(float x, float y, char c) { - drawContext.draw2D(geometry, font_tex, EGeometryType.Quad, c, 4, x, y, 0, line_height, line_height, 0, color, 1); - } - - /* - * (non-Javadoc) - * - * @see go.graphics.swing.text.TextDrawer#drawString(int, int, java.lang.String) - */ - @Override - public void drawString(float x, float y, String string) { - float x_offset = 0; - float y_offset = 0; - - for(int i = 0;i != string.length();i++) { - if(string.charAt(i) == '\n') { - y_offset += line_height; - } else { - drawChar(x+x_offset, y+y_offset, string.charAt(i)); - x_offset += char_widths[string.charAt(i)]*widthFactor; - } - } - } - - @Override - public float getWidth(String string) { - float tmp_width = 0; - for(int i = 0;i != string.length();i++) { - if(string.charAt(i) != '\n') { - tmp_width += char_widths[string.charAt(i)]*widthFactor; - } - } - return tmp_width; - } - - @Override - public float getHeight(String string) { - float tmp_height = line_height; - for(int i = 0;i != string.length();i++) { - if(string.charAt(i) == '\n') { - tmp_height += line_height; - } - } - return tmp_height; - } + @Override + protected void setTexParams() { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } } diff --git a/go.graphics.swing/src/main/resources/unified-multi.frag b/go.graphics.swing/src/main/resources/unified-multi.frag new file mode 100644 index 0000000000..84bb227ae0 --- /dev/null +++ b/go.graphics.swing/src/main/resources/unified-multi.frag @@ -0,0 +1,49 @@ +#version 150 + +#extension GL_NV_fragdepth : enable + +precision mediump float; + +in vec4 frag_color; +flat in int frag_mode; +in float frag_intensity; +in vec2 frag_texCoord; + +uniform sampler2D texHandle; +uniform float shadow_depth; + +out vec4 fragColor; + +void main() { + float fragDepth = gl_FragCoord.z; + fragColor = frag_color; + + bool textured = frag_mode!=0; + + if(textured) { + vec4 tex_color = texture(texHandle, frag_texCoord); + + bool image_fence = frag_mode>0; + bool torso_fence = frag_mode>1; + bool shadow_fence = abs(float(frag_mode))>2.0; + + if(torso_fence && tex_color.a < 0.1 && tex_color.r > 0.1) { // torso pixel + fragColor.rgb *= tex_color.b; + } else if(shadow_fence && tex_color.a < 0.1 && tex_color.g > 0.1) { // shadow pixel + fragColor.rgba = tex_color.aaag; + fragDepth += shadow_depth; + } else if(image_fence) { // image pixel + if(!torso_fence && !shadow_fence) { + fragColor *= tex_color; + } else { + fragColor = tex_color; + } + } + } + + if(fragColor.a < 0.5) discard; + + fragColor.rgb *= frag_intensity; + + gl_FragDepth = fragDepth; +} diff --git a/go.graphics.swing/src/main/resources/unified-multi.vert b/go.graphics.swing/src/main/resources/unified-multi.vert new file mode 100644 index 0000000000..00cc6daadc --- /dev/null +++ b/go.graphics.swing/src/main/resources/unified-multi.vert @@ -0,0 +1,32 @@ +#version 150 + +#extension GL_ARB_uniform_buffer_object: require + +precision mediump float; + +in vec3 position; //attribute +in vec2 scale; //attribute +in vec4 color; //attribute +in vec3 additional; //attribute + +uniform mat4 globalTransform; +uniform mat4 projection; + +layout(std140) uniform geometryDataBuffer { + vec4 geometryData[4*1000]; +}; + +out vec4 frag_color; +flat out int frag_mode; +out float frag_intensity; +out vec2 frag_texCoord; + +void main() { + frag_mode = int(additional.z); + frag_color = color; + frag_intensity = additional.x; + int index = int(additional.y)+gl_VertexID; + + gl_Position = projection * globalTransform * vec4(position+vec3(scale*geometryData[index].xy, 0.f), 1.f); + frag_texCoord = geometryData[index].zw; +} diff --git a/go.graphics/src/main/java/go/graphics/AdvancedUpdateBufferCache.java b/go.graphics/src/main/java/go/graphics/AdvancedUpdateBufferCache.java new file mode 100644 index 0000000000..37e0fb2a2b --- /dev/null +++ b/go.graphics/src/main/java/go/graphics/AdvancedUpdateBufferCache.java @@ -0,0 +1,54 @@ +package go.graphics; + +import java.nio.ByteBuffer; +import java.util.BitSet; + +import java8.util.function.Supplier; + +public class AdvancedUpdateBufferCache { + private ByteBuffer buffer; + private int bfr_data_steps; + private Supplier ctx_supp; + private Supplier bfr_supp; + private BitSet[] updated; + private int line_width; + + public AdvancedUpdateBufferCache(ByteBuffer buffer, int bfr_data_steps, Supplier ctx_supp, Supplier bfr_supp, int line_width) { + this.bfr_data_steps = bfr_data_steps; + this.line_width = line_width; + this.ctx_supp = ctx_supp; + this.bfr_supp = bfr_supp; + this.buffer = buffer; + + int lines = buffer.capacity()/bfr_data_steps/line_width; + updated = new BitSet[lines]; + for(int i = 0;i != lines;i++) updated[i] = new BitSet(line_width); + } + + public void gotoLine(int line, int start, int count) { + updated[line].set(start, start + count); + buffer.position((line*line_width+start) * bfr_data_steps); + } + + public void clearCacheRegion(int line, int start, int end) throws IllegalBufferException { + int urEnd = start; + while(urEnd < end) { + int urStart = updated[line].nextSetBit(urEnd); + if(urStart > end || urStart == -1) return; + urEnd = updated[line].nextClearBit(urStart); + if(urEnd > end || urEnd == -1) urEnd = end; + updateRegion(line, urStart, urEnd); + updated[line].clear(urStart, urEnd); + } + } + + private void updateRegion(int line, int start, int end) throws IllegalBufferException { + start += line*line_width; + end += line*line_width; + + buffer.limit(end * bfr_data_steps); + buffer.position(start * bfr_data_steps); + ctx_supp.get().updateBufferAt(bfr_supp.get(), start * bfr_data_steps, buffer); + buffer.limit(buffer.capacity()); + } +} diff --git a/go.graphics/src/main/java/go/graphics/BackgroundDrawHandle.java b/go.graphics/src/main/java/go/graphics/BackgroundDrawHandle.java new file mode 100644 index 0000000000..08c3387f0e --- /dev/null +++ b/go.graphics/src/main/java/go/graphics/BackgroundDrawHandle.java @@ -0,0 +1,21 @@ +package go.graphics; + +public class BackgroundDrawHandle extends GLResourceIndex { + + public final BufferHandle vertices; + public final TextureHandle texture; + public final BufferHandle colors; + + public BackgroundDrawHandle(GLDrawContext dc, int id, TextureHandle texture, BufferHandle vertices, BufferHandle colors) { + super(dc, id); + this.vertices = vertices; + this.texture = texture; + this.colors = colors; + } + + public int offset, lines, width, stride; + + public int getVertexArrayId() { + return id; + } +} diff --git a/go.graphics/src/main/java/go/graphics/GeometryHandle.java b/go.graphics/src/main/java/go/graphics/BufferHandle.java similarity index 73% rename from go.graphics/src/main/java/go/graphics/GeometryHandle.java rename to go.graphics/src/main/java/go/graphics/BufferHandle.java index eae234581b..ab94103529 100644 --- a/go.graphics/src/main/java/go/graphics/GeometryHandle.java +++ b/go.graphics/src/main/java/go/graphics/BufferHandle.java @@ -19,30 +19,18 @@ * * @author Michael Zangl */ -public class GeometryHandle extends GLBufferHandle { - private int vao; - private EGeometryFormatType format; +public class BufferHandle extends GLResourceIndex { - public GeometryHandle(GLDrawContext dc, int vbo, int vao, EGeometryFormatType format) { + public BufferHandle(GLDrawContext dc, int vbo) { super(dc, vbo); - this.vao = vao; - this.format = format; } - public EGeometryFormatType getFormat() { - return format; - } - - public int getInternalFormatId() { - return vao; - } - - public void setInternalFormatId(int vao) { - this.vao = vao; + public int getBufferId() { + return id; } @Override public String toString() { - return getClass().getSimpleName() + " [index=" + id + " ,vao=" + vao + " ,format=" + format + " ]"; + return getClass().getSimpleName() + " [index=" + id + "]"; } } diff --git a/go.graphics/src/main/java/go/graphics/EGeometryFormatType.java b/go.graphics/src/main/java/go/graphics/EBufferFormatType.java similarity index 59% rename from go.graphics/src/main/java/go/graphics/EGeometryFormatType.java rename to go.graphics/src/main/java/go/graphics/EBufferFormatType.java index 8a685ca67d..71141791d1 100644 --- a/go.graphics/src/main/java/go/graphics/EGeometryFormatType.java +++ b/go.graphics/src/main/java/go/graphics/EBufferFormatType.java @@ -1,20 +1,16 @@ package go.graphics; -public enum EGeometryFormatType { - Texture3D(5*4, 3*4, false), +public enum EBufferFormatType { Texture2D(4*4, 2*4, true), - VertexOnly2D(2*4, -1, true), - ColorOnly(4, 0, false); + VertexOnly2D(2*4, -1, true); private int bytesPerVertexSize; private int texCoordPos; - private boolean staticData; private boolean singleBuffer; - EGeometryFormatType(int bytesPerVertexSize, int texCoordPos, boolean singleBuffer) { + EBufferFormatType(int bytesPerVertexSize, int texCoordPos, boolean singleBuffer) { this.bytesPerVertexSize = bytesPerVertexSize; this.texCoordPos = texCoordPos; - this.staticData = staticData; this.singleBuffer = singleBuffer; } @@ -29,8 +25,4 @@ public int getTexCoordPos() { public boolean isSingleBuffer() { return singleBuffer; } - - public boolean isStaticData() { - return staticData; - } } diff --git a/go.graphics/src/main/java/go/graphics/EGeometryType.java b/go.graphics/src/main/java/go/graphics/EPrimitiveType.java similarity index 85% rename from go.graphics/src/main/java/go/graphics/EGeometryType.java rename to go.graphics/src/main/java/go/graphics/EPrimitiveType.java index 64b5a21ef8..fc0d73fbc8 100644 --- a/go.graphics/src/main/java/go/graphics/EGeometryType.java +++ b/go.graphics/src/main/java/go/graphics/EPrimitiveType.java @@ -1,6 +1,6 @@ package go.graphics; -public class EGeometryType { +public class EPrimitiveType { public static final int Quad = 6; public static final int Triangle = 4; public static final int LineLoop = 2; diff --git a/go.graphics/src/main/java/go/graphics/EUnifiedMode.java b/go.graphics/src/main/java/go/graphics/EUnifiedMode.java new file mode 100644 index 0000000000..7202c35c94 --- /dev/null +++ b/go.graphics/src/main/java/go/graphics/EUnifiedMode.java @@ -0,0 +1,9 @@ +package go.graphics; + +public class EUnifiedMode { + public static final int COLOR_ONLY = 0; + public static final int TEXTURE = 1; + public static final int SETTLER = 2; + public static final int SETTLER_SHADOW = 3; + public static final int SHADOW_ONLY = -3; +} diff --git a/jsettlers.common/src/main/java/jsettlers/common/statistics/FramerateComputer.java b/go.graphics/src/main/java/go/graphics/FramerateComputer.java similarity index 85% rename from jsettlers.common/src/main/java/jsettlers/common/statistics/FramerateComputer.java rename to go.graphics/src/main/java/go/graphics/FramerateComputer.java index b262d82cb2..f13013104f 100644 --- a/jsettlers.common/src/main/java/jsettlers/common/statistics/FramerateComputer.java +++ b/go.graphics/src/main/java/go/graphics/FramerateComputer.java @@ -12,7 +12,9 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. *******************************************************************************/ -package jsettlers.common.statistics; +package go.graphics; + +import java.util.concurrent.TimeUnit; /** * This class keeps track of the frames. @@ -24,6 +26,7 @@ public class FramerateComputer { private long calcFrameStart = System.nanoTime(); private double timePerFrame = 0; private long calcFrameEnd; + private long calcLastFrameStart; private int capturedFrames = 0; /** @@ -58,4 +61,17 @@ public double getRate() { public double getTime() { return timePerFrame / NS_PER_S; } + + public void nextFrame(int fpsLimit) { + nextFrame(); + + long ft = calcFrameEnd-calcLastFrameStart; + long minft = (long) (NS_PER_S/fpsLimit); + if(minft > ft) { + try { + TimeUnit.NANOSECONDS.sleep(minft-ft); + } catch (InterruptedException e) {} + } + calcLastFrameStart = System.nanoTime(); + } } diff --git a/go.graphics/src/main/java/go/graphics/GL2DrawContext.java b/go.graphics/src/main/java/go/graphics/GL2DrawContext.java deleted file mode 100644 index f897f35261..0000000000 --- a/go.graphics/src/main/java/go/graphics/GL2DrawContext.java +++ /dev/null @@ -1,5 +0,0 @@ -package go.graphics; - -public interface GL2DrawContext extends GLDrawContext { - void drawUnified2D(GeometryHandle geometry, TextureHandle texture, int primitive, int offset, int vertices, boolean image, boolean shadow, float x, float y, float z, float sx, float sy, float sz, AbstractColor color, float intensity) throws IllegalBufferException; - } diff --git a/go.graphics/src/main/java/go/graphics/GL32DrawContext.java b/go.graphics/src/main/java/go/graphics/GL32DrawContext.java new file mode 100644 index 0000000000..b04c679cd9 --- /dev/null +++ b/go.graphics/src/main/java/go/graphics/GL32DrawContext.java @@ -0,0 +1,5 @@ +package go.graphics; + +public interface GL32DrawContext { + public abstract void drawMultiUnified2D(TextureHandle texture, BufferHandle geometry, BufferHandle drawCalls, int drawCallCount); +} diff --git a/go.graphics/src/main/java/go/graphics/GLDrawContext.java b/go.graphics/src/main/java/go/graphics/GLDrawContext.java index 42fbc609cb..2bc72991b5 100644 --- a/go.graphics/src/main/java/go/graphics/GLDrawContext.java +++ b/go.graphics/src/main/java/go/graphics/GLDrawContext.java @@ -1,36 +1,27 @@ -/******************************************************************************* - * Copyright (c) 2015 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - *******************************************************************************/ package go.graphics; +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; +import java.util.ArrayList; +import java.util.List; + +import go.graphics.text.AbstractTextDrawer; import go.graphics.text.EFontSize; import go.graphics.text.TextDrawer; -import java.nio.ByteBuffer; -import java.nio.ShortBuffer; +public abstract class GLDrawContext { -/** - * This is the main OpenGL context - * - * @author michael - */ -public interface GLDrawContext { - void draw2D(GeometryHandle geometry, TextureHandle texture, int primitive, int offset, int vertices, float x, float y, float z, float sx, float sy, float sz, AbstractColor color, float intensity) throws IllegalBufferException; + public GLDrawContext() { + ManagedHandle.instance_count = 0; + } + + private List managedHandles = new ArrayList<>(); + + public abstract void setShadowDepthOffset(float depth); /** * Returns a texture id which is positive or 0. It returns a negative number on error. - * + * * @param width * @param height * The height of the image. @@ -39,17 +30,21 @@ public interface GLDrawContext { * blue and 4 bits alpha. * @return The id of the generated texture. */ - TextureHandle generateTexture(int width, int height, ShortBuffer data, String name); + public abstract TextureHandle generateTexture(int width, int height, ShortBuffer data, String name); - void drawTrianglesWithTextureColored(TextureHandle textureid, GeometryHandle vertexHandle, GeometryHandle paintHandle, int offset, int lines, int width, int stride, float x, float y) throws IllegalBufferException; + protected abstract void drawMulti(MultiDrawHandle call); + protected abstract void drawUnifiedArray(UnifiedDrawHandle call, int primitive, int vertexCount, float[] trans, float[] colors, int array_len); + protected abstract void drawUnified(UnifiedDrawHandle call, int primitive, int vertices, int mode, float x, float y, float z, float sx, float sy, AbstractColor color, float intensity); - void setHeightMatrix(float[] matrix); + public abstract void drawBackground(BackgroundDrawHandle call); - void setGlobalAttributes(float x, float y, float z, float sx, float sy, float sz); + public abstract void setHeightMatrix(float[] matrix); + + public abstract void setGlobalAttributes(float x, float y, float z, float sx, float sy, float sz); /** * Updates a part of a texture image. - * + * * @param textureIndex * The texture to use. * @param left @@ -59,17 +54,127 @@ public interface GLDrawContext { * @param data * @throws IllegalBufferException */ - void updateTexture(TextureHandle textureIndex, int left, int bottom, int width, int height, ShortBuffer data) throws IllegalBufferException; + public abstract void updateTexture(TextureHandle textureIndex, int left, int bottom, int width, int height, ShortBuffer data) throws IllegalBufferException; + + public abstract void resizeTexture(TextureHandle textureIndex, int width, int height, ShortBuffer data); + + public abstract void updateBufferAt(BufferHandle handle, int pos, ByteBuffer data) throws IllegalBufferException; + + public abstract BackgroundDrawHandle createBackgroundDrawCall(int vertices, TextureHandle texture); + + protected AbstractTextDrawer textDrawer; + private final TextDrawer[] sizedTextDrawers = new TextDrawer[EFontSize.values().length]; + + /** + * Gets a text drawer for the given text size. + * + * @param size + * The size for the drawer. + * @return An instance of a drawer for that size. + */ + public TextDrawer getTextDrawer(EFontSize size) { + if (sizedTextDrawers[size.ordinal()] == null) { + sizedTextDrawers[size.ordinal()] = textDrawer.derive(size); + } + return sizedTextDrawers[size.ordinal()]; + } + + /** + * + * @param vertices + * Maximum number of vertices + * @param name + * The label that the OpenGL handles get (nullable) + * @param texture + * It determines whether this handle is textured or only single colored + * @param data + * If data is not equal null this will be a readonly buffer filled with data + * @return + * A handle to draw via the unified shader + */ + public abstract UnifiedDrawHandle createUnifiedDrawCall(int vertices, String name, TextureHandle texture, float[] data); + + protected abstract MultiDrawHandle createMultiDrawCall(String name, ManagedHandle source); + + public static float[] createQuadGeometry(float lx, float ly, float hx, float hy, float lu, float lv, float hu, float hv) { + return new float[] { + // bottom right + hx, ly, hu, lv, + // top right + hx, hy, hu, hv, + // top left + lx, hy, lu, hv, + // bottom left + lx, ly, lu, lv, + }; + } + + private void addNewHandle() { + TextureHandle tex = generateTexture(ManagedHandle.TEX_DIM, ManagedHandle.TEX_DIM, null, "managed" + ManagedHandle.instance_count); + UnifiedDrawHandle parent = createUnifiedDrawCall(ManagedHandle.MAX_QUADS*4, "managed" + ManagedHandle.instance_count, tex, null); + managedHandles.add(new ManagedHandle(parent)); + } + + public ManagedUnifiedDrawHandle createManagedUnifiedDrawCall(ShortBuffer texData, float offsetX, float offsetY, int width, int height) { + for(ManagedHandle handle : managedHandles) { + int position; + if(handle.quad_index != ManagedHandle.MAX_QUADS && (position = handle.findTextureHole(width, height)) != -1) { + UIPoint corner; + if((corner = handle.addTexture(texData, width, height, position)) == null) continue; + + + float lu = (float) corner.getX(); + float lv = (float) corner.getY(); + float hu = lu + width/(float) ManagedHandle.TEX_DIM; + float hv = lv + height/(float) ManagedHandle.TEX_DIM; + + float[] data = createQuadGeometry(offsetX, -offsetY, offsetX+width, -offsetY-height, lu, lv, hu, hv); + + handle.addQuad(data); + + return new ManagedUnifiedDrawHandle(handle, lu, lv, hu, hv); + } + } + + addNewHandle(); + return createManagedUnifiedDrawCall(texData, offsetX, offsetY, width, height); + } + + private boolean valid = true; + + public void invalidate() { + valid = false; + } + + public boolean isValid() { + return valid; + } + + public abstract void clearDepthBuffer(); + + protected void add(UnifiedDrawHandle cache) { + caches.add(cache); + } - TextDrawer getTextDrawer(EFontSize size); + protected void remove(UnifiedDrawHandle cache) { + caches.remove(cache); + } - GeometryHandle storeGeometry(float[] geometry, EGeometryFormatType type, boolean writable, String name); + private List caches = new ArrayList<>(); - void updateGeometryAt(GeometryHandle handle, int pos, ByteBuffer data) throws IllegalBufferException; + public void finishFrame() { + for(int i = 0;i != caches.size(); i++) { + if(caches.get(i).flush()) i--; + } - GeometryHandle generateGeometry(int vertices, EGeometryFormatType type, boolean writable, String name); + for(ManagedHandle mh : managedHandles) { + if(mh.multiCache != null) mh.multiCache.flush(); + } + } - boolean isValid(); + protected long frameIndex = 0; - void deleteTexture(TextureHandle texture); + public void startFrame() { + frameIndex++; + } } diff --git a/go.graphics/src/main/java/go/graphics/GLBufferHandle.java b/go.graphics/src/main/java/go/graphics/GLResourceIndex.java similarity index 78% rename from go.graphics/src/main/java/go/graphics/GLBufferHandle.java rename to go.graphics/src/main/java/go/graphics/GLResourceIndex.java index 6c75c5264c..e28605f6d4 100644 --- a/go.graphics/src/main/java/go/graphics/GLBufferHandle.java +++ b/go.graphics/src/main/java/go/graphics/GLResourceIndex.java @@ -15,36 +15,27 @@ package go.graphics; /** - * This class represents an abstract buffer handle. + * This class represents an abstract resource handle. * * @author Michael Zangl */ -public abstract class GLBufferHandle { +public abstract class GLResourceIndex { protected GLDrawContext dc; protected int id; - public GLBufferHandle(GLDrawContext dc, int id) { + public GLResourceIndex(GLDrawContext dc, int id) { this.dc = dc; this.id = id; } /** - * Checks if this buffer is valid. + * Checks if this resource is valid. * - * @return true if the buffer is valid and can be used. + * @return true if the resource is valid and can be used. */ public boolean isValid() { return dc.isValid(); } - /** - * Gets the index by which this buffer is referenced internally. You should not need this. - * - * @return Thge buffer id. - */ - public int getInternalId() { - return id; - } - @Override public String toString() { return getClass().getSimpleName() + "[index=" + id + " ]"; diff --git a/go.graphics/src/main/java/go/graphics/ManagedHandle.java b/go.graphics/src/main/java/go/graphics/ManagedHandle.java new file mode 100644 index 0000000000..4c67c7d7d1 --- /dev/null +++ b/go.graphics/src/main/java/go/graphics/ManagedHandle.java @@ -0,0 +1,90 @@ +package go.graphics; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.ShortBuffer; +import java.util.Arrays; + +public class ManagedHandle { + + public static final int MAX_QUADS = 1000; + public static final int TEX_DIM = 2048; + + protected static int instance_count = 0; + + protected int quad_index = 0; + private int[] remaining_pixels = new int[TEX_DIM]; + + protected UnifiedDrawHandle bufferHolder; + protected final MultiDrawHandle multiCache; + + protected ManagedHandle(UnifiedDrawHandle bufferHolder) { + this.bufferHolder = bufferHolder; + multiCache = bufferHolder.dc.createMultiDrawCall("managed" + instance_count, this); + + Arrays.fill(remaining_pixels, TEX_DIM); + ManagedHandle.instance_count++; + } + + protected int findTextureHole(int width, int height) { + int placement_count = TEX_DIM - width; + + int[] placements = new int[placement_count]; + + for(int i = 0; i != placement_count; i++) { + int sum_of_remaining = 0; + int lowest_remaining = remaining_pixels[i]; + for(int j = 0; j != width; j++) { + sum_of_remaining += remaining_pixels[i+j]; + if(lowest_remaining > remaining_pixels[i+j]) lowest_remaining = remaining_pixels[i+j]; + } + + if(lowest_remaining < height) { + placements[i] = -1; + } else { + placements[i] = sum_of_remaining - (width * lowest_remaining); + } + } + + int lowest_damage_placement = -1; + for(int i = 0;i != placement_count; i++) { + if(placements[i] != -1) { + if(lowest_damage_placement == -1) { + lowest_damage_placement = i; + } else if(placements[lowest_damage_placement] > placements[i]) { + lowest_damage_placement = i; + } + } + } + + return lowest_damage_placement; + } + + protected UIPoint addTexture(ShortBuffer texData, int width, int height, int start) { + int leastRemaining = remaining_pixels[start]; + for(int i = 0; i != width; i++) { + if(leastRemaining > remaining_pixels[i+start]) { + leastRemaining = remaining_pixels[i+start]; + } + } + + if(leastRemaining < height) return null; + + // claim texture space + Arrays.fill(remaining_pixels, start, start+width, leastRemaining-height); + + // ...and populate it + try { + bufferHolder.dc.updateTexture(bufferHolder.texture, start, TEX_DIM-leastRemaining, width, height, texData); + } catch(IllegalBufferException e) {} + return new UIPoint(start/(float)TEX_DIM, (TEX_DIM-leastRemaining)/(float)TEX_DIM); + } + + private static final ByteBuffer dataBuffer = ByteBuffer.allocateDirect(4*4*4).order(ByteOrder.nativeOrder()); + protected void addQuad(float[] data) { + dataBuffer.asFloatBuffer().put(data); + try { + bufferHolder.dc.updateBufferAt(bufferHolder.vertices, quad_index*4*4*4, dataBuffer); + } catch (IllegalBufferException e) {} + } +} diff --git a/go.graphics/src/main/java/go/graphics/ManagedUnifiedDrawHandle.java b/go.graphics/src/main/java/go/graphics/ManagedUnifiedDrawHandle.java new file mode 100644 index 0000000000..90f374ae2f --- /dev/null +++ b/go.graphics/src/main/java/go/graphics/ManagedUnifiedDrawHandle.java @@ -0,0 +1,25 @@ +package go.graphics; + +public class ManagedUnifiedDrawHandle extends UnifiedDrawHandle { + + public final float texX, texY, texWidth, texHeight; + private final ManagedHandle parent; + + protected ManagedUnifiedDrawHandle(ManagedHandle parent, float texX, float texY, float texWidth, float texHeight) { + super(parent.bufferHolder.dc, parent.bufferHolder.id, 4*parent.quad_index++, 4, parent.bufferHolder.texture, parent.bufferHolder.vertices); + this.texX = texX; + this.texY = texY; + this.parent = parent; + this.texWidth = texWidth; + this.texHeight = texHeight; + } + + @Override + public void drawComplexQuad(int mode, float x, float y, float z, float sx, float sy, AbstractColor color, float intensity) { + if(parent.multiCache != null) { + parent.multiCache.schedule(this, mode, x, y, z, sx, sy, color, intensity); + } else { + super.drawComplexQuad(mode, x, y, z, sx, sy, color, intensity); + } + } +} diff --git a/go.graphics/src/main/java/go/graphics/MultiDrawHandle.java b/go.graphics/src/main/java/go/graphics/MultiDrawHandle.java new file mode 100644 index 0000000000..200f6cc73d --- /dev/null +++ b/go.graphics/src/main/java/go/graphics/MultiDrawHandle.java @@ -0,0 +1,66 @@ +package go.graphics; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public class MultiDrawHandle extends GLResourceIndex { + + public static final int MAX_CACHE_ENTRIES = 1000; + + public final UnifiedDrawHandle sourceQuads; + public final BufferHandle drawCalls; + public final int size; + private final ByteBuffer drawCallBuffer = ByteBuffer.allocateDirect(MAX_CACHE_ENTRIES*12*4).order(ByteOrder.nativeOrder()); + + public int used = 0; + + public MultiDrawHandle(GLDrawContext dc, int id, int size, ManagedHandle vertexProvider, BufferHandle drawCalls) { + super(dc, id); + + this.sourceQuads = vertexProvider.bufferHolder; + this.drawCalls = drawCalls; + this.size = size; + } + + public void schedule(ManagedUnifiedDrawHandle handle, int mode, float x, float y, float z, float sx, float sy, AbstractColor color, float intensity) { + if(used == MAX_CACHE_ENTRIES) flush(); + + int off = used*12*4; + drawCallBuffer.putFloat(off, x); + drawCallBuffer.putFloat(off+4, y); + drawCallBuffer.putFloat(off+8, z); + + drawCallBuffer.putFloat(off+12, sx); + drawCallBuffer.putFloat(off+16, sy); + + drawCallBuffer.putFloat(off+20, color!=null?color.red:1); + drawCallBuffer.putFloat(off+24, color!=null?color.green:1); + drawCallBuffer.putFloat(off+28, color!=null?color.blue:1); + drawCallBuffer.putFloat(off+32, color!=null?color.alpha:1); + drawCallBuffer.putFloat(off+36, intensity); + + drawCallBuffer.putFloat(off+40, handle.offset); + drawCallBuffer.putFloat(off+44, mode); + + used++; + } + + public void flush() { + if(used == 0) return; + + drawCallBuffer.limit(used*12*4); + + + try { + dc.updateBufferAt(drawCalls, 0, drawCallBuffer); + } catch (IllegalBufferException e) {} + dc.drawMulti(this); + + drawCallBuffer.limit(MAX_CACHE_ENTRIES*12*4); + used = 0; + } + + public int getVertexArrayId() { + return id; + } +} diff --git a/go.graphics/src/main/java/go/graphics/SharedGeometry.java b/go.graphics/src/main/java/go/graphics/SharedGeometry.java deleted file mode 100644 index 2dcd687b14..0000000000 --- a/go.graphics/src/main/java/go/graphics/SharedGeometry.java +++ /dev/null @@ -1,95 +0,0 @@ -package go.graphics; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.Calendar; - -public class SharedGeometry { - - private static final int CAPACITY = 1000; - private static final int QUAD_SIZE = 4*4*4; - private static int maxIndex = 0; - - private int size = 0; - private int index; - private GeometryHandle geometry; - private static final ByteBuffer generate_buffer = ByteBuffer.allocateDirect(QUAD_SIZE).order(ByteOrder.nativeOrder()); - - private static final ArrayList geometries = new ArrayList<>(); - - public static SharedGeometryHandle addGeometry(GLDrawContext dc, float[] data) throws IllegalBufferException { - if(staticdc == null) staticdc = dc; - int sgeometryIndex = 0; - - while(true) { - // create an instance if needed - if(geometries.size() == sgeometryIndex) geometries.add(new SharedGeometry(dc, ++maxIndex)); - - SharedGeometry geometry = geometries.get(sgeometryIndex); - // generate it - geometry.validate(dc); - - // skip if we wont fit - if (geometry.size < CAPACITY) { - // add it to our vbo - generate_buffer.asFloatBuffer().put(data); - dc.updateGeometryAt(geometry.geometry, QUAD_SIZE*geometry.size, generate_buffer); - generate_buffer.rewind(); - - geometry.size++; - return new SharedGeometryHandle(geometry); - } - - sgeometryIndex++; - } - } - - public static class SharedGeometryHandle { - public final GeometryHandle geometry; - public final int index; - private final int iteration = SharedGeometry.iteration; - - private SharedGeometryHandle(SharedGeometry shared) { - geometry = shared.geometry; - index = shared.size-1; - } - } - - private static int iteration = 0; - private static GLDrawContext staticdc = null; - - public static boolean isInvalid(GLDrawContext dc, SharedGeometryHandle handle) { - if(dc != staticdc) { - staticdc = dc; - iteration++; - } - return handle.iteration!=iteration; - } - - private void validate(GLDrawContext dc) { - if(!geometry.isValid()) { - geometry = dc.generateGeometry(CAPACITY*4, EGeometryFormatType.Texture2D, true, "sharedgeometry-" + index); - size = 0; - } - } - - private SharedGeometry(GLDrawContext dc, int index) { - this.index = index; - geometry = dc.generateGeometry(CAPACITY*4, EGeometryFormatType.Texture2D, true, "sharedgeometry-" + index); - } - - - public static float[] createQuadGeometry(float lx, float ly, float hx, float hy, float lu, float lv, float hu, float hv) { - return new float[] { - // bottom right - hx, ly, hu, lv, - // top right - hx, hy, hu, hv, - // top left - lx, hy, lu, hv, - // bottom left - lx, ly, lu, lv, - }; - } -} diff --git a/go.graphics/src/main/java/go/graphics/TextureHandle.java b/go.graphics/src/main/java/go/graphics/TextureHandle.java index 318bbb89b5..20ca37ad2b 100644 --- a/go.graphics/src/main/java/go/graphics/TextureHandle.java +++ b/go.graphics/src/main/java/go/graphics/TextureHandle.java @@ -19,9 +19,13 @@ * * @author Michael Zangl */ -public class TextureHandle extends GLBufferHandle { +public class TextureHandle extends GLResourceIndex { public TextureHandle(GLDrawContext dc, int texture) { super(dc, texture); } + + public int getTextureId() { + return id; + } } diff --git a/go.graphics/src/main/java/go/graphics/UnifiedDrawHandle.java b/go.graphics/src/main/java/go/graphics/UnifiedDrawHandle.java new file mode 100644 index 0000000000..97ddb15528 --- /dev/null +++ b/go.graphics/src/main/java/go/graphics/UnifiedDrawHandle.java @@ -0,0 +1,111 @@ +package go.graphics; + +public class UnifiedDrawHandle extends GLResourceIndex { + public final BufferHandle vertices; + public TextureHandle texture; + + public int offset; + public final int vertexCount; + + public UnifiedDrawHandle(GLDrawContext dc, int id, int offset, int vertexCount, TextureHandle texture, BufferHandle vertices) { + super(dc, id); + this.vertexCount = vertexCount; + this.vertices = vertices; + this.texture = texture; + this.offset = offset; + } + + private float[] trans; + private float[] colors; + private int cache_index = 0; + + private int cache_start_bias = 0; + private int frame_drawcalls = 0; + private long frameIndex = -1; + private boolean forceNoCache = false; + + public static final int CACHE_START_AT_BIAS = 1000; + public static final int MAX_CACHE_ENTRIES = 100; + + public void forceNoCache() { + forceNoCache = true; + } + + private void enableCaching() { + if(forceNoCache) return; + + trans = new float[MAX_CACHE_ENTRIES*4]; + colors = new float[MAX_CACHE_ENTRIES*4]; + + dc.add(this); + } + + private void disableCaching() { + trans = null; + colors = null; + dc.remove(this); + } + + private boolean nextFrame() { + frameIndex = dc.frameIndex; + + boolean modified = false; + if(trans == null && frame_drawcalls >= MAX_CACHE_ENTRIES) { + cache_start_bias++; + if(cache_start_bias == CACHE_START_AT_BIAS) { + enableCaching(); + modified = true; + } + } else if(trans != null && frame_drawcalls < MAX_CACHE_ENTRIES){ + cache_start_bias--; + + if(cache_start_bias == -CACHE_START_AT_BIAS) { + disableCaching(); + modified = true; + } + } + + frame_drawcalls = 0; + return modified; + } + + public boolean flush() { + boolean mod = (frameIndex != dc.frameIndex) && nextFrame(); + if(cache_index == 0) return mod; + + dc.drawUnifiedArray(this, EPrimitiveType.Quad, 4, trans, colors, cache_index); + cache_index = 0; + + return mod; + } + + public void drawSimple(int primitive, float x, float y, float z, float sx, float sy, AbstractColor color, float intensity) { + dc.drawUnified(this, primitive, vertexCount, texture!=null?EUnifiedMode.TEXTURE : EUnifiedMode.COLOR_ONLY, x, y, z, sx, sy, color, intensity); + } + + public void drawComplexQuad(int mode, float x, float y, float z, float sx, float sy, AbstractColor color, float intensity) { + if(frameIndex != dc.frameIndex) nextFrame(); + + if(trans != null && sx == 1 && sy == 1) { + if(cache_index == MAX_CACHE_ENTRIES) flush(); + + trans[cache_index*4] = x; + trans[cache_index*4+1] = y; + trans[cache_index*4+2] = z; + trans[cache_index*4+3] = (mode*10)+intensity+1; + + colors[cache_index*4] = color!=null?color.red:1; + colors[cache_index*4+1] = color!=null?color.green:1; + colors[cache_index*4+2] = color!=null?color.blue:1; + colors[cache_index*4+3] = color!=null?color.alpha:1; + cache_index++; + } else { + dc.drawUnified(this, EPrimitiveType.Quad, 4, mode, x, y, z, sx, sy, color, intensity); + } + frame_drawcalls++; + } + + public int getVertexArrayId() { + return id; + } +} diff --git a/go.graphics/src/main/java/go/graphics/UpdateGeometryCache.java b/go.graphics/src/main/java/go/graphics/UpdateGeometryCache.java new file mode 100644 index 0000000000..c334841ce5 --- /dev/null +++ b/go.graphics/src/main/java/go/graphics/UpdateGeometryCache.java @@ -0,0 +1,46 @@ +package go.graphics; + +import java.nio.ByteBuffer; + +import java8.util.function.Supplier; + +public class UpdateGeometryCache { + private int position = 0; + private int cache_size = 0; + private int cache_start = 0; + + private ByteBuffer buffer; + private int bfr_data_steps; + private Supplier ctx_supp; + private Supplier bfr_supp; + + public UpdateGeometryCache(ByteBuffer buffer, int bfr_data_steps, Supplier ctx_supp, Supplier bfr_supp) { + this.bfr_data_steps = bfr_data_steps; + this.ctx_supp = ctx_supp; + this.bfr_supp = bfr_supp; + this.buffer = buffer; + } + + public void gotoPos(int new_position) throws IllegalBufferException { + if(new_position == position+1) { + cache_size += bfr_data_steps; + } else { + clearCache(); + cache_size = bfr_data_steps; + cache_start = new_position; + } + position = new_position; + } + + public void clearCache() throws IllegalBufferException { + if(cache_size == 0) return; + + buffer.limit(cache_size); + buffer.rewind(); + ctx_supp.get().updateBufferAt(bfr_supp.get(), cache_start*bfr_data_steps, buffer); + buffer.limit(buffer.capacity()); + position = 0; + cache_size = 0; + cache_start = 0; + } +} diff --git a/go.graphics/src/main/java/go/graphics/area/Area.java b/go.graphics/src/main/java/go/graphics/area/Area.java index 56c4a1f781..468b1fe5b8 100644 --- a/go.graphics/src/main/java/go/graphics/area/Area.java +++ b/go.graphics/src/main/java/go/graphics/area/Area.java @@ -14,8 +14,6 @@ *******************************************************************************/ package go.graphics.area; -import java.util.ArrayList; -import java.util.Iterator; import java.util.LinkedList; import go.graphics.DrawmodeListener; @@ -25,19 +23,14 @@ import go.graphics.event.GOEvent; import go.graphics.event.GOEventHandlerProvider; import go.graphics.event.GOKeyEvent; -import go.graphics.event.GOModalEventHandler; import go.graphics.event.command.GOCommandEvent; -import go.graphics.event.command.GOCommandEventProxy; import go.graphics.event.interpreter.AbstractMouseEvent; import go.graphics.event.mouse.GODrawEvent; import go.graphics.event.mouse.GODrawEventProxy; import go.graphics.event.mouse.GOHoverEvent; import go.graphics.event.mouse.GOPanEvent; -import go.graphics.event.mouse.GOPanEventProxy; import go.graphics.event.mouse.GOZoomEvent; -import go.graphics.region.PositionedRegion; import go.graphics.region.Region; -import go.graphics.region.RegionContent; /** * This class represents an area. This is a rectangular part of the screen that consists of multiple regions. diff --git a/go.graphics/src/main/java/go/graphics/text/AbstractTextDrawer.java b/go.graphics/src/main/java/go/graphics/text/AbstractTextDrawer.java new file mode 100644 index 0000000000..18b8c57c26 --- /dev/null +++ b/go.graphics/src/main/java/go/graphics/text/AbstractTextDrawer.java @@ -0,0 +1,251 @@ +/******************************************************************************* + * Copyright (c) 2015 - 2018 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *******************************************************************************/ +package go.graphics.text; + +import go.graphics.AbstractColor; +import go.graphics.EUnifiedMode; +import go.graphics.GLDrawContext; +import go.graphics.TextureHandle; +import go.graphics.UnifiedDrawHandle; + + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.ShortBuffer; + +/** + * This class is a text drawer used to wrap the text renderer. + * + * @author michael + * @author paul + */ +public abstract class AbstractTextDrawer { + + protected static final String CHARACTERS; + protected static final int CHARACTER_COUNT; + protected static final int TEXTURE_LINE_LEN; + protected static final int TEXTURE_LINE_COUNT; + + static { + StringBuilder charsBuilder = new StringBuilder(); + for(char i = 0;i != 128; i++) charsBuilder.append(i); + + charsBuilder.append("ÆØåæéø"); // danish + charsBuilder.append("ÄÖÜẞäöüß"); // german + charsBuilder.append("¡¿áéíñóú–"); // spanish + charsBuilder.append("óąćꣳńŚśźŻż"); // polish + + // TODO russian characters are breaking some characters like + for some reason + charsBuilder.append("АБВГДЖЗИКЛМНОПРСТУФХШабвгдежзийклмнопрстуфхцчшщыьэюяё"); // russian + + + CHARACTERS = charsBuilder.toString(); + CHARACTER_COUNT = CHARACTERS.length(); + + TEXTURE_LINE_LEN = (int)Math.sqrt(CHARACTER_COUNT); + TEXTURE_LINE_COUNT = (int)Math.ceil(CHARACTER_COUNT/(float)TEXTURE_LINE_LEN); + } + + protected static final int TEXTURE_GENERATION_SIZE = 30; + + private final float scalingFactor; + + private UnifiedDrawHandle geometry; + private TextureHandle font_tex; + protected int gentex_line_height; + protected int tex_height; + protected int tex_width; + protected int[] char_widths = new int[CHARACTER_COUNT]; + protected final int[] heightPerSize = new int[EFontSize.values().length]; + + private final static int char_spacing = 2; // spacing between two characters (otherwise j and f would overlap with the next character) + + protected final T drawContext; + + /** + * Creates a new text drawer. + * + */ + public AbstractTextDrawer(T drawContext, float guiScale) { + this.drawContext = drawContext; + scalingFactor = guiScale <= 0.51f ? calculateScalingFactor() : guiScale; + + int descent = init(); + generateTexture(); + generateGeometry(descent); + } + + private int getMaxLen() { + int max_len = 0; + for(int l = 0;l != TEXTURE_LINE_COUNT;l++) { + int current_len = 0; + for(int c = 0;c != TEXTURE_LINE_LEN;c++) { + int i = l*TEXTURE_LINE_LEN+c; + if(i == CHARACTER_COUNT) break; + current_len += char_widths[i]+char_spacing; + max_len = Math.max(max_len, current_len); + } + } + return max_len; + } + + protected abstract float calculateScalingFactor(); + + protected abstract int init(); + + protected abstract int[] getRGB(); + + protected abstract void setupBitmapDraw(); + + protected abstract void drawChar(char[] character, int x, int y); + + protected abstract void endDraw(); + + protected abstract void setTexParams(); + + private void generateTexture() { + int max_len = getMaxLen(); + + tex_width = max_len; + tex_height = gentex_line_height*16; + + setupBitmapDraw(); + + for(int l = 0;l != TEXTURE_LINE_COUNT;l++) { + int line_offset = 0; + for (int c = 0; c != TEXTURE_LINE_LEN; c++) { + int i = l*TEXTURE_LINE_LEN+c; + if(i == CHARACTER_COUNT) break; + drawChar(new char[]{CHARACTERS.charAt(i)}, line_offset, l * gentex_line_height); + line_offset += char_widths[i]+char_spacing; + } + } + + ShortBuffer bfr = ByteBuffer.allocateDirect(tex_width*tex_height*2).order(ByteOrder.nativeOrder()).asShortBuffer(); + + int[] pixels = getRGB(); + endDraw(); + + final short alpha_channel = 0b1111; + final short alpha_white = ~alpha_channel; + for (int y = 0; y != tex_height; y++) { + for(int x = 0;x != tex_width;x++) { + int pixel = pixels[(tex_height-y-1)*tex_width+x]; + + short a = ((pixel >> 24) != 0 ? alpha_channel : 0); + bfr.put((short) (a | alpha_white)); + } + } + + bfr.rewind(); + font_tex = drawContext.generateTexture(max_len, tex_height, bfr, "text-drawer"); + + setTexParams(); + } + + private void generateGeometry(int descent) { + float[] geodata = new float[CHARACTER_COUNT*4*4]; + for(int l = 0;l != TEXTURE_LINE_COUNT;l++) { + int line_offset = 0; + for (int c = 0; c != TEXTURE_LINE_LEN; c++) { + if(l*TEXTURE_LINE_LEN+c == CHARACTER_COUNT) break; + + float dx = line_offset; + float dy = tex_height-(l*gentex_line_height+descent); + + float dw = char_widths[l*TEXTURE_LINE_LEN+c]; + float dh = gentex_line_height; + + float[] data = GLDrawContext.createQuadGeometry(0, 0,dw/(float)gentex_line_height, 1, dx/tex_width, dy/tex_height, (dx+dw)/tex_width, (dy+dh)/tex_height); + System.arraycopy(data, 0, geodata, (l*TEXTURE_LINE_LEN+c)*4*4, 4*4); + + line_offset += char_widths[l*TEXTURE_LINE_LEN+c]+char_spacing; + } + } + geometry = drawContext.createUnifiedDrawCall(CHARACTER_COUNT*4, "text-drawer", font_tex, geodata); + geometry.forceNoCache(); + } + + public TextDrawer derive(EFontSize size) { + return new SizedTextDrawer(size); + } + + + private class SizedTextDrawer implements TextDrawer { + + private final float widthFactor; + private final float lineHeight; + + private SizedTextDrawer(EFontSize size) { + lineHeight = heightPerSize[size.ordinal()]*scalingFactor; + widthFactor = lineHeight/gentex_line_height; + } + + private void drawChar(float x, float y, AbstractColor color, char c) { + geometry.offset = indexOf(c)*4; + geometry.drawComplexQuad(EUnifiedMode.TEXTURE, x, y, 0, lineHeight, lineHeight, color, 1); + geometry.flush(); + } + + /* + * (non-Javadoc) + * + * @see go.graphics.swing.text.TextDrawer#drawString(int, int, java.lang.String) + */ + @Override + public void drawString(float x, float y, AbstractColor color, String string) { + float x_offset = 0; + float y_offset = 0; + + for(int i = 0;i != string.length();i++) { + if(string.charAt(i) == '\n') { + y_offset += lineHeight; + x_offset = 0; + } else { + drawChar(x+x_offset, y+y_offset, color, string.charAt(i)); + x_offset += char_widths[indexOf(string.charAt(i))]*widthFactor; + } + } + } + + private int indexOf(char c) { + int indexOf = CHARACTERS.indexOf(c); + if(indexOf == -1) return 0; + return indexOf; + } + + @Override + public float getWidth(String string) { + float tmp_width = 0; + for(int i = 0;i != string.length();i++) { + if(string.charAt(i) != '\n') { + tmp_width += char_widths[indexOf(string.charAt(i))]*widthFactor; + } + } + return tmp_width; + } + + @Override + public float getHeight(String string) { + float tmp_height = lineHeight; + for(int i = 0;i != string.length();i++) { + if(string.charAt(i) == '\n') { + tmp_height += lineHeight; + } + } + return tmp_height; + } + } +} diff --git a/go.graphics/src/main/java/go/graphics/text/TextDrawer.java b/go.graphics/src/main/java/go/graphics/text/TextDrawer.java index f7660562b4..ce65011120 100644 --- a/go.graphics/src/main/java/go/graphics/text/TextDrawer.java +++ b/go.graphics/src/main/java/go/graphics/text/TextDrawer.java @@ -28,23 +28,34 @@ public interface TextDrawer { * @param text * The text to render. */ - void renderCentered(float cx, float cy, String text); + default void renderCentered(float cx, float cy, String text) { + drawString(cx-(getWidth(text)/2), cy-(getHeight(text)/2), text); + } /** * Draws a string - * - * @param x + * @param x + * Left bound. + * @param y + * Bottom line. + * @param string + */ + default void drawString(float x, float y, String string) { + drawString(x, y, null, string); + } + + /** + * Draws a string + * @param x * Left bound. * @param y * Bottom line. + * @param color * @param string - * The string to render */ - void drawString(float x, float y, String string); + void drawString(float x, float y, AbstractColor color, String string); float getWidth(String string); float getHeight(String string); - - void setColor(AbstractColor color); } \ No newline at end of file diff --git a/go.graphics/src/main/resources/background.vert b/go.graphics/src/main/resources/background.vert index 96ee427936..74ad0bc78a 100644 --- a/go.graphics/src/main/resources/background.vert +++ b/go.graphics/src/main/resources/background.vert @@ -7,7 +7,6 @@ attribute vec2 texcoord; attribute float color; uniform mat4 globalTransform; -uniform vec2 transform; uniform mat4 projection; uniform mat4 height; @@ -16,7 +15,6 @@ varying vec2 frag_texcoord; void main() { vec4 transformed = height * vec4(vertex, 1); - transformed.xy += transform; transformed.z = -.1; gl_Position = projection * globalTransform * transformed; diff --git a/go.graphics/src/main/resources/color.frag b/go.graphics/src/main/resources/color.frag deleted file mode 100644 index 67ad7b4b65..0000000000 --- a/go.graphics/src/main/resources/color.frag +++ /dev/null @@ -1,7 +0,0 @@ -#version 100 - -uniform mediump vec4 color; - -void main() { - gl_FragColor = color; -} diff --git a/go.graphics/src/main/resources/color.vert b/go.graphics/src/main/resources/color.vert deleted file mode 100644 index 0400682ee6..0000000000 --- a/go.graphics/src/main/resources/color.vert +++ /dev/null @@ -1,15 +0,0 @@ -#version 100 - -precision mediump float; - -attribute vec2 vertex; - -uniform mat4 globalTransform; -uniform vec3 transform[2]; -uniform mat4 projection; - -void main() { - vec4 transformed = vec4(vertex, 0, 1); - transformed.xyz = (transformed.xyz*transform[1])+transform[0]; - gl_Position = projection * globalTransform * transformed; -} diff --git a/go.graphics/src/main/resources/tex-unified.frag b/go.graphics/src/main/resources/tex-unified.frag deleted file mode 100644 index f6434b5252..0000000000 --- a/go.graphics/src/main/resources/tex-unified.frag +++ /dev/null @@ -1,31 +0,0 @@ -#version 100 - -precision mediump float; - -varying vec2 frag_texcoord; - -uniform sampler2D texHandle; -uniform vec3 uni_info; // x=image, y=shadow, z=intensity -uniform vec4 color; - -void main() { - vec4 tex_color = texture2D(texHandle, frag_texcoord); - gl_FragColor = vec4(0,0,0,0); - - if(uni_info.x > 0.1) { // draw image - if(tex_color.a < 0.1 && tex_color.r > 0.1) { // torso pixel - gl_FragColor.rgb = color.rgb*tex_color.b; - gl_FragColor.a = color.a; - } else { - gl_FragColor = tex_color; - } - } - - if(uni_info.y > 0.1 && tex_color.g > 0.1 && tex_color.a < 0.1) { // shadow pixel - gl_FragColor.rgba = tex_color.aaag; - } - - gl_FragColor.rgb *= uni_info.z; - - if(gl_FragColor.a < 0.5) discard; -} diff --git a/go.graphics/src/main/resources/tex.frag b/go.graphics/src/main/resources/tex.frag deleted file mode 100644 index 8a8d5ad89a..0000000000 --- a/go.graphics/src/main/resources/tex.frag +++ /dev/null @@ -1,13 +0,0 @@ -#version 100 - -precision mediump float; - -varying vec2 frag_texcoord; - -uniform sampler2D texHandle; -uniform vec4 color; - -void main() { - gl_FragColor = texture2D(texHandle, frag_texcoord)*color; - if(gl_FragColor.a < 0.5) discard; -} diff --git a/go.graphics/src/main/resources/unified-array.frag b/go.graphics/src/main/resources/unified-array.frag new file mode 100644 index 0000000000..c0e9a0faf6 --- /dev/null +++ b/go.graphics/src/main/resources/unified-array.frag @@ -0,0 +1,48 @@ +#version 300 es + +#extension GL_NV_fragdepth : enable + +precision mediump float; + +in vec2 frag_texcoord; +flat in int frag_mode; +in float frag_color[5]; + +uniform sampler2D texHandle; +uniform float shadow_depth; + +out vec4 fragColor; + +void main() { + float fragDepth = gl_FragCoord.z; + fragColor = vec4(frag_color[0], frag_color[1], frag_color[2], frag_color[3]); + + bool textured = frag_mode!=0; + + if(textured) { + vec4 tex_color = texture(texHandle, frag_texcoord); + + bool image_fence = frag_mode>0; + bool torso_fence = frag_mode>1; + bool shadow_fence = abs(float(frag_mode))>2.0; + + if(torso_fence && tex_color.a < 0.1 && tex_color.r > 0.1) { // torso pixel + fragColor.rgb *= tex_color.b; + } else if(shadow_fence && tex_color.a < 0.1 && tex_color.g > 0.1) { // shadow pixel + fragColor.rgba = tex_color.aaag; + fragDepth += shadow_depth; + } else if(image_fence) { // image pixel + if(!torso_fence && !shadow_fence) { + fragColor *= tex_color; + } else { + fragColor = tex_color; + } + } + } + + if(fragColor.a < 0.5) discard; + + fragColor.rgb *= frag_color[4]; + + gl_FragDepth = fragDepth; +} diff --git a/go.graphics/src/main/resources/unified-array.vert b/go.graphics/src/main/resources/unified-array.vert new file mode 100644 index 0000000000..d106a3a26a --- /dev/null +++ b/go.graphics/src/main/resources/unified-array.vert @@ -0,0 +1,36 @@ +#version 300 es + +precision mediump float; + +in vec2 vertex; //attribute +in vec2 texcoord; //attribute + +uniform mat4 globalTransform; +uniform mat4 projection; + +uniform vec4 color[100]; +uniform vec4 transform[100]; + +out vec2 frag_texcoord; +out float frag_color[5]; +flat out int frag_mode; + +void main() { + vec4 transformed = vec4(vertex, 0, 1); + transformed.xyz += transform[gl_InstanceID].xyz; + gl_Position = projection * globalTransform * transformed; + + + frag_color[0] = color[gl_InstanceID].r; + frag_color[1] = color[gl_InstanceID].g; + frag_color[2] = color[gl_InstanceID].b; + frag_color[3] = color[gl_InstanceID].a; + + float int_mode = transform[gl_InstanceID].w/10.0; + + + frag_mode = int(floor(int_mode)); + frag_color[4] = (int_mode-float(frag_mode))*10.0-1.0; + + if(frag_mode != 0) frag_texcoord = texcoord; +} diff --git a/go.graphics/src/main/resources/unified.frag b/go.graphics/src/main/resources/unified.frag new file mode 100644 index 0000000000..66f2451a70 --- /dev/null +++ b/go.graphics/src/main/resources/unified.frag @@ -0,0 +1,57 @@ +#version 100 + +#extension GL_NV_fragdepth : enable + +precision mediump float; + +varying vec2 frag_texcoord; + +uniform sampler2D texHandle; +uniform float shadow_depth; + +uniform lowp int mode; +uniform float color[5]; // r,g,b,a, intensity + +void main() { + float fragDepth = gl_FragCoord.z; + vec4 fragColor = vec4(color[0], color[1], color[2], color[3]); + + bool textured = mode!=0; + + if(textured) { + vec4 tex_color = texture2D(texHandle, frag_texcoord); + + bool image_fence = mode>0; + bool torso_fence = mode>1; + bool shadow_fence = abs(float(mode))>2.0; + + if(torso_fence && tex_color.a < 0.1 && tex_color.r > 0.1) { // torso pixel + fragColor.rgb *= tex_color.b; + } else if(shadow_fence && tex_color.a < 0.1 && tex_color.g > 0.1) { // shadow pixel + fragColor.rgba = tex_color.aaag; + fragDepth += shadow_depth; + } else if(image_fence) { // image pixel + if(!torso_fence && !shadow_fence) { + fragColor *= tex_color; + } else { + fragColor = tex_color; + } + } + } + + if(fragColor.a < 0.5) discard; + + fragColor.rgb *= color[4]; + + gl_FragColor = fragColor; + + #ifdef GL_NV_fragdepth + gl_FragDepth = fragDepth; + #else + #ifndef GL_ES + gl_FragDepth = fragDepth; + #endif + #endif + + +} diff --git a/go.graphics/src/main/resources/tex.vert b/go.graphics/src/main/resources/unified.vert similarity index 85% rename from go.graphics/src/main/resources/tex.vert rename to go.graphics/src/main/resources/unified.vert index ae7943b8d6..1762d83787 100644 --- a/go.graphics/src/main/resources/tex.vert +++ b/go.graphics/src/main/resources/unified.vert @@ -9,11 +9,14 @@ uniform mat4 globalTransform; uniform vec3 transform[2]; uniform mat4 projection; +uniform lowp int mode; + varying vec2 frag_texcoord; void main() { vec4 transformed = vec4(vertex, 0, 1); transformed.xyz = (transformed.xyz*transform[1])+transform[0]; gl_Position = projection * globalTransform * transformed; - frag_texcoord = texcoord; + + if(mode != 0) frag_texcoord = texcoord; } diff --git a/jsettlers.common/src/main/java/jsettlers/common/Color.java b/jsettlers.common/src/main/java/jsettlers/common/Color.java index ac7a0db051..fbf27f0630 100644 --- a/jsettlers.common/src/main/java/jsettlers/common/Color.java +++ b/jsettlers.common/src/main/java/jsettlers/common/Color.java @@ -23,7 +23,7 @@ * * @author Michael Zangl */ -public final class Color extends AbstractColor{ +public final class Color extends AbstractColor { /** * Constant to quickly access black. */ diff --git a/jsettlers.common/src/main/java/jsettlers/common/CommonConstants.java b/jsettlers.common/src/main/java/jsettlers/common/CommonConstants.java index 5e375732e5..76f199cd4c 100644 --- a/jsettlers.common/src/main/java/jsettlers/common/CommonConstants.java +++ b/jsettlers.common/src/main/java/jsettlers/common/CommonConstants.java @@ -14,8 +14,6 @@ *******************************************************************************/ package jsettlers.common; -import jsettlers.common.ai.EPlayerType; - public abstract class CommonConstants { /** * A byte value indicating that the given position is visible. @@ -26,6 +24,14 @@ public abstract class CommonConstants { */ public static final int FOG_OF_WAR_EXPLORED = 50; + /** + * How much the current fog of war status can be changed per second + */ + public static final int FOG_OF_WAR_DIM = 30; + + public static final int FOG_OF_WAR_DIM_FRAMERATE = 15; + public static final int FOG_OF_WAR_REF_UPDATE_FRAMERATE = 1; + /** * Radius of the area occupied by towers. */ diff --git a/jsettlers.common/src/main/java/jsettlers/common/map/IDirectGridProvider.java b/jsettlers.common/src/main/java/jsettlers/common/map/IDirectGridProvider.java index 388d903302..9b52baa436 100644 --- a/jsettlers.common/src/main/java/jsettlers/common/map/IDirectGridProvider.java +++ b/jsettlers.common/src/main/java/jsettlers/common/map/IDirectGridProvider.java @@ -10,4 +10,5 @@ public interface IDirectGridProvider { IMovable[] getMovableArray(); BitSet getBorderArray(); byte[][] getVisibleStatusArray(); + byte[] getHeightArray(); } diff --git a/jsettlers.common/src/main/java/jsettlers/common/statistics/IGameTimeProvider.java b/jsettlers.common/src/main/java/jsettlers/common/statistics/IGameTimeProvider.java index a9112ba415..3636a9a44f 100644 --- a/jsettlers.common/src/main/java/jsettlers/common/statistics/IGameTimeProvider.java +++ b/jsettlers.common/src/main/java/jsettlers/common/statistics/IGameTimeProvider.java @@ -27,6 +27,11 @@ public int getGameTime() { return 0; } + @Override + public float getGameSpeed() { + return 0; + } + @Override public boolean isGamePausing() { return false; @@ -40,6 +45,13 @@ public boolean isGamePausing() { */ int getGameTime(); + /** + * Get the current game time factor + * + * @return The current game speed in multiples of 1 + */ + float getGameSpeed(); + /** * Gets if the game is pausing. * diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/font/FontDrawer.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/font/FontDrawer.java index c57ccd62de..ef4e031e61 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/font/FontDrawer.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/font/FontDrawer.java @@ -18,7 +18,6 @@ import go.graphics.GLDrawContext; import go.graphics.text.EFontSize; import go.graphics.text.TextDrawer; -import jsettlers.common.Color; import jsettlers.common.images.DirectImageLink; import jsettlers.graphics.image.Image; import jsettlers.graphics.map.draw.ImageProvider; @@ -52,17 +51,12 @@ public FontDrawer(GLDrawContext gl, EFontSize size) { this.size = size; } - @Override - public void renderCentered(float cx, float cy, String text) { - drawString(cx - getWidth(text) / 2, cy - getHeight(text) / 2, text); - } - private int getCharIndex(char c) { return CHARACTERS.indexOf(Character.toUpperCase(c)); } @Override - public void drawString(float x, float y, String string) { + public void drawString(float x, float y, AbstractColor color, String string) { float cursorX = 0; float top = y + size.getSize(); @@ -110,8 +104,4 @@ public float getHeight(String string) { return size.getSize(); } - @Override - public void setColor(AbstractColor color) { - // TODO Support color changes. - } } diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/ImageIndexImage.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/ImageIndexImage.java index ef26086bb9..c1db562fb4 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/ImageIndexImage.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/ImageIndexImage.java @@ -14,14 +14,9 @@ *******************************************************************************/ package jsettlers.graphics.image; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -import go.graphics.EGeometryType; +import go.graphics.EPrimitiveType; import go.graphics.GLDrawContext; -import go.graphics.GeometryHandle; -import go.graphics.IllegalBufferException; -import go.graphics.SharedGeometry; +import go.graphics.UnifiedDrawHandle; import jsettlers.common.Color; /** @@ -35,7 +30,7 @@ public class ImageIndexImage extends Image { private final short width; private final short height; private final float[] geometry; - private SharedGeometry.SharedGeometryHandle geometryIndex = null; + private UnifiedDrawHandle geometryIndex = null; private final ImageIndexTexture texture; private final int offsetX; private final int offsetY; @@ -81,7 +76,9 @@ public class ImageIndexImage extends Image { this.vmax = vmax; this.isTorso = isTorso; - geometry = createGeometry(); + geometry = GLDrawContext.createQuadGeometry(-offsetX + IMAGE_DRAW_OFFSET, -offsetY + height + IMAGE_DRAW_OFFSET, + -offsetX + width + IMAGE_DRAW_OFFSET,-offsetY + IMAGE_DRAW_OFFSET, + umin, vmin, umax, vmax); } @Override @@ -105,39 +102,19 @@ public void drawOnlyImageAt(GLDrawContext gl, float x, float y, float z, Color t } } - private void draw(GLDrawContext gl, SharedGeometry.SharedGeometryHandle handle, float x, float y, float z, float sx, float sy, float sz, Color color, float fow) { - try { - if(handle == null) geometryIndex = handle = SharedGeometry.addGeometry(gl, geometry); - - gl.draw2D(handle.geometry, texture.getTextureIndex(gl), EGeometryType.Quad, handle.index, 4, x, y, z, sx, sy, sz, color, fow); - } catch (IllegalBufferException e) { - try { - texture.recreateTexture(); - gl.draw2D(handle.geometry, texture.getTextureIndex(gl), EGeometryType.Quad, handle.index, 4, x, y, z, sx, sy, sz, color, fow); - } catch (IllegalBufferException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } - } - } + private void draw(GLDrawContext gl, UnifiedDrawHandle handle, float x, float y, float z, float sx, float sy, float sz, Color color, float fow) { + if(handle == null || !handle.isValid()) geometryIndex = handle = gl.createUnifiedDrawCall(4, "image-index", texture.getTextureIndex(gl), geometry); - private float[] createGeometry() { - return SharedGeometry.createQuadGeometry(-offsetX + IMAGE_DRAW_OFFSET, -offsetY + IMAGE_DRAW_OFFSET, - -offsetX + width + IMAGE_DRAW_OFFSET,-offsetY + height + IMAGE_DRAW_OFFSET, - umin, vmax, umax, vmin); + handle.drawSimple(EPrimitiveType.Quad, x, y, z, sx, sy, color, fow); } - private SharedGeometry.SharedGeometryHandle imageRectHandle = null; + private UnifiedDrawHandle imageRectHandle = null; @Override public void drawImageAtRect(GLDrawContext gl, float x, float y, float width, float height) { - try { - if(imageRectHandle == null) imageRectHandle = SharedGeometry.addGeometry(gl, SharedGeometry.createQuadGeometry(0,1, 1, 0, umin, vmin, umax, vmax)); - draw(gl, imageRectHandle, x, y, 0, width, height, 0, null, 1); - } catch (IllegalBufferException e) { - e.printStackTrace(); - } + if(imageRectHandle == null || !imageRectHandle.isValid()) imageRectHandle = gl.createUnifiedDrawCall(4, "image-index", texture.getTextureIndex(gl), GLDrawContext.createQuadGeometry(0,1, 1, 0, umin, vmin, umax, vmax)); + draw(gl, imageRectHandle, x, y, 0, width, height, 0, null, 1); if (torso != null) { torso.drawImageAtRect(gl, x, y, width, height); diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/ImageIndexTexture.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/ImageIndexTexture.java index 713fb601a5..0de208f520 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/ImageIndexTexture.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/ImageIndexTexture.java @@ -87,11 +87,4 @@ private static int nextLowerPOT(double number) { } return i; } - - /** - * Informs this texture that it should be recreated. - */ - public void recreateTexture() { - textureIndex = null; - } } diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/MultiImageImage.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/MultiImageImage.java deleted file mode 100644 index 81459e6c09..0000000000 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/MultiImageImage.java +++ /dev/null @@ -1,146 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - *******************************************************************************/ -package jsettlers.graphics.image; - -import go.graphics.EGeometryType; -import go.graphics.GLDrawContext; -import go.graphics.IllegalBufferException; -import go.graphics.SharedGeometry; -import go.graphics.TextureHandle; -import jsettlers.common.Color; -import jsettlers.graphics.image.reader.ImageMetadata; - -/** - * This is an image inside a multi image map. - * - * @author michael - */ -public class MultiImageImage extends Image { - private final MultiImageMap map; - - private SharedGeometry.SharedGeometryHandle settlerGeometry; - private SharedGeometry.SharedGeometryHandle torsoGeometry; - private float[] settlerFloats; - private float[] settlerRectFloats; - private float[] torsoFloats = null; - - /** - * This is the data that is required to store the position of a {@link MultiImageImage}. - * - * @author Michael Zangl. - * - */ - private final class Data { - private int width; - - private int height; - - private int offsetX; - - private int offsetY; - - private float umin; - - private float umax; - - private float vmin; - - private float vmax; - } - - private final Data settler; - - private final Data torso; - - public MultiImageImage(MultiImageMap map, ImageMetadata settlerMeta, - int settlerx, int settlery, ImageMetadata torsoMeta, int torsox, - int torsoy) { - this.map = map; - - settler = new Data(); - settlerFloats = createGeometry(map, settlerMeta, settlerx, settlery, settler); - settlerRectFloats = SharedGeometry.createQuadGeometry(0, 1, 1, 0, settler.umin, settler.vmin, settler.umax, settler.vmax); - if (torsoMeta != null) { - torso = new Data(); - torsoFloats = createGeometry(map, torsoMeta, torsox, torsoy, torso); - } else { - torso = null; - torsoGeometry = null; - } - } - - private static final float IMAGE_DRAW_OFFSET = 0.5f; - - private static float[] createGeometry(MultiImageMap map, - ImageMetadata settlerMeta, int settlerx, int settlery, Data data) { - data.width = settlerMeta.width; - data.height = settlerMeta.height; - data.offsetX = settlerMeta.offsetX; - data.offsetY = settlerMeta.offsetY; - - data.umin = (float) settlerx / map.getWidth(); - data.umax = (float) (settlerx + settlerMeta.width) / map.getWidth(); - - data.vmin = (float) (settlery + settlerMeta.height) / map.getHeight(); - data.vmax = (float) settlery / map.getHeight(); - return SharedGeometry.createQuadGeometry(settlerMeta.offsetX + IMAGE_DRAW_OFFSET, -settlerMeta.offsetY + IMAGE_DRAW_OFFSET, - settlerMeta.offsetX + settlerMeta.width + IMAGE_DRAW_OFFSET, -settlerMeta.offsetY - settlerMeta.height + IMAGE_DRAW_OFFSET, - data.umin, data.vmax, data.umax, data.vmin); - } - - @Override - public void drawOnlyImageAt(GLDrawContext gl, float x, float y, float z, Color torsoColor, float fow) { - TextureHandle texture = map.getTexture(gl); - - try { - if(settlerGeometry == null) { - settlerGeometry = SharedGeometry.addGeometry(gl, settlerFloats); - if(torsoFloats != null) torsoGeometry = SharedGeometry.addGeometry(gl, torsoFloats); - } - gl.draw2D(settlerGeometry.geometry, texture, EGeometryType.Quad, settlerGeometry.index, 4, x, y, z, 1, 1, 1, null, fow); - - if(torsoFloats == null || torsoColor == null) return; - gl.draw2D(torsoGeometry.geometry, texture, EGeometryType.Quad, torsoGeometry.index, 4, x, y, z, 1, 1, 1, torsoColor, fow); - } catch (IllegalBufferException e) { - e.printStackTrace(); - } - } - - private static SharedGeometry.SharedGeometryHandle rectHandle; - - @Override - public void drawImageAtRect(GLDrawContext gl, float x, float y, float width, float height) { - try { - if(rectHandle == null) { - rectHandle = SharedGeometry.addGeometry(gl, settlerRectFloats); - settlerRectFloats = null; - } - - gl.draw2D(rectHandle.geometry, map.getTexture(gl), EGeometryType.Quad, rectHandle.index, 4, x, y, 0, width, height, 0, null, 1); - } catch (IllegalBufferException e) { - handleIllegalBufferException(e); - } - } - - @Override - public int getWidth() { - return settler.width; - } - - @Override - public int getHeight() { - return settler.height; - } -} diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/MultiImageMap.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/MultiImageMap.java deleted file mode 100644 index a8e45daed0..0000000000 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/MultiImageMap.java +++ /dev/null @@ -1,286 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 - 2018 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - *******************************************************************************/ -package jsettlers.graphics.image; - -import go.graphics.GLDrawContext; -import go.graphics.TextureHandle; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.ShortBuffer; - -import jsettlers.common.resources.ResourceManager; -import jsettlers.graphics.map.draw.GLPreloadTask; -import jsettlers.graphics.map.draw.ImageProvider; -import jsettlers.graphics.image.reader.AdvancedDatFileReader; -import jsettlers.graphics.image.reader.DatBitmapReader; -import jsettlers.graphics.image.reader.ImageArrayProvider; -import jsettlers.graphics.image.reader.ImageMetadata; -import jsettlers.graphics.image.reader.bytereader.ByteReader; - -/** - * This is a map of multiple images of one sequence. It always contains the settler image and the torso. This class allows packing the settler images - * to a single, big texture. - * - * @author Michael Zangl - */ -public class MultiImageMap implements ImageArrayProvider, GLPreloadTask { - - private final int width; - private final int height; - private int drawx = 0; // x coordinate of free space - private int linetop = 0; - private int linebottom = 0; - private int drawpointer = 0; - private boolean drawEnabled = false; - private boolean textureValid = false; - private TextureHandle texture = null; - private ShortBuffer buffers; - private ByteBuffer byteBuffer; - private String name; - - private final File cacheFile; - - /** - * Creates a new {@link MultiImageMap}. - * - * @param width - * The width of the base image. - * @param height - * The height of the base image. - * @param id - * The id of the map. - * @see #addSequences(AdvancedDatFileReader, int[]) - */ - public MultiImageMap(int width, int height, String id, String name) { - this.width = width; - this.height = height; - this.name = name; - File root = new File(ResourceManager.getResourcesDirectory(), "cache"); - cacheFile = new File(root, "cache-" + id); - } - - private void allocateBuffers() { - byteBuffer = ByteBuffer.allocateDirect(width * height * 2); - byteBuffer.order(ByteOrder.nativeOrder()); - buffers = byteBuffer.asShortBuffer(); - } - - /** - * Adds a list of textures to this file. The images can be referenced by the image handles added to addTo. - * - * @param dfr - * The reader to read the textures from. - * @param sequenceIndexes - * The indexes where the sequences start. - * @throws IOException - * If the file could not be read. - */ - public synchronized void addSequences(AdvancedDatFileReader dfr, int[] sequenceIndexes) throws IOException { - allocateBuffers(); - - ImageMetadata settlermeta = new ImageMetadata(); - ImageMetadata reusableTorsometa = new ImageMetadata(); - for (int seqindex : sequenceIndexes) { - long[] settlers = dfr.getSettlerPointers(seqindex); - long[] torsos = dfr.getTorsoPointers(seqindex); - - for (int i = 0; i < settlers.length; i++) { - ByteReader reader; - reader = dfr.getReaderForPointer(settlers[i]); - DatBitmapReader.uncompressImage(reader, - dfr.getSettlerTranslator(), settlermeta, - this); - - ImageMetadata torsometa; - if (torsos != null) { - torsometa = reusableTorsometa; - reader = dfr.getReaderForPointer(torsos[i]); - if (reader != null) { - DatBitmapReader.uncompressImage(reader, - dfr.getTorsoTranslator(), - torsometa, this); - } - } - } - } - - // request a opengl rerender, or do it ourselves on the next image - textureValid = false; - ImageProvider.getInstance().addPreloadTask(this); - } - - /** - * Forces the regeneration of the cache file. - */ - public synchronized void writeCache() { - FileOutputStream out = null; - try { - cacheFile.getParentFile().mkdirs(); - cacheFile.delete(); - File tempFile = new File(cacheFile.getParentFile(), cacheFile.getName() + ".tmp"); - out = new FileOutputStream(tempFile); - - try { - byte[] line = new byte[this.width * 2]; - byteBuffer.rewind(); - while (byteBuffer.hasRemaining()) { - byteBuffer.get(line); - out.write(line); - } - } finally { - out.close(); - } - - tempFile.renameTo(cacheFile); - - buffers = null; - byteBuffer = null; - } catch (IOException e) { - if (out != null) { - try { - out.close(); - } catch (IOException e1) { - } - } - e.printStackTrace(); - } - } - - /** - * Checks if this image map is can be loaded from the cache instead of regenerating it. - * - * @return true iff this file is cached. - */ - public synchronized boolean hasCache() { - return cacheFile.isFile(); - } - - @Override - public void startImage(int imageWidth, int imageHeight) throws IOException { - if (this.width < drawx + imageWidth) { - if (linebottom + imageHeight <= this.height) { - linetop = linebottom; - drawx = 0; - } else { - drawEnabled = false; - System.err.println("Error adding image to texture: " - + "there is no space to open a new row"); - return; - } - } - - if (linetop + imageHeight < this.height) { - drawEnabled = true; - textureValid = false; - drawpointer = drawx + linetop * this.width; - drawx += imageWidth; - linebottom = Math.max(linebottom, linetop + imageHeight); - } else { - System.err.println("Error adding image to texture: " - + "Line to low"); - drawEnabled = false; - return; - } - } - - @Override - public void writeLine(short[] data, int length) throws IOException { - if (drawEnabled) { - int dp = drawpointer; - buffers.position(dp); - buffers.put(data, 0, length); - drawpointer = dp + this.width; - } - } - - /** - * Gets the width of the underlying texture. - * - * @return The width. - */ - public int getWidth() { - return width; - } - - /** - * Gets the height of the underlying texture. - * - * @return The height. - */ - public int getHeight() { - return height; - } - - /** - * Gets the texture handle. - * - * @param gl - * The gl context to use when creating the texutre. - * @return A valid texture handle. - */ - public TextureHandle getTexture(GLDrawContext gl) { - if (!textureValid || !texture.isValid()) { - if (texture != null) { - gl.deleteTexture(texture); - } - try { - loadTexture(gl); - } catch (IOException e) { - e.printStackTrace(); - } - } - return texture; - } - - private synchronized void loadTexture(GLDrawContext gl) throws - IOException { - if (buffers == null) { - allocateBuffers(); - FileInputStream in = new FileInputStream(cacheFile); - try { - byte[] line = new byte[this.width * 2]; - while (in.available() > 0) { - if (in.read(line) <= 0) { - throw new IOException(); - } - byteBuffer.put(line); - } - byteBuffer.rewind(); - } finally { - in.close(); - } - } - - buffers.rewind(); - texture = gl.generateTexture(width, height, buffers, name); - System.out.println("opengl Texture: " + texture - + ", thread: " + Thread.currentThread().toString()); - if (texture != null) { - textureValid = true; - } - buffers = null; - byteBuffer = null; - } - - @Override - public void run(GLDrawContext context) { - getTexture(context); - } -} diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/NullImage.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/NullImage.java index d95740c885..210627ce23 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/NullImage.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/NullImage.java @@ -14,13 +14,11 @@ *******************************************************************************/ package jsettlers.graphics.image; -import java.nio.ShortBuffer; +import java.nio.ByteBuffer; -import go.graphics.EGeometryFormatType; -import go.graphics.EGeometryType; +import go.graphics.EPrimitiveType; import go.graphics.GLDrawContext; -import go.graphics.GeometryHandle; -import go.graphics.IllegalBufferException; +import go.graphics.UnifiedDrawHandle; import jsettlers.common.Color; import jsettlers.graphics.image.reader.ImageMetadata; @@ -60,11 +58,11 @@ public static NullImage getInstance() { } private NullImage() { - super(ShortBuffer.allocate(1), 1, 1, 0, 0, "placeholder/null"); + super(ByteBuffer.allocateDirect(2).asShortBuffer(), 1, 1, 0, 0, "placeholder/null"); } - private static GeometryHandle nullGeometry = null; + private static UnifiedDrawHandle nullGeometry = null; private static final float[] nullData = new float[] { -HALFSIZE, @@ -79,14 +77,10 @@ private NullImage() { @Override public void drawOnlyImageAt(GLDrawContext gl, float x, float y, float z, Color torsoColor, float fow) { - try { - if(nullGeometry == null || !nullGeometry.isValid()) nullGeometry = gl.storeGeometry(nullData, EGeometryFormatType.VertexOnly2D, false, "placeholder/null"); + if(nullGeometry == null || !nullGeometry.isValid()) nullGeometry = gl.createUnifiedDrawCall(4, "placeholder/null", null, nullData); - gl.draw2D(nullGeometry, null, EGeometryType.Quad, 0, 4, x, y, z, 1, 1, 1, null, NULL_IMAGE_ALPHA); - gl.draw2D(nullGeometry, null, EGeometryType.LineLoop, 0, 4, x, y, z, 1, 1, 1, Color.RED, 1); - } catch (IllegalBufferException e) { - e.printStackTrace(); - } + nullGeometry.drawSimple(EPrimitiveType.Quad, x, y, z, 1, 1, null, NULL_IMAGE_ALPHA); + nullGeometry.drawSimple(EPrimitiveType.LineLoop, x, y, z, 1, 1, Color.RED, 1); } private static SingleImage guiinstance; diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/SettlerImage.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/SettlerImage.java index 369c353554..0344fdb9cf 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/SettlerImage.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/SettlerImage.java @@ -14,13 +14,12 @@ *******************************************************************************/ package jsettlers.graphics.image; -import java.nio.ShortBuffer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; -import go.graphics.EGeometryType; -import go.graphics.GL2DrawContext; +import go.graphics.EUnifiedMode; import go.graphics.GLDrawContext; import go.graphics.IllegalBufferException; -import go.graphics.SharedGeometry; import jsettlers.common.Color; import jsettlers.graphics.image.reader.ImageMetadata; @@ -33,9 +32,9 @@ */ public class SettlerImage extends SingleImage { + public static float shadow_offset = 0; private SingleImage torso = null; private SingleImage shadow = null; - private boolean gl2 = false; /** * Creates a new settler image. @@ -50,69 +49,29 @@ public SettlerImage(ImageMetadata metadata, short[] data, String name) { } @Override - protected void checkHandles(GLDrawContext gl) throws IllegalBufferException { - if ((texture == null || !texture.isValid())) { - gl2 = gl instanceof GL2DrawContext && (this.torso != null || this.shadow != null); - if(gl2) generateUData(); + protected void checkHandles(GLDrawContext gl) { + if (geometryIndex == null || !geometryIndex.isValid()) { + generateUData(); } - super.checkHandles(gl); - if(!gl2) { - if (torso != null && torso.getWidth() == getWidth() - && torso.getHeight() == getHeight() - && torso.getOffsetX() == getOffsetX() - && torso.getOffsetY() == getOffsetY()) { - torso.setGeometry(geometryIndex); - } - } - } - - private boolean gl2Draw(GLDrawContext gl, float x, float y, float z, Color torsoColor, float fow, boolean settler, boolean shadow) { - try { - checkHandles(gl); - if(!gl2) return false; - ((GL2DrawContext)gl).drawUnified2D(geometryIndex.geometry, texture, EGeometryType.Quad, geometryIndex.index, 4, settler, shadow, x, y, z, 1, 1, 1, torsoColor, fow); - } catch(IllegalBufferException e) { - e.printStackTrace(); - } - - return true; } @Override public void drawAt(GLDrawContext gl, float x, float y, float z, Color torsoColor, float fow) { - if(gl2Draw(gl, x, y, z, torsoColor, fow, true, true)) return; - drawOnlyImageAt(gl, x, y, z, torsoColor, fow); - drawOnlyShadowAt(gl, x, y, z); + checkHandles(gl); + geometryIndex.drawComplexQuad(EUnifiedMode.SETTLER_SHADOW, x, y, z, 1, 1, torsoColor, fow); } @Override public void drawOnlyImageAt(GLDrawContext gl, float x, float y, float z, Color torsoColor, float fow) { - if(gl2Draw(gl, x, y, z, torsoColor, fow, true, false)) return; - try { - checkHandles(gl); - gl.draw2D(geometryIndex.geometry, texture, EGeometryType.Quad, geometryIndex.index, 4, x, y, z, 1, 1, 1, null, fow); - - if(torso != null && torsoColor != null) { - torso.checkHandles(gl); - gl.draw2D(torso.geometryIndex.geometry, torso.texture, EGeometryType.Quad, torso.geometryIndex.index, 4, x, y, z, 1, 1, 1, torsoColor, fow); - } - } catch (IllegalBufferException e) { - handleIllegalBufferException(e); - } + checkHandles(gl); + geometryIndex.drawComplexQuad(EUnifiedMode.SETTLER, x, y, z, 1, 1, torsoColor, fow); } @Override public void drawOnlyShadowAt(GLDrawContext gl, float x, float y, float z) { - if(gl2Draw(gl, x, y, z, null, 0, false, true)) return; - if(shadow != null) { - try { - shadow.checkHandles(gl); - gl.draw2D(shadow.geometryIndex.geometry, shadow.texture, EGeometryType.Quad, shadow.geometryIndex.index, 4, x, y, z, 1, 1, 1, null, 1); - } catch (IllegalBufferException e) { - handleIllegalBufferException(e); - } - } + checkHandles(gl); + geometryIndex.drawComplexQuad(EUnifiedMode.SHADOW_ONLY, x, y, z, 1, 1, Color.TRANSPARENT, 1); } /** @@ -162,7 +121,7 @@ private void generateUData() { twidth = tx-toffsetX; theight = ty-toffsetY; - tdata = ShortBuffer.allocate(twidth * theight); + tdata = ByteBuffer.allocateDirect(twidth * theight * 2).order(ByteOrder.nativeOrder()).asShortBuffer(); short[] temp = new short[0]; diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/SingleImage.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/SingleImage.java index 3c71d80121..0a4cd4ced3 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/SingleImage.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/SingleImage.java @@ -18,16 +18,14 @@ import java.nio.ByteOrder; import java.nio.ShortBuffer; -import go.graphics.EGeometryFormatType; -import go.graphics.EGeometryType; +import go.graphics.EPrimitiveType; import go.graphics.GLDrawContext; -import go.graphics.GeometryHandle; import go.graphics.IllegalBufferException; -import go.graphics.SharedGeometry; -import go.graphics.TextureHandle; +import go.graphics.ManagedUnifiedDrawHandle; import java.awt.image.BufferedImage; +import go.graphics.UnifiedDrawHandle; import jsettlers.common.Color; import jsettlers.graphics.image.reader.ImageMetadata; @@ -48,8 +46,7 @@ public class SingleImage extends Image implements ImageDataPrivider { protected final int offsetY; protected String name; - protected TextureHandle texture = null; - protected SharedGeometry.SharedGeometryHandle geometryIndex = null; + protected ManagedUnifiedDrawHandle geometryIndex = null; /** * Creates a new image by the given buffer. @@ -84,7 +81,14 @@ protected SingleImage(ShortBuffer data, int width, int height, int offsetX, * The data to use. */ public SingleImage(ImageMetadata metadata, short[] data, String name) { - this(ShortBuffer.wrap(data), metadata.width, metadata.height, metadata.offsetX, metadata.offsetY, name); + this(wrap(data), metadata.width, metadata.height, metadata.offsetX, metadata.offsetY, name); + } + + private static ShortBuffer wrap(short[] data) { + ShortBuffer bfr = ByteBuffer.allocateDirect(data.length*2).order(ByteOrder.nativeOrder()).asShortBuffer(); + bfr.put(data); + bfr.rewind(); + return bfr; } @Override @@ -109,12 +113,14 @@ public int getOffsetY() { @Override public void drawImageAtRect(GLDrawContext gl, float x, float y, float width, float height) { - try { - checkStaticHandles(gl); - gl.draw2D(rectHandle.geometry, texture, EGeometryType.Quad, rectHandle.index, 4, x, y, 0, twidth/this.width*width, theight/this.height*height, 0, null, 1); - } catch (IllegalBufferException e) { - handleIllegalBufferException(e); - } + checkStaticHandles(gl); + + // dark magic + float sx = width/(float)twidth; + float sy = height/(float)theight; + float tx = x - offsetX*sx; + float ty = y + height + offsetY*sy; + geometryIndex.drawSimple(EPrimitiveType.Quad, tx, ty, 0, sx, sy, null, 1); } @Override @@ -124,44 +130,24 @@ public ShortBuffer getData() { @Override public void drawOnlyImageAt(GLDrawContext gl, float x, float y, float z, Color torsoColor, float fow) { - try { - checkHandles(gl); - gl.draw2D(geometryIndex.geometry, texture, EGeometryType.Quad, geometryIndex.index, 4, x, y, z, 1, 1, 1, null, 1); - } catch (IllegalBufferException e) { - handleIllegalBufferException(e); - } + checkHandles(gl); + geometryIndex.drawSimple(EPrimitiveType.Quad, x, y, z, 1, 1, null, 1); } - protected void checkHandles(GLDrawContext gl) throws IllegalBufferException { - if (texture == null || !texture.isValid()) { - texture = gl.generateTexture(twidth, theight, tdata, name); - } - - if(geometryIndex == null || SharedGeometry.isInvalid(gl, geometryIndex)) { - geometryIndex = SharedGeometry.addGeometry(gl, getGeometry()); + protected void checkHandles(GLDrawContext gl) { + if(geometryIndex == null || !geometryIndex.isValid()) { + geometryIndex = gl.createManagedUnifiedDrawCall(tdata, toffsetX, toffsetY, twidth, theight); } } - private void checkStaticHandles(GLDrawContext gl) throws IllegalBufferException { + private void checkStaticHandles(GLDrawContext gl) { checkHandles(gl); if(buildHandle == null || !buildHandle.isValid()) { - buildHandle = gl.generateGeometry(3, EGeometryFormatType.Texture2D, true, "building-progress"); + buildHandle = gl.createUnifiedDrawCall(3, "building-progress", geometryIndex.texture, null); } - if(rectHandle == null || SharedGeometry.isInvalid(gl, rectHandle)) { - rectHandle = SharedGeometry.addGeometry(gl, SharedGeometry.createQuadGeometry(0, 1, 1, 0, 0, 0, 1, 1)); - } - } - - protected float[] getGeometry() { - return SharedGeometry.createQuadGeometry(toffsetX, -toffsetY, toffsetX + twidth, -toffsetY - theight, 0, 0, 1, 1); - } - - protected void setGeometry(SharedGeometry.SharedGeometryHandle geometry) { - geometryIndex = geometry; } - private static GeometryHandle buildHandle = null; - private static SharedGeometry.SharedGeometryHandle rectHandle = null; + private static UnifiedDrawHandle buildHandle = null; private static final ByteBuffer buildBfr = ByteBuffer.allocateDirect(4*4*3).order(ByteOrder.nativeOrder()); /** @@ -182,7 +168,7 @@ protected void setGeometry(SharedGeometry.SharedGeometryHandle geometry) { * @param color */ public void drawTriangle(GLDrawContext gl, float viewX, - float viewY, float u1, float v1, float u2, float v2, float u3, float v3, float color) { + float viewY, float u1, float v1, float u2, float v2, float u3, float v3, float z, float color) { try { checkStaticHandles(gl); float left = toffsetX + viewX; @@ -202,22 +188,23 @@ public void drawTriangle(GLDrawContext gl, float viewX, buildBfr.asFloatBuffer().put(new float[] { u1 * twidth, -v1 * theight, - u1, - v1, + geometryIndex.texX+u1*(geometryIndex.texWidth-geometryIndex.texX), + geometryIndex.texY+v1*(geometryIndex.texHeight-geometryIndex.texY), u2 * twidth, -v2 * theight, - u2, - v2, + geometryIndex.texX+u2*(geometryIndex.texWidth-geometryIndex.texX), + geometryIndex.texY+v2*(geometryIndex.texHeight-geometryIndex.texY), u3 * twidth, -v3 * theight, - u3, - v3, + geometryIndex.texX+u3*(geometryIndex.texWidth-geometryIndex.texX), + geometryIndex.texY+v3*(geometryIndex.texHeight-geometryIndex.texY), }); - gl.updateGeometryAt(buildHandle, 0, buildBfr); - gl.draw2D(buildHandle, texture, EGeometryType.Triangle, 0, 3, left, top, 0, 1, 1, 1, null, color); + buildHandle.texture = geometryIndex.texture; + gl.updateBufferAt(buildHandle.vertices, 0, buildBfr); + buildHandle.drawSimple(EPrimitiveType.Triangle, left, top, z, 1, 1, null, color); } catch (IllegalBufferException e) { handleIllegalBufferException(e); } diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/reader/AdvancedDatFileReader.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/reader/AdvancedDatFileReader.java index 1035cc5f20..3e4d41e308 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/reader/AdvancedDatFileReader.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/reader/AdvancedDatFileReader.java @@ -23,8 +23,6 @@ import java8.util.stream.IntStreams; import jsettlers.graphics.image.SingleImage; import jsettlers.graphics.image.Image; -import jsettlers.graphics.image.SingleImage; -import jsettlers.graphics.image.MultiImageMap; import jsettlers.graphics.image.NullImage; import jsettlers.graphics.image.SettlerImage; import jsettlers.graphics.image.reader.bytereader.ByteReader; @@ -719,17 +717,6 @@ public ByteReader getReaderForPointer(long pointer) throws IOException { return reader; } - @Override - public void generateImageMap(int width, int height, int[] sequences, String id, String name) throws IOException { - initializeIfNeeded(); - - MultiImageMap map = new MultiImageMap(width, height, id, name); - if (!map.hasCache()) { - map.addSequences(this, sequences); - map.writeCache(); - } - } - public DatBitmapTranslator getSettlerTranslator() { return settlerTranslator; } diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/reader/DatFileReader.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/reader/DatFileReader.java index fc30232d10..f924f05da0 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/reader/DatFileReader.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/reader/DatFileReader.java @@ -27,6 +27,4 @@ public interface DatFileReader extends DatFileSet { ByteReader getReaderForLandscape(int index) throws IOException; - void generateImageMap(int width, int height, int[] sequences, String id, String name) throws IOException; - } diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/reader/EmptyDatFile.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/reader/EmptyDatFile.java index 7dee9d1c7b..3fc3cd3c6d 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/reader/EmptyDatFile.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/reader/EmptyDatFile.java @@ -61,10 +61,4 @@ public DatBitmapTranslator getLandscapeTranslator() { public ByteReader getReaderForLandscape(int index) throws IOException { throw new UnsupportedOperationException(); } - - @Override - public void generateImageMap(int width, int height, int[] sequences, String id, String name) throws IOException { - throw new UnsupportedOperationException(); - } - } diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/MapContent.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/MapContent.java index 77c94f5b22..5f88ef4d83 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/MapContent.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/MapContent.java @@ -14,16 +14,12 @@ *******************************************************************************/ package jsettlers.graphics.map; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.util.BitSet; -import go.graphics.EGeometryFormatType; -import go.graphics.EGeometryType; +import go.graphics.EPrimitiveType; import go.graphics.GLDrawContext; -import go.graphics.GeometryHandle; -import go.graphics.IllegalBufferException; import go.graphics.UIPoint; +import go.graphics.UnifiedDrawHandle; import go.graphics.event.GOEvent; import go.graphics.event.GOEventHandler; import go.graphics.event.GOKeyEvent; @@ -69,7 +65,7 @@ import jsettlers.common.position.FloatRectangle; import jsettlers.common.position.ShortPoint2D; import jsettlers.common.selectable.ISelectionSet; -import jsettlers.common.statistics.FramerateComputer; +import go.graphics.FramerateComputer; import jsettlers.common.statistics.IGameTimeProvider; import jsettlers.common.player.IInGamePlayer; import jsettlers.common.player.EWinState; @@ -117,7 +113,6 @@ */ public final class MapContent implements RegionContent, IMapInterfaceListener, ActionFireable, ActionThreadBlockingListener { private static final AnimationSequence GOTO_ANIMATION = new AnimationSequence(new OriginalImageLink(EImageLinkType.SETTLER, 3, 1).getName(), 0, 2); - private static final float UI_OVERLAY_Z = .95f; private final class ZoomEventHandler implements GOModalEventHandler { float startZoom = context.getScreen().getZoom(); @@ -163,10 +158,11 @@ private void eventDataChanged(float zoomFactor, UIPoint p) { private final IMapObject[] objectsGrid; private final IMovable[] movableGrid; private final BitSet borderGrid; + private final byte[] heightGrid; private final short width, height; private final boolean isVisibleGridAvailable; - private final Background background = new Background(); + private final Background background; private final MapDrawContext context; @@ -219,12 +215,8 @@ private void eventDataChanged(float zoomFactor, UIPoint p) { private UIPoint currentSelectionAreaStart; private IInGamePlayer localPlayer; - public MapContent(IStartedGame game, SoundPlayer soundPlayer, int fpsLimit, ETextDrawPosition textDrawPosition) { - this(game, soundPlayer, fpsLimit, textDrawPosition,null); - } - - public MapContent(IStartedGame game, SoundPlayer soundPlayer, ETextDrawPosition textDrawPosition, IControls controls) { - this(game, soundPlayer, 60, textDrawPosition,controls); + public MapContent(IStartedGame game, SoundPlayer soundPlayer, ETextDrawPosition textDrawPosition) { + this(game, soundPlayer, textDrawPosition,null); } /** @@ -238,18 +230,20 @@ public MapContent(IStartedGame game, SoundPlayer soundPlayer, ETextDrawPosition * @param controls * The menus on the side (swing) or on the bottom (android) */ - private MapContent(IStartedGame game, SoundPlayer soundPlayer, int fpsLimit, ETextDrawPosition textDrawPosition, IControls controls) { + public MapContent(IStartedGame game, SoundPlayer soundPlayer, ETextDrawPosition textDrawPosition, IControls controls) { this.map = game.getMap(); if(map instanceof IDirectGridProvider) { IDirectGridProvider dgp = (IDirectGridProvider) map; objectsGrid = dgp.getObjectArray(); movableGrid = dgp.getMovableArray(); borderGrid = dgp.getBorderArray(); + heightGrid = dgp.getHeightArray(); isVisibleGridAvailable = true; } else { objectsGrid = null; movableGrid = null; borderGrid = null; + heightGrid = null; isVisibleGridAvailable = false; } width = map.getWidth(); @@ -261,13 +255,14 @@ private MapContent(IStartedGame game, SoundPlayer soundPlayer, int fpsLimit, ETe this.textDrawer = new ReplaceableTextDrawer(); this.context = new MapDrawContext(map); this.soundmanager = new SoundManager(soundPlayer); + this.background = new Background(); objectDrawer = new MapObjectDrawer(context, soundmanager); backgroundSound = new BackgroundSound(context, soundmanager); backgroundSound.start(); if (controls == null) { - this.controls = new OriginalControls(this, game.getInGamePlayer()); + this.controls = new OriginalControls(this, game); } else { this.controls = controls; } @@ -275,8 +270,6 @@ private MapContent(IStartedGame game, SoundPlayer soundPlayer, int fpsLimit, ETe this.connector = new MapInterfaceConnector(this); this.connector.addListener(this); - - map.setBackgroundListener(background); } private void resizeTo(int newWindowWidth, int newWindowHeight) { @@ -310,13 +303,13 @@ public void drawContent(GLDrawContext gl, int newWidth, int newHeight) { this.objectDrawer.increaseAnimationStep(); this.context.begin(gl); - long start = System.currentTimeMillis(); + long start = System.nanoTime(); FloatRectangle screen = this.context.getScreen().getPosition().bigger(SCREEN_PADDING); drawBackground(screen); - long backgroundDuration = System.currentTimeMillis() - start; + long backgroundDuration = System.nanoTime() - start; - start = System.currentTimeMillis(); + start = System.nanoTime(); drawMain(screen); if (scrollMarker != null) { @@ -327,10 +320,11 @@ public void drawContent(GLDrawContext gl, int newWidth, int newHeight) { } this.context.end(); - long foregroundDuration = System.currentTimeMillis() - start; + long foregroundDuration = System.nanoTime() - start; - start = System.currentTimeMillis(); - gl.setGlobalAttributes(0, 0, UI_OVERLAY_Z, 1, 1, 1); + start = System.nanoTime(); + gl.clearDepthBuffer(); + gl.setGlobalAttributes(0, 0, 0, 1, 1, 1); drawSelectionHint(gl); controls.drawAt(gl); drawMessages(gl); @@ -342,10 +336,10 @@ public void drawContent(GLDrawContext gl, int newWidth, int newHeight) { drawActionThreadSlow(gl); } drawTooltip(gl); - long uiTime = System.currentTimeMillis() - start; + long uiTime = System.nanoTime() - start; if (CommonConstants.ENABLE_GRAPHICS_TIMES_DEBUG_OUTPUT) { - System.out.println("Background: " + backgroundDuration + "ms, Foreground: " + foregroundDuration + "ms, UI: " + uiTime + "ms"); + System.out.println("Background: " + backgroundDuration/1000 + "µs, Foreground: " + foregroundDuration/1000 + "µs, UI: " + uiTime/1000 + "µs"); } } catch (Throwable t) { System.err.println("Main draw handler cought throwable:"); @@ -386,8 +380,7 @@ private void drawWinStateMsg(GLDrawContext gl) { Color color = localPlayer.getWinState() == EWinState.WON ? Color.GREEN : Color.RED; final String msg = Labels.getString("winstate_" + localPlayer.getWinState()); TextDrawer drawer = textDrawer.getTextDrawer(gl, EFontSize.HEADLINE); - drawer.setColor(color); - drawer.drawString(windowWidth / 2, windowHeight - 2 * EFontSize.HEADLINE.getSize(), msg); + drawer.drawString(windowWidth / 2, windowHeight - 2 * EFontSize.HEADLINE.getSize(), color, msg); } private void drawMessages(GLDrawContext gl) { @@ -402,24 +395,14 @@ private void drawMessages(GLDrawContext gl) { String name = getPlayername(m.getSender()) + ":"; Color color = context.getPlayerColor(m.getSender()); float width = drawer.getWidth(name); - float bright = color.getRed() + color.getGreen() + color.getBlue(); - if (bright < .9f) { - // black - drawer.setColor(new Color(1, 1, 1, a/2)); - } else if (bright < 2f) { - // bad visibility - drawer.setColor(new Color(1, 1, 1, a/2)); - } for (int i = -1; i < 3; i++) { - drawer.drawString(x + i, y - 1, name); + drawer.drawString(x + i, y - 1, new Color(1, 1, 1, a/2), name); } - drawer.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), a)); - drawer.drawString(x, y, name); + drawer.drawString(x, y, new Color(color.getRed(), color.getGreen(), color.getBlue(), a), name); x += width + 10; } - drawer.setColor(new Color(1, 1, 1, a)); - drawer.drawString(x, y, Labels.getString(m.getMessageLabel())); + drawer.drawString(x, y, new Color(1, 1, 1, a), Labels.getString(m.getMessageLabel())); messageIndex++; } @@ -438,51 +421,18 @@ private void adaptScreenSize() { oldScreen = newScreen; } - private GeometryHandle selectionArea = null; - private boolean updateSelectionArea = true; - private ByteBuffer selectionAreaBuffer = ByteBuffer.allocateDirect(4*2*4).order(ByteOrder.nativeOrder()); - - private void updateSelectionArea() { - float x1 = (float) this.currentSelectionAreaStart.getX(); - float y1 = (float) this.currentSelectionAreaStart.getY(); - float x2 = (float) this.currentSelectionAreaEnd.getX(); - float y2 = (float) this.currentSelectionAreaEnd.getY(); - - selectionAreaBuffer.putFloat(x1); - selectionAreaBuffer.putFloat(y1); - - selectionAreaBuffer.putFloat(x2); - selectionAreaBuffer.putFloat(y1); - - selectionAreaBuffer.putFloat(x2); - selectionAreaBuffer.putFloat(y2); - - selectionAreaBuffer.putFloat(x1); - selectionAreaBuffer.putFloat(y2); - } + private UnifiedDrawHandle selectionArea = null; private void drawSelectionHint(GLDrawContext gl) { if (this.currentSelectionAreaStart != null && this.currentSelectionAreaEnd != null) { if(selectionArea == null || !selectionArea.isValid()) { - selectionArea = gl.generateGeometry(4, EGeometryFormatType.VertexOnly2D, true, "selection-area"); - } - - if(updateSelectionArea) { - updateSelectionArea(); - try { - gl.updateGeometryAt(selectionArea, 0, selectionAreaBuffer); - } catch (IllegalBufferException e) { - e.printStackTrace(); - } - updateSelectionArea = false; + selectionArea = gl.createUnifiedDrawCall(4, "selection-area", null, new float[] {0, 0, 1, 0, 1, 1, 0, 1}); } - try { - gl.draw2D(selectionArea, null, EGeometryType.LineLoop, 0, 4, 0, 0, 0, 1, 1, 1, null, 1); - } catch (IllegalBufferException e) { - e.printStackTrace(); - } + float width = (float)(currentSelectionAreaEnd.getX() - currentSelectionAreaStart.getX()); + float height = (float)(currentSelectionAreaEnd.getY() - currentSelectionAreaStart.getY()); + selectionArea.drawSimple(EPrimitiveType.LineLoop, (float)currentSelectionAreaStart.getX(), (float)currentSelectionAreaStart.getY(), 0, width, height, null, 1); } } @@ -549,7 +499,7 @@ private void drawMain(FloatRectangle screen) { double bottomDrawY = screen.getMinY() - OVERDRAW_BOTTOM_PX; boolean linePartiallyVisible = true; - for (int line = 0; line < area.getHeight() + 50 && linePartiallyVisible; line++) { + for(int line = 0; line < area.getHeight() + 50 && linePartiallyVisible; line++) { int y = area.getLineY(line); if (y < 0) { continue; @@ -561,10 +511,10 @@ private void drawMain(FloatRectangle screen) { int endX = Math.min(area.getLineEndX(line), width - 1); int startX = Math.max(area.getLineStartX(line), 0); - for (int x = startX; x <= endX; x++) { + for(int x = startX; x <= endX; x++) { drawTile(x, y); - if (!linePartiallyVisible) { - double drawSpaceY = this.context.getConverter().getViewY(x, y, this.context.getHeight(x, y)); + if(!linePartiallyVisible) { + double drawSpaceY = this.context.getConverter().getViewY(x, y, heightGrid == null ? this.context.getHeight(x, y) : heightGrid[y*width+x]); if (drawSpaceY > bottomDrawY) { linePartiallyVisible = true; } @@ -572,9 +522,9 @@ private void drawMain(FloatRectangle screen) { } } - if (placementBuilding != null) { + if(placementBuilding != null) { ShortPoint2D underMouse = this.context.getPositionOnScreen((float) mousePosition.getX(), (float) mousePosition.getY()); - if (0 <= underMouse.x && underMouse.x < width && 0 <= underMouse.y && underMouse.y < height) { + if(0 <= underMouse.x && underMouse.x < width && 0 <= underMouse.y && underMouse.y < height) { IMapObject mapObject = map.getMapObjectsAt(underMouse.x, underMouse.y); if (mapObject != null && mapObject.getMapObject(EMapObjectType.CONSTRUCTION_MARK) != null) { // if there is a construction mark @@ -583,7 +533,7 @@ private void drawMain(FloatRectangle screen) { } } - if (debugColorMode != EDebugColorModes.NONE) { + if(debugColorMode != EDebugColorModes.NONE) { drawDebugColors(); } } @@ -592,34 +542,26 @@ private void drawTile(int x, int y) { int tileIndex = x+y*width; IMapObject object = objectsGrid != null ? objectsGrid[tileIndex] : map.getMapObjectsAt(x, y); - if (object != null) { + if(object != null) { this.objectDrawer.drawMapObject(x, y, object); } - if (y > 3) { - object = objectsGrid != null ? objectsGrid[tileIndex-3*width] :map.getMapObjectsAt(x, y - 3); - if (object != null && object.getObjectType() == EMapObjectType.BUILDING && ((IBuilding) object).getBuildingType() == EBuildingType.STOCK) { - this.objectDrawer.drawStockFront(x, y - 3, (IBuilding) object); - } - } - if (y < height - 3) { + if(y < height - 3) { object = objectsGrid != null ? objectsGrid[tileIndex+3*width] : map.getMapObjectsAt(x, y + 3); - if (object != null) { + if(object != null) { EMapObjectType type = object.getObjectType(); - if (type == EMapObjectType.BUILDING && ((IBuilding) object).getBuildingType() == EBuildingType.STOCK) { - this.objectDrawer.drawStockBack(x, y + 3, (IBuilding) object); - } else if (type == EMapObjectType.DOCK) { + if(type == EMapObjectType.DOCK) { this.objectDrawer.drawDock(x, y + 3, object); } } } IMovable movable = movableGrid != null ? movableGrid[tileIndex] : map.getMovableAt(x, y); - if (movable != null) { + if(movable != null) { this.objectDrawer.draw(movable); } - if (borderGrid != null ? borderGrid.get(tileIndex) : map.isBorder(x, y)) { + if(borderGrid != null ? borderGrid.get(tileIndex) : map.isBorder(x, y)) { byte player = map.getPlayerIdAt(x, y); objectDrawer.drawPlayerBorderObject(x, y, player); } @@ -638,28 +580,20 @@ private void drawTile(int x, int y) { }; // @formatter:on - private GeometryHandle shapeHandle = null; + private UnifiedDrawHandle shapeHandle = null; private void drawDebugColors() { GLDrawContext gl = this.context.getGl(); - if(shapeHandle == null || !shapeHandle.isValid()) shapeHandle = gl.storeGeometry(shape, EGeometryFormatType.VertexOnly2D, false, "debugshape"); - - int drawX = context.getOffsetX(); - int drawY = context.getOffsetY(); + if(shapeHandle == null || !shapeHandle.isValid()) shapeHandle = gl.createUnifiedDrawCall(4, "debugshape", null, shape); context.getScreenArea().stream().filterBounds(width, height).forEach((x, y) -> { - try { - int argb = map.getDebugColorAt(x, y, debugColorMode); - if (argb != 0) { - int height = context.getHeight(x, y); - float dx = drawX+context.getConverter().getViewX(x, y, height); - float dy = drawY+context.getConverter().getViewY(x, y, height); - gl.draw2D(shapeHandle, null, EGeometryType.Quad, 0, 4, dx, dy, .5f, 1, 1, 1, Color.fromShort((short) argb), 1); - } - } catch (IllegalBufferException e) { - // TODO: Create a crash report - // This should never happen since we only use texture 0 (no texture) + int argb = map.getDebugColorAt(x, y, debugColorMode); + if (argb != 0) { + int height = context.getHeight(x, y); + float dx = context.getConverter().getViewX(x, y, height); + float dy = context.getConverter().getViewY(x, y, height); + shapeHandle.drawSimple(EPrimitiveType.Quad, dx, dy, .5f, 1, 1, Color.fromShort((short) argb), 1); } }); } @@ -859,7 +793,6 @@ private void handleDraw(GODrawEvent drawEvent) { private void handleDrawOnMap(GODrawEvent drawEvent) { this.currentSelectionAreaStart = drawEvent.getDrawPosition(); - updateSelectionArea = true; drawEvent.setHandler(this.drawSelectionHandler); } @@ -942,7 +875,6 @@ private void updateSelectionArea(UIPoint mousePosition, boolean finished) { this.currentSelectionAreaEnd = null; } else { this.currentSelectionAreaEnd = mousePosition; - updateSelectionArea = true; } } diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/MapDrawContext.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/MapDrawContext.java index 61ab436802..575c559238 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/MapDrawContext.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/MapDrawContext.java @@ -148,10 +148,10 @@ public void begin(GLDrawContext gl2) { // beginTime = System.nanoTime(); float zoom = screen.getZoom(); - gl2.setGlobalAttributes(0, 0, 0, zoom, zoom, 1); offsetX = (int) (-screen.getLeft()+.5f); offsetY = (int) (-screen.getBottom()+.5f); + gl2.setGlobalAttributes(offsetX, offsetY, 0, zoom, zoom, 1); } private int offsetX, offsetY; diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/OriginalControls.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/OriginalControls.java index 9cf53a212d..d15e2a225e 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/OriginalControls.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/OriginalControls.java @@ -22,7 +22,7 @@ import jsettlers.common.map.shapes.MapRectangle; import jsettlers.common.action.EActionType; import jsettlers.common.action.IAction; -import jsettlers.common.player.IInGamePlayer; +import jsettlers.common.menu.IStartedGame; import jsettlers.common.position.FloatRectangle; import jsettlers.common.position.ShortPoint2D; import jsettlers.common.selectable.ISelectionSet; @@ -67,16 +67,14 @@ public class OriginalControls implements IControls { /** * Creates a new {@link OriginalControls} overlay. - * - * @param actionFireable + * @param actionFireable * The {@link ActionFireable} to send actions from the user to. - * @param player - * The player this interface should be for. + * @param game */ - public OriginalControls(ActionFireable actionFireable, IInGamePlayer player) { + public OriginalControls(ActionFireable actionFireable, IStartedGame game) { layoutProperties = ControlPanelLayoutProperties.getLayoutPropertiesFor(DEFAULT_LAYOUT_SIZE); final MiniMapLayoutProperties miniMap = layoutProperties.miniMap; - mainPanel = new MainPanel(actionFireable, player); + mainPanel = new MainPanel(actionFireable, game); chatButton = new Button( new ShowChatAction(), miniMap.IMAGELINK_BUTTON_CHAT_ACTIVE, miniMap.IMAGELINK_BUTTON_CHAT_INACTIVE, ""); diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/MainPanel.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/MainPanel.java index 5b93ce9a94..b73ac53020 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/MainPanel.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/MainPanel.java @@ -14,6 +14,8 @@ *******************************************************************************/ package jsettlers.graphics.map.controls.original.panel; +import go.graphics.GLDrawContext; +import go.graphics.text.EFontSize; import jsettlers.common.action.Action; import jsettlers.common.action.EActionType; import jsettlers.common.action.IAction; @@ -25,7 +27,7 @@ import jsettlers.common.images.OriginalImageLink; import jsettlers.common.map.IGraphicsGrid; import jsettlers.common.map.shapes.MapRectangle; -import jsettlers.common.player.IInGamePlayer; +import jsettlers.common.menu.IStartedGame; import jsettlers.common.position.ShortPoint2D; import jsettlers.graphics.action.ActionFireable; import jsettlers.graphics.action.AskSetTradingWaypointAction; @@ -37,6 +39,8 @@ import jsettlers.graphics.map.controls.original.panel.content.ESecondaryTabType; import jsettlers.graphics.map.controls.original.panel.content.MessageContent; import jsettlers.graphics.ui.Button; +import jsettlers.graphics.ui.CountArrows; +import jsettlers.graphics.ui.Label; import jsettlers.graphics.ui.LabeledButton; import jsettlers.graphics.ui.UIPanel; @@ -48,6 +52,8 @@ public class MainPanel extends UIPanel { public static final int BUTTONS_FILE = 3; + private IStartedGame game; + private final UIPanel tabpanel = new UIPanel(); private final Button button_build = new TabButton(ContentType.BUILD_NORMAL, BUTTONS_FILE, 51, 60, ""); @@ -77,6 +83,22 @@ public class MainPanel extends UIPanel { private final UIPanel gamePanel = new UIPanel(); + private final CountArrows changeSpeedArrows = new CountArrows(() -> new Action(EActionType.SPEED_FASTER), () -> new Action(EActionType.SPEED_SLOWER)); + private final Label speedLabel = new Label("", EFontSize.NORMAL) { + @Override + public synchronized void drawAt(GLDrawContext gl) { + setText(((int)(game.getGameTimeProvider().getGameSpeed()*10))/10f + "x"); + super.drawAt(gl); + } + }; + + private final LabeledButton pausedButton = new LabeledButton(Labels.getString("game-menu-pause"), new Action(EActionType.SPEED_TOGGLE_PAUSE)) { + @Override + public boolean isActive() { + return game.getGameTimeProvider().isGamePausing(); + } + }; + private final LabeledButton exitButton = new LabeledButton(Labels.getString("game-menu-quit"), new Action(EActionType.EXIT)); private final LabeledButton saveButton = new LabeledButton(Labels.getString("game-menu-save"), new Action(EActionType.SAVE)); private final LabeledButton cancelButton = new LabeledButton(Labels.getString("game-menu-cancel"), new ExecutableAction() { @@ -87,8 +109,12 @@ public void execute() { }); { - gamePanel.addChild(saveButton, .1f, .4f, .9f, .5f); - gamePanel.addChild(exitButton, .1f, .25f, .9f, .35f); + gamePanel.addChild(changeSpeedArrows, .1f, .9f, .25f, 1f); + gamePanel.addChild(pausedButton, .25f, .9f, .60f, 1f); + gamePanel.addChild(speedLabel, .60f, .9f, .9f, 1f); + + gamePanel.addChild(saveButton, .1f, .34f, .9f, .44f); + gamePanel.addChild(exitButton, .1f, .22f, .9f, .32f); gamePanel.addChild(cancelButton, .1f, .1f, .9f, .2f); } @@ -129,10 +155,11 @@ public void execute() { */ private final ActionFireable actionFireable; - public MainPanel(ActionFireable actionFireable, IInGamePlayer player) { + public MainPanel(ActionFireable actionFireable, IStartedGame game) { this.actionFireable = actionFireable; - ContentType.WARRIORS.setPlayer(player); - ContentType.SETTLER_STATISTIC.setPlayer(player); + this.game = game; + ContentType.WARRIORS.setPlayer(game.getInGamePlayer()); + ContentType.SETTLER_STATISTIC.setPlayer(game.getInGamePlayer()); layoutPanel(ControlPanelLayoutProperties.getLayoutPropertiesFor(480)); } diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/content/ActionProvidedBarFill.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/content/ActionProvidedBarFill.java index 2f2d8b45ed..49766da7cf 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/content/ActionProvidedBarFill.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/content/ActionProvidedBarFill.java @@ -27,8 +27,8 @@ public interface IBarFillActionProvider { private IBarFillActionProvider actionProvider; - public ActionProvidedBarFill(IBarFillActionProvider actionProvider, String name) { - super(name); + public ActionProvidedBarFill(IBarFillActionProvider actionProvider) { + super(); this.actionProvider = actionProvider; } diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/content/BarFill.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/content/BarFill.java index c198c8de2b..e2fe9d3f7c 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/content/BarFill.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/content/BarFill.java @@ -14,14 +14,9 @@ *******************************************************************************/ package jsettlers.graphics.map.controls.original.panel.content; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -import go.graphics.EGeometryFormatType; -import go.graphics.EGeometryType; +import go.graphics.EPrimitiveType; import go.graphics.GLDrawContext; -import go.graphics.GeometryHandle; -import go.graphics.IllegalBufferException; +import go.graphics.UnifiedDrawHandle; import jsettlers.common.Color; import jsettlers.common.images.EImageLinkType; import jsettlers.common.images.ImageLink; @@ -49,16 +44,11 @@ public class BarFill extends UIPanel { private float barFillPercentage = 0; private float descriptionPercentage = 0; - public BarFill(String name) { + public BarFill() { setBackground(barImageLink); - this.name = name; } - private GeometryHandle geometry = null; - private static ByteBuffer geometryBfr = ByteBuffer.allocateDirect(4*4*2).order(ByteOrder.nativeOrder()); - private FloatRectangle writtenPosition = null; - private float writtenMaxX = -1; - private String name; + private static UnifiedDrawHandle geometry = null; private static final Color barColor = new Color(0, .78f, .78f, 1); @@ -66,22 +56,9 @@ public BarFill(String name) { public void drawAt(GLDrawContext gl) { FloatRectangle position = getPosition(); float fillX = barFillPercentage < .01f ? 0 : barFillPercentage > .99f ? 1 : EMPTY_X * (1 - barFillPercentage) + FULL_X * barFillPercentage; - float maxX = position.getMinX() * (1 - fillX) + position.getMaxX() * fillX; - - try { - if (geometry == null || !geometry.isValid()) geometry = gl.generateGeometry(4, EGeometryFormatType.VertexOnly2D, false, name); - if (!position.equals(writtenPosition) || writtenMaxX != maxX) { - writtenPosition = position; - writtenMaxX = maxX; - geometryBfr.asFloatBuffer().put(new float[]{ - maxX, position.getMinY(), position.getMinX(), position.getMinY(), - position.getMinX(), position.getMaxY(), maxX, position.getMaxY()}); - gl.updateGeometryAt(geometry, 0, geometryBfr); - } - gl.draw2D(geometry, null, EGeometryType.Quad, 0, 4, 0, 0, 0, 1, 1, 1, barColor, 1); - } catch(IllegalBufferException ex) { - ex.printStackTrace(); - } + + if(geometry == null || !geometry.isValid()) geometry = gl.createUnifiedDrawCall(4, "barfill", null, new float[] {0, 0, 0, 1, 1, 1, 1, 0}); + geometry.drawSimple(EPrimitiveType.Quad, position.getMinX(), position.getMinY(), 0, position.getWidth()*fillX, position.getHeight(), barColor, 1); super.drawBackground(gl); } diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/content/material/distribution/DistributionPanel.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/content/material/distribution/DistributionPanel.java index 37a90d9334..300803e227 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/content/material/distribution/DistributionPanel.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/content/material/distribution/DistributionPanel.java @@ -99,7 +99,7 @@ private static class BuildingDistributionSettingPanel extends UIPanel { } else { return null; } - }, Labels.getName(buildingType) + "-distribution-barfill"); + }); Label rowTitle = new Label(Labels.getName(buildingType), EFontSize.SMALL, EHorizontalAlignment.LEFT); addChild(rowTitle, 0f, 1f - textHeight, 1f, 1f); diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/content/material/production/MaterialsProductionPanel.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/content/material/production/MaterialsProductionPanel.java index bd91fde40d..8fcb6110db 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/content/material/production/MaterialsProductionPanel.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/content/material/production/MaterialsProductionPanel.java @@ -16,25 +16,20 @@ import go.graphics.text.EFontSize; import jsettlers.common.buildings.IMaterialProductionSettings; -import jsettlers.common.images.EImageLinkType; -import jsettlers.common.images.ImageLink; -import jsettlers.common.images.OriginalImageLink; import jsettlers.common.map.IGraphicsGrid; import jsettlers.common.material.EMaterialType; -import jsettlers.common.position.IPositionSupplier; import jsettlers.common.position.ShortPoint2D; import jsettlers.graphics.action.ActionFireable; import jsettlers.common.action.SetMaterialProductionAction; import jsettlers.graphics.localization.Labels; import jsettlers.graphics.map.controls.original.panel.content.AbstractContentProvider; +import jsettlers.graphics.ui.CountArrows; import jsettlers.graphics.map.controls.original.panel.content.BarFill; import jsettlers.graphics.map.controls.original.panel.content.ESecondaryTabType; import jsettlers.graphics.map.controls.original.panel.content.ActionProvidedBarFill; import jsettlers.graphics.map.controls.original.panel.content.updaters.UiContentUpdater; import jsettlers.graphics.map.controls.original.panel.content.updaters.UiLocationDependingContentUpdater; -import jsettlers.graphics.ui.Button; import jsettlers.graphics.ui.Label; -import jsettlers.graphics.ui.SetMaterialProductionButton; import jsettlers.graphics.ui.UIPanel; import java.util.Arrays; @@ -61,7 +56,6 @@ public class MaterialsProductionPanel extends AbstractContentProvider { private static final float weaponsTitleMarginBottom = weaponsTitleMarginBottom_px / contentHeight_px; private static class Row extends UIPanel implements UiContentUpdater.IUiContentReceiver { - private static final ImageLink arrowsImageLink = new OriginalImageLink(EImageLinkType.GUI, 3, 231, 0); // checked in the original game private static final float iconWidth = iconSize_px / contentWidth_px; private static final float quantityTextWidth = 18f / contentWidth_px; private static final float quantityTextMarginV = 5f / iconSize_px; @@ -85,16 +79,10 @@ public Row(final EMaterialType materialType) { lblQuantity = new Label(Labels.getString(Integer.toString(quantity)), EFontSize.NORMAL); - IPositionSupplier positionSupplier = () -> position; - Button upButton = new SetMaterialProductionButton(positionSupplier, type, SetMaterialProductionAction.EMaterialProductionType.INCREASE); - Button downButton = new SetMaterialProductionButton(positionSupplier, type, SetMaterialProductionAction.EMaterialProductionType.DECREASE); + arrows = new CountArrows(() -> new SetMaterialProductionAction(position, type, SetMaterialProductionAction.EMaterialProductionType.INCREASE, 0), + () -> new SetMaterialProductionAction(position, type, SetMaterialProductionAction.EMaterialProductionType.DECREASE, 0)); - arrows = new UIPanel(); - arrows.setBackground(arrowsImageLink); - arrows.addChild(upButton, 0f, 0.5f, 1f, 1f); - arrows.addChild(downButton, 0f, 0f, 1f, 0.5f); - - barFill = new ActionProvidedBarFill(fillForClick -> new SetMaterialProductionAction(position, materialType, SetMaterialProductionAction.EMaterialProductionType.SET_RATIO, fillForClick), Labels.getName(materialType, false) + "-production-barfill"); + barFill = new ActionProvidedBarFill(fillForClick -> new SetMaterialProductionAction(position, materialType, SetMaterialProductionAction.EMaterialProductionType.SET_RATIO, fillForClick)); float left = 0; addChild(goodsIcon, left, 0f, left += iconWidth, 1f); diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/selection/BuildingSelectionContent.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/selection/BuildingSelectionContent.java index 9d94025003..fcb958d39e 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/selection/BuildingSelectionContent.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/controls/original/panel/selection/BuildingSelectionContent.java @@ -37,6 +37,7 @@ import jsettlers.common.action.SetTradingWaypointAction; import jsettlers.common.action.SetTradingWaypointAction.EWaypointType; import jsettlers.common.action.SoldierAction; +import jsettlers.graphics.image.Image; import jsettlers.graphics.localization.Labels; import jsettlers.graphics.map.controls.original.panel.button.SelectionManagedMaterialButton; import jsettlers.graphics.map.controls.original.panel.button.SelectionManager; @@ -654,6 +655,8 @@ protected void drawBackground(GLDrawContext gl) { for (ImageLink link : links) { ImageProvider.getInstance().getImage(link).drawAt(gl, cx, cy, 0, null, 1); + // TODO implement depth sorting in UI + gl.finishFrame(); } } diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/draw/Background.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/draw/Background.java index 996a3f052f..9697cf0621 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/draw/Background.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/draw/Background.java @@ -19,13 +19,12 @@ import java.nio.ByteOrder; import java.util.BitSet; -import go.graphics.EGeometryFormatType; -import go.graphics.GL2DrawContext; +import go.graphics.BackgroundDrawHandle; import go.graphics.GLDrawContext; -import go.graphics.GeometryHandle; import go.graphics.IllegalBufferException; import go.graphics.TextureHandle; +import go.graphics.UpdateGeometryCache; import jsettlers.common.CommonConstants; import jsettlers.common.landscape.ELandscapeType; import jsettlers.common.map.IDirectGridProvider; @@ -42,7 +41,7 @@ * The map background. *

* This class draws the map background (landscape) layer. It has support for smooth FOW transitions and buffers the background to make it faster. - * + * * @author Michael Zangl */ public class Background implements IGraphicsBackgroundListener { @@ -68,7 +67,7 @@ public class Background implements IGraphicsBackgroundListener { * x and y coordinates are in Grid units. *

* The third entry is the size of the texture. It must be 1 for border tiles and 2..5 for continuous images. Always 1 more than they are wide. - * + * *

 	 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 	 * |0             |1             |3             |4             |5             |7             | 5| 6|
@@ -851,12 +850,15 @@ public class Background implements IGraphicsBackgroundListener {
 
 	private static TextureHandle texture = null;
 
-	private GeometryHandle shapeHandle = null;
-	private GeometryHandle colorHandle = null;
+	private BackgroundDrawHandle backgroundHandle = null;
+
+	private boolean hasdgp;
+	private byte[][] dgpVisibleStatus;
+	private byte[] dgpHeightGrid;
 
-	private boolean useFloatColors;
 	private boolean updateGeometry = false;
 	private BitSet mapInvalid = new BitSet();
+	private int mapWidth, mapHeight;
 
 	private static short[] preloadedTexture = null;
 
@@ -870,7 +872,7 @@ private static short[] getTexture() {
 		return data;
 	}
 
-	public static void preloadTexture() {
+	static void preloadTexture() {
 		synchronized (preloadMutex) {
 			if (preloadedTexture == null) {
 				preloadedTexture = getTexture();
@@ -925,10 +927,11 @@ public void writeLine(short[] data, int length) {
 
 	/**
 	 * Generates the texture data.
-	 * 
+	 *
 	 * @param data
 	 *            The texture data buffer.
 	 * @throws IOException
+	 *            If the necessary file reader is missing
 	 */
 	private static void addTextures(short[] data) throws IOException {
 		DatFileReader reader = ImageProvider.getInstance().getFileReader(LAND_FILE);
@@ -965,7 +968,7 @@ private static void addTextures(short[] data) throws IOException {
 
 	/**
 	 * Gets the image number of the border
-	 * 
+	 *
 	 * @param outer
 	 *            The outer landscape (that has two triangle edges).
 	 * @param inner
@@ -1169,37 +1172,45 @@ private static int getBorder(ELandscapeType outer, ELandscapeType inner, boolean
 
 	/**
 	 * Draws a given map content.
-	 * 
+	 *
 	 * @param context
 	 *            The context to draw at.
 	 * @param screen
 	 */
 	public void drawMapContent(MapDrawContext context, FloatRectangle screen) {
+		IDirectGridProvider dgp = context.getFow();
+		hasdgp = dgp != null;
+		if(hasdgp) {
+			dgpVisibleStatus = dgp.getVisibleStatusArray();
+			dgpHeightGrid = dgp.getHeightArray();
+		}
+
 		try {
-			if(shapeHandle == null) {
+			if(backgroundHandle == null) {
 				bufferWidth = context.getMap().getWidth()-1;
 				bufferHeight = context.getMap().getHeight()-1;
+				mapWidth = context.getMap().getWidth();
+				mapHeight = context.getMap().getHeight();
 
 				generateFogOfWarBuffer(context);
 				mapInvalid = new BitSet(bufferWidth*bufferHeight);
 				draw_stride = (2*bufferWidth)+1;
 			}
 
-			if(shapeHandle == null || !shapeHandle.isValid()) {
-				useFloatColors = (context.getGl() instanceof GL2DrawContext);
+			if(backgroundHandle == null || !backgroundHandle.isValid()) {
 				generateGeometry(context);
 				context.getGl().setHeightMatrix(context.getConverter().getMatrixWithHeight());
 			}
 
 			GLDrawContext gl = context.getGl();
 			MapRectangle screenArea = context.getConverter().getMapForScreen(screen);
-			int offset = screenArea.getMinY()*bufferWidth+screenArea.getMinX();
 
 			updateGeometry(context, screenArea);
 
-			float x = context.getOffsetX();
-			float y = context.getOffsetY();
-			gl.drawTrianglesWithTextureColored(getTexture(context.getGl()), shapeHandle, colorHandle, offset*2, screenArea.getHeight(), screenArea.getWidth()*2, draw_stride, x, y);
+			backgroundHandle.offset = (screenArea.getMinY() * bufferWidth + screenArea.getMinX())*2;
+			backgroundHandle.lines = screenArea.getHeight();
+			backgroundHandle.width = screenArea.getWidth()*2;
+			gl.drawBackground(backgroundHandle);
 
 			resetFOWDimStatus();
 		} catch (IllegalBufferException e) {
@@ -1214,47 +1225,37 @@ private void resetFOWDimStatus() {
 
 	private void generateGeometry(MapDrawContext context) throws IllegalBufferException {
 		int vertices = bufferWidth*bufferHeight*3*2;
-		shapeHandle = context.getGl().generateGeometry(vertices, EGeometryFormatType.Texture3D, false, "background-shape");
-		colorHandle = context.getGl().generateGeometry(vertices, EGeometryFormatType.ColorOnly, true, "background-color");
+		backgroundHandle = context.getGl().createBackgroundDrawCall(vertices, getTexture(context.getGl()));
+		backgroundHandle.stride = (2*bufferWidth)+1;
 
-		ByteBuffer shape_bfr = ByteBuffer.allocateDirect(BYTES_PER_FIELD_SHAPE*bufferWidth).order(ByteOrder.nativeOrder());
-		ByteBuffer color_bfr = ByteBuffer.allocateDirect(BYTES_PER_FIELD_COLOR*bufferWidth).order(ByteOrder.nativeOrder());
+		shape_bfr = ByteBuffer.allocateDirect(BYTES_PER_FIELD_SHAPE*bufferWidth).order(ByteOrder.nativeOrder());
+		color_bfr = ByteBuffer.allocateDirect(BYTES_PER_FIELD_COLOR*bufferWidth).order(ByteOrder.nativeOrder());
 
 		for(int y = 0;y != bufferHeight;y++) {
 			for(int x = 0; x != bufferWidth;x++) {
 				addTrianglesToGeometry(context, shape_bfr, x, y);
 			}
-			context.getGl().updateGeometryAt(shapeHandle, BYTES_PER_FIELD_SHAPE*bufferWidth*y, shape_bfr);
 			shape_bfr.rewind();
+			context.getGl().updateBufferAt(backgroundHandle.vertices, BYTES_PER_FIELD_SHAPE*bufferWidth*y, shape_bfr);
 		}
 		for(int y = 0;y != bufferHeight;y++) {
 			int line_bfr4 = y*bufferWidth*4;
 			for(int x = 0; x != bufferWidth;x++) {
 				addColorTrianglesToGeometry(context, color_bfr, x, y, line_bfr4+x*4);
 			}
-			context.getGl().updateGeometryAt(colorHandle, BYTES_PER_FIELD_COLOR*bufferWidth*y, color_bfr);
 			color_bfr.rewind();
+			context.getGl().updateBufferAt(backgroundHandle.colors, BYTES_PER_FIELD_COLOR * bufferWidth * y, color_bfr);
 		}
-	}
-
-	private final ByteBuffer shape_update_bfr = ByteBuffer.allocateDirect(BYTES_PER_FIELD_SHAPE).order(ByteOrder.nativeOrder());
-	private final ByteBuffer color_update_bfr = ByteBuffer.allocateDirect(BYTES_PER_FIELD_COLOR).order(ByteOrder.nativeOrder());
 
-	private void updateMapType(MapDrawContext context, int x, int y) throws IllegalBufferException {
-		int bfr_pos = y*bufferWidth+x;
-
-		if(mapInvalid.get(bfr_pos)) {
-			mapInvalid.clear(bfr_pos);
-			shape_update_bfr.rewind();
-			addTrianglesToGeometry(context, shape_update_bfr, x, y);
-			context.getGl().updateGeometryAt(shapeHandle, bfr_pos * BYTES_PER_FIELD_SHAPE, shape_update_bfr);
-		}
+		shape_bfr.limit(BYTES_PER_FIELD_SHAPE);
+		color_cache = new UpdateGeometryCache(color_bfr, BYTES_PER_FIELD_COLOR, context::getGl, () -> backgroundHandle.colors);
 	}
 
-	private void updateGeometry(MapDrawContext context, MapRectangle screen) {
-		IDirectGridProvider vsp = context.getFow();
-		byte[][] visibleStatus = vsp != null ? vsp.getVisibleStatusArray() : null;
+	private UpdateGeometryCache color_cache;
+	private ByteBuffer shape_bfr;
+	private ByteBuffer color_bfr;
 
+	private void updateGeometry(MapDrawContext context, MapRectangle screen) {
 		try {
 			int height = screen.getHeight();
 			int width = screen.getWidth();
@@ -1272,23 +1273,26 @@ private void updateGeometry(MapDrawContext context, MapRectangle screen) {
 				int linewidth = (width + lineStartX) < bufferWidth ? width + lineStartX : bufferWidth;
 				int linex = lineStartX < 0 ? 0 : lineStartX;
 
-				int line_bfr_pos4 = y*bufferWidth*4;
-				int line_bfr_pos = y*bufferWidth;
-				for (int x = linex; x < linewidth; x++) {
-					int bfr_pos4 = line_bfr_pos4+x*4;
-					int bfr_pos = line_bfr_pos+x;
+				int bfr_pos = y*bufferWidth+linex;
+				int bfr_pos4 = bfr_pos*4;
+
+				boolean changes = false;
 
-					byte fow = visibleStatus != null ? visibleStatus[x][y] : CommonConstants.FOG_OF_WAR_VISIBLE;
+				for (int x = linex; x < linewidth; x++) {
+					byte fow = dgpVisibleStatus!=null ? dgpVisibleStatus[x][y] : CommonConstants.FOG_OF_WAR_VISIBLE;
 					if(fow != fogOfWarStatus[bfr_pos4]) {
-						color_update_bfr.rewind();
+						color_cache.gotoPos(bfr_pos);
+						changes = true;
 						dimFogOfWarBuffer(context, bfr_pos4, x, y);
 						dimFogOfWarBuffer(context, bfr_pos4+1, x + 1, y);
 						dimFogOfWarBuffer(context, bfr_pos4+2, x, y + 1);
 						dimFogOfWarBuffer(context, bfr_pos4+3, x + 1, y + 1);
-						addColorTrianglesToGeometry(context, color_update_bfr, x, y, bfr_pos4);
-						context.getGl().updateGeometryAt(colorHandle, bfr_pos * BYTES_PER_FIELD_COLOR, color_update_bfr);
+						addColorTrianglesToGeometry(context, color_bfr, x, y, bfr_pos4);
 					}
+					bfr_pos++;
+					bfr_pos4 += 4;
 				}
+				if(changes) color_cache.clearCache();
 			}
 
 			if (updateGeometry) {
@@ -1297,9 +1301,17 @@ private void updateGeometry(MapDrawContext context, MapRectangle screen) {
 
 					int linewidth = (width+lineStartX) < bufferWidth ? width+lineStartX : bufferWidth;
 					int linex = lineStartX < 0 ? 0 : lineStartX;
+					int bfr_pos = y*bufferWidth+linex;
 
 					for (int x = linex; x < linewidth; x++) {
-						updateMapType(context, x, y);
+						if(mapInvalid.get(bfr_pos)) {
+							mapInvalid.clear(bfr_pos);
+							shape_bfr.rewind();
+							addTrianglesToGeometry(context, shape_bfr, x, y);
+							shape_bfr.rewind();
+							context.getGl().updateBufferAt(backgroundHandle.vertices, bfr_pos * BYTES_PER_FIELD_SHAPE, shape_bfr);
+						}
+						bfr_pos++;
 					}
 				}
 				updateGeometry = false;
@@ -1318,20 +1330,21 @@ private synchronized void invalidateShapePoint(int x, int y) {
 	private void generateFogOfWarBuffer(MapDrawContext context) {
 		fogOfWarStatus = new byte[bufferWidth*bufferHeight*4];
 
+		int fieldOffset = 0;
 		for(int y = 0;y != bufferHeight;y++) {
 			for(int x = 0; x != bufferWidth;x++) {
-				int fieldOffset = getBufferPosition(x, y);
-				fogOfWarStatus[fieldOffset*4] = context.getVisibleStatus(x, y);
-				fogOfWarStatus[(fieldOffset*4)+1] = context.getVisibleStatus(x+1, y);
-				fogOfWarStatus[(fieldOffset*4)+2] = context.getVisibleStatus(x+1, y+1);
-				fogOfWarStatus[(fieldOffset*4)+3] = context.getVisibleStatus(x, y+1);
+				fogOfWarStatus[fieldOffset*4] = dgpVisibleStatus!=null ? dgpVisibleStatus[x][y] : context.getVisibleStatus(x, y);
+				fogOfWarStatus[(fieldOffset*4)+1] = dgpVisibleStatus!=null ? dgpVisibleStatus[x+1][y  ] : context.getVisibleStatus(x+1, y);
+				fogOfWarStatus[(fieldOffset*4)+2] = dgpVisibleStatus!=null ? dgpVisibleStatus[x+1][y+1] : context.getVisibleStatus(x+1, y+1);
+				fogOfWarStatus[(fieldOffset*4)+3] = dgpVisibleStatus!=null ? dgpVisibleStatus[x  ][y+1] : context.getVisibleStatus(x, y+1);
+				fieldOffset++;
 			}
 		}
 	}
 
 	/**
 	 * Dims the fog of war buffer
-	 * 
+	 *
 	 * @param context
 	 *            The context
 	 * @param offset
@@ -1344,7 +1357,7 @@ private void generateFogOfWarBuffer(MapDrawContext context) {
 	 */
 	private void dimFogOfWarBuffer(MapDrawContext context, int offset, int x, int y) {
 		if (!fowDimmed.get(offset)) {
-			fogOfWarStatus[offset] = dim(fogOfWarStatus[offset], context.getVisibleStatus(x, y));
+			fogOfWarStatus[offset] = dim(fogOfWarStatus[offset], dgpVisibleStatus!=null ? dgpVisibleStatus[x][y] : context.getVisibleStatus(x, y));
 			fowDimmed.set(offset);
 		}
 	}
@@ -1369,7 +1382,7 @@ private int getBufferPosition(int x, int y) {
 
 	/**
 	 * Adds the two triangles for a point to the list of verteces
-	 * 
+	 *
 	 * @param context
 	 * @param buffer
 	 * @param x
@@ -1390,12 +1403,11 @@ private void addColorTrianglesToGeometry(MapDrawContext context, ByteBuffer buff
 		addColorPointToGeometry(context, buffer, x + 1, y, fogBase + 1);
 	}
 
-	private void addTriangleToGeometry(MapDrawContext context, ByteBuffer buffer, int x, int y, boolean up, int useSecondParameter) {
-		int x1 = x;
+	private void addTriangleToGeometry(MapDrawContext context, ByteBuffer buffer, int x1, int y, boolean up, int useSecondParameter) {
 		int y1 = y + (up?1:0);
-		int x2 = x + (up?0:1);
+		int x2 = x1 + (up?0:1);
 		int y2 = y + (up?0:1);
-		int x3 = x + 1;
+		int x3 = x1 + 1;
 		int y3 = y + (up?1:0);
 
 		ELandscapeType leftLandscape = context.getLandscape(x1, y1);
@@ -1425,7 +1437,7 @@ private void addTriangleToGeometry(MapDrawContext context, ByteBuffer buffer, in
 		int addDx = 0;
 		int addDy = 0;
 		if (positions[2] >= 2) {
-			addDx = x * DrawConstants.DISTANCE_X - y * DrawConstants.DISTANCE_X / 2;
+			addDx = x1 * DrawConstants.DISTANCE_X - y * DrawConstants.DISTANCE_X / 2;
 			addDy = y * DrawConstants.DISTANCE_Y;
 			addDx = realModulo(addDx, (positions[2] - 1) * TEXTURE_GRID);
 			addDy = realModulo(addDy, (positions[2] - 1) * TEXTURE_GRID);
@@ -1458,19 +1470,23 @@ private void addTriangleToGeometry(MapDrawContext context, ByteBuffer buffer, in
 	private void addPointToGeometry(MapDrawContext context, ByteBuffer buffer, int x, int y, float u, float v) {
 		buffer.putFloat(x);
 		buffer.putFloat(y);
-		buffer.putFloat(context.getHeight(x, y));
+		buffer.putFloat(dgpVisibleStatus!=null ? dgpHeightGrid[y*mapWidth+x] : context.getHeight(x, y));
 		buffer.putFloat(u);
 		buffer.putFloat(v);
 	}
 
 	private void addColorPointToGeometry(MapDrawContext context, ByteBuffer buffer, int x, int y, int fogOffset) {
 		float fColor;
-		if (x <= 0 || x >= context.getMap().getWidth() - 2 || y <= 0 || y >= context.getMap().getHeight() - 2 || context.getVisibleStatus(x, y) <= 0) {
+		if (x <= 0 || x >= mapWidth - 2 || y <= 0 || y >= mapHeight - 2 || (dgpVisibleStatus!=null ? dgpVisibleStatus[x][y] : context.getVisibleStatus(x, y)) <= 0) {
 			fColor = 0;
 		} else {
-			int height1 = context.getHeight(x, y - 1);
-			int height2 = context.getHeight(x, y);
-			fColor = 0.85f + (height1 - height2) * .15f;
+			int dHeight;
+			if(hasdgp) {
+				dHeight = dgpHeightGrid[(y-1)*mapWidth+x] - dgpHeightGrid[y*mapWidth+x];
+			} else {
+				dHeight = context.getHeight(x, y-1) - context.getHeight(x, y);
+			}
+			fColor = 0.85f + dHeight * .15f;
 			if (fColor > 1.0f) {
 				fColor = 1.0f;
 			} else if (fColor < 0.4f) {
@@ -1479,17 +1495,7 @@ private void addColorPointToGeometry(MapDrawContext context, ByteBuffer buffer,
 			fColor *= (float) fogOfWarStatus[fogOffset] / CommonConstants.FOG_OF_WAR_VISIBLE;
 		}
 
-		if(useFloatColors) {
-			buffer.putFloat(fColor);
-		} else {
-			byte color;
-			fColor *= 255f;
-			color = (byte) (int) fColor;
-			buffer.put(color);
-			buffer.put(color);
-			buffer.put(color);
-			buffer.put((byte) 255);
-		}
+		buffer.putFloat(fColor);
 	}
 
 	private static int realModulo(int number, int modulo) {
@@ -1511,7 +1517,7 @@ public void backgroundShapeChangedAt(int x, int y) {
 	/**
 	 * Invalidates the background texture.
 	 */
-	public static void invalidateTexture() {
+	static void invalidateTexture() {
 		texture = null;
 	}
 }
diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/draw/ImagePreloadTask.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/draw/ImagePreloadTask.java
index 56df52beca..2781d25b55 100644
--- a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/draw/ImagePreloadTask.java
+++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/draw/ImagePreloadTask.java
@@ -22,205 +22,5 @@ public void run() {
 		SettlerImageMap.getInstance();
 
 		Background.preloadTexture();
-
-		ImageProvider ip = ImageProvider.getInstance();
-		try {
-			ip.getFileReader(1).generateImageMap(1024, 2048, new int[] {
-					// trees
-					1,// grown
-					2,// grown
-					3,
-					4,// grown
-					6,
-					7,// grown
-					8,// grown
-					9,
-					16,// grown
-					17,// grown
-					18,
-					// water
-					26,
-					// stones
-					31,
-					// goods
-					33,
-					34,
-					35,
-					36,
-					37,
-					38,
-					39,
-					40,
-					41,
-					42,
-					43,
-					// signs
-					93,
-					94,
-					95,
-					96,
-					97,
-					98,
-					99,
-					// arrows
-					100,
-					101,
-					102,
-					103,
-					104,
-					105,
-			}, "1", "trees/signs/arrows");
-		} catch (Throwable e) {
-		}
-
-		try {
-			ip.getFileReader(10).generateImageMap(2048, 2048, new int[] {
-					// settlers
-					0,
-					1,
-					2,
-					3,
-					4,
-					5,
-					6,
-					7,
-					8,
-					9,
-					10,
-					11,
-					12,
-					13,
-					14,
-					15,
-					16,
-					17,
-					18,
-					19,
-					20,
-					21,
-					22,
-					23,
-					24,
-					25,
-					26,
-					27,
-					28,
-					29,
-					30,
-					31,
-					32,
-					33,
-					34,
-					45
-			}, "10", "settlers");
-		} catch (Throwable e) {
-			e.printStackTrace();
-		}
-
-		try {
-			ip.getFileReader(11).generateImageMap(2048, 2048, new int[] {
-					// workers
-					13,
-					14,
-					15,
-					16,
-					17,
-					18,
-					19,
-					20,
-					21,
-					22,
-					23,
-					24,
-					25,
-					26,
-					27,
-					28,
-					29,
-					30,
-					31,
-					32,
-					33,
-					34,
-					35,
-					36,
-
-					// pioneer
-					37,
-					38,
-					39,
-
-					// priest
-					188,
-
-					// pioneer
-					204,
-					205,
-					206,
-
-					// building workers
-					206,
-					207,
-					208,
-					209,
-					210,
-					211,
-					212,
-					213,
-					214,
-					215,
-					216,
-					217,
-					218,
-					219,
-					220,
-					221,
-					222,
-					223,
-
-					231,
-					232,
-			}, "11", "workers/civil-units");
-		} catch (Throwable e) {
-			e.printStackTrace();
-		}
-
-		try {
-			ip.getFileReader(12).generateImageMap(2048, 2048, new int[] {
-					// soldiers
-
-					// swordsman
-					9,
-					10,
-					11,
-					12,
-					13,
-					14,
-
-					// pikeman
-					15,
-					// 16,
-					17,
-					18,
-					// 19,
-					20,
-
-					// bowman
-					21,
-					// 22,
-					23,
-					24,
-					// 25,
-					26,
-
-					// ghost
-					27,
-
-					// inside tower
-					28
-			}, "12", "soldiers");
-		} catch (Throwable e) {
-			e.printStackTrace();
-		}
 	}
 }
diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/draw/MapObjectDrawer.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/draw/MapObjectDrawer.java
index 0a6d6897ed..5c699905da 100644
--- a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/draw/MapObjectDrawer.java
+++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/draw/MapObjectDrawer.java
@@ -20,6 +20,7 @@
 import java.util.List;
 
 import go.graphics.GLDrawContext;
+import go.graphics.GL32DrawContext;
 import jsettlers.common.Color;
 import jsettlers.common.CommonConstants;
 import jsettlers.common.buildings.EBuildingType;
@@ -175,8 +176,10 @@ public class MapObjectDrawer {
 	private static final float BUILDING_SELECTION_MARKER_Z = 0.9f;
 	private static final float FLAG_ROOF_Z                 = 0.89f;
 	private static final float SMOKE_Z                     = 0.9f;
-	private static final float WAVES_Z                     = -0.1f;
-	private static final float BORDER_STONE_Z              = -0.1f;
+	private static final float BACKGROUND_Z                = -0.1f;
+	private final float z_per_y;
+	private final float shadow_offset;
+	private final float construction_offset;
 
 	private static final int SHIP_IMAGE_FILE          = 36;
 	private static final int FERRY_BASE_SEQUENCE      = 4;
@@ -199,6 +202,8 @@ public class MapObjectDrawer {
 	private ImageProvider   imageProvider;
 	private SettlerImageMap imageMap;
 	private float           betweenTilesY;
+	private Image playerBorderObjectImage;
+	//private SharedDrawing playerBorderObjectUpdater = null;
 
 	/**
 	 * Creates a new {@link MapObjectDrawer}.
@@ -210,6 +215,10 @@ public class MapObjectDrawer {
 	public MapObjectDrawer(MapDrawContext context, SoundManager sound) {
 		this.context = context;
 		this.sound = sound;
+
+		z_per_y = 1f/(context.getMap().getHeight()*100);
+		shadow_offset = 10 * z_per_y;
+		construction_offset = z_per_y;
 	}
 
 	public void setVisibleGrid(byte[][] visibleGrid) {
@@ -250,39 +259,7 @@ public void drawDock(int x, int y, IMapObject object) {
 		}
 		float color = getColor(fogStatus);
 		Image image = imageProvider.getImage(new OriginalImageLink(EImageLinkType.SETTLER, 1, 112, 0));
-		draw(image, x, y, 0, getColor(object), color);
-	}
-
-	public void drawStockBack(int x, int y, IBuilding stock) {
-		forceSetup();
-		byte fogStatus = visibleGrid != null ? visibleGrid[x][y] : CommonConstants.FOG_OF_WAR_VISIBLE;
-		if (fogStatus == 0) {
-			return;
-		}
-		float color = getColor(fogStatus);
-		float state = stock.getStateProgress();
-		if (state >= 0.99) {
-			ImageLink[] images = EBuildingType.STOCK.getImages();
-			draw(imageProvider.getImage(images[0]), x, y, 0, color);
-			draw(imageProvider.getImage(images[1]), x, y, 0, color);
-			draw(imageProvider.getImage(images[5]), x, y, 0, color);
-		}
-	}
-
-	public void drawStockFront(int x, int y, IBuilding stock) {
-		forceSetup();
-		byte fogStatus = visibleGrid != null ? visibleGrid[x][y] : CommonConstants.FOG_OF_WAR_VISIBLE;
-		if (fogStatus == 0) {
-			return;
-		}
-		float color = getColor(fogStatus);
-		float state = stock.getStateProgress();
-		if (state >= 0.99) {
-			ImageLink[] images = EBuildingType.STOCK.getImages();
-			for (int i = 2; i < 5; i++) {
-				draw(imageProvider.getImage(images[i]), x, y, 0, color);
-			}
-		}
+		draw(image, x, y,  BACKGROUND_Z, getColor(object), color);
 	}
 
 	private void drawShipInConstruction(int x, int y, IShipInConstruction ship) {
@@ -318,19 +295,17 @@ private void drawShip(IMovable ship, int x, int y) {
 
 		// get drawing position
 		Color color = context.getPlayerColor(ship.getPlayer().getPlayerId());
-		float viewX = context.getOffsetX();
-		float viewY = context.getOffsetY();
+		float viewX = 0;
+		float viewY = 0;
 		if (ship.getAction() == EMovableAction.WALKING) {
-			int originX = x - direction.getGridDeltaX();
-			int originY = y - direction.getGridDeltaY();
-			viewX += betweenTilesX(originX, originY, x, y, ship.getMoveProgress());
+			viewX += betweenTilesX(x, y, direction.getInverseDirection(), 1-ship.getMoveProgress());
 			viewY += betweenTilesY;
 		} else {
 			viewX += mapCoordinateConverter.getViewX(x, y, height);
 			viewY += mapCoordinateConverter.getViewY(x, y, height);
 		}
 		// draw ship body
-		drawShipLink(SHIP_IMAGE_FILE, baseSequence, shipImageDirection, glDrawContext, viewX, viewY, color, shade);
+		drawShipLink(baseSequence, shipImageDirection, glDrawContext, viewX, viewY, y, color, shade);
 		// prepare freight drawing
 		List passengerList = ship.getPassengers();
 
@@ -383,7 +358,7 @@ private void drawShip(IMovable ship, int x, int y) {
 					Image image = this.imageMap.getImageForSettler(passenger.getMovableType(), EMovableAction.NO_ACTION,
 						EMaterialType.NO_MATERIAL, getPassengerDirection(direction, shipPosition, i), 0
 					);
-					image.drawAt(glDrawContext, viewX + xShift, viewY + yShift + PASSENGER_DECK_HEIGHT, 0, color, shade);
+					image.drawAt(glDrawContext, viewX + xShift, viewY + yShift + PASSENGER_DECK_HEIGHT, getZ(0, y), color, shade);
 				}
 			}
 		} else {
@@ -398,13 +373,13 @@ EMaterialType.NO_MATERIAL, getPassengerDirection(direction, shipPosition, i), 0
 					if (material != null && count > 0) {
 						Sequence seq = this.imageProvider.getSettlerSequence(OBJECTS_FILE, material.getStackIndex());
 						Image image = seq.getImageSafe(count - 1, () -> Labels.getName(material, false));
-						image.drawAt(glDrawContext, viewX + xShift, viewY + yShift + CARGO_DECK_HEIGHT, 0, color, shade);
+						image.drawAt(glDrawContext, viewX + xShift, viewY + yShift + CARGO_DECK_HEIGHT, getZ(0, y), color, shade);
 					}
 				}
 			}
 		}
 		// draw sail
-		drawShipLink(SHIP_IMAGE_FILE, sailSequence, shipImageDirection, glDrawContext, viewX, viewY, color, shade);
+		drawShipLink(sailSequence, shipImageDirection, glDrawContext, viewX, viewY, y, color, shade);
 		if (shipType == EMovableType.FERRY) {
 			// draw passengers in front of the sail
 			for (int i = 0; i < numberOfFreight; i++) {
@@ -416,7 +391,7 @@ EMaterialType.NO_MATERIAL, getPassengerDirection(direction, shipPosition, i), 0
 					Image image = this.imageMap.getImageForSettler(passenger.getMovableType(), EMovableAction.NO_ACTION,
 						EMaterialType.NO_MATERIAL, getPassengerDirection(direction, shipPosition, i), 0
 					);
-					image.drawAt(glDrawContext, viewX + xShift, viewY + yShift + PASSENGER_DECK_HEIGHT, 0, color, shade);
+					image.drawAt(glDrawContext, viewX + xShift, viewY + yShift + PASSENGER_DECK_HEIGHT, getZ(0, y), color, shade);
 				}
 			}
 		} else {
@@ -431,13 +406,13 @@ EMaterialType.NO_MATERIAL, getPassengerDirection(direction, shipPosition, i), 0
 					if (material != null && count > 0) {
 						Sequence seq = this.imageProvider.getSettlerSequence(OBJECTS_FILE, material.getStackIndex());
 						Image image = seq.getImageSafe(count - 1, () -> Labels.getName(material, false));
-						image.drawAt(glDrawContext, viewX + xShift, viewY + yShift + CARGO_DECK_HEIGHT, 0, color, shade);
+						image.drawAt(glDrawContext, viewX + xShift, viewY + yShift + CARGO_DECK_HEIGHT, getZ(0, y), color, shade);
 					}
 				}
 			}
 		}
 		// draw ship front
-		drawShipLink(SHIP_IMAGE_FILE, baseSequence + 2, shipImageDirection, glDrawContext, viewX, viewY, color, shade);
+		drawShipLink(baseSequence + 2, shipImageDirection, glDrawContext, viewX, viewY, y, color, shade);
 		if (ship.isSelected()) {
 			drawSelectionMark(viewX, viewY, ship.getHealth() / shipType.getHealth());
 		}
@@ -450,10 +425,10 @@ private EDirection getPassengerDirection(EDirection shipDirection, ShortPoint2D
 		return shipDirection.getNeighbor(((x + seatIndex + slowerAnimationStep) / 8 + (y + seatIndex + slowerAnimationStep) / 11 + seatIndex) % 3 - 1);
 	}
 
-	private void drawShipLink(int imageFile, int sequence, EDirection direction, GLDrawContext gl, float viewX, float viewY, Color color, float shade) {
-		ImageLink shipLink = new OriginalImageLink(EImageLinkType.SETTLER, imageFile, sequence, direction.ordinal);
+	private void drawShipLink(int sequence, EDirection direction, GLDrawContext gl, float viewX, float viewY, float y, Color color, float shade) {
+		ImageLink shipLink = new OriginalImageLink(EImageLinkType.SETTLER, MapObjectDrawer.SHIP_IMAGE_FILE, sequence, direction.ordinal);
 		Image image = imageProvider.getImage(shipLink);
-		image.drawAt(gl, viewX, viewY, 0, color, shade);
+		image.drawAt(gl, viewX, viewY, getZ(0, y), color, shade);
 	}
 
 	private void drawObject(int x, int y, IMapObject object, float color) {
@@ -572,11 +547,7 @@ private void drawObject(int x, int y, IMapObject object, float color) {
 				break;
 
 			case BUILDING:
-				IBuilding building = (IBuilding) object;
-				if (building.getBuildingType() == EBuildingType.STOCK && building.getStateProgress() >= 0.99) {
-					return;
-				}
-				drawBuilding(x, y, building, color);
+				drawBuilding(x, y, (IBuilding) object, color);
 				break;
 
 			case PLACEMENT_BUILDING:
@@ -690,9 +661,21 @@ private void forceSetup() {
 		if (imageProvider == null) {
 			imageProvider = ImageProvider.getInstance();
 			imageMap = SettlerImageMap.getInstance();
+
+			playerBorderObjectImage = imageProvider.getSettlerSequence(FILE_BORDER_POST, 65).getImageSafe(0, () -> "border-indicator");
+		}
+
+		if(context.getGl() != lastDC) {
+			lastDC = context.getGl();
+
+			context.getGl().setShadowDepthOffset(shadow_offset);
+			SettlerImage.shadow_offset = shadow_offset;
+
 		}
 	}
 
+	private GLDrawContext lastDC = null;
+
 	/**
 	 * Draws any type of movable.
 	 *
@@ -859,8 +842,6 @@ private void drawMovableAt(IMovable movable, int x, int y) {
 		Color color = context.getPlayerColor(movable.getPlayer().getPlayerId());
 		float shade = MapObjectDrawer.getColor(fogStatus);
 		Image image;
-		int offX = context.getOffsetX();
-		int offY = context.getOffsetY();
 		float viewX;
 		float viewY;
 		int height = context.getHeight(x, y);
@@ -879,7 +860,7 @@ private void drawMovableAt(IMovable movable, int x, int y) {
 			viewY = context.getConverter().getViewY(smokeX, smokeY, height);
 			ImageLink link = new OriginalImageLink(EImageLinkType.SETTLER, 13, 43, (int) (moveProgress * 40));
 			image = imageProvider.getImage(link);
-			image.drawAt(context.getGl(), viewX+offX, viewY+offY, 0, color, shade);
+			image.drawAt(context.getGl(), viewX, viewY, getZ(0, smokeY), color, shade);
 		}
 
 		// melter action
@@ -893,7 +874,7 @@ private void drawMovableAt(IMovable movable, int x, int y) {
 			int metal = (movable.getGarrisonedBuildingType() == EBuildingType.IRONMELT) ? 37 : 36;
 			ImageLink link = new OriginalImageLink(EImageLinkType.SETTLER, 13, metal, number > 24 ? 24 : number);
 			image = imageProvider.getImage(link);
-			image.drawAt(context.getGl(), viewX+offX, viewY+offY, 0, color, shade);
+			image.drawAt(context.getGl(), viewX, viewY, getZ(0, metalY), color, shade);
 			// draw smoke
 			int smokeX = x - 9;
 			int smokeY = y - 14;
@@ -901,34 +882,30 @@ private void drawMovableAt(IMovable movable, int x, int y) {
 			viewY = context.getConverter().getViewY(smokeX, smokeY, height);
 			link = new OriginalImageLink(EImageLinkType.SETTLER, 13, 42, number > 35 ? 35 : number);
 			image = imageProvider.getImage(link);
-			image.drawAt(context.getGl(), viewX+offX, viewY+offY, SMOKE_Z, color, shade);
+			image.drawAt(context.getGl(), viewX, viewY, SMOKE_Z, color, shade);
 		}
 
 		if (movable.getAction() == EMovableAction.WALKING) {
-			int originX = x - movable.getDirection().getGridDeltaX();
-			int originY = y - movable.getDirection().getGridDeltaY();
-			viewX = betweenTilesX(originX, originY, x, y, moveProgress);
+			viewX = betweenTilesX(x, y, movable.getDirection().getInverseDirection(), 1-moveProgress);
 			viewY = betweenTilesY;
 		} else {
 			viewX = context.getConverter().getViewX(x, y, height);
 			viewY = context.getConverter().getViewY(x, y, height);
 		}
 		image = this.imageMap.getImageForSettler(movable, moveProgress);
-		image.drawAt(context.getGl(), viewX+offX, viewY+offY, 0, color, shade);
+		image.drawAt(context.getGl(), viewX, viewY, getZ(0, y), color, shade);
 
 		if (movable.isSelected()) {
-			drawSelectionMark(viewX+offX, viewY+offY, movable.getHealth() / movableType.getHealth());
+			drawSelectionMark(viewX, viewY, movable.getHealth() / movableType.getHealth());
 		}
 	}
 
-	private float betweenTilesX(int startX, int startY, int destinationX, int destinationY, float progress) {
+	private float betweenTilesX(int startX, int startY, EDirection direction, float progress) {
 		float theight = context.getHeight(startX, startY);
-		float dheight = context.getHeight(destinationX, destinationY);
+		float dheight = context.getHeight(startX+direction.gridDeltaX, startY+direction.gridDeltaY);
 		MapCoordinateConverter converter = context.getConverter();
-		float x = (1 - progress) * converter.getViewX(startX, startY, theight)
-			+ progress * converter.getViewX(destinationX, destinationY, dheight);
-		betweenTilesY = (1 - progress) * converter.getViewY(startX, startY, theight)
-			+ progress * converter.getViewY(destinationX, destinationY, dheight);
+		float x = converter.getViewX(startX+progress*direction.gridDeltaX, startY+progress*direction.gridDeltaY, theight+progress*(dheight-theight));
+		betweenTilesY = converter.getViewY(startX+progress*direction.gridDeltaX, startY+progress*direction.gridDeltaY, theight+progress*(dheight-theight));
 		return x;
 	}
 
@@ -988,10 +965,25 @@ private void drawArrow(MapDrawContext context, IArrowMapObject object,
 
 
 		boolean onGround = progress >= 1;
-		float x = betweenTilesX(object.getSourceX(), object.getSourceY(), object.getTargetX(), object.getTargetY(), progress) + context.getOffsetX();
 
-		Image image = this.imageProvider.getSettlerSequence(OBJECTS_FILE, sequence).getImageSafe(index, () -> "arrow-" + object.getDirection() + "-" + progress);
-		image.drawAt(context.getGl(), x, betweenTilesY + context.getOffsetY() + 20 * progress * (1 - progress) + 20, onGround?-.1f:0, null, color);
+		int startX = object.getSourceX();
+		int startY = object.getSourceY();
+		int destinationX = object.getTargetX();
+		int destinationY = object.getTargetY();
+		float theight = this.context.getHeight(startX, startY);
+		float dheight = this.context.getHeight(destinationX, destinationY);
+
+		float x = startX+progress*(destinationX-startX);
+		float y = startY+progress*(destinationY-startY);
+		float h = theight+progress*(dheight-theight);
+
+
+		MapCoordinateConverter converter = this.context.getConverter();
+		float viewX = converter.getViewX(x, y, h);
+		float viewY = converter.getViewY(x, y, h);
+
+		Image image = this.imageProvider.getSettlerSequence(OBJECTS_FILE, sequence).getImageSafe(index, () -> "arrow-" + object.getDirection() + "-" + index);
+		image.drawAt(context.getGl(), viewX, viewY + 20 * progress * (1 - progress) + 20, getZ(onGround?BACKGROUND_Z:0, y), null, color);
 	}
 
 	private void drawStones(int x, int y, int availableStones, float color) {
@@ -1005,7 +997,7 @@ private void drawWaves(int x, int y, float color) {
 		int len = seq.length();
 		int step = (animationStep / 2 + x / 2 + y / 2) % len;
 		if (step < len) {
-			draw(seq.getImageSafe(step, () -> "wave"), x, y, WAVES_Z, color); // waves must not be drawn on top of other things than water
+			draw(seq.getImageSafe(step, () -> "wave"), x, y, BACKGROUND_Z, color); // waves must not be drawn on top of other things than water
 		}
 	}
 
@@ -1110,7 +1102,6 @@ private void drawTree(int x, int y, float color) {
 	 * 		The player.
 	 */
 	public void drawPlayerBorderObject(int x, int y, byte player) {
-		// TODO: use instanced rendering for better android performance
 		forceSetup();
 
 		byte fogStatus = visibleGrid != null ? visibleGrid[x][y] : CommonConstants.FOG_OF_WAR_VISIBLE;
@@ -1118,7 +1109,8 @@ public void drawPlayerBorderObject(int x, int y, byte player) {
 			return; // break
 		}
 		Color color = context.getPlayerColor(player);
-		draw(imageProvider.getSettlerSequence(FILE_BORDER_POST, 65).getImageSafe(0, () -> "border-indicator"), x, y, BORDER_STONE_Z, color);
+
+		draw(playerBorderObjectImage, x, y, BACKGROUND_Z, color);
 	}
 
 	private static int getTreeType(int x, int y) {
@@ -1224,11 +1216,17 @@ private void drawBuilding(int x, int y, IBuilding building, float color) {
 				}
 				playSound(building, SOUND_MILL, x, y);
 
+			} else if(type == EBuildingType.STOCK) {
+				float[] zvalues = new float[] {-4*z_per_y, -2*z_per_y, 2*z_per_y, 3*z_per_y, 2*z_per_y, -2*z_per_y};
+				ImageLink[] images = EBuildingType.STOCK.getImages();
+				for (int i = 0; i != 6; i++) {
+					draw(imageProvider.getImage(images[i]), x, y, zvalues[i], color);
+				}
 			} else {
 				ImageLink[] images = type.getImages();
 				if (images.length > 0) {
 					Image image = imageProvider.getImage(images[0]);
-					draw(image, x, y, 0, null, color, building.getBuildingType() == EBuildingType.MARKET_PLACE);
+					draw(image, x, y, building.getBuildingType() == EBuildingType.MARKET_PLACE ? BACKGROUND_Z : 0, null, color);
 				}
 
 				byte fow = visibleGrid != null ? visibleGrid[x][y] : CommonConstants.FOG_OF_WAR_VISIBLE;
@@ -1312,9 +1310,9 @@ private void drawOccupiers(int x, int y, IOccupied building, float baseColor) {
 						image = this.imageMap.getImageForSettler(movable, movable.getMoveProgress());
 						break;
 				}
-				float viewX = towerX + place.getOffsetX() + context.getOffsetX();
-				float viewY = towerY + place.getOffsetY() + context.getOffsetY();
-				image.drawAt(gl, viewX, viewY, 0, color, baseColor);
+				float viewX = towerX + place.getOffsetX();
+				float viewY = towerY + place.getOffsetY();
+				image.drawAt(gl, viewX, viewY, getZ(0, y), color, baseColor);
 
 				if (place.getSoldierClass() == ESoldierClass.BOWMAN) {
 					playMovableSound(movable);
@@ -1338,8 +1336,8 @@ private void drawWithConstructionMask(int x, int y, float maskState, Image unsaf
 			return; // should not happen
 		}
 		int height = context.getHeight(x, y);
-		float viewX = context.getConverter().getViewX(x, y, height)+context.getOffsetX();
-		float viewY = context.getConverter().getViewY(x, y, height)+context.getOffsetY();
+		float viewX = context.getConverter().getViewX(x, y, height);
+		float viewY = context.getConverter().getViewY(x, y, height);
 
 		SingleImage image = (SingleImage) unsafeImage;
 		// number of tiles in x direction, can be adjusted for performance
@@ -1348,12 +1346,12 @@ private void drawWithConstructionMask(int x, int y, float maskState, Image unsaf
 		float topLineBottom = 1 - maskState;
 		float topLineTop = Math.max(0, topLineBottom - .1f);
 
-		image.drawTriangle(context.getGl(), viewX, viewY, 0, 1, 1, 1, 0, topLineBottom, color);
-		image.drawTriangle(context.getGl(), viewX, viewY, 1, 1, 1, topLineBottom, 0, topLineBottom, color);
+		image.drawTriangle(context.getGl(), viewX, viewY, 0, 1, 1, 1, 0,  topLineBottom, getZ(construction_offset, y),color);
+		image.drawTriangle(context.getGl(), viewX, viewY, 1, 1, 1, topLineBottom, 0,  topLineBottom, getZ(construction_offset, y),color);
 
 		for (int i = 0; i < tiles; i++) {
 			image.drawTriangle(context.getGl(), viewX, viewY, 1.0f / tiles * i,
-				topLineBottom, 1.0f / tiles * (i + 1), topLineBottom, 1.0f / tiles * (i + .5f), topLineTop, color
+				topLineBottom, 1.0f / tiles * (i + 1), topLineBottom, 1.0f / tiles * (i + .5f),  topLineTop, getZ(construction_offset, y),color
 			);
 		}
 	}
@@ -1399,52 +1397,41 @@ private void drawByProgressWithHeight(int x, int y, int height, float progress,
 	}
 
 	private void draw(Image image, int x, int y, float z, Color color) {
-		int height = context.getHeight(x, y);
-		float viewX = context.getConverter().getViewX(x, y, height)+context.getOffsetX();
-		float viewY = context.getConverter().getViewY(x, y, height)+context.getOffsetY();
-
-		image.drawAt(context.getGl(), viewX, viewY, z, color, 1);
+		draw(image, x, y, z, color, 1);
 	}
 
-	private void draw(Image image, int x, int y, float z, Color color, float fowDim, boolean background) {
-		if (background) {
-			z -= 0.1f;
-		}
+	private void draw(Image image, int x, int y, float z, Color color, float fowDim) {
 		int height = context.getHeight(x, y);
-		float viewX = context.getConverter().getViewX(x, y, height)+context.getOffsetX();
-		float viewY = context.getConverter().getViewY(x, y, height)+context.getOffsetY();
+		float viewX = context.getConverter().getViewX(x, y, height);
+		float viewY = context.getConverter().getViewY(x, y, height);
 
-		image.drawAt(context.getGl(), viewX, viewY, z, color, fowDim);
+		image.drawAt(context.getGl(), viewX, viewY, getZ(z, y), color, fowDim);
 	}
 
 	private void draw(Image image, int x, int y, float z, float fowDim) {
-		draw(image, x, y, z,null, fowDim, false);
-	}
-
-	private void draw(Image image, int x, int y, float z, Color color, float fowDim) {
-		draw(image, x, y, z, color, fowDim, false);
+		draw(image, x, y, z, null, fowDim);
 	}
 
 	private void drawOnlyImage(Image image, int x, int y, float z, Color torsoColor, float color) {
 		int height = context.getHeight(x, y);
-		float viewX = context.getConverter().getViewX(x, y, height)+context.getOffsetX();
-		float viewY = context.getConverter().getViewY(x, y, height)+context.getOffsetY();
-		image.drawOnlyImageAt(context.getGl(), viewX, viewY, z, torsoColor, color);
+		float viewX = context.getConverter().getViewX(x, y, height);
+		float viewY = context.getConverter().getViewY(x, y, height);
+		image.drawOnlyImageAt(context.getGl(), viewX, viewY, getZ(z, y), torsoColor, color);
 	}
 
 	private void drawOnlyShadow(Image image, int x, int y) {
 		int height = context.getHeight(x, y);
-		float viewX = context.getConverter().getViewX(x, y, height)+context.getOffsetX();
-		float viewY = context.getConverter().getViewY(x, y, height)+context.getOffsetY();
-		image.drawOnlyShadowAt(context.getGl(), viewX, viewY, 0);
+		float viewX = context.getConverter().getViewX(x, y, height);
+		float viewY = context.getConverter().getViewY(x, y, height);
+		image.drawOnlyShadowAt(context.getGl(), viewX, viewY, getZ(0, y));
 	}
 
 	private void drawWithHeight(Image image, int x, int y, int height, float color) {
 		int baseHeight = context.getHeight(x, y);
-		float viewX = context.getConverter().getViewX(x, y, baseHeight + height)+context.getOffsetX();
-		float viewY = context.getConverter().getViewY(x, y, baseHeight + height)+context.getOffsetY();
+		float viewX = context.getConverter().getViewX(x, y, baseHeight + height);
+		float viewY = context.getConverter().getViewY(x, y, baseHeight + height);
 
-		image.drawAt(context.getGl(), viewX, viewY, 0, null, color);
+		image.drawAt(context.getGl(), viewX, viewY, getZ(0, y), null, color);
 	}
 
 	public void drawMoveToMarker(ShortPoint2D moveToMarker, float progress) {
@@ -1455,4 +1442,8 @@ public void drawMoveToMarker(ShortPoint2D moveToMarker, float progress) {
 	public void drawGotoMarker(ShortPoint2D gotoMarker, Image image) {
 		draw(image, gotoMarker.x, gotoMarker.y, FLAG_ROOF_Z,null, 1);
 	}
+
+	private float getZ(float offset, float y) {
+		return y*z_per_y+offset;
+	}
 }
diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/geometry/MapCoordinateConverter.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/geometry/MapCoordinateConverter.java
index 36772cc8f7..fb2ddfdc58 100644
--- a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/geometry/MapCoordinateConverter.java
+++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/geometry/MapCoordinateConverter.java
@@ -17,9 +17,7 @@
 import java.awt.geom.AffineTransform;
 
 import go.graphics.UIPoint;
-import jsettlers.common.map.shapes.IMapArea;
 import jsettlers.common.map.shapes.MapRectangle;
-import jsettlers.common.map.shapes.Parallelogram;
 import jsettlers.common.position.FloatRectangle;
 import jsettlers.common.position.ShortPoint2D;
 
diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/minimap/Minimap.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/minimap/Minimap.java
index aa364bd721..4eba231a75 100644
--- a/jsettlers.graphics/src/main/java/jsettlers/graphics/map/minimap/Minimap.java
+++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/map/minimap/Minimap.java
@@ -14,10 +14,8 @@
  *******************************************************************************/
 package jsettlers.graphics.map.minimap;
 
-import go.graphics.EGeometryFormatType;
-import go.graphics.EGeometryType;
+import go.graphics.EPrimitiveType;
 import go.graphics.GLDrawContext;
-import go.graphics.GeometryHandle;
 import go.graphics.IllegalBufferException;
 import go.graphics.TextureHandle;
 import java.nio.ByteBuffer;
@@ -25,6 +23,7 @@
 import java.nio.ShortBuffer;
 import java.util.LinkedList;
 
+import go.graphics.UnifiedDrawHandle;
 import jsettlers.common.map.IGraphicsGrid;
 import jsettlers.common.map.shapes.MapRectangle;
 import jsettlers.common.position.ShortPoint2D;
@@ -91,52 +90,34 @@ public void setSize(int width, int height) {
 	}
 
 	private boolean updateGeometry = true;
-	private GeometryHandle geometry = null;
-	private GeometryHandle lineGeometry = null;
+	private UnifiedDrawHandle geometry = null;
+	private UnifiedDrawHandle lineGeometry = null;
 	private static final ByteBuffer lineBfr = ByteBuffer.allocateDirect(12*4).order(ByteOrder.nativeOrder());
 
 	private ByteBuffer bfr = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder());
 
-	private void replaceGeometryValue(GLDrawContext context, int pos, float value) throws IllegalBufferException {
-		bfr.rewind();
-		bfr.putFloat(value);
-		context.updateGeometryAt(geometry, pos*4, bfr);
+	private void replaceBufferValue(GLDrawContext context, int pos, float value) throws IllegalBufferException {
+		bfr.putFloat(0, value);
+		context.updateBufferAt(geometry.vertices, pos*4, bfr);
 	}
 
 	public void draw(GLDrawContext context, float x, float y) {
 		boolean imageWasCreatedJustNow = false;
 		try {
-			if(geometry == null || !geometry.isValid()) {
-				geometry = context.storeGeometry( new float[] {0, 0, 0, 0, width, 0, 1, 0,(stride + 1) * width, height, 1, 1, stride * width, height, 0, 1,}, EGeometryFormatType.Texture2D, false, "minimap");
-				lineGeometry = context.generateGeometry(6, EGeometryFormatType.VertexOnly2D, true, "minimap-frame");
-			}
-
-			if(updateGeometry) {
-				lineBfr.asFloatBuffer().put(miniMapShapeCalculator.getMiniMapShapeNodes(), 0, 12);
-				context.updateGeometryAt(lineGeometry, 0, lineBfr);
-
-				replaceGeometryValue(context, 4, width);
-				replaceGeometryValue(context, 8, (stride + 1) * width);
-				replaceGeometryValue(context, 9, height);
-				replaceGeometryValue(context, 12, stride * width);
-				replaceGeometryValue(context, 13, height);
-				updateGeometry = false;
-			}
-
 			synchronized (updateMutex) {
 				if (!imageIsValid || texture == null || !texture.isValid()) {
 					imageWasCreatedJustNow = true;
-					if (texture != null && texture.isValid()) {
-						context.deleteTexture(texture);
-						texture = null;
-					}
 					ShortBuffer data = ByteBuffer.allocateDirect(width * height * 2)
 												 .order(ByteOrder.nativeOrder()).asShortBuffer();
 					for (int i = 0; i < width * height; i++) {
 						data.put(LineLoader.BLACK);
 					}
 					data.position(0);
-					texture = context.generateTexture(width, height, data, "minimap");
+					if(texture != null && texture.isValid()) {
+						context.resizeTexture(texture, width, height, data);
+					} else {
+						texture = context.generateTexture(width, height, data, "minimap");
+					}
 					updatedLines.clear();
 					imageIsValid = true;
 				}
@@ -158,9 +139,25 @@ public void draw(GLDrawContext context, float x, float y) {
 				updateMutex.notifyAll();
 			}
 
-			context.draw2D(geometry, texture, EGeometryType.Quad, 0, 4, x, y, 0, 1, 1, 1, null, 1);
+			if(geometry == null || !geometry.isValid()) {
+				geometry = context.createUnifiedDrawCall(4, "minimap", texture, new float[] {0, 0, 0, 0, width, 0, 1, 0,(stride + 1) * width, height, 1, 1, stride * width, height, 0, 1});
+				lineGeometry = context.createUnifiedDrawCall(6, "minimap-frame", null, null);
+			}
+
+			if(updateGeometry) {
+				lineBfr.asFloatBuffer().put(miniMapShapeCalculator.getMiniMapShapeNodes(), 0, 12);
+				context.updateBufferAt(lineGeometry.vertices, 0, lineBfr);
+
+				replaceBufferValue(context, 4, width);
+				replaceBufferValue(context, 8, (stride + 1) * width);
+				replaceBufferValue(context, 9, height);
+				replaceBufferValue(context, 12, stride * width);
+				replaceBufferValue(context, 13, height);
+				updateGeometry = false;
+			}
 
-			drawViewMark(context, x, y);
+			geometry.drawSimple(EPrimitiveType.Quad, x, y, 0, 1, 1, null, 1);
+			lineGeometry.drawSimple(EPrimitiveType.LineLoop, x, y, 0, 1, 1, null, 1);
 		} catch (IllegalBufferException e) {
 			if (imageWasCreatedJustNow) {
 				// TODO: Error reporting
@@ -175,14 +172,6 @@ public void draw(GLDrawContext context, float x, float y) {
 		}
 	}
 
-	private void drawViewMark(GLDrawContext context, float x, float y) {
-		try {
-			context.draw2D(lineGeometry, null, EGeometryType.LineLoop, 0, 6, x, y, 0, 1, 1, 1, null, 1);
-		} catch (IllegalBufferException e) {
-			e.printStackTrace();
-		}
-	}
-
 	public int getWidth() {
 		return width;
 	}
diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/Button.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/Button.java
index a824265972..bd1ac1ee78 100644
--- a/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/Button.java
+++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/Button.java
@@ -49,7 +49,7 @@ public Button(Action action, ImageLink image, ImageLink active, String descripti
 
 	@Override
 	protected ImageLink getBackgroundImage() {
-		return active ? activeImage : image;
+		return isActive() ? activeImage : image;
 	}
 
 	public boolean isActive() {
diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/CountArrows.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/CountArrows.java
new file mode 100644
index 0000000000..644241223f
--- /dev/null
+++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/CountArrows.java
@@ -0,0 +1,39 @@
+package jsettlers.graphics.ui;
+
+import java8.util.function.Supplier;
+
+import jsettlers.common.action.Action;
+import jsettlers.common.images.EImageLinkType;
+import jsettlers.common.images.ImageLink;
+import jsettlers.common.images.OriginalImageLink;
+
+public class CountArrows extends UIPanel {
+	private static final ImageLink arrowsImageLink = new OriginalImageLink(EImageLinkType.GUI, 3, 231, 0); // checked in the original game
+
+	public CountArrows(Supplier increase, Supplier decrease) {
+		Button upButton = new ArrowButton(increase);
+		Button downButton = new ArrowButton(decrease);
+
+		addChild(upButton, 0f, 0.5f, 1f, 1f);
+		addChild(downButton, 0f, 0f, 1f, 0.5f);}
+
+	@Override
+	protected ImageLink getBackgroundImage() {
+		return arrowsImageLink;
+	}
+
+	private class ArrowButton extends Button {
+
+		private Supplier action;
+
+		public ArrowButton(Supplier action) {
+			super(null, null, null, "");
+			this.action = action;
+		}
+
+		@Override
+		public Action getAction() {
+			return action.get();
+		}
+	}
+}
diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/Label.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/Label.java
index 29b513f552..db6d39addf 100644
--- a/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/Label.java
+++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/Label.java
@@ -17,7 +17,6 @@
 import go.graphics.GLDrawContext;
 import go.graphics.text.EFontSize;
 import go.graphics.text.TextDrawer;
-import jsettlers.common.Color;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -150,7 +149,6 @@ public synchronized void drawAt(GLDrawContext gl) {
 		super.drawAt(gl);
 
 		TextDrawer drawer = gl.getTextDrawer(size);
-		drawer.setColor(Color.WHITE);
 
 		if (Double.isNaN(spaceWidth)) {
 			spaceWidth = drawer.getWidth(" ");
diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/LabeledButton.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/LabeledButton.java
index f54662ffa4..5ccbe03588 100644
--- a/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/LabeledButton.java
+++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/LabeledButton.java
@@ -17,7 +17,6 @@
 import go.graphics.GLDrawContext;
 import go.graphics.text.EFontSize;
 import go.graphics.text.TextDrawer;
-import jsettlers.common.Color;
 import jsettlers.common.images.EImageLinkType;
 import jsettlers.common.images.OriginalImageLink;
 import jsettlers.common.action.Action;
@@ -55,7 +54,6 @@ public void drawAt(GLDrawContext gl) {
 		super.drawAt(gl);
 
 		TextDrawer drawer = gl.getTextDrawer(size);
-		drawer.setColor(Color.WHITE);
 		drawer.renderCentered(getPosition().getCenterX(), getPosition().getCenterY(), text);
 	}
 
diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/SetMaterialProductionButton.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/SetMaterialProductionButton.java
deleted file mode 100644
index 0779d950b7..0000000000
--- a/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/SetMaterialProductionButton.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * ****************************************************************************
- * Copyright (c) 2015 - 2017
- * 

- * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - *

- * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - *

- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * ***************************************************************************** - */ -package jsettlers.graphics.ui; - -import jsettlers.common.material.EMaterialType; -import jsettlers.common.position.IPositionSupplier; -import jsettlers.common.action.Action; -import jsettlers.common.action.SetMaterialProductionAction; - -/** - * @author codingberlin - */ -public class SetMaterialProductionButton extends Button { - - private final IPositionSupplier positionSupplier; - private final EMaterialType materialType; - private final SetMaterialProductionAction.EMaterialProductionType productionType; - - public SetMaterialProductionButton(IPositionSupplier positionSupplier, EMaterialType materialType, SetMaterialProductionAction.EMaterialProductionType productionType) { - super(null); - this.positionSupplier = positionSupplier; - this.materialType = materialType; - this.productionType = productionType; - } - - public Action getAction() { - return new SetMaterialProductionAction(positionSupplier.getPosition(), materialType, productionType, 0); - } -} diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/UIInput.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/UIInput.java index 6577a00b33..8cca56ce9b 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/UIInput.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/ui/UIInput.java @@ -14,11 +14,9 @@ *******************************************************************************/ package jsettlers.graphics.ui; -import go.graphics.EGeometryFormatType; -import go.graphics.EGeometryType; +import go.graphics.EPrimitiveType; import go.graphics.GLDrawContext; -import go.graphics.GeometryHandle; -import go.graphics.IllegalBufferException; +import go.graphics.UnifiedDrawHandle; import go.graphics.event.GOEvent; import go.graphics.event.GOEventHandler; import go.graphics.event.GOKeyEvent; @@ -63,7 +61,7 @@ public void finished(GOEvent event) { @Override public void aborted(GOEvent event) {} - private static GeometryHandle geometry = null; + private static UnifiedDrawHandle geometry = null; @Override public void drawAt(GLDrawContext gl) { @@ -75,15 +73,11 @@ public void drawAt(GLDrawContext gl) { float x = getPosition().getMinX() + 2; drawer.drawString(x, y, inputString.toString()); - if(geometry == null || !geometry.isValid()) geometry = gl.storeGeometry(new float[] {0, 0, 0, 1}, EGeometryFormatType.VertexOnly2D, false, "uiinput-line"); + if(geometry == null || !geometry.isValid()) geometry = gl.createUnifiedDrawCall(2, "uiinput-line", null, new float[] {0, 0, 0, 1}); float carretX = x + drawer.getWidth(inputString.substring(0, carret) + "X") - drawer.getWidth("X"); - try { - gl.draw2D(geometry, null, EGeometryType.LineStrip, 0, 2, carretX, y, 0, 0, textHeight, 0, null, 1); - } catch (IllegalBufferException e) { - e.printStackTrace(); - } + geometry.drawSimple(EPrimitiveType.LineStrip, carretX, y, 0, 0, textHeight, null, 1); } @Override diff --git a/jsettlers.graphics/src/main/resources/jsettlers/graphics/localization/labels_de.properties b/jsettlers.graphics/src/main/resources/jsettlers/graphics/localization/labels_de.properties index 713155347c..91fd033f7b 100644 --- a/jsettlers.graphics/src/main/resources/jsettlers/graphics/localization/labels_de.properties +++ b/jsettlers.graphics/src/main/resources/jsettlers/graphics/localization/labels_de.properties @@ -324,6 +324,7 @@ settings-server = Mehrspielerserver settings-volume = Lautstärke settings-back = Abbrechen settings-ok = OK +game-menu-pause = Pause game-menu-cancel = Abbrechen game-menu-quit = Beenden game-menu-save = Speichern diff --git a/jsettlers.graphics/src/main/resources/jsettlers/graphics/localization/labels_en.properties b/jsettlers.graphics/src/main/resources/jsettlers/graphics/localization/labels_en.properties index a0f62417b7..52adc2e199 100644 --- a/jsettlers.graphics/src/main/resources/jsettlers/graphics/localization/labels_en.properties +++ b/jsettlers.graphics/src/main/resources/jsettlers/graphics/localization/labels_en.properties @@ -326,6 +326,7 @@ settings-fps-limit = fps limit settings-backend = drawing backend settings-back = Back settings-ok = OK +game-menu-pause = pause game-menu-cancel = Cancel game-menu-quit = Quit game game-menu-save = Save game diff --git a/jsettlers.logic/src/main/java/jsettlers/ai/highlevel/AiStatistics.java b/jsettlers.logic/src/main/java/jsettlers/ai/highlevel/AiStatistics.java index cc2f1334f3..d01ef8801e 100644 --- a/jsettlers.logic/src/main/java/jsettlers/ai/highlevel/AiStatistics.java +++ b/jsettlers.logic/src/main/java/jsettlers/ai/highlevel/AiStatistics.java @@ -38,7 +38,6 @@ import jsettlers.common.landscape.ELandscapeType; import jsettlers.common.landscape.EResourceType; import jsettlers.common.map.partition.IPartitionData; -import jsettlers.common.map.shapes.MapNeighboursArea; import jsettlers.common.mapobject.EMapObjectType; import jsettlers.common.material.EMaterialType; import jsettlers.common.movable.EDirection; @@ -288,10 +287,18 @@ private int mapInformationPlayerIdOfPosition(short x, short y) { } private boolean hasNeighborIngestibleByPioneersOf(int x, int y, Player player) { - return !MapNeighboursArea.stream(x, y) - .filterBounds(mainGrid.getWidth(), mainGrid.getHeight()) - .filter((currX, currY) -> isIngestibleByPioneersOf(currX, currY, player)) - .isEmpty(); + short width = mainGrid.getWidth(); + short height = mainGrid.getHeight(); + + for (EDirection direction : EDirection.values()) { + int dx = direction.gridDeltaX + x; + int dy = direction.gridDeltaY + y; + + if(dx >= 0 && dy >= 0 && dx < width && dy < height && isIngestibleByPioneersOf(dx, dy, player)) { + return true; + } + } + return false; } private boolean isIngestibleByPioneersOf(int x, int y, Player player) { diff --git a/jsettlers.logic/src/main/java/jsettlers/algorithms/distances/DistancesCalculationAlgorithm.java b/jsettlers.logic/src/main/java/jsettlers/algorithms/distances/DistancesCalculationAlgorithm.java index 36611660af..e134d4fb17 100644 --- a/jsettlers.logic/src/main/java/jsettlers/algorithms/distances/DistancesCalculationAlgorithm.java +++ b/jsettlers.logic/src/main/java/jsettlers/algorithms/distances/DistancesCalculationAlgorithm.java @@ -14,7 +14,7 @@ *******************************************************************************/ package jsettlers.algorithms.distances; -import jsettlers.common.map.shapes.MapNeighboursArea; +import jsettlers.common.movable.EDirection; import jsettlers.common.utils.coordinates.ICoordinatePredicate; import java.util.BitSet; @@ -44,9 +44,15 @@ public static BitSet calculatePositionsInDistance(int width, int height, ICoordi int x = index % width; int y = index / width; - MapNeighboursArea.stream(x, y).filterBounds(width, height).forEach((neighborX, neighborY) -> { // set neighbors for next run - neighbors.set(width * neighborY + neighborX); - }); + for (EDirection direction : EDirection.values()) { + int dx = direction.gridDeltaX + x; + int dy = direction.gridDeltaY + y; + + if(dx >= 0 && dy >= 0 && dx < width && dy < height) { + // set neighbors for next run + neighbors.set(width * dy + dx); + } + } } next = neighbors; @@ -63,9 +69,15 @@ private static void setupInitial(int width, int height, ICoordinatePredicate pro if (provider.test(x, y)) { done.set(width * y + x); // set as done - MapNeighboursArea.stream(x, y).filterBounds(width, height).forEach((neighborX, neighborY) -> { // set neighbors for next run - next.set(width * neighborY + neighborX); - }); + for (EDirection direction : EDirection.values()) { + int dx = direction.gridDeltaX + x; + int dy = direction.gridDeltaY + y; + + if(dx >= 0 && dy >= 0 && dx < width && dy < height) { + // set neighbors for next run + next.set(width * dy + dx); + } + } } } } diff --git a/jsettlers.logic/src/main/java/jsettlers/algorithms/fogofwar/CachedViewCircle.java b/jsettlers.logic/src/main/java/jsettlers/algorithms/fogofwar/CachedViewCircle.java index f3b2fd3858..7743275770 100644 --- a/jsettlers.logic/src/main/java/jsettlers/algorithms/fogofwar/CachedViewCircle.java +++ b/jsettlers.logic/src/main/java/jsettlers/algorithms/fogofwar/CachedViewCircle.java @@ -20,9 +20,9 @@ /** * Caches a {@link MapCircle} and the calculated view distances of the circles positions. - * + * * @author Andreas Eberle - * + * */ public final class CachedViewCircle { diff --git a/jsettlers.logic/src/main/java/jsettlers/algorithms/fogofwar/FogOfWar.java b/jsettlers.logic/src/main/java/jsettlers/algorithms/fogofwar/FogOfWar.java index 26bf85ee8e..030faa16c8 100644 --- a/jsettlers.logic/src/main/java/jsettlers/algorithms/fogofwar/FogOfWar.java +++ b/jsettlers.logic/src/main/java/jsettlers/algorithms/fogofwar/FogOfWar.java @@ -29,7 +29,7 @@ /** * This class holds the fog of war for a given map and team. - * + * * @author Andreas Eberle */ public final class FogOfWar implements Serializable { @@ -70,7 +70,7 @@ public void start(IFogOfWarGrid grid) { /** * Gets the visible status of a map pint - * + * * @param x * The x coordinate of the point in 0..(mapWidth - 1) * @param y diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/MainGrid.java b/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/MainGrid.java index 8f2f0e3f6b..b09a13a61d 100644 --- a/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/MainGrid.java +++ b/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/MainGrid.java @@ -166,7 +166,8 @@ public MainGrid(String mapId, String mapName, short width, short height, PlayerS this.flagsGrid = new FlagsGrid(width, height); this.movablePathfinderGrid = new MovablePathfinderGrid(); - this.mapObjectsManager = new MapObjectsManager(new MapObjectsManagerGrid()); + MapObjectsManagerGrid grid = new MapObjectsManagerGrid(); + this.mapObjectsManager = new MapObjectsManager(grid); this.objectsGrid = new ObjectsGrid(width, height); this.landscapeGrid = new LandscapeGrid(width, height, flagsGrid); @@ -798,6 +799,11 @@ public final byte getHeightAt(int x, int y) { return landscapeGrid.getHeightAt(x, y); } + @Override + public byte[] getHeightArray() { + return landscapeGrid.getHeightArray(); + } + @Override public final ELandscapeType getLandscapeTypeAt(int x, int y) { return landscapeGrid.getLandscapeTypeAt(x, y); diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/landscape/LandscapeGrid.java b/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/landscape/LandscapeGrid.java index bb186a1eab..62784238b5 100644 --- a/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/landscape/LandscapeGrid.java +++ b/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/landscape/LandscapeGrid.java @@ -108,6 +108,10 @@ public final byte getHeightAt(int x, int y) { return heightGrid[x + y * width]; } + public byte[] getHeightArray() { + return heightGrid; + } + public final ELandscapeType getLandscapeTypeAt(int x, int y) { return ELandscapeType.VALUES[landscapeGrid[x + y * width]]; } diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/partition/manager/PartitionManager.java b/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/partition/manager/PartitionManager.java index 56d168678e..a6aa6d5bbd 100644 --- a/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/partition/manager/PartitionManager.java +++ b/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/partition/manager/PartitionManager.java @@ -240,14 +240,17 @@ public void removePositionTo(final int x, final int y, PartitionManager newManag } private void removePositionTo(ShortPoint2D pos, LinkedList fromList, LinkedList toList, boolean newHasSamePlayer) { - Iterator iter = fromList.iterator(); - while (iter.hasNext()) { - T curr = iter.next(); + int len = fromList.size(); + for(int i = 0;i != len;) { + T curr = fromList.get(i); if (curr.getPosition().equals(pos)) { - iter.remove(); if (newHasSamePlayer) { toList.offer(curr); } + fromList.remove(i); + len--; + } else { + i++; } } } diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/partition/manager/datastructures/PositionableList.java b/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/partition/manager/datastructures/PositionableList.java index de2458332e..d5aae8b28b 100644 --- a/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/partition/manager/datastructures/PositionableList.java +++ b/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/partition/manager/datastructures/PositionableList.java @@ -132,6 +132,8 @@ public boolean isEmpty() { } public void moveObjectsAtPositionTo(ShortPoint2D position, PositionableList newList, Consumer movedVisitor) { + if(data.isEmpty()) return; + Iterator iterator = data.iterator(); while (iterator.hasNext()) { T curr = iterator.next(); diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/strategies/military/InfantryStrategy.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/strategies/military/InfantryStrategy.java index 0928d56772..1627d50665 100644 --- a/jsettlers.logic/src/main/java/jsettlers/logic/movable/strategies/military/InfantryStrategy.java +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/strategies/military/InfantryStrategy.java @@ -42,6 +42,7 @@ public final ESoldierClass getSoldierClass() { @Override protected boolean isEnemyAttackable(IAttackable enemy, boolean isInTower) { + if(!enemy.isAlive()) return false; int maxDistance = movable.getPosition().getOnGridDistTo(enemy.getPosition()); return (maxDistance == 1 || (!enemy.isTower() && super.getMovableType().isPikeman() && maxDistance <= 2)); } diff --git a/jsettlers.logic/src/main/java/jsettlers/main/GameTimeProvider.java b/jsettlers.logic/src/main/java/jsettlers/main/GameTimeProvider.java index ca92f6b01c..1f868b67bb 100644 --- a/jsettlers.logic/src/main/java/jsettlers/main/GameTimeProvider.java +++ b/jsettlers.logic/src/main/java/jsettlers/main/GameTimeProvider.java @@ -40,4 +40,9 @@ public int getGameTime() { public boolean isGamePausing() { return gameClock.isPausing(); } + + @Override + public float getGameSpeed() { + return gameClock.getGameSpeed(); + } } diff --git a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/JSettlersFrame.java b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/JSettlersFrame.java index da23250481..9cee56e8e2 100644 --- a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/JSettlersFrame.java +++ b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/JSettlersFrame.java @@ -114,6 +114,7 @@ private void updateFullScreenMode() { GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice graphicsDevice = graphicsEnvironment.getDefaultScreenDevice(); graphicsDevice.setFullScreenWindow(fullScreen ? this : null); + if(areaContainer != null) areaContainer.notifyResize(); } private void abortRedrawTimerIfPresent() { @@ -160,16 +161,20 @@ public void setContent(MapContent content) { Area area = new Area(); area.set(region); - redrawTimer = new Timer("opengl-redraw"); - redrawTimer.schedule(new TimerTask() { - @Override - public void run() { - region.requestRedraw(); - } - }, 100, 1000/SettingsManager.getInstance().getFpsLimit()); + int fpsLimit = SettingsManager.getInstance().getFpsLimit(); + if(fpsLimit != 0) { + redrawTimer = new Timer("opengl-redraw"); + redrawTimer.schedule(new TimerTask() { + @Override + public void run() { + region.requestRedraw(); + } + }, 100, (long) (1000.0 / fpsLimit)); + } SwingUtilities.invokeLater(() -> { - setContentPane(areaContainer = new AreaContainer(area, SettingsManager.getInstance().getBackend(), SettingsManager.getInstance().isGraphicsDebug())); + setContentPane(areaContainer = new AreaContainer(area, SettingsManager.getInstance().getBackend(), SettingsManager.getInstance().isGraphicsDebug(), SettingsManager.getInstance().getGuiScale())); + areaContainer.updateFPSLimit(fpsLimit); revalidate(); repaint(); }); @@ -195,7 +200,7 @@ public void showJoinMultiplayerMenu(IJoinPhaseMultiplayerGameConnector joinPhase } public IMapInterfaceConnector showStartedGame(IStartedGame startedGame) { - MapContent content = new MapContent(startedGame, soundPlayer, SettingsManager.getInstance().getFpsLimit(), ETextDrawPosition.TOP_RIGHT); + MapContent content = new MapContent(startedGame, soundPlayer, ETextDrawPosition.TOP_RIGHT); SwingUtilities.invokeLater(() -> setContent(content)); startedGame.setGameExitListener(exitGame -> SwingUtilities.invokeLater(this::showMainMenu)); return content.getInterfaceConnector(); diff --git a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/lookandfeel/ui/SettlerProgressbar.java b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/lookandfeel/ui/SettlerProgressbar.java index 8a65b25fb6..6fdbffcfbc 100644 --- a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/lookandfeel/ui/SettlerProgressbar.java +++ b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/lookandfeel/ui/SettlerProgressbar.java @@ -46,6 +46,7 @@ public void installUI(JComponent c) { c.setOpaque(false); c.setBorder(BorderFactory.createLineBorder(Color.WHITE, 2)); + c.setFont(UIDefaults.FONT_SMALL); JProgressBar pg = (JProgressBar) c; pg.setForeground(FOREGROUND_COLOR); diff --git a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/lookandfeel/ui/SettlersComboboxUi.java b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/lookandfeel/ui/SettlersComboboxUi.java index e86e88e4d4..1856ea5e96 100644 --- a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/lookandfeel/ui/SettlersComboboxUi.java +++ b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/lookandfeel/ui/SettlersComboboxUi.java @@ -50,6 +50,7 @@ public void installUI(JComponent c) { ((JComboBox) c).setRenderer(new SettlersListCellRenderer()); c.setBorder(BorderFactory.createLineBorder(Color.WHITE)); + c.setFont(UIDefaults.FONT_SMALL); } @Override diff --git a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/lookandfeel/ui/TextAreaUiDark.java b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/lookandfeel/ui/TextAreaUiDark.java index 75e3029ce6..025c41e4a8 100644 --- a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/lookandfeel/ui/TextAreaUiDark.java +++ b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/lookandfeel/ui/TextAreaUiDark.java @@ -31,6 +31,7 @@ public class TextAreaUiDark extends BasicTextAreaUI { public void installUI(JComponent c) { super.installUI(c); TextComponentHelper.installUi((JTextComponent) c); + c.setFont(UIDefaults.FONT_SMALL); } @Override diff --git a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/lookandfeel/ui/TextFieldUiDark.java b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/lookandfeel/ui/TextFieldUiDark.java index 4a5e6ba919..b3d710ea4c 100644 --- a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/lookandfeel/ui/TextFieldUiDark.java +++ b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/lookandfeel/ui/TextFieldUiDark.java @@ -31,6 +31,7 @@ public class TextFieldUiDark extends MetalTextFieldUI { public void installUI(JComponent c) { super.installUI(c); TextComponentHelper.installUi((JTextComponent) c); + c.setFont(UIDefaults.FONT_SMALL); } @Override diff --git a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/lookandfeel/ui/UIDefaults.java b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/lookandfeel/ui/UIDefaults.java index 1decfb42b0..cb0febfca4 100644 --- a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/lookandfeel/ui/UIDefaults.java +++ b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/lookandfeel/ui/UIDefaults.java @@ -17,6 +17,8 @@ import java.awt.Color; import java.awt.Font; +import jsettlers.main.swing.settings.SettingsManager; + /** * Constant colors for L&F * @@ -41,7 +43,8 @@ private UIDefaults() { /** * Default font */ - public static final Font FONT = new Font("Sans", Font.BOLD, 14); + public static final Font FONT = new Font("Sans", Font.BOLD, (int)(14*SettingsManager.getInstance().getGuiScale())); + public static final Font FONT_SMALL = new Font("Sans", Font.PLAIN, (int)(10*SettingsManager.getInstance().getGuiScale())); /** * Default font diff --git a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/menu/joinpanel/JoinGamePanel.java b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/menu/joinpanel/JoinGamePanel.java index 18546cea0a..0d618ac006 100644 --- a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/menu/joinpanel/JoinGamePanel.java +++ b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/menu/joinpanel/JoinGamePanel.java @@ -129,7 +129,13 @@ private void createStructure() { JPanel settingsPanelWrapper = new JPanel(); westPanel.add(settingsPanelWrapper, BorderLayout.CENTER); settingsPanelWrapper.add(settingsPanel); - settingsPanel.setLayout(new GridLayout(0, 2, 20, 20)); + //settingsPanel.setLayout(new GridLayout(0, 2, 20, 0)); + JPanel settingsLabelPanel = new JPanel(); + settingsLabelPanel.setLayout(new GridLayout(3, 0, 0, 20)); + JPanel settingsComboBoxPanel = new JPanel(); + settingsComboBoxPanel.setLayout(new GridLayout(3, 0, 0, 20)); + settingsPanel.add(settingsLabelPanel); + settingsPanel.add(settingsComboBoxPanel); mapPanel.setLayout(new BorderLayout()); JPanel mapNameLabelWrapper = new JPanel(); mapPanel.add(mapNameLabelWrapper, BorderLayout.NORTH); @@ -137,12 +143,12 @@ private void createStructure() { mapNameLabel.setHorizontalAlignment(SwingConstants.CENTER); mapPanel.add(mapImage, BorderLayout.CENTER); mapImage.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); - settingsPanel.add(numberOfPlayersLabel); - settingsPanel.add(numberOfPlayersComboBox); - settingsPanel.add(startResourcesLabel); - settingsPanel.add(startResourcesComboBox); - settingsPanel.add(peaceTimeLabel); - settingsPanel.add(peaceTimeComboBox); + settingsLabelPanel.add(numberOfPlayersLabel); + settingsComboBoxPanel.add(numberOfPlayersComboBox); + settingsLabelPanel.add(startResourcesLabel); + settingsComboBoxPanel.add(startResourcesComboBox); + settingsLabelPanel.add(peaceTimeLabel); + settingsComboBoxPanel.add(peaceTimeComboBox); sendChatMessageButton.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 15)); JPanel chatPanel = new JPanel(); chatPanel.setLayout(new BorderLayout(0, 10)); diff --git a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/menu/openpanel/MapListCellRenderer.java b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/menu/openpanel/MapListCellRenderer.java index d6fcf11370..d7b309f407 100644 --- a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/menu/openpanel/MapListCellRenderer.java +++ b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/menu/openpanel/MapListCellRenderer.java @@ -41,6 +41,7 @@ import jsettlers.logic.map.loading.newmap.MapFileHeader; import jsettlers.main.swing.JSettlersSwingUtil; import jsettlers.main.swing.lookandfeel.ELFStyle; +import jsettlers.main.swing.lookandfeel.ui.UIDefaults; /** * Render to open an existing map @@ -160,6 +161,11 @@ public MapListCellRenderer() { descriptionLabel.setForeground(FOREGROUND); playerCountLabel.setForeground(Color.BLUE); + mapNameLabel.setFont(UIDefaults.FONT_SMALL); + mapIdLabel.setFont(UIDefaults.FONT_SMALL); + descriptionLabel.setFont(UIDefaults.FONT_SMALL); + playerCountLabel.setFont(UIDefaults.FONT_SMALL); + contentsPanel.setLayout(new BorderLayout()); contentsPanel.add(rightPanelPart, BorderLayout.CENTER); contentsPanel.add(iconLabel, BorderLayout.WEST); diff --git a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/menu/settingsmenu/SettingsMenuPanel.java b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/menu/settingsmenu/SettingsMenuPanel.java index b27670f251..8696cfb5df 100644 --- a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/menu/settingsmenu/SettingsMenuPanel.java +++ b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/menu/settingsmenu/SettingsMenuPanel.java @@ -45,8 +45,9 @@ public class SettingsMenuPanel extends JPanel { * Name of the player */ private final JTextField playerNameField = new JTextField(); - private final SettingsSlider volumeSlider = new SettingsSlider("%", 0,100); - private final SettingsSlider fpsLimitSlider = new SettingsSlider("fps", 1,240); + private final SettingsSlider volumeSlider = new SettingsSlider("%", 0,100, null); + private final SettingsSlider fpsLimitSlider = new SettingsSlider("fps", 0,240, "timerless redraw"); + private final SettingsSlider guiScaleSlider = new SettingsSlider("%", 50,400, "system default"); private final BackendSelector backendSelector = new BackendSelector(); /** @@ -77,6 +78,8 @@ public SettingsMenuPanel(MainMenuPanel mainMenuPanel) { addSetting("settings-fps-limit", fpsLimitSlider); addSetting("settings-backend", backendSelector); + + addSetting("settings-gui-scale", guiScaleSlider); initButton(); } @@ -111,7 +114,8 @@ private void initButton() { settingsManager.setUserName(playerNameField.getText()); settingsManager.setVolume(volumeSlider.getValue() / 100f); settingsManager.setFpsLimit(fpsLimitSlider.getValue()); - settingsManager.setBackend(backendSelector.getSelectedItem().toString()); + settingsManager.setBackend(backendSelector.getSelectedItem()+""); + settingsManager.setGuiScale(guiScaleSlider.getValue()/100f); mainMenuPanel.reset(); }); @@ -128,5 +132,6 @@ public void initializeValues() { volumeSlider.setValue((int) (settingsManager.getVolume() * 100)); fpsLimitSlider.setValue(settingsManager.getFpsLimit()); backendSelector.setSelectedItem(settingsManager.getBackend()); + guiScaleSlider.setValue(Math.round(settingsManager.getGuiScale()*100)); } } diff --git a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/menu/settingsmenu/SettingsSlider.java b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/menu/settingsmenu/SettingsSlider.java index 9bf725bc96..af39a24f3c 100644 --- a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/menu/settingsmenu/SettingsSlider.java +++ b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/menu/settingsmenu/SettingsSlider.java @@ -21,7 +21,8 @@ * Slider to select volume in settings *

* This slider is technically based on a progress bar, but looks and works like the production sliders in the original game. (blue bars) The slider - * lets the user select a value from 0 to 100%, the value is also displayed as string + * lets the user select a value from {@link SettingsSlider#getMinimum()} to {@link SettingsSlider#getMaximum()}, the value is also displayed as string.
+ * {@link SettingsSlider#minString} is shown if {@link SettingsSlider#getValue()} == {@link SettingsSlider#getMinimum()} && {@link SettingsSlider#minString} != null * * @author Andreas Butti */ @@ -29,11 +30,15 @@ public class SettingsSlider extends SettlersSlider { private static final long serialVersionUID = 1L; private String unit; + private String minString; + private int minValue; - public SettingsSlider(String unit, int min_value, int max_value) { + public SettingsSlider(String unit, int min_value, int max_value, String minString) { setStringPainted(true); this.unit = unit; + this.minString = minString; + minValue = min_value; setMinimum(min_value); setMaximum(max_value); setValue(50); @@ -45,6 +50,10 @@ public SettingsSlider(String unit, int min_value, int max_value) { @Override public void setValue(int n) { super.setValue(n); - setString(n + unit); + if(n == minValue && minString != null) { + setString(minString); + } else { + setString(n + unit); + } } } diff --git a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/settings/SettingsManager.java b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/settings/SettingsManager.java index 5bdab0cbdb..a67bce8a24 100644 --- a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/settings/SettingsManager.java +++ b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/settings/SettingsManager.java @@ -56,6 +56,7 @@ public class SettingsManager implements ISoundSettingsProvider { private static final String SETTING_FULL_SCREEN_MODE = "fullScreenMode"; private static final String SETTING_GRAPHICS_DEBUG = "debug-opengl"; + private static final String SETTINGS_GUI_SCALE = "gui-scale"; private static final String SETTING_CONTROL_ALL = "control-all"; private static final String SETTING_ACTIVATE_ALL_PLAYERS = "activate-all-players"; private static final String SETTING_ENABLE_CONSOLE_LOGGING = "console-output"; @@ -189,7 +190,7 @@ public int getFpsLimit() { String fpsLimitString = get(SETTING_FPS_LIMIT); try { int fps_limit = fpsLimitString != null ? Integer.parseInt(fpsLimitString) : 60; - return Math.max(Math.min(fps_limit, 240), 1); + return Math.max(Math.min(fps_limit, 240), 0); } catch (NumberFormatException e) { } return 1; @@ -204,6 +205,8 @@ public void setVolume(float volume) { public void setFpsLimit(int fpsLimit) {set(SETTING_FPS_LIMIT,Integer.toString(fpsLimit));} + public void setGuiScale(float scale) {set(SETTINGS_GUI_SCALE, ""+scale);} + public void setBackend(String backend) {set(SETTING_BACKEND, backend);} public void setFullScreenMode(boolean fullScreenMode) { @@ -285,4 +288,13 @@ public void setUserName(String userName) { public boolean isGraphicsDebug() { return getOptional(SETTING_GRAPHICS_DEBUG); } + + public float getGuiScale() { + String guiScaleString = get(SETTINGS_GUI_SCALE); + try { + return Math.max(guiScaleString != null ? Float.parseFloat(guiScaleString) : 1, 0.5f); + } catch (NumberFormatException e) { + } + return 0.5f; + } } diff --git a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/control/EditorControl.java b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/control/EditorControl.java index bf1283e997..ac3dff0af0 100644 --- a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/control/EditorControl.java +++ b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/control/EditorControl.java @@ -283,7 +283,7 @@ public void buildMapEditingWindow() { Area area = new Area(); final Region region = new Region(Region.POSITION_CENTER); area.set(region); - displayPanel = new AreaContainer(area, SettingsManager.getInstance().getBackend(), SettingsManager.getInstance().isGraphicsDebug()); + displayPanel = new AreaContainer(area, SettingsManager.getInstance().getBackend(), SettingsManager.getInstance().isGraphicsDebug(), SettingsManager.getInstance().getGuiScale()); displayPanel.setMinimumSize(new Dimension(640, 480)); displayPanel.setFocusable(true); root.add(displayPanel, BorderLayout.CENTER); diff --git a/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-0m.zmap b/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-0m.zmap index 414068e1ba..0da0ff7fca 100644 Binary files a/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-0m.zmap and b/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-0m.zmap differ diff --git a/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-10m.zmap b/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-10m.zmap index 51a43175e8..840a43ebc6 100644 Binary files a/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-10m.zmap and b/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-10m.zmap differ diff --git a/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-20m.zmap b/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-20m.zmap index 5520e26b97..717538eeda 100644 Binary files a/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-20m.zmap and b/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-20m.zmap differ diff --git a/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-40m.zmap b/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-40m.zmap index 724fb0b015..53c86e6aca 100644 Binary files a/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-40m.zmap and b/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-40m.zmap differ diff --git a/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-65m.zmap b/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-65m.zmap index 02b7d63522..5da02793bc 100644 Binary files a/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-65m.zmap and b/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-65m.zmap differ diff --git a/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-90m.zmap b/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-90m.zmap index 77ff7d6e92..6cd6cd8481 100644 Binary files a/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-90m.zmap and b/jsettlers.testutils/src/main/resources/jsettlers/integration/replay/fullproduction/savegame-90m.zmap differ diff --git a/jsettlers.tools/src/main/java/go/graphics/swing/test/LwjglTest.java b/jsettlers.tools/src/main/java/go/graphics/swing/test/LwjglTest.java index 9035ddb8b9..74757a588e 100644 --- a/jsettlers.tools/src/main/java/go/graphics/swing/test/LwjglTest.java +++ b/jsettlers.tools/src/main/java/go/graphics/swing/test/LwjglTest.java @@ -14,12 +14,10 @@ *******************************************************************************/ package go.graphics.swing.test; -import go.graphics.EGeometryFormatType; -import go.graphics.EGeometryType; +import go.graphics.EPrimitiveType; import go.graphics.GLDrawContext; -import go.graphics.GeometryHandle; -import go.graphics.IllegalBufferException; import go.graphics.UIPoint; +import go.graphics.UnifiedDrawHandle; import go.graphics.area.Area; import go.graphics.event.GOEvent; import go.graphics.event.GOModalEventHandler; @@ -31,9 +29,6 @@ import go.graphics.region.RegionContent; import go.graphics.swing.AreaContainer; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - import javax.swing.JFrame; /** @@ -115,25 +110,18 @@ public void handleEvent(GOEvent event) { private final Object pointLock = new Object(); - private GeometryHandle pointGeometry = null; - private ByteBuffer bfr = ByteBuffer.allocateDirect(4*2*2).order(ByteOrder.nativeOrder()); + private static UnifiedDrawHandle pointGeometry = null; @Override public void drawContent(GLDrawContext gl2, int width, int height) { if(point_index < 2) return; - if(pointGeometry == null) pointGeometry = gl2.generateGeometry(2, EGeometryFormatType.VertexOnly2D, true, null); + if(pointGeometry == null || !pointGeometry.isValid()) pointGeometry = gl2.createUnifiedDrawCall(2, null, null, new float[] {0, 0, 1, 1}); synchronized (pointLock) { - try { - for (int i = 1; i != point_index; i++) { - bfr.asFloatBuffer().put(new float[]{pointx[i - 1], pointy[i - 1], pointx[i], pointy[i]}); - gl2.updateGeometryAt(pointGeometry, 0, bfr); - gl2.draw2D(pointGeometry, null, EGeometryType.LineStrip, 0, 2, 0, 0, 0, 1, 1, 1, null, 1); - } - } catch (IllegalBufferException ex) { - ex.printStackTrace(); + for (int i = 1; i != point_index; i++) { + pointGeometry.drawSimple(EPrimitiveType.LineStrip, pointx[i-1], pointy[i-1], 0, pointx[i-1]-pointx[i], pointy[i-1]-pointy[i], null, 1); } pointx[0] = pointx[point_index-1]; pointy[0] = pointy[point_index-1]; diff --git a/jsettlers.tools/src/main/java/jsettlers/CharacterExtractor.java b/jsettlers.tools/src/main/java/jsettlers/CharacterExtractor.java new file mode 100644 index 0000000000..1d522e2a5c --- /dev/null +++ b/jsettlers.tools/src/main/java/jsettlers/CharacterExtractor.java @@ -0,0 +1,34 @@ +package jsettlers; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.util.stream.IntStream; + +public class CharacterExtractor { + public static void main(String[] args) { + if(args.length == 1) { + extract(args[0]); + } else { + System.err.println("filename needed"); + } + } + + private static void extract(String filename) { + File f = new File(filename); + StringBuilder content = new StringBuilder(); + try { + BufferedReader reader = new BufferedReader(new FileReader(f)); + String line; + while((line = reader.readLine()) != null) { + content.append(line); + } + } catch(Throwable thrown) { + thrown.printStackTrace(); + } + + + Object[] specialCharacters = IntStream.range(0, content.length()).mapToObj(content::charAt).filter(character -> character > 127).distinct().sorted().toArray(); + for(Object specialCharacter : specialCharacters) System.out.print(specialCharacter); + } +} diff --git a/jsettlers.tools/src/main/java/jsettlers/graphics/debug/DatFileTester.java b/jsettlers.tools/src/main/java/jsettlers/graphics/debug/DatFileTester.java index dd8414ade5..7c91a399c6 100644 --- a/jsettlers.tools/src/main/java/jsettlers/graphics/debug/DatFileTester.java +++ b/jsettlers.tools/src/main/java/jsettlers/graphics/debug/DatFileTester.java @@ -14,11 +14,10 @@ *******************************************************************************/ package jsettlers.graphics.debug; -import go.graphics.EGeometryFormatType; -import go.graphics.EGeometryType; +import go.graphics.EPrimitiveType; import go.graphics.GLDrawContext; -import go.graphics.GeometryHandle; import go.graphics.IllegalBufferException; +import go.graphics.UnifiedDrawHandle; import go.graphics.area.Area; import go.graphics.event.GOEvent; import go.graphics.event.GOKeyEvent; @@ -34,7 +33,6 @@ import jsettlers.common.utils.mutables.Mutable; import jsettlers.graphics.image.SingleImage; import jsettlers.graphics.image.Image; -import jsettlers.graphics.image.SingleImage; import jsettlers.graphics.image.SettlerImage; import jsettlers.graphics.image.reader.AdvancedDatFileReader; import jsettlers.graphics.image.reader.DatFileReader; @@ -168,7 +166,6 @@ private void drawSequences(GLDrawContext gl2, int width, int h maxheight = drawSequence(gl2, width, height, y, seq); - drawer.setColor(Color.WHITE); drawer.drawString(offsetX+20, offsetY+y+20, seqIndex + ":"); seqIndex++; @@ -191,19 +188,19 @@ private int drawSequence(GLDrawContext gl2, int width, int hei return maxheight; } - private GeometryHandle lineGeometry = null; + private UnifiedDrawHandle lineGeometry = null; private ByteBuffer lineBfr = ByteBuffer.allocateDirect(3*4).order(ByteOrder.nativeOrder()); private void drawImage(GLDrawContext gl2, int y, int index, int x, SingleImage image) { image.drawAt(gl2, x - image.getOffsetX(), y + image.getHeight() + image.getOffsetY(), 0, colors[index % colors.length], 1); - if(lineGeometry == null) lineGeometry = gl2.generateGeometry(3, EGeometryFormatType.VertexOnly2D, true, null); + if(lineGeometry == null) lineGeometry = gl2.createUnifiedDrawCall(3, null, null, null); try { lineBfr.asFloatBuffer().put(new float[] {image.getHeight() + image.getOffsetY(), - image.getOffsetX(), image.getHeight() + image.getOffsetY() }, 0, 3); - gl2.updateGeometryAt(lineGeometry, 3*4, lineBfr); + gl2.updateBufferAt(lineGeometry.vertices, 3*4, lineBfr); - gl2.draw2D(lineGeometry, null, EGeometryType.LineStrip, 0, 3, x, y, 0, 1, 1, 1, Color.RED, 1); + lineGeometry.drawSimple(EPrimitiveType.LineStrip, x, y, 0, 1, 1, Color.RED, 1); } catch (IllegalBufferException e) { e.printStackTrace(); } diff --git a/jsettlers.tools/src/main/java/jsettlers/graphics/debug/DatFileViewer.java b/jsettlers.tools/src/main/java/jsettlers/graphics/debug/DatFileViewer.java index 97b53174a2..a089a4faa4 100644 --- a/jsettlers.tools/src/main/java/jsettlers/graphics/debug/DatFileViewer.java +++ b/jsettlers.tools/src/main/java/jsettlers/graphics/debug/DatFileViewer.java @@ -44,12 +44,11 @@ import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; -import go.graphics.EGeometryFormatType; -import go.graphics.EGeometryType; +import go.graphics.EPrimitiveType; import go.graphics.GLDrawContext; -import go.graphics.GeometryHandle; import go.graphics.IllegalBufferException; import go.graphics.UIPoint; +import go.graphics.UnifiedDrawHandle; import go.graphics.event.GOEvent; import go.graphics.event.GOEventHandlerProvider; import go.graphics.event.GOKeyEvent; @@ -58,26 +57,31 @@ import go.graphics.event.mouse.GOZoomEvent; import go.graphics.swing.GLContainer; import go.graphics.swing.contextcreator.EBackendType; +import go.graphics.swing.contextcreator.GLContextException; import go.graphics.text.EFontSize; import go.graphics.text.TextDrawer; import jsettlers.common.Color; +import jsettlers.common.resources.SettlersFolderChecker; import jsettlers.common.utils.FileUtils; import jsettlers.graphics.image.SingleImage; import jsettlers.graphics.image.Image; -import jsettlers.graphics.image.SingleImage; import jsettlers.graphics.image.SettlerImage; import jsettlers.graphics.image.reader.AdvancedDatFileReader; import jsettlers.graphics.image.reader.DatFileType; import jsettlers.graphics.image.sequence.Sequence; import jsettlers.graphics.image.sequence.SequenceList; +import jsettlers.main.swing.SwingManagedJSettlers; +import jsettlers.main.swing.settings.SettingsManager; public class DatFileViewer extends JFrame implements ListSelectionListener { private JLabel lblDatType; private JLabel lblNumUiSeqs; private JLabel lblNumSettlerSeqs; private JLabel lblNumLandscapeSeqs; + private JLabel directoryLabel; private JList listView; private Surface glCanvas; + private File defaultDirectory; private File gfxDirectory; private DefaultListModel listItems; private AdvancedDatFileReader reader; @@ -90,6 +94,7 @@ private enum ImageSet { public static void main(String[] args) { try { + SwingManagedJSettlers.setupResources(false, args); UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); new DatFileViewer(); } catch (Exception ex) { @@ -100,6 +105,8 @@ public static void main(String[] args) { private DatFileViewer() { glCanvas = new Surface(); + defaultDirectory = SettlersFolderChecker.checkSettlersFolder(SettingsManager.getInstance().getSettlersFolder()).gfxFolder; + JPanel infoField = new JPanel(); infoField.setLayout(new BoxLayout(infoField, BoxLayout.PAGE_AXIS)); @@ -117,8 +124,13 @@ private DatFileViewer() { listView = new JList<>(listItems); listView.getSelectionModel().addListSelectionListener(this); + JSplitPane filePane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); + filePane.setDividerSize(0); + filePane.setTopComponent(directoryLabel = new JLabel()); + filePane.setBottomComponent(new JScrollPane(listView)); + JSplitPane splitPane2 = new JSplitPane(JSplitPane.VERTICAL_SPLIT); - splitPane2.setTopComponent(new JScrollPane(listView)); + splitPane2.setTopComponent(filePane); splitPane2.setBottomComponent(infoField); splitPane2.setResizeWeight(0.5); splitPane2.setEnabled(false); @@ -128,6 +140,8 @@ private DatFileViewer() { splitPane.setRightComponent(glCanvas); splitPane.setResizeWeight(0.10); + updateDirectory(defaultDirectory); + this.setTitle("DatFileViewer"); this.setJMenuBar(createMenu()); this.getContentPane().add(splitPane); @@ -141,6 +155,12 @@ private JMenuBar createMenu() { JMenuItem openDirItem = new JMenuItem("GFX Folder"); openMenu.add(openDirItem); + JMenuItem openDefaultItem = null; + + if(defaultDirectory != null) { + openMenu.add(openDefaultItem = new JMenuItem("Default Folder")); + } + JMenu exportMenu = new JMenu("Export Images"); JMenuItem exportThis = new JMenuItem("from this file"); JMenuItem exportAll = new JMenuItem("from all files"); @@ -159,17 +179,16 @@ private JMenuBar createMenu() { JFileChooser openDirDlg = new JFileChooser(); openDirDlg.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); if (openDirDlg.showDialog(null, null) == JFileChooser.APPROVE_OPTION) { - gfxDirectory = new File(openDirDlg.getSelectedFile().getAbsolutePath()); - - FileUtils.iterateChildren(gfxDirectory, (currentFile) -> { - String fileName = currentFile.getName(); - if (currentFile.isFile() && fileName.endsWith(".dat")) { - listItems.addElement(currentFile.getName()); - } - }); + updateDirectory(new File(openDirDlg.getSelectedFile().getAbsolutePath())); } }); + if(openDefaultItem != null) { + openDefaultItem.addActionListener((e) -> { + updateDirectory(defaultDirectory); + }); + } + exportThis.addActionListener((e) -> onExportSelectedFile()); exportAll.addActionListener((e) -> onExportAllFiles()); @@ -195,6 +214,18 @@ private JMenuBar createMenu() { return bar; } + private void updateDirectory(File dir) { + gfxDirectory = dir; + directoryLabel.setText(gfxDirectory.getAbsolutePath()); + listItems.clear(); + FileUtils.iterateChildren(gfxDirectory, (currentFile) -> { + String fileName = currentFile.getName(); + if (currentFile.isFile() && fileName.endsWith(".dat")) { + listItems.addElement(currentFile.getName()); + } + }); + } + @Override public void valueChanged(ListSelectionEvent e) { if (e.getFirstIndex() < 0 || e.getValueIsAdjusting()) { return; } @@ -350,7 +381,7 @@ public void invalidate() { } @Override - public void draw() { + public void draw() throws GLContextException { super.draw(); redraw(context, getWidth(), getHeight()); } @@ -408,7 +439,6 @@ private void drawMultipleSequences(GLDrawContext gl2, int y, S int maxHeight = drawSingleSequence(gl2, y, 20, seq); - drawer.setColor(Color.WHITE); drawer.drawString(-20, y + 20, seqIndex + ":"); seqIndex++; @@ -430,19 +460,19 @@ private int drawSingleSequence(GLDrawContext gl2, int y, int x return maxHeight; } - private GeometryHandle lineGeometry = null; + private UnifiedDrawHandle lineGeometry = null; private ByteBuffer lineBfr = ByteBuffer.allocateDirect(3*4).order(ByteOrder.nativeOrder()); private void drawImage(GLDrawContext gl2, int x, int y, int index, SingleImage image) { image.drawAt(gl2, x - image.getOffsetX(), y + image.getHeight() + image.getOffsetY(), 0, colors[index % colors.length], 1); - if(lineGeometry == null) lineGeometry = gl2.generateGeometry(3, EGeometryFormatType.VertexOnly2D, true, null); + if(lineGeometry == null) lineGeometry = gl2.createUnifiedDrawCall(3, null ,null, null); try { lineBfr.asFloatBuffer().put(new float[] {image.getHeight() + image.getOffsetY(), -image.getOffsetX(), image.getHeight() + image.getOffsetY()}, 0, 3); - gl2.updateGeometryAt(lineGeometry, 3*4, lineBfr); + gl2.updateBufferAt(lineGeometry.vertices, 3*4, lineBfr); - gl2.draw2D(lineGeometry, null, EGeometryType.LineStrip, 0, 3, x, y, 0, 1, 1, 1, Color.RED, 1); + lineGeometry.drawSimple(EPrimitiveType.LineStrip, x, y, 0, 1, 1, Color.RED, 1); } catch (IllegalBufferException e) { e.printStackTrace(); }