Re-written flat render pipeline to support transforms
FlatRender now supports arbitrary transforms GLSL 1.40 is now used in shaders
This commit is contained in:
parent
da61f834e5
commit
7dee1eb95f
@ -33,6 +33,6 @@ public class Colors {
|
||||
|
||||
DEBUG_CYAN = 0x00FFFF,
|
||||
DEBUG_MAGENTA = 0xFF00FF,
|
||||
DEBUG_YELLOW = 0x00FFFF;
|
||||
DEBUG_YELLOW = 0xFFFF00;
|
||||
|
||||
}
|
||||
|
@ -26,18 +26,19 @@ public class InputHandler {
|
||||
|
||||
private static final EventBus INPUT_EVENT_BUS = new EventBus("Input");
|
||||
|
||||
// ScrollEvent Start
|
||||
// ScrollEvent
|
||||
|
||||
private static class ModifiableWheelScrollEvent extends WheelScrollEvent {
|
||||
|
||||
public void initialize(double xoffset, double yoffset) {
|
||||
this.xoffset = xoffset;
|
||||
this.yoffset = yoffset;
|
||||
this.xOffset = xoffset;
|
||||
this.yOffset = yoffset;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final ModifiableWheelScrollEvent THE_SCROLL_EVENT = new ModifiableWheelScrollEvent();
|
||||
private static final ModifiableWheelScrollEvent THE_SCROLL_EVENT =
|
||||
new ModifiableWheelScrollEvent();
|
||||
|
||||
static void handleMouseWheel(
|
||||
long window,
|
||||
@ -49,7 +50,7 @@ public class InputHandler {
|
||||
dispatch(THE_SCROLL_EVENT);
|
||||
}
|
||||
|
||||
// KeyEvent Start
|
||||
// KeyEvent
|
||||
|
||||
private static class ModifiableKeyEvent extends KeyEvent {
|
||||
|
||||
@ -62,7 +63,8 @@ public class InputHandler {
|
||||
|
||||
}
|
||||
|
||||
private static final ModifiableKeyEvent THE_KEY_EVENT = new ModifiableKeyEvent();
|
||||
private static final ModifiableKeyEvent THE_KEY_EVENT =
|
||||
new ModifiableKeyEvent();
|
||||
|
||||
static void handleKeyInput(
|
||||
long window,
|
||||
@ -76,7 +78,7 @@ public class InputHandler {
|
||||
dispatch(THE_KEY_EVENT);
|
||||
}
|
||||
|
||||
// CursorEvent Start
|
||||
// CursorMoveEvent
|
||||
|
||||
private static class ModifiableCursorMoveEvent extends CursorMoveEvent {
|
||||
|
||||
@ -111,6 +113,8 @@ public class InputHandler {
|
||||
CURSOR_POSITION.set(x, y);
|
||||
}
|
||||
|
||||
// Misc
|
||||
|
||||
public static double getCursorX() {
|
||||
return CURSOR_POSITION.x;
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ public class CombinedShader extends Shader {
|
||||
}
|
||||
|
||||
private static String combine(String[] resources) {
|
||||
StringBuilder accumulator = new StringBuilder("#version 120\n");
|
||||
StringBuilder accumulator = new StringBuilder("#version 140\n");
|
||||
|
||||
for (String resourceName : resources) {
|
||||
Resource resource = getShaderResource(resourceName);
|
||||
|
@ -18,16 +18,16 @@
|
||||
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 AssembledFlatRenderHelper helper =
|
||||
new AssembledFlatRenderHelper();
|
||||
|
||||
private final RenderTarget target = new RenderTarget();
|
||||
|
||||
private boolean needsReassembly = true;
|
||||
private Clip[] clips = null;
|
||||
private RenderTarget.Clip[] clips = null;
|
||||
|
||||
public AssembledFlatLayer(String name) {
|
||||
super(name);
|
||||
@ -57,64 +57,11 @@ public abstract class AssembledFlatLayer extends Layer {
|
||||
doReassemble();
|
||||
}
|
||||
|
||||
for (Clip clip : clips) {
|
||||
for (RenderTarget.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,35 @@
|
||||
/*******************************************************************************
|
||||
* Optica
|
||||
* Copyright (C) 2020 Wind Corporation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.optica.client.graphics.flat;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
public class AssembledFlatRenderHelper extends FlatRenderHelper {
|
||||
|
||||
private FloatBuffer masks;
|
||||
|
||||
@Override
|
||||
protected FloatBuffer getMasks() {
|
||||
return masks;
|
||||
}
|
||||
|
||||
public void setMasks(FloatBuffer masks) {
|
||||
this.masks = masks;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*******************************************************************************
|
||||
* Optica
|
||||
* Copyright (C) 2020 Wind Corporation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.optica.client.graphics.flat;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import glm.mat._4.Mat4;
|
||||
|
||||
public class DefaultFlatRenderHelper extends FlatRenderHelper {
|
||||
|
||||
private final TransformedMask transformer = new TransformedMask();
|
||||
|
||||
protected final MaskStack maskStack = new MaskStack();
|
||||
|
||||
protected final boolean[] hasMask = new boolean[TRANSFORM_STACK_SIZE];
|
||||
|
||||
public void pushMask(Mask mask, Mat4 transform) {
|
||||
pushMask(transformer.set(mask, transform), transform);
|
||||
}
|
||||
|
||||
public void pushMask(TransformedMask mask, Mat4 transform) {
|
||||
hasMask[transformStack.getSize()] = true;
|
||||
pushTransform().mul(transform);
|
||||
maskStack.pushMask(mask);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mat4 pushTransform() {
|
||||
hasMask[transformStack.getSize()] = false;
|
||||
return super.pushTransform();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void popTransform() {
|
||||
super.popTransform();
|
||||
|
||||
if (hasMask[transformStack.getSize()]) {
|
||||
maskStack.popMask();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
maskStack.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FloatBuffer getMasks() {
|
||||
return maskStack.getBuffer();
|
||||
}
|
||||
|
||||
}
|
@ -17,69 +17,28 @@
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.optica.client.graphics.flat;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import glm.mat._4.Mat4;
|
||||
import ru.windcorp.optica.client.graphics.backend.GraphicsInterface;
|
||||
import ru.windcorp.optica.client.graphics.model.ShapeRenderHelper;
|
||||
|
||||
public class FlatRenderHelper extends ShapeRenderHelper {
|
||||
public abstract class FlatRenderHelper extends ShapeRenderHelper {
|
||||
|
||||
private static final float MAX_DEPTH = 1 << 16;
|
||||
protected static final float MAX_DEPTH = 1 << 16;
|
||||
|
||||
private final Mask mask = new Mask();
|
||||
protected final Mat4 finalTransform = new Mat4();
|
||||
|
||||
{
|
||||
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();
|
||||
}
|
||||
protected abstract FloatBuffer getMasks();
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
|
||||
setupScreenTransform();
|
||||
mask.set(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
private void setupScreenTransform() {
|
||||
public Mat4 getFinalTransform() {
|
||||
float width = GraphicsInterface.getFramebufferWidth();
|
||||
float height = GraphicsInterface.getFramebufferHeight();
|
||||
|
||||
getTransform().translate(-1, +1, 0)
|
||||
.scale(2 / width, -2 / height, 1 / MAX_DEPTH);
|
||||
return finalTransform.identity().translate(-1, +1, 0)
|
||||
.scale(2 / width, -2 / height, 1 / MAX_DEPTH)
|
||||
.mul(getTransform());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,10 +19,12 @@ package ru.windcorp.optica.client.graphics.flat;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
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.backend.shaders.uniforms.Uniform1Int;
|
||||
import ru.windcorp.optica.client.graphics.backend.shaders.uniforms.Uniform2Float;
|
||||
import ru.windcorp.optica.client.graphics.model.Shape;
|
||||
import ru.windcorp.optica.client.graphics.model.ShapeRenderHelper;
|
||||
import ru.windcorp.optica.client.graphics.model.ShapeRenderProgram;
|
||||
@ -42,17 +44,19 @@ public class FlatRenderProgram extends ShapeRenderProgram {
|
||||
return def;
|
||||
}
|
||||
|
||||
public static final int MASK_STACK_SIZE = 16; // As in Flat.fragment.glsl
|
||||
|
||||
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";
|
||||
MASK_COUNT_UNIFORM_NAME = "maskCount",
|
||||
MASKS_UNIFORM_NAME = "masks";
|
||||
|
||||
private final Uniform2Int maskStartUniform;
|
||||
private final Uniform2Int maskEndUniform;
|
||||
private final Uniform1Int maskCountUniform;
|
||||
private final Uniform2Float masksUniform;
|
||||
|
||||
public FlatRenderProgram(
|
||||
String[] vertexShaderResources,
|
||||
@ -63,8 +67,8 @@ public class FlatRenderProgram extends ShapeRenderProgram {
|
||||
attachFragmentShader(fragmentShaderResources)
|
||||
);
|
||||
|
||||
this.maskStartUniform = getUniform(MASK_START_UNIFORM_NAME).as2Int();
|
||||
this.maskEndUniform = getUniform(MASK_END_UNIFORM_NAME).as2Int();
|
||||
this.maskCountUniform = getUniform(MASK_COUNT_UNIFORM_NAME).as1Int();
|
||||
this.masksUniform = getUniform(MASKS_UNIFORM_NAME).as2Float();
|
||||
}
|
||||
|
||||
private static String[] attachVertexShader(String[] others) {
|
||||
@ -77,15 +81,8 @@ public class FlatRenderProgram extends ShapeRenderProgram {
|
||||
|
||||
@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);
|
||||
super.render(helper, shape);
|
||||
glEnable(GL_CULL_FACE);
|
||||
}
|
||||
|
||||
@ -94,8 +91,29 @@ public class FlatRenderProgram extends ShapeRenderProgram {
|
||||
super.configure(argHelper);
|
||||
FlatRenderHelper helper = ((FlatRenderHelper) argHelper);
|
||||
|
||||
maskStartUniform.set(helper.getStartX(), helper.getStartY());
|
||||
maskEndUniform.set(helper.getEndX(), helper.getEndY());
|
||||
configureMasks(helper.getMasks());
|
||||
}
|
||||
|
||||
private void configureMasks(FloatBuffer masks) {
|
||||
int pos = masks.position();
|
||||
int limit = masks.limit();
|
||||
int size = pos / TransformedMask.SIZE_IN_FLOATS;
|
||||
|
||||
maskCountUniform.set(size);
|
||||
|
||||
masks.flip();
|
||||
masksUniform.set(masks);
|
||||
|
||||
// for (int i = 0; i < pos; ++i) {
|
||||
// if (i % TransformedMask.SIZE_IN_FLOATS == 0) {
|
||||
// System.out.print(" | ");
|
||||
// }
|
||||
// System.out.print(masks.get(i) + "; ");
|
||||
// }
|
||||
// System.out.println();
|
||||
|
||||
masks.limit(limit);
|
||||
masks.position(pos);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,14 +21,17 @@ import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
|
||||
import glm.mat._4.Mat4;
|
||||
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.model.LambdaModel;
|
||||
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;
|
||||
import ru.windcorp.optica.client.graphics.world.LayerWorld;
|
||||
|
||||
public class LayerTestUI extends AssembledFlatLayer {
|
||||
|
||||
@ -40,33 +43,42 @@ public class LayerTestUI extends AssembledFlatLayer {
|
||||
|
||||
private boolean flag = false;
|
||||
|
||||
private static int width = 512 + 256;
|
||||
private static final int height = 64;
|
||||
private static final int border = 5;
|
||||
private static final int WIDTH = 512 + 256;
|
||||
private static final int HEIGHT = 80;
|
||||
private static final int BORDER = 5;
|
||||
|
||||
@Override
|
||||
protected void assemble(RenderTarget target) {
|
||||
|
||||
|
||||
final int boxColor = flag ? 0xEE8888 : 0xEEEE88;
|
||||
final int borderColor = flag ? 0xAA4444 : 0xAAAA44;
|
||||
final int boxShadowColor = flag ? 0x440000 : 0x444400;
|
||||
|
||||
int x = (getWidth() - width) / 2;
|
||||
int y = getHeight() - height;
|
||||
int x = (getWidth() - WIDTH) / 2;
|
||||
int y = getHeight() - HEIGHT - 2*BORDER;
|
||||
|
||||
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);
|
||||
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 int texShadow = 2;
|
||||
final int texSize = height - 4*border;
|
||||
final int 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"));
|
||||
target.pushTransform(new Mat4().identity().translate(x + 2*BORDER, y + 2*BORDER, 0));
|
||||
|
||||
final Texture compassBg = qtex("compass_icon");
|
||||
final Texture compassFg = qtex("compass_icon_arrow");
|
||||
|
||||
target.drawTexture(texShadow, texShadow, texSize, texSize, Colors.BLACK, compassBg);
|
||||
target.drawTexture(0, 0, texSize, texSize, compassBg);
|
||||
|
||||
target.addCustomRenderer(new LambdaModel(LambdaModel.lambdaBuilder()
|
||||
.addDynamicPart(
|
||||
target.createRectagle(0, 0, texSize, texSize, 0xFFFFFF, compassFg),
|
||||
mat -> mat.translate(texSize/2, texSize/2, 0).rotateZ(-LayerWorld.tmp_the_camera.getYaw()).translate(-texSize/2, -texSize/2, 0)
|
||||
)
|
||||
));
|
||||
target.popTransform();
|
||||
|
||||
drawCross(target);
|
||||
}
|
||||
|
@ -65,6 +65,14 @@ public class Mask {
|
||||
this.endY = endY;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return getEndX() - getStartX();
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return getEndY() - getStartY();
|
||||
}
|
||||
|
||||
public void set(int startX, int startY, int endX, int endY) {
|
||||
this.startX = startX;
|
||||
this.startY = startY;
|
||||
@ -72,8 +80,14 @@ public class Mask {
|
||||
this.endY = endY;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return startX >= endX || startY >= endY;
|
||||
public void set(Mask copyFrom) {
|
||||
set(copyFrom.startX, copyFrom.startY, copyFrom.endX, copyFrom.endY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + getStartX() + "; " + getStartY() +
|
||||
") -> (" + getEndX() + "; " + getEndY() + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
/*******************************************************************************
|
||||
* Optica
|
||||
* Copyright (C) 2020 Wind Corporation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.optica.client.graphics.flat;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
|
||||
public class MaskStack {
|
||||
|
||||
private final FloatBuffer buffer = BufferUtils.createFloatBuffer(
|
||||
FlatRenderProgram.MASK_STACK_SIZE * TransformedMask.SIZE_IN_FLOATS
|
||||
);
|
||||
|
||||
public void pushMask(TransformedMask mask) {
|
||||
mask.writeToBuffer(buffer);
|
||||
}
|
||||
|
||||
public void popMask() {
|
||||
buffer.position(buffer.position() - TransformedMask.SIZE_IN_FLOATS);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
FloatBuffer getBuffer() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
}
|
@ -21,7 +21,9 @@ import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import glm.mat._4.Mat4;
|
||||
import glm.vec._3.Vec3;
|
||||
import ru.windcorp.optica.client.graphics.Colors;
|
||||
import ru.windcorp.optica.client.graphics.backend.Usage;
|
||||
@ -33,10 +35,53 @@ import ru.windcorp.optica.client.graphics.texture.Texture;
|
||||
|
||||
public class RenderTarget {
|
||||
|
||||
private final List<AssembledFlatLayer.Clip> assembled = new ArrayList<>();
|
||||
private static final Mat4 IDENTITY = new Mat4().identity();
|
||||
|
||||
private final Deque<Mask> maskStack = new LinkedList<>();
|
||||
public static class Clip {
|
||||
|
||||
private final MaskStack masks = new MaskStack();
|
||||
private final Mat4 transform;
|
||||
private final WorldRenderable renderable;
|
||||
|
||||
public Clip(
|
||||
Iterable<TransformedMask> masks,
|
||||
Mat4 transform,
|
||||
WorldRenderable renderable
|
||||
) {
|
||||
for (TransformedMask mask : masks) {
|
||||
this.masks.pushMask(mask);
|
||||
}
|
||||
|
||||
this.transform = transform == null ? IDENTITY : transform;
|
||||
this.renderable = Objects.requireNonNull(renderable, "renderable");
|
||||
}
|
||||
|
||||
public Mat4 getTransform() {
|
||||
return transform;
|
||||
}
|
||||
|
||||
public WorldRenderable getRenderable() {
|
||||
return renderable;
|
||||
}
|
||||
|
||||
public void render(AssembledFlatRenderHelper helper) {
|
||||
helper.setMasks(masks.getBuffer());
|
||||
helper.pushTransform().mul(getTransform());
|
||||
|
||||
try {
|
||||
getRenderable().render(helper);
|
||||
} finally {
|
||||
helper.popTransform();
|
||||
helper.setMasks(null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final List<Clip> assembled = new ArrayList<>();
|
||||
|
||||
private final Deque<TransformedMask> maskStack = new LinkedList<>();
|
||||
private final Deque<Mat4> transformStack = new LinkedList<>();
|
||||
private final List<Face> currentClipFaces = new ArrayList<>();
|
||||
|
||||
private int depth = 0;
|
||||
@ -45,20 +90,55 @@ public class RenderTarget {
|
||||
reset();
|
||||
}
|
||||
|
||||
public void pushMaskStartEnd(int startX, int startY, int endX, int endY) {
|
||||
assembleCurrentClipFromFaces();
|
||||
maskStack.push(intersect(getMask(), startX, startY, endX, endY));
|
||||
protected void assembleCurrentClipFromFaces() {
|
||||
if (!currentClipFaces.isEmpty()) {
|
||||
Face[] faces = currentClipFaces.toArray(
|
||||
new Face[currentClipFaces.size()]
|
||||
);
|
||||
currentClipFaces.clear();
|
||||
|
||||
Shape shape = new Shape(
|
||||
Usage.STATIC, FlatRenderProgram.getDefault(), faces
|
||||
);
|
||||
|
||||
assembled.add(new Clip(
|
||||
maskStack, getTransform(), shape
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
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 Clip[] assemble() {
|
||||
assembleCurrentClipFromFaces();
|
||||
|
||||
Clip[] result = assembled.toArray(
|
||||
new Clip[assembled.size()]
|
||||
);
|
||||
|
||||
reset();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
maskStack.clear();
|
||||
transformStack.clear();
|
||||
currentClipFaces.clear();
|
||||
assembled.clear();
|
||||
|
||||
transformStack.add(new Mat4().identity());
|
||||
depth = 0;
|
||||
}
|
||||
|
||||
public void pushMaskStartEnd(int startX, int startY, int endX, int endY) {
|
||||
assembleCurrentClipFromFaces();
|
||||
|
||||
pushTransform(new Mat4().identity().translate(startX, startY, 0));
|
||||
|
||||
maskStack.push(
|
||||
new TransformedMask(
|
||||
new Mask(startX, startY, endX, endY),
|
||||
getTransform()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ -76,64 +156,32 @@ public class RenderTarget {
|
||||
public void popMask() {
|
||||
assembleCurrentClipFromFaces();
|
||||
maskStack.pop();
|
||||
popTransform();
|
||||
}
|
||||
|
||||
public Mask getMask() {
|
||||
public TransformedMask 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() {
|
||||
public void pushTransform(Mat4 transform) {
|
||||
assembleCurrentClipFromFaces();
|
||||
|
||||
AssembledFlatLayer.Clip[] result = assembled.toArray(
|
||||
new AssembledFlatLayer.Clip[assembled.size()]
|
||||
);
|
||||
|
||||
reset();
|
||||
|
||||
return result;
|
||||
transformStack.push(getTransform().mul(transform, transform));
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
maskStack.clear();
|
||||
currentClipFaces.clear();
|
||||
assembled.clear();
|
||||
public void popTransform() {
|
||||
assembleCurrentClipFromFaces();
|
||||
transformStack.pop();
|
||||
}
|
||||
|
||||
maskStack.add(new Mask(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE));
|
||||
depth = 0;
|
||||
public Mat4 getTransform() {
|
||||
return transformStack.getFirst();
|
||||
}
|
||||
|
||||
public void addCustomRenderer(WorldRenderable renderable) {
|
||||
assembleCurrentClipFromFaces();
|
||||
|
||||
Mask mask = getMask();
|
||||
if (mask.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
assembled.add(new AssembledFlatLayer.Clip(mask, renderable));
|
||||
assembled.add(new Clip(
|
||||
maskStack, getTransform(), renderable
|
||||
));
|
||||
}
|
||||
|
||||
protected void addFaceToCurrentClip(Face face) {
|
||||
@ -144,16 +192,9 @@ public class RenderTarget {
|
||||
int x, int y, int width, int height,
|
||||
int color, Texture texture
|
||||
) {
|
||||
float depth = this.depth--;
|
||||
|
||||
addFaceToCurrentClip(Faces.createRectangle(
|
||||
FlatRenderProgram.getDefault(),
|
||||
texture,
|
||||
createVectorFromRGBInt(color),
|
||||
new Vec3(x, y + height, depth), // Flip
|
||||
new Vec3(width, 0, 0),
|
||||
new Vec3(0, -height, 0)
|
||||
));
|
||||
addFaceToCurrentClip(
|
||||
createRectagleFace(x, y, width, height, color, texture)
|
||||
);
|
||||
}
|
||||
|
||||
public void drawTexture(
|
||||
@ -170,6 +211,38 @@ public class RenderTarget {
|
||||
drawTexture(x, y, width, height, color, null);
|
||||
}
|
||||
|
||||
public void fill(int color) {
|
||||
fill(
|
||||
Integer.MIN_VALUE / 2, Integer.MIN_VALUE / 2,
|
||||
Integer.MAX_VALUE, Integer.MAX_VALUE,
|
||||
color
|
||||
);
|
||||
}
|
||||
|
||||
public Face createRectagleFace(
|
||||
int x, int y, int width, int height, int color, Texture texture
|
||||
) {
|
||||
float depth = this.depth--;
|
||||
|
||||
return Faces.createRectangle(
|
||||
FlatRenderProgram.getDefault(),
|
||||
texture,
|
||||
createVectorFromRGBInt(color),
|
||||
new Vec3(x, y + height, depth), // Flip
|
||||
new Vec3(width, 0, 0),
|
||||
new Vec3(0, -height, 0)
|
||||
);
|
||||
}
|
||||
|
||||
public Shape createRectagle(
|
||||
int x, int y, int width, int height, int color, Texture texture
|
||||
) {
|
||||
return new Shape(
|
||||
Usage.STATIC, FlatRenderProgram.getDefault(),
|
||||
createRectagleFace(x, y, width, height, color, texture)
|
||||
);
|
||||
}
|
||||
|
||||
private static Vec3 createVectorFromRGBInt(int rgb) {
|
||||
int r = (rgb & 0xFF0000) >> 16;
|
||||
int g = (rgb & 0x00FF00) >> 8;
|
||||
|
@ -0,0 +1,158 @@
|
||||
/*******************************************************************************
|
||||
* Optica
|
||||
* Copyright (C) 2020 Wind Corporation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.optica.client.graphics.flat;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import glm.mat._4.Mat4;
|
||||
import glm.vec._2.Vec2;
|
||||
import glm.vec._4.Vec4;
|
||||
import ru.windcorp.optica.client.graphics.backend.GraphicsInterface;
|
||||
|
||||
public class TransformedMask {
|
||||
|
||||
public static final int SIZE_IN_FLOATS = 2 * 3 * 2;
|
||||
|
||||
private final Vec2 origin = new Vec2();
|
||||
private final Vec2 width = new Vec2();
|
||||
private final Vec2 height = new Vec2();
|
||||
|
||||
private final Vec2 counterOrigin = new Vec2();
|
||||
private final Vec2 counterWidth = new Vec2();
|
||||
private final Vec2 counterHeight = new Vec2();
|
||||
|
||||
// Temporary values, objects cached for efficiency
|
||||
private Vec4 startXstartY = null;
|
||||
private Vec4 startXendY = null;
|
||||
private Vec4 endXstartY = null;
|
||||
private Vec4 endXendY = null;
|
||||
|
||||
public TransformedMask(
|
||||
Vec2 origin, Vec2 width, Vec2 height,
|
||||
Vec2 counterOrigin, Vec2 counterWidth, Vec2 counterHeight
|
||||
) {
|
||||
set(origin, width, height, counterOrigin, counterWidth, counterHeight);
|
||||
}
|
||||
|
||||
public TransformedMask(Mask mask, Mat4 transform) {
|
||||
set(mask, transform);
|
||||
}
|
||||
|
||||
public TransformedMask() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public TransformedMask set(
|
||||
Vec2 origin, Vec2 width, Vec2 height,
|
||||
Vec2 counterOrigin, Vec2 counterWidth, Vec2 counterHeight
|
||||
) {
|
||||
this.origin.set(origin.x, origin.y);
|
||||
this.width.set(width.x, width.y);
|
||||
this.height.set(height.x, height.y);
|
||||
this.counterOrigin.set(counterOrigin.x, counterOrigin.y);
|
||||
this.counterWidth.set(counterWidth.x, counterWidth.y);
|
||||
this.counterHeight.set(counterHeight.x, counterHeight.y);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public TransformedMask set(Mask mask, Mat4 transform) {
|
||||
applyTransform(mask, transform);
|
||||
setFields();
|
||||
return this;
|
||||
}
|
||||
|
||||
private void applyTransform(Mask mask, Mat4 transform) {
|
||||
ensureTemporaryVariablesExist();
|
||||
|
||||
int relX = mask.getWidth();
|
||||
int relY = mask.getHeight();
|
||||
|
||||
startXstartY.set( 0, 0, 0, 1);
|
||||
startXendY .set( 0, relY, 0, 1);
|
||||
endXstartY .set(relX, 0, 0, 1);
|
||||
endXendY .set(relX, relY, 0, 1);
|
||||
|
||||
transform.mul(startXstartY);
|
||||
transform.mul(startXendY);
|
||||
transform.mul(endXstartY);
|
||||
transform.mul(endXendY);
|
||||
|
||||
flipY();
|
||||
}
|
||||
|
||||
private void ensureTemporaryVariablesExist() {
|
||||
if (startXstartY == null) {
|
||||
startXstartY = new Vec4();
|
||||
startXendY = new Vec4();
|
||||
endXstartY = new Vec4();
|
||||
endXendY = new Vec4();
|
||||
}
|
||||
}
|
||||
|
||||
private void flipY() {
|
||||
float height = GraphicsInterface.getFramebufferHeight();
|
||||
|
||||
startXstartY.y = height - startXstartY.y;
|
||||
startXendY.y = height - startXendY.y;
|
||||
endXstartY.y = height - endXstartY.y;
|
||||
endXendY.y = height - endXendY.y;
|
||||
}
|
||||
|
||||
private void setFields() {
|
||||
origin.set(
|
||||
startXstartY.x,
|
||||
startXstartY.y
|
||||
);
|
||||
|
||||
width.set(
|
||||
endXstartY.x - startXstartY.x,
|
||||
endXstartY.y - startXstartY.y
|
||||
);
|
||||
|
||||
height.set(
|
||||
startXendY.x - startXstartY.x,
|
||||
startXendY.y - startXstartY.y
|
||||
);
|
||||
|
||||
counterOrigin.set(
|
||||
endXendY.x,
|
||||
endXendY.y
|
||||
);
|
||||
|
||||
counterWidth.set(
|
||||
startXendY.x - endXendY.x,
|
||||
startXendY.y - endXendY.y
|
||||
);
|
||||
|
||||
counterHeight.set(
|
||||
endXstartY.x - endXendY.x,
|
||||
endXstartY.y - endXendY.y
|
||||
);
|
||||
}
|
||||
|
||||
public void writeToBuffer(FloatBuffer output) {
|
||||
output.put(origin.x).put(origin.y);
|
||||
output.put(width.x).put(width.y);
|
||||
output.put(height.x).put(height.y);
|
||||
output.put(counterOrigin.x).put(counterOrigin.y);
|
||||
output.put(counterWidth.x).put(counterWidth.y);
|
||||
output.put(counterHeight.x).put(counterHeight.y);
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,25 @@
|
||||
/*******************************************************************************
|
||||
* Optica
|
||||
* Copyright (C) 2020 Wind Corporation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.optica.client.graphics.input;
|
||||
|
||||
public abstract class WheelEvent extends InputEvent {
|
||||
|
||||
@Override
|
||||
public abstract WheelEvent snapshot();
|
||||
|
||||
}
|
||||
|
@ -1,51 +1,86 @@
|
||||
/*******************************************************************************
|
||||
* Optica
|
||||
* Copyright (C) 2020 Wind Corporation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.optica.client.graphics.input;
|
||||
|
||||
import glm.vec._2.d.Vec2d;
|
||||
|
||||
public class WheelScrollEvent extends WheelEvent {
|
||||
|
||||
protected double xoffset;
|
||||
protected double yoffset;
|
||||
protected double xOffset;
|
||||
protected double yOffset;
|
||||
|
||||
protected WheelScrollEvent(double xoffset, double yoffset) {
|
||||
this.xoffset = xoffset;
|
||||
this.yoffset = yoffset;
|
||||
protected WheelScrollEvent(double xOffset, double yOffset) {
|
||||
this.xOffset = xOffset;
|
||||
this.yOffset = yOffset;
|
||||
}
|
||||
|
||||
protected WheelScrollEvent() {}
|
||||
|
||||
public boolean isUp() { return yoffset > 0; }
|
||||
public boolean isUp() {
|
||||
return yOffset > 0;
|
||||
}
|
||||
|
||||
public boolean isDown() { return yoffset < 0; }
|
||||
public boolean isDown() {
|
||||
return yOffset < 0;
|
||||
}
|
||||
|
||||
public boolean isRight() { return xoffset > 0; }
|
||||
public boolean isRight() {
|
||||
return xOffset > 0;
|
||||
}
|
||||
|
||||
public boolean isLeft() { return xoffset < 0; }
|
||||
public boolean isLeft() {
|
||||
return xOffset < 0;
|
||||
}
|
||||
|
||||
public boolean hasVerticalMovement() { return yoffset != 0; }
|
||||
public boolean hasVerticalMovement() {
|
||||
return yOffset != 0;
|
||||
}
|
||||
|
||||
public boolean hasHorizontalMovement() { return xoffset != 0; }
|
||||
public boolean hasHorizontalMovement() {
|
||||
return xOffset != 0;
|
||||
}
|
||||
|
||||
public double getX() { return xoffset; }
|
||||
public double getX() {
|
||||
return xOffset;
|
||||
}
|
||||
|
||||
public double getY() { return yoffset; }
|
||||
public double getY() {
|
||||
return yOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WheelEvent snapshot() {
|
||||
return new StaticWheelScrollEvent(xoffset, yoffset, getTime());
|
||||
return new StaticWheelScrollEvent(xOffset, yOffset, getTime());
|
||||
}
|
||||
|
||||
private class StaticWheelScrollEvent extends WheelScrollEvent {
|
||||
|
||||
private final double time;
|
||||
|
||||
public StaticWheelScrollEvent(double xoffset, double yoffset, double time) {
|
||||
super(xoffset, yoffset);
|
||||
public StaticWheelScrollEvent(
|
||||
double xOffset, double yOffset, double time
|
||||
) {
|
||||
super(xOffset, yOffset);
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getTime() { return time; }
|
||||
public double getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WheelEvent snapshot() {
|
||||
|
@ -0,0 +1,125 @@
|
||||
/*******************************************************************************
|
||||
* Optica
|
||||
* Copyright (C) 2020 Wind Corporation
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
package ru.windcorp.optica.client.graphics.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.google.common.primitives.Booleans;
|
||||
|
||||
import glm.mat._4.Mat4;
|
||||
|
||||
public class LambdaModel extends DynamicModel {
|
||||
|
||||
private static final Mat4 IDENTITY = new Mat4();
|
||||
|
||||
@FunctionalInterface
|
||||
public static interface TransformGetter {
|
||||
void transform(Mat4 result);
|
||||
}
|
||||
|
||||
private final TransformGetter[] getters;
|
||||
|
||||
public LambdaModel(
|
||||
WorldRenderable[] parts,
|
||||
Mat4[] transforms,
|
||||
boolean[] dynamic,
|
||||
TransformGetter[] getters
|
||||
) {
|
||||
super(parts, transforms, dynamic);
|
||||
this.getters = getters;
|
||||
}
|
||||
|
||||
public LambdaModel(Builder builder) {
|
||||
this(
|
||||
builder.getParts(), builder.getTransforms(),
|
||||
builder.getDynamics(), builder.getGetters()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getDynamicTransform(int shapeIndex, Mat4 result) {
|
||||
getters[shapeIndex].transform(result);
|
||||
}
|
||||
|
||||
public static Builder lambdaBuilder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private final List<WorldRenderable> parts = new ArrayList<>();
|
||||
private final List<Mat4> transforms = new ArrayList<>();
|
||||
private final List<Boolean> dynamics = new ArrayList<>();
|
||||
private final List<TransformGetter> getters = new ArrayList<>();
|
||||
|
||||
protected Builder() {}
|
||||
|
||||
private Builder addPart(
|
||||
WorldRenderable part,
|
||||
Mat4 transform,
|
||||
TransformGetter getter
|
||||
) {
|
||||
parts.add(Objects.requireNonNull(part, "part"));
|
||||
transforms.add(Objects.requireNonNull(transform, "transform"));
|
||||
dynamics.add(getter != null);
|
||||
getters.add(getter);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addStaticPart(
|
||||
WorldRenderable part,
|
||||
Mat4 transform
|
||||
) {
|
||||
return addPart(part, new Mat4(transform), null);
|
||||
}
|
||||
|
||||
public Builder addDynamicPart(
|
||||
WorldRenderable part,
|
||||
TransformGetter getter
|
||||
) {
|
||||
return addPart(part, new Mat4(), getter);
|
||||
}
|
||||
|
||||
public Builder addStaticPart(
|
||||
WorldRenderable part
|
||||
) {
|
||||
return addStaticPart(part, IDENTITY);
|
||||
}
|
||||
|
||||
private WorldRenderable[] getParts() {
|
||||
return parts.toArray(new WorldRenderable[parts.size()]);
|
||||
}
|
||||
|
||||
private Mat4[] getTransforms() {
|
||||
return transforms.toArray(new Mat4[transforms.size()]);
|
||||
}
|
||||
|
||||
private boolean[] getDynamics() {
|
||||
return Booleans.toArray(dynamics);
|
||||
}
|
||||
|
||||
private TransformGetter[] getGetters() {
|
||||
return getters.toArray(new TransformGetter[getters.size()]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -24,7 +24,7 @@ public class ShapeRenderHelper {
|
||||
|
||||
protected static final int TRANSFORM_STACK_SIZE = 64;
|
||||
|
||||
private final StashingStack<Mat4> transformStack = new StashingStack<>(
|
||||
protected final StashingStack<Mat4> transformStack = new StashingStack<>(
|
||||
TRANSFORM_STACK_SIZE, Mat4::new
|
||||
);
|
||||
|
||||
|
@ -35,6 +35,8 @@ import ru.windcorp.optica.common.world.WorldData;
|
||||
|
||||
public class LayerWorld extends Layer {
|
||||
|
||||
public static Camera tmp_the_camera;
|
||||
|
||||
private final Camera camera = new Camera(
|
||||
new Vec3(8, 8, 8),
|
||||
0, 0,
|
||||
@ -59,6 +61,7 @@ public class LayerWorld extends Layer {
|
||||
|
||||
public LayerWorld() {
|
||||
super("World");
|
||||
tmp_the_camera = camera;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -208,6 +208,26 @@ public class StashingStack<T> implements Iterable<T> {
|
||||
return newElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified element from the stack. Indexing starts from the
|
||||
* bottom of the stack. If the index is out of bounds, an
|
||||
* {@link IndexOutOfBoundsException} is thrown.
|
||||
* @param index index of the element to retrieve,
|
||||
* <tt>[0; {@link #getSize()})</tt>
|
||||
* @return the requested element
|
||||
* @throws IndexOutOfBoundsException if the index is negative or greater
|
||||
* than head
|
||||
*/
|
||||
public T get(int index) {
|
||||
if (index > head) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Requested index " + index + " > head " + head
|
||||
);
|
||||
}
|
||||
|
||||
return (T) contents[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all elements from the stack.
|
||||
*/
|
||||
|
@ -1,13 +1,26 @@
|
||||
#version 120
|
||||
#version 140
|
||||
|
||||
uniform ivec2 maskStart;
|
||||
uniform ivec2 maskEnd;
|
||||
uniform int maskCount;
|
||||
|
||||
uniform vec2 masks[3 * 2 * 16];
|
||||
|
||||
bool isInMaskPrimitive(int primitive) {
|
||||
vec2 origin = masks[3 * primitive + 0];
|
||||
vec2 width = masks[3 * primitive + 1];
|
||||
vec2 height = masks[3 * primitive + 2];
|
||||
|
||||
vec2 current = gl_FragCoord.xy - origin;
|
||||
|
||||
mat2 matrix = mat2(width, height);
|
||||
vec2 relative = inverse(matrix) * current;
|
||||
|
||||
return relative.x >= 0 && relative.y >= 0 && relative.x + relative.y <= 1;
|
||||
}
|
||||
|
||||
void applyMask() {
|
||||
if (
|
||||
gl_FragCoord.x < maskStart.x || gl_FragCoord.x >= maskEnd.x ||
|
||||
gl_FragCoord.y < maskStart.y || gl_FragCoord.y >= maskEnd.y
|
||||
) {
|
||||
for (int i = 0; i < maskCount; ++i) {
|
||||
if (!(isInMaskPrimitive(2 * i + 0) || isInMaskPrimitive(2 * i + 1))) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
#version 120
|
||||
#version 140
|
||||
|
||||
void flatTransferToFragment() {
|
||||
shapeTransferToFragment();
|
||||
|
@ -1,4 +1,4 @@
|
||||
#version 120
|
||||
#version 140
|
||||
|
||||
void main(void) {
|
||||
applyTexture();
|
||||
|
@ -1,4 +1,4 @@
|
||||
#version 120
|
||||
#version 140
|
||||
|
||||
void main(void) {
|
||||
gl_Position = applyFinalTransform(vec4(inputPositions, 1.0));
|
||||
|
@ -1,4 +1,4 @@
|
||||
#version 120
|
||||
#version 140
|
||||
|
||||
varying vec3 varyingColorMultiplier;
|
||||
varying vec2 varyingTextureCoords;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#version 120
|
||||
#version 140
|
||||
|
||||
attribute vec3 inputPositions;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#version 120
|
||||
#version 140
|
||||
|
||||
varying vec3 varyingNormals;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#version 120
|
||||
#version 140
|
||||
|
||||
attribute vec3 inputNormals;
|
||||
varying vec3 varyingNormals;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#version 120
|
||||
#version 140
|
||||
|
||||
void main(void) {
|
||||
applyTexture();
|
||||
|
@ -1,4 +1,4 @@
|
||||
#version 120
|
||||
#version 140
|
||||
|
||||
void main(void) {
|
||||
gl_Position = applyFinalTransform(vec4(inputPositions, 1.0));
|
||||
|
BIN
src/main/resources/assets/textures/compass_icon.png
Normal file
BIN
src/main/resources/assets/textures/compass_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
BIN
src/main/resources/assets/textures/compass_icon_arrow.png
Normal file
BIN
src/main/resources/assets/textures/compass_icon_arrow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 236 B |
Reference in New Issue
Block a user