Updated block selection display and added block placing

- Packaged everything related to block selection into Selection class
- Selection box is a bit less obnoxious
- Added the ability to place Test:Stone blocks





- This is technically a Minecraft clone now. I don't know how to feel
about this tbh
This commit is contained in:
OLEGSHA 2020-11-20 11:50:24 +03:00
parent a7eb90865f
commit 7f381c7a1f
8 changed files with 237 additions and 64 deletions

View File

@ -20,6 +20,8 @@ package ru.windcorp.progressia.client.graphics.world;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import glm.mat._4.Mat4;
import glm.vec._3.Vec3;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.client.Client; import ru.windcorp.progressia.client.Client;
import ru.windcorp.progressia.client.ClientState; import ru.windcorp.progressia.client.ClientState;
@ -28,9 +30,15 @@ import ru.windcorp.progressia.client.graphics.Layer;
import ru.windcorp.progressia.client.graphics.backend.FaceCulling; import ru.windcorp.progressia.client.graphics.backend.FaceCulling;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.input.bus.Input; import ru.windcorp.progressia.client.graphics.input.bus.Input;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram;
import ru.windcorp.progressia.client.graphics.model.Shapes.PppBuilder;
import ru.windcorp.progressia.client.graphics.model.StaticModel;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.common.Units; import ru.windcorp.progressia.common.Units;
import ru.windcorp.progressia.common.collision.Collideable; import ru.windcorp.progressia.common.collision.Collideable;
import ru.windcorp.progressia.common.collision.colliders.Collider; import ru.windcorp.progressia.common.collision.colliders.Collider;
import ru.windcorp.progressia.common.util.FloatMathUtils;
import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.test.CollisionModelRenderer; import ru.windcorp.progressia.test.CollisionModelRenderer;
import ru.windcorp.progressia.test.TestPlayerControls; import ru.windcorp.progressia.test.TestPlayerControls;
@ -127,19 +135,48 @@ public class LayerWorld extends Layer {
tmp_colliderWorkspace tmp_colliderWorkspace
); );
} }
private static final Renderable SELECTION_BOX = tmp_createSelectionBox();
private void tmp_drawSelectionBox() { private void tmp_drawSelectionBox() {
LocalPlayer player = client.getLocalPlayer(); LocalPlayer player = client.getLocalPlayer();
if (player == null) return; if (player == null) return;
Vec3i lookingAt = player.getLookingAt(); Vec3i selection = player.getSelection().getBlock();
if (lookingAt == null) return; if (selection == null) return;
helper.pushTransform().translate(lookingAt.x, lookingAt.y, lookingAt.z).scale(1.1f); helper.pushTransform().translate(selection.x, selection.y, selection.z);
CollisionModelRenderer.renderCollisionModel(client.getWorld().getData().getCollisionModelOfBlock(lookingAt), helper); SELECTION_BOX.render(helper);
helper.popTransform(); helper.popTransform();
} }
private static Renderable tmp_createSelectionBox() {
StaticModel.Builder b = StaticModel.builder();
ShapeRenderProgram p = WorldRenderProgram.getDefault();
final float f = 1e-2f;
final float scale = 1 - f/2;
final Vec3 color = new Vec3(1, 1, 1).mul(0);
for (float phi = 0; phi < 2*FloatMathUtils.PI_F; phi += FloatMathUtils.PI_F/2) {
Mat4 rot = new Mat4().identity().rotateZ(phi).scale(scale);
b.addPart(new PppBuilder(p, (Texture) null).setOrigin(
new Vec3(-f - 0.5f, -f - 0.5f, -f - 0.5f)
).setSize(f, f, 2*f + 1).setColorMultiplier(color).create(), rot);
b.addPart(new PppBuilder(p, (Texture) null).setOrigin(
new Vec3(-f - 0.5f, - 0.5f, -f - 0.5f)
).setSize(f, 1, f).setColorMultiplier(color).create(), rot);
b.addPart(new PppBuilder(p, (Texture) null).setOrigin(
new Vec3(-f - 0.5f, - 0.5f, + 0.5f)
).setSize(f, 1, f).setColorMultiplier(color).create(), rot);
}
return new StaticModel(b);
}
private void tmp_applyFriction(EntityData entity) { private void tmp_applyFriction(EntityData entity) {
final float frictionCoeff = 1 - 1e-5f; final float frictionCoeff = 1 - 1e-5f;
entity.getVelocity().mul(frictionCoeff); entity.getVelocity().mul(frictionCoeff);

View File

@ -1,60 +1,24 @@
package ru.windcorp.progressia.client.graphics.world; package ru.windcorp.progressia.client.graphics.world;
import glm.vec._3.Vec3;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.client.world.WorldRender; import ru.windcorp.progressia.client.world.WorldRender;
import ru.windcorp.progressia.client.world.entity.EntityRenderable; import ru.windcorp.progressia.client.world.entity.EntityRenderable;
import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.BlockRay;
import ru.windcorp.progressia.common.world.Player; import ru.windcorp.progressia.common.world.Player;
import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.entity.EntityData;
public class LocalPlayer extends Player { public class LocalPlayer extends Player {
private Vec3i lookingAt = new Vec3i(); private final Selection selection = new Selection();
private boolean isLookingAtBlock = false;
private BlockRay lookingAtRay = new BlockRay();
public LocalPlayer(EntityData entity) { public LocalPlayer(EntityData entity) {
super(entity); super(entity);
} }
public Vec3i getLookingAt() { public Selection getSelection() {
return isLookingAtBlock ? lookingAt : null; return selection;
} }
public void update(WorldRender world) { public void update(WorldRender world) {
updateLookingAt(world); getSelection().update(world, getEntity());
}
private void updateLookingAt(WorldRender world) {
Vec3 direction = Vectors.grab3();
Vec3 start = Vectors.grab3();
BlockRay ray = lookingAtRay;
EntityData player = getEntity();
player.getLookingAtVector(direction);
world.getEntityRenderable(player).getViewPoint(start);
start.add(player.getPosition());
isLookingAtBlock = false;
for (ray.start(start, direction); ray.getDistance() < 6; ray.next()) {
Vec3i blockInWorld = ray.current();
if (world.getData().getCollisionModelOfBlock(blockInWorld) != null) {
isLookingAtBlock = true;
lookingAt.set(blockInWorld.x, blockInWorld.y, blockInWorld.z);
break;
}
}
ray.end();
Vectors.release(direction);
Vectors.release(start);
} }
public EntityRenderable getRenderable(WorldRender world) { public EntityRenderable getRenderable(WorldRender world) {

View File

@ -0,0 +1,72 @@
package ru.windcorp.progressia.client.graphics.world;
import glm.vec._2.Vec2;
import glm.vec._3.Vec3;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.client.world.WorldRender;
import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.BlockRay;
import ru.windcorp.progressia.common.world.block.BlockFace;
import ru.windcorp.progressia.common.world.entity.EntityData;
public class Selection {
private final Vec3i block = new Vec3i();
private BlockFace surface = null;
private final Vec2 pointOnSurface = new Vec2(0.5f, 0.5f);
private final Vec3 point = new Vec3();
private boolean exists = false;
private BlockRay ray = new BlockRay();
public void update(WorldRender world, EntityData player) {
Vec3 direction = Vectors.grab3();
Vec3 start = Vectors.grab3();
player.getLookingAtVector(direction);
world.getEntityRenderable(player).getViewPoint(start);
start.add(player.getPosition());
exists = false;
for (ray.start(start, direction); ray.getDistance() < 6; ray.next()) {
Vec3i blockInWorld = ray.current();
if (world.getData().getCollisionModelOfBlock(blockInWorld) != null) {
exists = true;
block.set(blockInWorld.x, blockInWorld.y, blockInWorld.z);
ray.getPoint(point);
surface = ray.getCurrentFace();
// TODO selectedPointOnSurface
break;
}
}
ray.end();
Vectors.release(direction);
Vectors.release(start);
}
public Vec3i getBlock() {
return exists ? block : null;
}
public Vec3 getPoint() {
return exists ? point : null;
}
public BlockFace getSurface() {
return exists ? surface : null;
}
public Vec2 getPointOnSurface() {
return exists ? pointOnSurface : null;
}
public boolean exists() {
return exists;
}
}

View File

@ -4,6 +4,7 @@ import glm.vec._3.Vec3;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.util.VectorUtil; import ru.windcorp.progressia.common.util.VectorUtil;
import ru.windcorp.progressia.common.util.VectorUtil.Axis; import ru.windcorp.progressia.common.util.VectorUtil.Axis;
import ru.windcorp.progressia.common.world.block.BlockFace;
import static java.lang.Math.*; import static java.lang.Math.*;
@ -15,6 +16,7 @@ public class BlockRay {
private float distance; private float distance;
private final Vec3i block = new Vec3i(); private final Vec3i block = new Vec3i();
private BlockFace currentFace = null;
private boolean isValid = false; private boolean isValid = false;
@ -65,11 +67,8 @@ public class BlockRay {
// position.(axis) = round(position.(axis)) // position.(axis) = round(position.(axis))
VectorUtil.set(position, axis, round(VectorUtil.get(position, axis))); VectorUtil.set(position, axis, round(VectorUtil.get(position, axis)));
return block; this.currentFace = computeCurrentFace(axis, (int) signum(VectorUtil.get(direction, axis)));
}
public Vec3i current() {
checkState();
return block; return block;
} }
@ -87,6 +86,32 @@ public class BlockRay {
return (edge - c) / dir; return (edge - c) / dir;
} }
private BlockFace computeCurrentFace(Axis axis, int sign) {
if (sign == 0) throw new IllegalStateException("sign is zero");
switch (axis) {
case X: return sign > 0 ? BlockFace.SOUTH : BlockFace.NORTH;
case Y: return sign > 0 ? BlockFace.EAST : BlockFace.WEST;
default:
case Z: return sign > 0 ? BlockFace.BOTTOM : BlockFace.TOP;
}
}
public Vec3i current() {
checkState();
return block;
}
public Vec3 getPoint(Vec3 output) {
output.set(position);
output.add(0.5f); // Make sure we're in the block-center coordinate system
return output;
}
public BlockFace getCurrentFace() {
return currentFace;
}
public float getDistance() { public float getDistance() {
checkState(); checkState();
return distance; return distance;

View File

@ -88,6 +88,17 @@ public class WorldData {
return result; return result;
} }
public BlockData getBlock(Vec3i blockInWorld) {
ChunkData chunk = getChunkByBlock(blockInWorld);
if (chunk == null) return null;
Vec3i blockInChunk = Vectors.grab3i();
Coordinates.convertInWorldToInChunk(blockInWorld, blockInChunk);
BlockData result = chunk.getBlock(blockInChunk);
return result;
}
public Collection<ChunkData> getChunks() { public Collection<ChunkData> getChunks() {
return chunks; return chunks;
} }

View File

@ -25,12 +25,12 @@ import glm.vec._3.i.Vec3i;
public final class BlockFace extends BlockRelation { public final class BlockFace extends BlockRelation {
public static final BlockFace public static final BlockFace
TOP = new BlockFace( 0, 0, +1, true), TOP = new BlockFace( 0, 0, +1, true, "TOP"),
BOTTOM = new BlockFace( 0, 0, -1, false), BOTTOM = new BlockFace( 0, 0, -1, false, "BOTTOM"),
NORTH = new BlockFace(+1, 0, 0, true), NORTH = new BlockFace(+1, 0, 0, true, "NORTH"),
SOUTH = new BlockFace(-1, 0, 0, false), SOUTH = new BlockFace(-1, 0, 0, false, "SOUTH"),
WEST = new BlockFace( 0, +1, 0, false), WEST = new BlockFace( 0, +1, 0, false, "WEST"),
EAST = new BlockFace( 0, -1, 0, true); EAST = new BlockFace( 0, -1, 0, true, "EAST");
private static final ImmutableList<BlockFace> ALL_FACES = private static final ImmutableList<BlockFace> ALL_FACES =
ImmutableList.of(TOP, BOTTOM, NORTH, SOUTH, WEST, EAST); ImmutableList.of(TOP, BOTTOM, NORTH, SOUTH, WEST, EAST);
@ -77,15 +77,21 @@ public final class BlockFace extends BlockRelation {
private static int nextId = 0; private static int nextId = 0;
private final int id; private final int id;
private final String name;
private BlockFace counterFace; private BlockFace counterFace;
private final boolean isPrimary; private final boolean isPrimary;
private BlockFace(int x, int y, int z, boolean isPrimary) { private BlockFace(int x, int y, int z, boolean isPrimary, String name) {
super(x, y, z); super(x, y, z);
this.id = nextId++; this.id = nextId++;
this.isPrimary = isPrimary; this.isPrimary = isPrimary;
this.name = name;
} }
public String getName() {
return name;
}
public boolean isPrimary() { public boolean isPrimary() {
return isPrimary; return isPrimary;
} }
@ -132,5 +138,10 @@ public final class BlockFace extends BlockRelation {
public int getManhattanDistance() { public int getManhattanDistance() {
return 1; return 1;
} }
@Override
public String toString() {
return getName();
}
} }

View File

@ -0,0 +1,22 @@
package ru.windcorp.progressia.test;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.comms.controls.ControlData;
public class ControlPlaceBlockData extends ControlData {
private final Vec3i blockInWorld = new Vec3i();
public ControlPlaceBlockData(String id) {
super(id);
}
public Vec3i getBlockInWorld() {
return blockInWorld;
}
public void setBlockInWorld(Vec3i blockInWorld) {
this.blockInWorld.set(blockInWorld.x, blockInWorld.y, blockInWorld.z);
}
}

View File

@ -13,6 +13,7 @@ import ru.windcorp.progressia.client.comms.controls.*;
import ru.windcorp.progressia.client.graphics.input.KeyEvent; import ru.windcorp.progressia.client.graphics.input.KeyEvent;
import ru.windcorp.progressia.client.graphics.input.KeyMatcher; import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
import ru.windcorp.progressia.client.graphics.world.LocalPlayer; import ru.windcorp.progressia.client.graphics.world.LocalPlayer;
import ru.windcorp.progressia.client.graphics.world.Selection;
import ru.windcorp.progressia.client.world.block.*; import ru.windcorp.progressia.client.world.block.*;
import ru.windcorp.progressia.client.world.entity.*; import ru.windcorp.progressia.client.world.entity.*;
import ru.windcorp.progressia.client.world.tile.*; import ru.windcorp.progressia.client.world.tile.*;
@ -20,10 +21,12 @@ import ru.windcorp.progressia.common.collision.AABB;
import ru.windcorp.progressia.common.collision.CollisionModel; import ru.windcorp.progressia.common.collision.CollisionModel;
import ru.windcorp.progressia.common.comms.controls.*; import ru.windcorp.progressia.common.comms.controls.*;
import ru.windcorp.progressia.common.state.StatefulObjectRegistry.Factory; import ru.windcorp.progressia.common.state.StatefulObjectRegistry.Factory;
import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.ChunkData;
import ru.windcorp.progressia.common.world.block.*; import ru.windcorp.progressia.common.world.block.*;
import ru.windcorp.progressia.common.world.entity.*; import ru.windcorp.progressia.common.world.entity.*;
import ru.windcorp.progressia.common.world.tile.*; import ru.windcorp.progressia.common.world.tile.*;
import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.comms.controls.*; import ru.windcorp.progressia.server.comms.controls.*;
import ru.windcorp.progressia.server.world.block.*; import ru.windcorp.progressia.server.world.block.*;
import ru.windcorp.progressia.server.world.entity.*; import ru.windcorp.progressia.server.world.entity.*;
@ -130,12 +133,19 @@ public class TestContent {
KeyEvent.class, KeyEvent.class,
TestContent::onBlockBreakTrigger, TestContent::onBlockBreakTrigger,
KeyMatcher.of(GLFW.GLFW_MOUSE_BUTTON_LEFT).matcher(), KeyMatcher.of(GLFW.GLFW_MOUSE_BUTTON_LEFT).matcher(),
i -> getLookingAt() != null i -> getSelection().exists()
)); ));
logic.register(ControlLogic.of("Test:BreakBlock", (server, packet, client) -> { logic.register(ControlLogic.of("Test:BreakBlock", TestContent::onBlockBreakReceived));
Vec3i blockInWorld = ((ControlBreakBlockData) packet.getControl()).getBlockInWorld();
server.getAdHocChanger().setBlock(blockInWorld, BlockDataRegistry.getInstance().get("Test:Air")); data.register("Test:PlaceBlock", ControlPlaceBlockData::new);
})); triggers.register(ControlTriggers.of(
"Test:PlaceBlock",
KeyEvent.class,
TestContent::onBlockPlaceTrigger,
KeyMatcher.of(GLFW.GLFW_MOUSE_BUTTON_RIGHT).matcher(),
i -> getSelection().exists()
));
logic.register(ControlLogic.of("Test:PlaceBlock", TestContent::onBlockPlaceReceived));
} }
private static void register(BlockData x) { private static void register(BlockData x) {
@ -191,18 +201,39 @@ public class TestContent {
EntityLogicRegistry.getInstance().register(x); EntityLogicRegistry.getInstance().register(x);
} }
private static Vec3i getLookingAt() { private static Selection getSelection() {
ru.windcorp.progressia.client.Client client = ClientState.getInstance(); ru.windcorp.progressia.client.Client client = ClientState.getInstance();
if (client == null) return null; if (client == null) return null;
LocalPlayer player = client.getLocalPlayer(); LocalPlayer player = client.getLocalPlayer();
if (player == null) return null; if (player == null) return null;
return player.getLookingAt(); return player.getSelection();
} }
private static void onBlockBreakTrigger(ControlData control) { private static void onBlockBreakTrigger(ControlData control) {
((ControlBreakBlockData) control).setBlockInWorld(getLookingAt()); ((ControlBreakBlockData) control).setBlockInWorld(getSelection().getBlock());
}
private static void onBlockBreakReceived(Server server, PacketControl packet, ru.windcorp.progressia.server.comms.Client client) {
Vec3i blockInWorld = ((ControlBreakBlockData) packet.getControl()).getBlockInWorld();
server.getAdHocChanger().setBlock(blockInWorld, BlockDataRegistry.getInstance().get("Test:Air"));
}
private static void onBlockPlaceTrigger(ControlData control) {
Vec3i blockInWorld = Vectors.grab3i();
Vec3i selectedBlock = getSelection().getBlock();
blockInWorld.set(selectedBlock.x, selectedBlock.y, selectedBlock.z).add(getSelection().getSurface().getVector());
((ControlPlaceBlockData) control).setBlockInWorld(blockInWorld);
Vectors.release(blockInWorld);
}
private static void onBlockPlaceReceived(Server server, PacketControl packet, ru.windcorp.progressia.server.comms.Client client) {
Vec3i blockInWorld = ((ControlPlaceBlockData) packet.getControl()).getBlockInWorld();
if (server.getWorld().getData().getChunkByBlock(blockInWorld) == null) return;
server.getAdHocChanger().setBlock(blockInWorld, BlockDataRegistry.getInstance().get("Test:Stone"));
} }
} }