package speiger.src.coreengine.rendering.gui.renderer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import org.lwjgl.opengl.GL11; import org.lwjgl.system.MemoryUtil; import speiger.src.collections.objects.lists.ObjectArrayList; import speiger.src.collections.objects.queues.ObjectArrayFIFOQueue; import speiger.src.collections.objects.queues.ObjectPriorityDequeue; import speiger.src.collections.utils.Stack; import speiger.src.coreengine.assets.reloader.IReloadableResource; import speiger.src.coreengine.math.misc.Facing; import speiger.src.coreengine.math.vector.floats.Vec2f; import speiger.src.coreengine.math.vector.floats.Vec3f; import speiger.src.coreengine.math.vector.floats.Vec4f; import speiger.src.coreengine.math.vector.matrix.Matrix4f; import speiger.src.coreengine.math.vector.quaternion.Quaternion; import speiger.src.coreengine.rendering.gui.GuiManager; import speiger.src.coreengine.rendering.gui.helper.Align; import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox; import speiger.src.coreengine.rendering.gui.renderer.buffer.RenderBuffer; import speiger.src.coreengine.rendering.models.DrawCall; import speiger.src.coreengine.rendering.shader.uniforms.UniformVec2f; import speiger.src.coreengine.rendering.tesselation.GLCall; import speiger.src.coreengine.rendering.tesselation.Tesselator; import speiger.src.coreengine.rendering.tesselation.VertexType; import speiger.src.coreengine.rendering.textures.base.ITexture; import speiger.src.coreengine.rendering.textures.base.TextureManager; import speiger.src.coreengine.rendering.utils.GLUtils; import speiger.src.coreengine.utils.collections.pools.SimplePool; public class UIRenderer implements IReloadableResource { SimplePool recylcePool = new SimplePool<>(100, Matrix4f::new); Stack transformStack = new ObjectArrayList<>(); Matrix4f transform = new Matrix4f(); boolean isFastTransform = true; SimplePool fastPool = new SimplePool<>(100, () -> Vec4f.newMutable(0F, 0F, 0F, 1F)); Stack fastStack = new ObjectArrayList<>(); Vec4f fastTransform = Vec4f.newMutable(0F, 0F, 0F, 1F); SimplePool callPool = new SimplePool<>(100, GLCall::new); ObjectPriorityDequeue activeCalls = new ObjectArrayFIFOQueue<>(); Tesselator bufferHelper = new Tesselator(655340); FloatBuffer renderBuffer = MemoryUtil.memAllocFloat(655340); Tesselator renderer = new Tesselator(renderBuffer); ITexture currentTexture; boolean drawing = false; boolean useTexture = false; Vec4f currentFrame = Vec4f.newMutable(); float roundedNess = 0F; GuiModel texturedModel = GuiModel.createTextureModel(Short.MAX_VALUE * 2); Align horizontal = Align.CENTER; Align vertical = Align.CENTER; Vec3f helper = Vec3f.newMutable(); Vec4f transformHelper = Vec4f.newMutable(); Vec4f alignHelper = Vec4f.newMutable(); Vec3f alignTranslation = Vec3f.newMutable(0F); Vec3f alignScale = Vec3f.newMutable(1F); GuiManager manager; public UIRenderer(GuiManager manager) { this.manager = manager; TextureManager.INSTANCE.getReloader().addReloadableResource(this); } public RenderBuffer createBuffer() { return new RenderBuffer(bufferHelper); } public UIRenderer push() { if(isFastTransform) { fastStack.push(fastPool.get().set(fastTransform)); return this; } transformStack.push(recylcePool.get().load(transform)); return this; } public UIRenderer pop() { if(isFastTransform) { Vec4f pop = fastStack.pop(); fastTransform.set(pop); fastPool.accept(pop); return this; } Matrix4f pop = transformStack.pop(); transform.load(pop); pop.getScale(alignScale); recylcePool.accept(pop); return this; } public UIRenderer setVisibility(float visibility) { sanityCheck("Visibilty Change"); renderer.setVisibilty(visibility); return this; } public UIRenderer setBrightness(float brightness) { sanityCheck("Brightness change"); renderer.setBrightness(brightness); return this; } public UIRenderer resetEffects() { renderer.setBrightness(1F).setVisibilty(1F); return this; } public UIRenderer setActiveTexture(ITexture texture) { if(getTextureId(currentTexture) != getTextureId(texture)) { addDrawCall(); currentTexture = texture; } return this; } public UIRenderer setRoundness(IGuiBox box, float roundedNess) { if(roundedNess <= 0) { if(this.roundedNess > 0F) addDrawCall(); this.roundedNess = roundedNess; return this; } alignHelper.set(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY()); if(!currentFrame.equals(alignHelper) || this.roundedNess != roundedNess) { addDrawCall(); currentFrame.set(alignHelper); this.roundedNess = roundedNess; } return this; } protected void applyEffects(UniformVec2f vec) { vec.storeData(renderer.getBrightness(), renderer.getVisibility()); } public UIRenderer translate(float x, float y) { return translate(x, y, 0F); } public UIRenderer translate(Vec2f offset) { return translate(offset.getX(), offset.getY(), 0F); } public UIRenderer translate(float x, float y, float z) { if(isFastTransform) { fastTransform.add(x, y, z, 0F); return this; } transform.translate(helper.set(x, y, z)); return this; } public UIRenderer translate(Vec3f offset) { if(isFastTransform) { fastTransform.add(offset.getX(), offset.getY(), offset.getZ(), 0F); return this; } transform.translate(offset); return this; } public UIRenderer rotate(Quaternion quad) { if(isFastTransform) return this; transform.rotate(quad); return this; } public UIRenderer rotateX(float angle) { if(isFastTransform) return this; transform.rotateX((float)Math.toRadians(angle)); return this; } public UIRenderer rotateY(float angle) { if(isFastTransform) return this; transform.rotateY((float)Math.toRadians(angle)); return this; } public UIRenderer rotateZ(float angle) { if(isFastTransform) return this; transform.rotateZ((float)Math.toRadians(angle)); return this; } public UIRenderer scale(float x, float y, float z) { if(isFastTransform) return this; transform.scale(helper.set(x, y, z)); alignScale.multiply(x, y, z); return this; } public UIRenderer scale(float scale) { if(isFastTransform) { fastTransform.multiply(1F, 1F, 1F, scale); return this; } transform.scale(helper.set(scale, scale, scale)); alignScale.multiply(scale); return this; } public UIRenderer unscale(float x, float y, float z) { if(isFastTransform) return this; transform.scale(helper.set(1.0F / x, 1.0F / y, 1.0F / z)); alignScale.multiply(helper); return this; } public UIRenderer unscale(float scale) { scale = 1.0F / scale; if(isFastTransform) { fastTransform.multiply(1F, 1F, 1F, scale); return this; } transform.scale(helper.set(scale, scale, scale)).getScale(alignScale); alignScale.multiply(scale); return this; } public UIRenderer resetTransform() { if(isFastTransform) { fastTransform.set(0F, 0F, 0F, 1F); return this; } transform.setIdentity(); alignScale.set(Vec3f.ONE); return this; } public float getCurrentZ() { return isFastTransform ? fastTransform.getZ() : transform.get(3, 2); } public ITexture getActiveTexture() { return currentTexture; } protected Matrix4f getCurrentMatrix() { return transform; } public Matrix4f createCurrentMatrix(Matrix4f matrix) { return isFastTransform ? matrix.setIdentity().translate(fastTransform.getX(), fastTransform.getY(), fastTransform.getZ()) : matrix.load(transform); } public UIRenderer setFastTransform(boolean fast) { isFastTransform = fast; return this; } public UIRenderer beginFrame() { if(drawing) { throw new RuntimeException("Already Drawing!"); } drawing = true; fastTransform.set(0F, 0F, 0F, 1F); roundedNess = 0F; currentFrame.negate(); transform.setIdentity(); alignScale.set(Vec3f.ONE); transformStack.clear(); fastStack.clear(); return this; } public UIRenderer flush() { if(!renderer.isDrawing()) { return this; } renderer.finishData(); int count = renderer.getVertexCount(); if(count <= 0) { return this; } if(count > (activeCalls.isEmpty() ? 0 : activeCalls.last().getCount())) { addDrawCall(); } texturedModel.storeData(renderBuffer); GuiShader shader = manager.getShader(); shader.bind(manager.getOrthoMatrixBuffer()); texturedModel.bindArray(); int last = 0; while(!activeCalls.isEmpty()) { GLCall call = activeCalls.dequeue(); shader.texture.storeTexture(call.getTexture()); shader.useTexture.storeData(call.getTexture() > 0 ? 1F : 0F); shader.roundeness.storeData(call.getRoundedNess()); if(call.getRoundedNess() > 0F) shader.bounds.storeData(call.getFrame()); GLUtils.drawArrays(call.getType(), last, call.getCount() - last); last = call.getCount(); callPool.accept(call); } activeCalls.clear(); texturedModel.unbindArray(); shader.stopShader(); renderer.resetVertexes(); return this; } public UIRenderer endFrame() { if(!drawing) { throw new RuntimeException("Not Drawing!"); } drawing = false; isFastTransform = true; flush(); fastTransform.set(0F, 0F, 0F, 1F); transform.setIdentity(); renderer.resetEffects(); useTexture = false; while(!transformStack.isEmpty()) { recylcePool.accept(transformStack.pop()); } while(!fastStack.isEmpty()) { fastPool.accept(fastStack.pop()); } return this; } @Override public void reload() { texturedModel.remove(); MemoryUtil.memFree(renderBuffer); texturedModel = GuiModel.createTextureModel(Short.MAX_VALUE * 2); renderBuffer = MemoryUtil.memAllocFloat(655340); renderer = new Tesselator(renderBuffer); } @Override public void destroy() { texturedModel.remove(); MemoryUtil.memFree(renderBuffer); TextureManager.INSTANCE.getReloader().removeReloadableResource(this); } public UIRenderer drawBuffers(Iterable drawCalls, float width, float height) { sanityCheck("Buffer Drawing"); applyAlignment(0, 0, width, height, 0, alignHelper); for(DrawCall call : drawCalls) { ensureDrawing(call.getGLType(), call.getTextureId() > 0); FloatBuffer buffer = ByteBuffer.wrap(call.getData()).order(ByteOrder.nativeOrder()).asFloatBuffer(); for(int i = 0,m=buffer.remaining();i+9<=m;i+=9) { pos(transformHelper.set(buffer.get(i)+alignHelper.getX(), buffer.get(i+1)+alignHelper.getY(), buffer.get(i+2), 1F)) .tex(buffer.get(i+3), buffer.get(i+4)) .color4f(buffer.get(i+5), buffer.get(i+6), buffer.get(i+7), buffer.get(i+8)).endVertex(); } } alignTranslation.set(0F, 0F, 0F); return this; } public UIRenderer drawQuad(IGuiBox box, int color) { return drawQuad(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), color); } public UIRenderer drawQuad(IGuiBox box, float zLevel, int color) { return drawQuad(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), zLevel, color); } public UIRenderer drawQuad(float minX, float minY, float maxX, float maxY, int color) { return drawQuad(minX, minY, maxX, maxY, 0.0F, color); } public UIRenderer drawQuad(float minX, float minY, float maxX, float maxY, float zLevel, int color) { ensureDrawing(GL11.GL_TRIANGLES, false); applyAlignment(minX, minY, maxX, maxY, zLevel, alignHelper); pos(transformHelper.set(alignHelper.getX(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); pos(transformHelper.set(alignHelper.getZ(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); alignTranslation.set(0F, 0F, 0F); return this; } public UIRenderer drawTexturedQuad(IGuiBox box, int color, float minU, float minV, float maxU, float maxV) { return drawTexturedQuad(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), color, minU, minV, maxU, maxV); } public UIRenderer drawTexturedQuad(IGuiBox box, float zLevel, int color, float minU, float minV, float maxU, float maxV) { return drawTexturedQuad(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), zLevel, color, minU, minV, maxU, maxV); } public UIRenderer drawTexturedQuad(float minX, float minY, float maxX, float maxY, int color, float minU, float minV, float maxU, float maxV) { return drawTexturedQuad(minX, minY, maxX, maxY, 0F, color, minU, minV, maxU, maxV); } public UIRenderer drawTexturedQuad(float minX, float minY, float maxX, float maxY, float zLevel, int color, float minU, float minV, float maxU, float maxV) { ensureDrawing(GL11.GL_TRIANGLES, true); applyAlignment(minX, minY, maxX, maxY, zLevel, alignHelper); pos(transformHelper.set(alignHelper.getX(), alignHelper.getW(), 0F, 1F)).tex(minU, maxV).color4f(color).endVertex(); pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(minU, minV).color4f(color).endVertex(); pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(maxU, maxV).color4f(color).endVertex(); pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(maxU, maxV).color4f(color).endVertex(); pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(minU, minV).color4f(color).endVertex(); pos(transformHelper.set(alignHelper.getZ(), alignHelper.getY(), 0F, 1F)).tex(maxU, minV).color4f(color).endVertex(); alignTranslation.set(0F, 0F, 0F); return this; } public UIRenderer drawGradientQuad(IGuiBox box, int from, int to, Facing direction) { return drawGradientQuad(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), from, to, direction); } public UIRenderer drawGradientQuad(float minX, float minY, float maxX, float maxY, int from, int to, Facing direction) { return drawGradientQuad(minX, minY, maxX, maxY, 0F, from, to, direction); } public UIRenderer drawGradientQuad(float minX, float minY, float maxX, float maxY, float zLevel, int start, int end, Facing direction) { if(!direction.isPositive()) { int wrapper = start; start = end; end = wrapper; } ensureDrawing(GL11.GL_TRIANGLES, false); applyAlignment(minX, minY, maxX, maxY, zLevel, alignHelper); pos(transformHelper.set(alignHelper.getX(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? start : end).endVertex(); pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? start : start).endVertex(); pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? end : end).endVertex(); pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? end : end).endVertex(); pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? start : start).endVertex(); pos(transformHelper.set(alignHelper.getZ(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(direction.isXAxis() ? end : start).endVertex(); alignTranslation.set(0F, 0F, 0F); return this; } public UIRenderer drawFrame(IGuiBox box, int color) { return drawFrame(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), color); } public UIRenderer drawFrame(IGuiBox box, float zLevel, int color) { return drawFrame(box.getMinX(), box.getMinY(), box.getMaxX(), box.getMaxY(), zLevel, color); } public UIRenderer drawFrame(float minX, float minY, float maxX, float maxY, int color) { return drawFrame(minX, minY, maxX, maxY, 0F, color); } public UIRenderer drawFrame(float minX, float minY, float maxX, float maxY, float zLevel, int color) { ensureDrawing(GL11.GL_LINES, false); applyAlignment(minX, minY, maxX, maxY, zLevel, alignHelper); pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); pos(transformHelper.set(alignHelper.getZ(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); pos(transformHelper.set(alignHelper.getZ(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); pos(transformHelper.set(alignHelper.getX(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); pos(transformHelper.set(alignHelper.getX(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); alignTranslation.set(0F, 0F, 0F); return this; } public UIRenderer drawLine(float minX, float minY, float maxX, float maxY, int color) { return drawLine(minX, minY, maxX, maxY, 0F, color); } public UIRenderer drawLine(float minX, float minY, float maxX, float maxY, float zLevel, int color) { ensureDrawing(GL11.GL_LINES, false); applyAlignment(minX, minY, maxX, maxY, zLevel, alignHelper); pos(transformHelper.set(alignHelper.getX(), alignHelper.getY(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); pos(transformHelper.set(alignHelper.getZ(), alignHelper.getW(), 0F, 1F)).tex(0F, 0F).color4f(color).endVertex(); alignTranslation.set(0F, 0F, 0F); return this; } public UIRenderer startCustomShape(int shapeType, boolean needsTexture) { sanityCheck("Custom Shape"); ensureDrawing(shapeType, needsTexture); return this; } public UIRenderer pos(float x, float y, float z) { pos(transformHelper.set(x, y, z, 1F)); return this; } public UIRenderer pos(Vec3f pos) { return pos(pos.getX(), pos.getY(), pos.getZ()); } public UIRenderer color(float r, float g, float b) { renderer.color4f(r, g, b); return this; } public UIRenderer color(float r, float g, float b, float a) { renderer.color4f(r, g, b, a); return this; } public UIRenderer color(int color) { renderer.color4f(color); return this; } public UIRenderer tex(float u, float v) { renderer.tex(u, v); return this; } public UIRenderer tex(Vec2f tex) { return tex(tex.getX(), tex.getY()); } public UIRenderer endVertex() { renderer.endVertex(); return this; } protected void sanityCheck(String task) { if(drawing) { return; } throw new RuntimeException(task); } protected Tesselator pos(Vec4f pos) { if(isFastTransform) { return renderer.pos(fastTransform(pos.getX(), fastTransform.getX(), alignTranslation.getX()), fastTransform(pos.getY(), fastTransform.getY(), alignTranslation.getY()), fastTransform(pos.getZ(), fastTransform.getZ(), alignTranslation.getZ())); } float x = (transform.get(0) * pos.getX() + transform.get(4) * pos.getY() + transform.get(8) * pos.getZ() + transform.get(12)) + (alignTranslation.getX() * alignScale.getX()); float y = (transform.get(1) * pos.getX() + transform.get(5) * pos.getY() + transform.get(9) * pos.getZ() + transform.get(13)) + (alignTranslation.getY() * alignScale.getY()); float z = (transform.get(2) * pos.getX() + transform.get(6) * pos.getY() + transform.get(10) * pos.getZ() + transform.get(14)) + (alignTranslation.getZ() * alignScale.getZ()); return renderer.pos(x, y, z); } private float fastTransform(float pos, float offset, float align) { return ((pos + align) * fastTransform.getW()) + offset; } protected void applyAlignment(float minX, float minY, float maxX, float maxY, float zLevel, Vec4f result) { applyAlignment(minX, minY, maxX, maxY, zLevel, vertical, horizontal, renderer, result); } protected void applyAlignment(float minX, float minY, float maxX, float maxY, float zLevel, Align vertical, Align horizontal, Tesselator tes, Vec4f result) { float width = maxX - minX; float height = maxY - minY; float xOffset = 0F; float yOffset = 0F; switch(vertical) { case LEFT_TOP: yOffset = 0; result.setY(0F).setW(height); break; case CENTER: yOffset = height / 2F; result.setY(-yOffset).setW(yOffset); break; case RIGHT_BOTTOM: yOffset = maxY; result.setY(-height).setW(0F); break; } switch(horizontal) { case LEFT_TOP: xOffset = 0; result.setX(0F).setZ(width); break; case CENTER: xOffset = width / 2F; result.setX(-xOffset).setZ(xOffset); break; case RIGHT_BOTTOM: xOffset = maxX; result.setX(-width).setZ(0F); break; } alignTranslation.set(minX + xOffset, minY + yOffset, zLevel); } protected void ensureDrawing(int glState, boolean textureNeeded) { if(!renderer.isDrawing()) { renderer.begin(glState, VertexType.UI); } if(glState != renderer.getGLDrawType() || useTexture != textureNeeded) { addDrawCall(); renderer.changeGLType(glState); useTexture = textureNeeded; } } protected void addDrawCall() { if(getDrawCall() >= renderer.getVertexCount()) { return; } GLCall call = callPool.get(); call.setType(renderer.getGLDrawType()); call.setCount(renderer.getVertexCount()); call.setTexture(getTextureId(useTexture ? currentTexture : null)); call.setFrame(currentFrame); call.setRounded(roundedNess); activeCalls.enqueue(call); } private int getDrawCall() { return activeCalls.isEmpty() ? 0 : activeCalls.last().getCount(); } private int getTextureId(ITexture texture) { return texture == null ? 0 : texture.getTextureId(); } }