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:
OLEGSHA 2020-08-03 21:44:36 +03:00
parent da61f834e5
commit 7dee1eb95f
31 changed files with 812 additions and 264 deletions

View File

@ -33,6 +33,6 @@ public class Colors {
DEBUG_CYAN = 0x00FFFF,
DEBUG_MAGENTA = 0xFF00FF,
DEBUG_YELLOW = 0x00FFFF;
DEBUG_YELLOW = 0xFFFF00;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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();
}
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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();
}
protected abstract FloatBuffer getMasks();
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() {
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());
}
}

View File

@ -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);
}
}

View File

@ -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,37 +43,46 @@ 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;
y -= 2*border;
int x = (getWidth() - WIDTH) / 2;
int y = getHeight() - HEIGHT - 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);
}
private void drawCross(RenderTarget target) {
int cx = getWidth() / 2;
int cy = getHeight() / 2;

View File

@ -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() + ")";
}
}

View File

@ -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;
}
}

View File

@ -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();
maskStack.add(new Mask(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE));
depth = 0;
public void popTransform() {
assembleCurrentClipFromFaces();
transformStack.pop();
}
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;

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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() {

View File

@ -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()]);
}
}
}

View File

@ -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
);

View File

@ -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

View File

@ -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;&nbsp;{@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.
*/

View File

@ -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
) {
discard;
for (int i = 0; i < maskCount; ++i) {
if (!(isInMaskPrimitive(2 * i + 0) || isInMaskPrimitive(2 * i + 1))) {
discard;
}
}
}
}

View File

@ -1,4 +1,4 @@
#version 120
#version 140
void flatTransferToFragment() {
shapeTransferToFragment();

View File

@ -1,4 +1,4 @@
#version 120
#version 140
void main(void) {
applyTexture();

View File

@ -1,4 +1,4 @@
#version 120
#version 140
void main(void) {
gl_Position = applyFinalTransform(vec4(inputPositions, 1.0));

View File

@ -1,4 +1,4 @@
#version 120
#version 140
varying vec3 varyingColorMultiplier;
varying vec2 varyingTextureCoords;

View File

@ -1,4 +1,4 @@
#version 120
#version 140
attribute vec3 inputPositions;

View File

@ -1,4 +1,4 @@
#version 120
#version 140
varying vec3 varyingNormals;

View File

@ -1,4 +1,4 @@
#version 120
#version 140
attribute vec3 inputNormals;
varying vec3 varyingNormals;

View File

@ -1,4 +1,4 @@
#version 120
#version 140
void main(void) {
applyTexture();

View File

@ -1,4 +1,4 @@
#version 120
#version 140
void main(void) {
gl_Position = applyFinalTransform(vec4(inputPositions, 1.0));

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 B