Added GUI render and support for faces without textures
- Added FlatRenderProgram to render 2D graphics - Can be used as-is - Supports rectangular coordinate-aligned nested masks - Added AssembledFlatLayer to cache 2D graphics - Use RenderTarget for add elements during reassembly - ShapeRenderProgram now allows null textures (defaults to white BG) - Sprite now deduces size based on primitive's content size - unless given size explicitly - ShapeRenderHelper.*WorldTransform methods renamed to .*Transform
This commit is contained in:
parent
47ea102ed7
commit
1ed8f93af4
@ -21,6 +21,8 @@ import ru.windcorp.optica.Proxy;
|
|||||||
import ru.windcorp.optica.client.graphics.GUI;
|
import ru.windcorp.optica.client.graphics.GUI;
|
||||||
import ru.windcorp.optica.client.graphics.backend.GraphicsBackend;
|
import ru.windcorp.optica.client.graphics.backend.GraphicsBackend;
|
||||||
import ru.windcorp.optica.client.graphics.backend.RenderTaskQueue;
|
import ru.windcorp.optica.client.graphics.backend.RenderTaskQueue;
|
||||||
|
import ru.windcorp.optica.client.graphics.flat.FlatRenderProgram;
|
||||||
|
import ru.windcorp.optica.client.graphics.flat.LayerTestUI;
|
||||||
import ru.windcorp.optica.client.graphics.world.LayerWorld;
|
import ru.windcorp.optica.client.graphics.world.LayerWorld;
|
||||||
import ru.windcorp.optica.client.graphics.world.WorldRenderProgram;
|
import ru.windcorp.optica.client.graphics.world.WorldRenderProgram;
|
||||||
|
|
||||||
@ -30,6 +32,7 @@ public class ClientProxy implements Proxy {
|
|||||||
public void initialize() {
|
public void initialize() {
|
||||||
GraphicsBackend.initialize();
|
GraphicsBackend.initialize();
|
||||||
try {
|
try {
|
||||||
|
RenderTaskQueue.waitAndInvoke(FlatRenderProgram::init);
|
||||||
RenderTaskQueue.waitAndInvoke(WorldRenderProgram::init);
|
RenderTaskQueue.waitAndInvoke(WorldRenderProgram::init);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
@ -37,6 +40,7 @@ public class ClientProxy implements Proxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
GUI.addBottomLayer(new LayerWorld());
|
GUI.addBottomLayer(new LayerWorld());
|
||||||
|
GUI.addTopLayer(new LayerTestUI());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
21
src/main/java/ru/windcorp/optica/client/graphics/Colors.java
Normal file
21
src/main/java/ru/windcorp/optica/client/graphics/Colors.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package ru.windcorp.optica.client.graphics;
|
||||||
|
|
||||||
|
public class Colors {
|
||||||
|
|
||||||
|
public static final int
|
||||||
|
WHITE = 0xFFFFFF,
|
||||||
|
BLACK = 0x000000,
|
||||||
|
|
||||||
|
GRAY_4 = 0x444444,
|
||||||
|
GRAY = 0x888888,
|
||||||
|
GRAY_A = 0xAAAAAA,
|
||||||
|
|
||||||
|
DEBUG_RED = 0xFF0000,
|
||||||
|
DEBUG_GREEN = 0x00FF00,
|
||||||
|
DEBUG_BLUE = 0x0000FF,
|
||||||
|
|
||||||
|
DEBUG_CYAN = 0x00FFFF,
|
||||||
|
DEBUG_MAGENTA = 0xFF00FF,
|
||||||
|
DEBUG_YELLOW = 0x00FFFF;
|
||||||
|
|
||||||
|
}
|
@ -17,6 +17,8 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package ru.windcorp.optica.client.graphics;
|
package ru.windcorp.optica.client.graphics;
|
||||||
|
|
||||||
|
import ru.windcorp.optica.client.graphics.backend.GraphicsInterface;
|
||||||
|
|
||||||
public abstract class Layer {
|
public abstract class Layer {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
@ -43,5 +45,13 @@ public abstract class Layer {
|
|||||||
protected abstract void initialize();
|
protected abstract void initialize();
|
||||||
|
|
||||||
protected abstract void doRender();
|
protected abstract void doRender();
|
||||||
|
|
||||||
|
protected float getWidth() {
|
||||||
|
return GraphicsInterface.getFramebufferWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected float getHeight() {
|
||||||
|
return GraphicsInterface.getFramebufferHeight();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,103 @@
|
|||||||
|
package ru.windcorp.optica.client.graphics.flat;
|
||||||
|
|
||||||
|
import ru.windcorp.optica.client.graphics.Layer;
|
||||||
|
import ru.windcorp.optica.client.graphics.model.WorldRenderable;
|
||||||
|
|
||||||
|
public abstract class AssembledFlatLayer extends Layer {
|
||||||
|
|
||||||
|
private final FlatRenderHelper helper = new FlatRenderHelper();
|
||||||
|
|
||||||
|
private final RenderTarget target = new RenderTarget();
|
||||||
|
|
||||||
|
private boolean needsReassembly = true;
|
||||||
|
private Clip[] clips = null;
|
||||||
|
|
||||||
|
public AssembledFlatLayer(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initialize() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void markForReassembly() {
|
||||||
|
needsReassembly = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doReassemble() {
|
||||||
|
assemble(target);
|
||||||
|
clips = target.assemble();
|
||||||
|
needsReassembly = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void assemble(RenderTarget target);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doRender() {
|
||||||
|
if (needsReassembly) {
|
||||||
|
doReassemble();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Clip clip : clips) {
|
||||||
|
clip.render(helper);
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Clip {
|
||||||
|
|
||||||
|
private final Mask mask = new Mask();
|
||||||
|
private final WorldRenderable renderable;
|
||||||
|
|
||||||
|
public Clip(
|
||||||
|
int startX, int startY,
|
||||||
|
int endX, int endY,
|
||||||
|
WorldRenderable renderable
|
||||||
|
) {
|
||||||
|
mask.set(startX, startY, endX, endY);
|
||||||
|
this.renderable = renderable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Clip(
|
||||||
|
Mask mask,
|
||||||
|
WorldRenderable renderable
|
||||||
|
) {
|
||||||
|
this(
|
||||||
|
mask.getStartX(), mask.getStartY(),
|
||||||
|
mask.getEndX(), mask.getEndY(),
|
||||||
|
renderable
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStartX() {
|
||||||
|
return mask.getStartX();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStartY() {
|
||||||
|
return mask.getStartY();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEndX() {
|
||||||
|
return mask.getEndX();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEndY() {
|
||||||
|
return mask.getEndY();
|
||||||
|
}
|
||||||
|
|
||||||
|
public WorldRenderable getRenderable() {
|
||||||
|
return renderable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render(FlatRenderHelper helper) {
|
||||||
|
helper.pushMask(getStartX(), getStartY(), getEndX(), getEndY());
|
||||||
|
renderable.render(helper);
|
||||||
|
helper.popTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package ru.windcorp.optica.client.graphics.flat;
|
||||||
|
|
||||||
|
public class FlatGraphics {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package ru.windcorp.optica.client.graphics.flat;
|
||||||
|
|
||||||
|
import ru.windcorp.optica.client.graphics.backend.GraphicsInterface;
|
||||||
|
import ru.windcorp.optica.client.graphics.model.ShapeRenderHelper;
|
||||||
|
|
||||||
|
public class FlatRenderHelper extends ShapeRenderHelper {
|
||||||
|
|
||||||
|
private final Mask mask = new Mask();
|
||||||
|
|
||||||
|
{
|
||||||
|
setupScreenTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FlatRenderHelper pushMask(
|
||||||
|
int startX, int startY,
|
||||||
|
int endX, int endY
|
||||||
|
) {
|
||||||
|
mask.set(startX, startY, endX, endY);
|
||||||
|
pushTransform().translate(startX, startY, 0);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FlatRenderHelper pushMask(Mask mask) {
|
||||||
|
return pushMask(
|
||||||
|
mask.getStartX(), mask.getStartY(),
|
||||||
|
mask.getEndX(), mask.getEndY()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStartX() {
|
||||||
|
return mask.getStartX();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStartY() {
|
||||||
|
return mask.getStartY();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEndX() {
|
||||||
|
return mask.getEndX();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEndY() {
|
||||||
|
return mask.getEndY();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRenderable() {
|
||||||
|
return !mask.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
super.reset();
|
||||||
|
|
||||||
|
setupScreenTransform();
|
||||||
|
mask.set(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupScreenTransform() {
|
||||||
|
float width = GraphicsInterface.getFramebufferWidth();
|
||||||
|
float height = GraphicsInterface.getFramebufferHeight();
|
||||||
|
|
||||||
|
getTransform().translate(-1, +1, 0).scale(2 / width, -2 / height, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package ru.windcorp.optica.client.graphics.flat;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL11.*;
|
||||||
|
|
||||||
|
import com.google.common.collect.ObjectArrays;
|
||||||
|
|
||||||
|
import ru.windcorp.optica.client.graphics.backend.shaders.uniforms.Uniform2Int;
|
||||||
|
import ru.windcorp.optica.client.graphics.model.Face;
|
||||||
|
import ru.windcorp.optica.client.graphics.model.Shape;
|
||||||
|
import ru.windcorp.optica.client.graphics.model.ShapeRenderHelper;
|
||||||
|
import ru.windcorp.optica.client.graphics.model.ShapeRenderProgram;
|
||||||
|
|
||||||
|
public class FlatRenderProgram extends ShapeRenderProgram {
|
||||||
|
|
||||||
|
private static FlatRenderProgram def = null;
|
||||||
|
|
||||||
|
public static void init() {
|
||||||
|
def = new FlatRenderProgram(
|
||||||
|
new String[] {"FlatDefault.vertex.glsl"},
|
||||||
|
new String[] {"FlatDefault.fragment.glsl"}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FlatRenderProgram getDefault() {
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String FLAT_VERTEX_SHADER_RESOURCE =
|
||||||
|
"Flat.vertex.glsl";
|
||||||
|
private static final String FLAT_FRAGMENT_SHADER_RESOURCE =
|
||||||
|
"Flat.fragment.glsl";
|
||||||
|
|
||||||
|
private static final String
|
||||||
|
MASK_START_UNIFORM_NAME = "maskStart",
|
||||||
|
MASK_END_UNIFORM_NAME = "maskEnd";
|
||||||
|
|
||||||
|
private final Uniform2Int maskStartUniform;
|
||||||
|
private final Uniform2Int maskEndUniform;
|
||||||
|
|
||||||
|
public FlatRenderProgram(
|
||||||
|
String[] vertexShaderResources,
|
||||||
|
String[] fragmentShaderResources
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
attachVertexShader(vertexShaderResources),
|
||||||
|
attachFragmentShader(fragmentShaderResources)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.maskStartUniform = getUniform(MASK_START_UNIFORM_NAME).as2Int();
|
||||||
|
this.maskEndUniform = getUniform(MASK_END_UNIFORM_NAME).as2Int();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] attachVertexShader(String[] others) {
|
||||||
|
return ObjectArrays.concat(FLAT_VERTEX_SHADER_RESOURCE, others);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] attachFragmentShader(String[] others) {
|
||||||
|
return ObjectArrays.concat(FLAT_FRAGMENT_SHADER_RESOURCE, others);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(ShapeRenderHelper helper, Shape shape) {
|
||||||
|
if (((FlatRenderHelper) helper).isRenderable()) {
|
||||||
|
super.render(helper, shape);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderFace(Face face) {
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
super.renderFace(face);
|
||||||
|
glEnable(GL_CULL_FACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(ShapeRenderHelper argHelper) {
|
||||||
|
super.configure(argHelper);
|
||||||
|
FlatRenderHelper helper = ((FlatRenderHelper) argHelper);
|
||||||
|
|
||||||
|
maskStartUniform.set(helper.getStartX(), helper.getStartY());
|
||||||
|
maskEndUniform.set(helper.getEndX(), helper.getEndY());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
package ru.windcorp.optica.client.graphics.flat;
|
||||||
|
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
|
||||||
|
import ru.windcorp.optica.client.graphics.Colors;
|
||||||
|
import ru.windcorp.optica.client.graphics.backend.GraphicsInterface;
|
||||||
|
import ru.windcorp.optica.client.graphics.input.KeyEvent;
|
||||||
|
import ru.windcorp.optica.client.graphics.texture.SimpleTexture;
|
||||||
|
import ru.windcorp.optica.client.graphics.texture.Sprite;
|
||||||
|
import ru.windcorp.optica.client.graphics.texture.Texture;
|
||||||
|
import ru.windcorp.optica.client.graphics.texture.TextureManager;
|
||||||
|
import ru.windcorp.optica.client.graphics.texture.TextureSettings;
|
||||||
|
|
||||||
|
public class LayerTestUI extends AssembledFlatLayer {
|
||||||
|
|
||||||
|
public LayerTestUI() {
|
||||||
|
super("TestUI");
|
||||||
|
|
||||||
|
GraphicsInterface.subscribeToInputEvents(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean flag = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assemble(RenderTarget target) {
|
||||||
|
final float width = 512 + 256;
|
||||||
|
final float height = 64;
|
||||||
|
final float border = 5;
|
||||||
|
|
||||||
|
final int boxColor = flag ? 0xEE8888 : 0xEEEE88;
|
||||||
|
final int borderColor = flag ? 0xAA4444 : 0xAAAA44;
|
||||||
|
final int boxShadowColor = flag ? 0x440000 : 0x444400;
|
||||||
|
|
||||||
|
float x = (getWidth() - width) / 2;
|
||||||
|
float y = getHeight() - height;
|
||||||
|
|
||||||
|
y -= 2*border;
|
||||||
|
|
||||||
|
target.fill(x + border, y + border, width, height, boxShadowColor);
|
||||||
|
target.fill(x - 1, y - 1, width + 2, height + 2, boxShadowColor);
|
||||||
|
target.fill(x, y, width, height, borderColor);
|
||||||
|
target.fill(x + border, y + border, width - 2*border, height - 2*border, boxColor);
|
||||||
|
|
||||||
|
final float texShadow = 2;
|
||||||
|
final float texSize = height - 4*border;
|
||||||
|
|
||||||
|
target.fill(x + 2*border + texShadow, y + 2*border + texShadow, texSize, texSize, Colors.BLACK);
|
||||||
|
target.drawTexture(x + 2*border, y + 2*border, texSize, texSize, qtex("compass"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onKeyEvent(KeyEvent event) {
|
||||||
|
if (event.isRepeat() || event.getKey() != GLFW.GLFW_KEY_LEFT_CONTROL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
flag = event.isPress();
|
||||||
|
markForReassembly();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TMP texture loader
|
||||||
|
*/
|
||||||
|
|
||||||
|
private static final TextureSettings TEXTURE_SETTINGS =
|
||||||
|
new TextureSettings(false);
|
||||||
|
|
||||||
|
private static Texture qtex(String name) {
|
||||||
|
return new SimpleTexture(new Sprite(
|
||||||
|
TextureManager.load(name, TEXTURE_SETTINGS)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
package ru.windcorp.optica.client.graphics.flat;
|
||||||
|
|
||||||
|
public class Mask {
|
||||||
|
|
||||||
|
private int startX;
|
||||||
|
private int startY;
|
||||||
|
private int endX;
|
||||||
|
private int endY;
|
||||||
|
|
||||||
|
public Mask(int startX, int startY, int endX, int endY) {
|
||||||
|
this.startX = startX;
|
||||||
|
this.startY = startY;
|
||||||
|
this.endX = endX;
|
||||||
|
this.endY = endY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mask() {}
|
||||||
|
|
||||||
|
public int getStartX() {
|
||||||
|
return startX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStartX(int startX) {
|
||||||
|
this.startX = startX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStartY() {
|
||||||
|
return startY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStartY(int startY) {
|
||||||
|
this.startY = startY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEndX() {
|
||||||
|
return endX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEndX(int endX) {
|
||||||
|
this.endX = endX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEndY() {
|
||||||
|
return endY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEndY(int endY) {
|
||||||
|
this.endY = endY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(int startX, int startY, int endX, int endY) {
|
||||||
|
this.startX = startX;
|
||||||
|
this.startY = startY;
|
||||||
|
this.endX = endX;
|
||||||
|
this.endY = endY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return startX >= endX || startY >= endY;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,166 @@
|
|||||||
|
package ru.windcorp.optica.client.graphics.flat;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import glm.vec._3.Vec3;
|
||||||
|
import ru.windcorp.optica.client.graphics.Colors;
|
||||||
|
import ru.windcorp.optica.client.graphics.backend.Usage;
|
||||||
|
import ru.windcorp.optica.client.graphics.model.Face;
|
||||||
|
import ru.windcorp.optica.client.graphics.model.Faces;
|
||||||
|
import ru.windcorp.optica.client.graphics.model.Shape;
|
||||||
|
import ru.windcorp.optica.client.graphics.model.WorldRenderable;
|
||||||
|
import ru.windcorp.optica.client.graphics.texture.Texture;
|
||||||
|
|
||||||
|
public class RenderTarget {
|
||||||
|
|
||||||
|
private static final int MAX_DEPTH = 1 << 16;
|
||||||
|
|
||||||
|
private final List<AssembledFlatLayer.Clip> assembled = new ArrayList<>();
|
||||||
|
|
||||||
|
private final Deque<Mask> maskStack = new LinkedList<>();
|
||||||
|
|
||||||
|
private final List<Face> currentClipFaces = new ArrayList<>();
|
||||||
|
|
||||||
|
private int depth = 0;
|
||||||
|
|
||||||
|
public RenderTarget() {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pushMaskStartEnd(int startX, int startY, int endX, int endY) {
|
||||||
|
assembleCurrentClipFromFaces();
|
||||||
|
maskStack.push(intersect(getMask(), startX, startY, endX, endY));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Mask intersect(
|
||||||
|
Mask current,
|
||||||
|
int startX, int startY, int endX, int endY
|
||||||
|
) {
|
||||||
|
return new Mask(
|
||||||
|
Math.max(startX, current.getStartX()),
|
||||||
|
Math.max(startY, current.getStartY()),
|
||||||
|
Math.min(endX, current.getEndX()),
|
||||||
|
Math.min(endY, current.getEndY())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pushMask(Mask mask) {
|
||||||
|
pushMaskStartEnd(
|
||||||
|
mask.getStartX(), mask.getStartY(),
|
||||||
|
mask.getEndX(), mask.getEndY()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pushMaskStartSize(int x, int y, int width, int height) {
|
||||||
|
pushMaskStartEnd(x, y, x + width, y + height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void popMask() {
|
||||||
|
assembleCurrentClipFromFaces();
|
||||||
|
maskStack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mask getMask() {
|
||||||
|
return maskStack.getFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assembleCurrentClipFromFaces() {
|
||||||
|
if (!currentClipFaces.isEmpty()) {
|
||||||
|
|
||||||
|
Mask mask = getMask();
|
||||||
|
if (mask.isEmpty()) {
|
||||||
|
currentClipFaces.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Face[] faces = currentClipFaces.toArray(
|
||||||
|
new Face[currentClipFaces.size()]
|
||||||
|
);
|
||||||
|
currentClipFaces.clear();
|
||||||
|
|
||||||
|
Shape shape = new Shape(
|
||||||
|
Usage.STATIC, FlatRenderProgram.getDefault(), faces
|
||||||
|
);
|
||||||
|
|
||||||
|
assembled.add(new AssembledFlatLayer.Clip(mask, shape));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AssembledFlatLayer.Clip[] assemble() {
|
||||||
|
assembleCurrentClipFromFaces();
|
||||||
|
|
||||||
|
AssembledFlatLayer.Clip[] result = assembled.toArray(
|
||||||
|
new AssembledFlatLayer.Clip[assembled.size()]
|
||||||
|
);
|
||||||
|
|
||||||
|
reset();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reset() {
|
||||||
|
maskStack.clear();
|
||||||
|
currentClipFaces.clear();
|
||||||
|
assembled.clear();
|
||||||
|
|
||||||
|
maskStack.add(new Mask(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE));
|
||||||
|
depth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCustomRenderer(WorldRenderable renderable) {
|
||||||
|
assembleCurrentClipFromFaces();
|
||||||
|
|
||||||
|
Mask mask = getMask();
|
||||||
|
if (mask.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assembled.add(new AssembledFlatLayer.Clip(mask, renderable));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addFaceToCurrentClip(Face face) {
|
||||||
|
currentClipFaces.add(face);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawTexture(
|
||||||
|
float x, float y, float width, float height,
|
||||||
|
int color, Texture texture
|
||||||
|
) {
|
||||||
|
float depth = this.depth-- / (float) MAX_DEPTH;
|
||||||
|
|
||||||
|
addFaceToCurrentClip(Faces.createRectangle(
|
||||||
|
FlatRenderProgram.getDefault(),
|
||||||
|
texture,
|
||||||
|
createVectorFromRGBInt(color),
|
||||||
|
new Vec3(x, y + height, depth), // Flip
|
||||||
|
new Vec3(width, 0, depth),
|
||||||
|
new Vec3(0, -height, depth)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawTexture(
|
||||||
|
float x, float y, float width, float height,
|
||||||
|
Texture texture
|
||||||
|
) {
|
||||||
|
drawTexture(x, y, width, height, Colors.WHITE, texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fill(
|
||||||
|
float x, float y, float width, float height,
|
||||||
|
int color
|
||||||
|
) {
|
||||||
|
drawTexture(x, y, width, height, color, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vec3 createVectorFromRGBInt(int rgb) {
|
||||||
|
int r = (rgb & 0xFF0000) >> 16;
|
||||||
|
int g = (rgb & 0x00FF00) >> 8;
|
||||||
|
int b = (rgb & 0x0000FF);
|
||||||
|
|
||||||
|
return new Vec3(r / 256f, g / 256f, b / 256f);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -237,7 +237,7 @@ public class Face {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setTexture(Texture texture) {
|
public void setTexture(Texture texture) {
|
||||||
this.texture = Objects.requireNonNull(texture, "texture");
|
this.texture = texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -36,10 +36,10 @@ public abstract class Model implements WorldRenderable {
|
|||||||
Mat4 transform = getTransform(i);
|
Mat4 transform = getTransform(i);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
helper.pushWorldTransform().mul(transform);
|
helper.pushTransform().mul(transform);
|
||||||
part.render(helper);
|
part.render(helper);
|
||||||
} finally {
|
} finally {
|
||||||
helper.popWorldTransform();
|
helper.popTransform();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,21 +32,21 @@ public class ShapeRenderHelper {
|
|||||||
transformStack.push().identity();
|
transformStack.push().identity();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Mat4 pushWorldTransform() {
|
public Mat4 pushTransform() {
|
||||||
Mat4 previous = transformStack.getHead();
|
Mat4 previous = transformStack.getHead();
|
||||||
return transformStack.push().set(previous);
|
return transformStack.push().set(previous);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void popWorldTransform() {
|
public void popTransform() {
|
||||||
transformStack.removeHead();
|
transformStack.removeHead();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Mat4 getWorldTransform() {
|
public Mat4 getTransform() {
|
||||||
return transformStack.getHead();
|
return transformStack.getHead();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Mat4 getFinalTransform() {
|
public Mat4 getFinalTransform() {
|
||||||
return getWorldTransform();
|
return getTransform();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
|
@ -35,6 +35,7 @@ import ru.windcorp.optica.client.graphics.backend.shaders.Program;
|
|||||||
import ru.windcorp.optica.client.graphics.backend.shaders.attributes.*;
|
import ru.windcorp.optica.client.graphics.backend.shaders.attributes.*;
|
||||||
import ru.windcorp.optica.client.graphics.backend.shaders.uniforms.*;
|
import ru.windcorp.optica.client.graphics.backend.shaders.uniforms.*;
|
||||||
import ru.windcorp.optica.client.graphics.texture.Sprite;
|
import ru.windcorp.optica.client.graphics.texture.Sprite;
|
||||||
|
import ru.windcorp.optica.client.graphics.texture.Texture;
|
||||||
|
|
||||||
public class ShapeRenderProgram extends Program {
|
public class ShapeRenderProgram extends Program {
|
||||||
|
|
||||||
@ -53,6 +54,7 @@ public class ShapeRenderProgram extends Program {
|
|||||||
POSITIONS_ATTRIBUTE_NAME = "inputPositions",
|
POSITIONS_ATTRIBUTE_NAME = "inputPositions",
|
||||||
COLOR_MULTIPLER_ATTRIBUTE_NAME = "inputColorMultiplier",
|
COLOR_MULTIPLER_ATTRIBUTE_NAME = "inputColorMultiplier",
|
||||||
TEXTURE_COORDS_ATTRIBUTE_NAME = "inputTextureCoords",
|
TEXTURE_COORDS_ATTRIBUTE_NAME = "inputTextureCoords",
|
||||||
|
USE_TEXTURE_UNIFORM_NAME = "useTexture",
|
||||||
TEXTURE_SLOT_UNIFORM_NAME = "textureSlot",
|
TEXTURE_SLOT_UNIFORM_NAME = "textureSlot",
|
||||||
TEXTURE_START_UNIFORM_NAME = "textureStart",
|
TEXTURE_START_UNIFORM_NAME = "textureStart",
|
||||||
TEXTURE_SIZE_UNIFORM_NAME = "textureSize";
|
TEXTURE_SIZE_UNIFORM_NAME = "textureSize";
|
||||||
@ -61,6 +63,7 @@ public class ShapeRenderProgram extends Program {
|
|||||||
private final AttributeVertexArray positionsAttribute;
|
private final AttributeVertexArray positionsAttribute;
|
||||||
private final AttributeVertexArray colorsAttribute;
|
private final AttributeVertexArray colorsAttribute;
|
||||||
private final AttributeVertexArray textureCoordsAttribute;
|
private final AttributeVertexArray textureCoordsAttribute;
|
||||||
|
private final Uniform1Int useTextureUniform;
|
||||||
private final Uniform1Int textureSlotUniform;
|
private final Uniform1Int textureSlotUniform;
|
||||||
private final Uniform2Float textureStartUniform;
|
private final Uniform2Float textureStartUniform;
|
||||||
private final Uniform2Float textureSizeUniform;
|
private final Uniform2Float textureSizeUniform;
|
||||||
@ -90,6 +93,9 @@ public class ShapeRenderProgram extends Program {
|
|||||||
this.textureCoordsAttribute =
|
this.textureCoordsAttribute =
|
||||||
getAttribute(TEXTURE_COORDS_ATTRIBUTE_NAME).asVertexArray();
|
getAttribute(TEXTURE_COORDS_ATTRIBUTE_NAME).asVertexArray();
|
||||||
|
|
||||||
|
this.useTextureUniform = getUniform(USE_TEXTURE_UNIFORM_NAME)
|
||||||
|
.as1Int();
|
||||||
|
|
||||||
this.textureSlotUniform = getUniform(TEXTURE_SLOT_UNIFORM_NAME)
|
this.textureSlotUniform = getUniform(TEXTURE_SLOT_UNIFORM_NAME)
|
||||||
.as1Int();
|
.as1Int();
|
||||||
|
|
||||||
@ -174,13 +180,20 @@ public class ShapeRenderProgram extends Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void renderFace(Face face) {
|
protected void renderFace(Face face) {
|
||||||
Sprite sprite = face.getTexture().getSprite();
|
Texture texture = face.getTexture();
|
||||||
|
|
||||||
sprite.getPrimitive().bind(0);
|
if (texture != null) {
|
||||||
textureSlotUniform.set(0);
|
Sprite sprite = texture.getSprite();
|
||||||
|
|
||||||
textureStartUniform.set(sprite.getStart());
|
sprite.getPrimitive().bind(0);
|
||||||
textureSizeUniform.set(sprite.getSize());
|
textureSlotUniform.set(0);
|
||||||
|
useTextureUniform.set(1);
|
||||||
|
|
||||||
|
textureStartUniform.set(sprite.getStart());
|
||||||
|
textureSizeUniform.set(sprite.getSize());
|
||||||
|
} else {
|
||||||
|
useTextureUniform.set(0);
|
||||||
|
}
|
||||||
|
|
||||||
GL11.glDrawElements(
|
GL11.glDrawElements(
|
||||||
GL11.GL_TRIANGLES,
|
GL11.GL_TRIANGLES,
|
||||||
|
@ -24,7 +24,6 @@ import glm.vec._2.Vec2;
|
|||||||
public class Sprite {
|
public class Sprite {
|
||||||
|
|
||||||
private static final Vec2 ORIGIN = new Vec2(0, 0);
|
private static final Vec2 ORIGIN = new Vec2(0, 0);
|
||||||
private static final Vec2 FULL_PRIMITIVE = new Vec2(1, 1);
|
|
||||||
|
|
||||||
private final TexturePrimitive primitive;
|
private final TexturePrimitive primitive;
|
||||||
|
|
||||||
@ -38,7 +37,10 @@ public class Sprite {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Sprite(TexturePrimitive primitive) {
|
public Sprite(TexturePrimitive primitive) {
|
||||||
this(primitive, ORIGIN, FULL_PRIMITIVE);
|
this(primitive, ORIGIN, new Vec2(
|
||||||
|
primitive.getWidth() / (float) primitive.getBufferWidth(),
|
||||||
|
primitive.getHeight() / (float) primitive.getBufferHeight()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public TexturePrimitive getPrimitive() {
|
public TexturePrimitive getPrimitive() {
|
||||||
|
@ -48,7 +48,7 @@ public class WorldRenderHelper extends ShapeRenderHelper {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mat4 getFinalTransform() {
|
public Mat4 getFinalTransform() {
|
||||||
return finalTransform.set(getViewTransform()).mul(getWorldTransform());
|
return finalTransform.set(getViewTransform()).mul(getTransform());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -96,7 +96,7 @@ public class WorldRenderProgram extends ShapeRenderProgram {
|
|||||||
@Override
|
@Override
|
||||||
protected void configure(ShapeRenderHelper helper) {
|
protected void configure(ShapeRenderHelper helper) {
|
||||||
super.configure(helper);
|
super.configure(helper);
|
||||||
worldTransformUniform.set(helper.getWorldTransform());
|
worldTransformUniform.set(helper.getTransform());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -75,7 +75,7 @@ public class ChunkRender {
|
|||||||
buildModel();
|
buildModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer.pushWorldTransform().translate(
|
renderer.pushTransform().translate(
|
||||||
data.getX() * ChunkData.BLOCKS_PER_CHUNK,
|
data.getX() * ChunkData.BLOCKS_PER_CHUNK,
|
||||||
data.getY() * ChunkData.BLOCKS_PER_CHUNK,
|
data.getY() * ChunkData.BLOCKS_PER_CHUNK,
|
||||||
data.getZ() * ChunkData.BLOCKS_PER_CHUNK
|
data.getZ() * ChunkData.BLOCKS_PER_CHUNK
|
||||||
@ -83,7 +83,7 @@ public class ChunkRender {
|
|||||||
|
|
||||||
model.render(renderer);
|
model.render(renderer);
|
||||||
|
|
||||||
renderer.popWorldTransform();
|
renderer.popTransform();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildModel() {
|
private void buildModel() {
|
||||||
|
@ -56,7 +56,7 @@ public class WorldRender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void render(ShapeRenderHelper renderer) {
|
public void render(ShapeRenderHelper renderer) {
|
||||||
renderer.pushWorldTransform().rotateX(-Math.PI / 2);
|
renderer.pushTransform().rotateX(-Math.PI / 2);
|
||||||
|
|
||||||
for (ChunkRender chunk : getChunks()) {
|
for (ChunkRender chunk : getChunks()) {
|
||||||
chunk.render(renderer);
|
chunk.render(renderer);
|
||||||
|
13
src/main/resources/assets/shaders/Flat.fragment.glsl
Normal file
13
src/main/resources/assets/shaders/Flat.fragment.glsl
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#version 120
|
||||||
|
|
||||||
|
uniform ivec2 maskStart;
|
||||||
|
uniform ivec2 maskEnd;
|
||||||
|
|
||||||
|
void applyMask() {
|
||||||
|
if (
|
||||||
|
gl_FragCoord.x < maskStart.x || gl_FragCoord.x >= maskEnd.x ||
|
||||||
|
gl_FragCoord.y < maskStart.y || gl_FragCoord.y >= maskEnd.y
|
||||||
|
) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
5
src/main/resources/assets/shaders/Flat.vertex.glsl
Normal file
5
src/main/resources/assets/shaders/Flat.vertex.glsl
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#version 120
|
||||||
|
|
||||||
|
void flatTransferToFragment() {
|
||||||
|
shapeTransferToFragment();
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
#version 120
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
applyTexture();
|
||||||
|
applyColorMultiplier();
|
||||||
|
applyAlpha();
|
||||||
|
applyMask();
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
#version 120
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
gl_Position = applyFinalTransform(vec4(inputPositions, 1.0));
|
||||||
|
flatTransferToFragment();
|
||||||
|
}
|
@ -7,14 +7,20 @@ uniform sampler2D textureSlot;
|
|||||||
uniform vec2 textureStart;
|
uniform vec2 textureStart;
|
||||||
uniform vec2 textureSize;
|
uniform vec2 textureSize;
|
||||||
|
|
||||||
|
uniform bool useTexture;
|
||||||
|
|
||||||
void applyTexture() {
|
void applyTexture() {
|
||||||
gl_FragColor = texture2D(
|
if (!useTexture) {
|
||||||
textureSlot,
|
gl_FragColor = vec4(1, 1, 1, 1);
|
||||||
vec2(
|
} else {
|
||||||
varyingTextureCoords[0] * textureSize[0] + textureStart[0],
|
gl_FragColor = texture2D(
|
||||||
varyingTextureCoords[1] * textureSize[1] + textureStart[1]
|
textureSlot,
|
||||||
)
|
vec2(
|
||||||
);
|
varyingTextureCoords[0] * textureSize[0] + textureStart[0],
|
||||||
|
varyingTextureCoords[1] * textureSize[1] + textureStart[1]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void multiply(inout vec4 vector, float scalar) {
|
void multiply(inout vec4 vector, float scalar) {
|
||||||
|
BIN
src/main/resources/assets/textures/windcorp.png
Normal file
BIN
src/main/resources/assets/textures/windcorp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
Reference in New Issue
Block a user