From 9dc315487457d3c48e607eabede73ef074de205b Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Fri, 4 Sep 2020 21:54:55 +0300 Subject: [PATCH] Added entities - Added EntityData, EntityRender and EntityLogic - Added QuadripedModel - Added ComplexTexture to simplify syntax when working with complex models - Added the most adorable creature in the universe (as a test feature) - Camera is now 3rd person (temporary) - Direction can now be switched with F5 --- .../jputil/functions/FloatSupplier.java | 8 + .../ru/windcorp/progressia/client/Client.java | 10 + .../progressia/client/ClientState.java | 5 + .../progressia/client/TestContent.java | 43 ++- .../client/TestEntityRenderJavapony.java | 325 ++++++++++++++++++ .../client/graphics/model/Shapes.java | 22 ++ .../graphics/texture/ComplexTexture.java | 66 ++++ .../client/graphics/world/Camera.java | 6 + .../client/graphics/world/LayerWorld.java | 14 + .../progressia/client/world/ChunkRender.java | 6 + .../progressia/client/world/WorldRender.java | 19 + .../client/world/entity/EntityRender.java | 15 + .../world/entity/EntityRenderRegistry.java | 35 ++ .../client/world/entity/QuadripedModel.java | 195 +++++++++++ .../progressia/common/world/ChunkData.java | 18 + .../common/world/block/BlockFace.java | 16 + .../common/world/entity/EntityData.java | 62 ++++ .../world/entity/EntityDataRegistry.java | 13 + .../server/world/entity/EntityLogic.java | 11 + .../world/entity/EntityLogicRegistry.java | 14 + .../assets/textures/entities/javapony.png | Bin 0 -> 3347 bytes 21 files changed, 888 insertions(+), 15 deletions(-) create mode 100644 src/main/java/ru/windcorp/jputil/functions/FloatSupplier.java create mode 100644 src/main/java/ru/windcorp/progressia/client/TestEntityRenderJavapony.java create mode 100644 src/main/java/ru/windcorp/progressia/client/graphics/texture/ComplexTexture.java create mode 100644 src/main/java/ru/windcorp/progressia/client/world/entity/EntityRender.java create mode 100644 src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java create mode 100644 src/main/java/ru/windcorp/progressia/client/world/entity/QuadripedModel.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/entity/EntityDataRegistry.java create mode 100644 src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogic.java create mode 100644 src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogicRegistry.java create mode 100644 src/main/resources/assets/textures/entities/javapony.png diff --git a/src/main/java/ru/windcorp/jputil/functions/FloatSupplier.java b/src/main/java/ru/windcorp/jputil/functions/FloatSupplier.java new file mode 100644 index 0000000..060b06a --- /dev/null +++ b/src/main/java/ru/windcorp/jputil/functions/FloatSupplier.java @@ -0,0 +1,8 @@ +package ru.windcorp.jputil.functions; + +@FunctionalInterface +public interface FloatSupplier { + + float getAsFloat(); + +} diff --git a/src/main/java/ru/windcorp/progressia/client/Client.java b/src/main/java/ru/windcorp/progressia/client/Client.java index b257e58..5115cc5 100644 --- a/src/main/java/ru/windcorp/progressia/client/Client.java +++ b/src/main/java/ru/windcorp/progressia/client/Client.java @@ -6,10 +6,12 @@ import ru.windcorp.progressia.client.comms.ServerCommsChannel; import ru.windcorp.progressia.client.graphics.world.Camera; import ru.windcorp.progressia.client.world.WorldRender; import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.common.world.entity.EntityData; public class Client { private final WorldRender world; + private EntityData localPlayer; private final Camera camera = new Camera( new Vec3(-6, -6, 20), @@ -30,6 +32,14 @@ public class Client { return world; } + public EntityData getLocalPlayer() { + return localPlayer; + } + + public void setLocalPlayer(EntityData localPlayer) { + this.localPlayer = localPlayer; + } + public Camera getCamera() { return camera; } diff --git a/src/main/java/ru/windcorp/progressia/client/ClientState.java b/src/main/java/ru/windcorp/progressia/client/ClientState.java index 1a3eb73..eab5cfc 100644 --- a/src/main/java/ru/windcorp/progressia/client/ClientState.java +++ b/src/main/java/ru/windcorp/progressia/client/ClientState.java @@ -1,5 +1,6 @@ package ru.windcorp.progressia.client; +import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.client.comms.localhost.LocalServerCommsChannel; import ru.windcorp.progressia.client.graphics.GUI; import ru.windcorp.progressia.client.graphics.flat.LayerTestUI; @@ -27,6 +28,10 @@ public class ClientState { ServerState.getInstance() )); + client.setLocalPlayer( + world.getChunk(new Vec3i(0, 0, 0)).getEntities().get(0) + ); + setInstance(client); GUI.addBottomLayer(new LayerWorld(client)); diff --git a/src/main/java/ru/windcorp/progressia/client/TestContent.java b/src/main/java/ru/windcorp/progressia/client/TestContent.java index 3a93f54..d49b081 100644 --- a/src/main/java/ru/windcorp/progressia/client/TestContent.java +++ b/src/main/java/ru/windcorp/progressia/client/TestContent.java @@ -6,28 +6,22 @@ import static ru.windcorp.progressia.client.world.tile.TileRenderRegistry.getTil import org.lwjgl.glfw.GLFW; import glm.vec._3.i.Vec3i; -import ru.windcorp.progressia.client.comms.controls.ControlTriggerOnKeyPress; -import ru.windcorp.progressia.client.comms.controls.ControlTriggerRegistry; +import ru.windcorp.progressia.client.comms.controls.*; import ru.windcorp.progressia.client.graphics.input.KeyMatcher; import ru.windcorp.progressia.client.world.block.*; -import ru.windcorp.progressia.client.world.tile.TileRender; -import ru.windcorp.progressia.client.world.tile.TileRenderGrass; -import ru.windcorp.progressia.client.world.tile.TileRenderRegistry; -import ru.windcorp.progressia.client.world.tile.TileRenderSimple; -import ru.windcorp.progressia.common.comms.controls.ControlData; -import ru.windcorp.progressia.common.comms.controls.ControlDataRegistry; -import ru.windcorp.progressia.common.comms.controls.PacketControl; +import ru.windcorp.progressia.client.world.entity.*; +import ru.windcorp.progressia.client.world.tile.*; +import ru.windcorp.progressia.common.comms.controls.*; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.block.*; -import ru.windcorp.progressia.common.world.tile.TileData; -import ru.windcorp.progressia.common.world.tile.TileDataRegistry; +import ru.windcorp.progressia.common.world.entity.*; +import ru.windcorp.progressia.common.world.tile.*; import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.comms.Client; -import ru.windcorp.progressia.server.comms.controls.ControlLogic; -import ru.windcorp.progressia.server.comms.controls.ControlLogicRegistry; +import ru.windcorp.progressia.server.comms.controls.*; import ru.windcorp.progressia.server.world.block.*; -import ru.windcorp.progressia.server.world.tile.TileLogic; -import ru.windcorp.progressia.server.world.tile.TileLogicRegistry; +import ru.windcorp.progressia.server.world.entity.*; +import ru.windcorp.progressia.server.world.tile.*; public class TestContent { @@ -39,6 +33,7 @@ public class TestContent { private static void registerWorldContent() { registerBlocks(); registerTiles(); + registerEntities(); } private static void registerBlocks() { @@ -81,6 +76,12 @@ public class TestContent { register(new TileLogic("Test", "Sand")); } + private static void registerEntities() { + register(new EntityData("Test", "Javapony")); + register(new TestEntityRenderJavapony()); + register(new EntityLogic("Test", "Javapony")); + } + private static void regsiterControls() { ControlDataRegistry.getInstance().register(new ControlData("Test", "Switch000")); ControlTriggerRegistry.getInstance().register(new ControlTriggerOnKeyPress("Test", "Switch000", new KeyMatcher(GLFW.GLFW_KEY_G, new int[0], 0)::matches)); @@ -111,6 +112,10 @@ public class TestContent { TileDataRegistry.getInstance().register(x); } + private static void register(EntityData x) { + EntityDataRegistry.getInstance().register(x); + } + private static void register(BlockRender x) { BlockRenderRegistry.getInstance().register(x); } @@ -119,6 +124,10 @@ public class TestContent { TileRenderRegistry.getInstance().register(x); } + private static void register(EntityRender x) { + EntityRenderRegistry.getInstance().register(x); + } + private static void register(BlockLogic x) { BlockLogicRegistry.getInstance().register(x); } @@ -126,5 +135,9 @@ public class TestContent { private static void register(TileLogic x) { TileLogicRegistry.getInstance().register(x); } + + private static void register(EntityLogic x) { + EntityLogicRegistry.getInstance().register(x); + } } diff --git a/src/main/java/ru/windcorp/progressia/client/TestEntityRenderJavapony.java b/src/main/java/ru/windcorp/progressia/client/TestEntityRenderJavapony.java new file mode 100644 index 0000000..14ccabf --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/TestEntityRenderJavapony.java @@ -0,0 +1,325 @@ +package ru.windcorp.progressia.client; + +import java.util.ArrayList; +import java.util.List; + +import glm.vec._3.Vec3; +import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; +import ru.windcorp.progressia.client.graphics.backend.Usage; +import ru.windcorp.progressia.client.graphics.model.Face; +import ru.windcorp.progressia.client.graphics.model.Faces; +import ru.windcorp.progressia.client.graphics.model.LambdaModel; +import ru.windcorp.progressia.client.graphics.model.Renderable; +import ru.windcorp.progressia.client.graphics.model.Shape; +import ru.windcorp.progressia.client.graphics.model.Shapes.PppBuilder; +import ru.windcorp.progressia.client.graphics.model.StaticModel; +import ru.windcorp.progressia.client.graphics.texture.ComplexTexture; +import ru.windcorp.progressia.client.graphics.texture.Texture; +import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram; +import ru.windcorp.progressia.client.world.entity.EntityRender; +import ru.windcorp.progressia.client.world.entity.EntityRenderRegistry; +import ru.windcorp.progressia.client.world.entity.QuadripedModel; +import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.common.world.entity.EntityData; + +public class TestEntityRenderJavapony extends EntityRender { + + private final Renderable body; + private final Renderable head; + private final Renderable foreLeg; + private final Renderable hindLeg; + + public TestEntityRenderJavapony() { + super("Test", "Javapony"); + + ComplexTexture texture = new ComplexTexture( + EntityRenderRegistry.getEntityTexture("javapony"), + 256, 128 + ); + + this.body = createBody(texture); + this.head = createHead(texture); + this.foreLeg = createForeLeg(texture); + this.hindLeg = createHindLeg(texture); + } + + private static Renderable createBody(ComplexTexture texture) { + LambdaModel.Builder b = LambdaModel.lambdaBuilder(); + + b.addStaticPart(createMainBody(texture)); + + Texture tailStartTexture = texture.get(128, 96, 8, 32); + + b.addStaticPart( + new PppBuilder( + WorldRenderProgram.getDefault(), + BlockFace.mapToFaces( + tailStartTexture, tailStartTexture, + tailStartTexture, tailStartTexture, + tailStartTexture, tailStartTexture + ) + ) + .setOrigin(-60, -4, 14) + .setDepth(32, 0, -16).setWidth(8).setHeight(8) + .create() + ); + + Texture neckTexture = texture.get(0, 48, 16, 16); + + b.addStaticPart( + new PppBuilder( + WorldRenderProgram.getDefault(), + BlockFace.mapToFaces( + neckTexture, neckTexture, neckTexture, + neckTexture, neckTexture, neckTexture + ) + ) + .setOrigin(0, -8, 8) + .setWidth(16).setDepth(16).setHeight(4, 0, 16) + .create() + ); + + b.addDynamicPart( + createTail(texture), + m -> m + .translate(-60, 0, 24) + .rotateX(0.05f * Math.sin(GraphicsInterface.getTime())) + .rotateY(0.05f * Math.sin(Math.PI / 3 * GraphicsInterface.getTime())) + ); + + return new LambdaModel(b); + } + + private static Renderable createMainBody(ComplexTexture texture) { + WorldRenderProgram program = WorldRenderProgram.getDefault(); + List faces = new ArrayList<>(); + + final Vec3 color = new Vec3(1, 1, 1); + + // F BODY + faces.add(Faces.createRectangle( + program, texture.get(80, 16, 32, 32), color, + new Vec3(+16, -16, -16), + new Vec3(0, +32, 0), new Vec3(0, 0, +32), + false + )); + + // NECK BASE + faces.add(Faces.createRectangle( + program, texture.get(80, 48, 32, 16), color, + new Vec3(+16, -16, +16), + new Vec3(0, +32, 0), new Vec3(-16, 0, 0), + false + )); + + // T BODY (BACK) + faces.add(Faces.createRectangle( + program, texture.get(128, 0, 32, 48), color, + new Vec3(0, -16, +16), + new Vec3(0, +32, 0), new Vec3(-48, 0, 0), + false + )); + + // BOTTOM B (upper) + faces.add(Faces.createRectangle( + program, texture.get(144, 48, 32, 16), color, + new Vec3(-48, -16, 0), + new Vec3(0, 32, 0), new Vec3(0, 0, 16), + true + )); + + // BOTTOM B (lower) + faces.add(Faces.createRectangle( + program, texture.get(144, 48, 32, 16), color, + new Vec3(-48, -16, -16), + new Vec3(0, 32, 0), new Vec3(0, 0, 16), + true + )); + + // BOTTOM B (stomach) + faces.add(Faces.createRectangle( + program, texture.get(144, 48, 32, 16), color, + new Vec3(-48, -16, -16), + new Vec3(0, 32, 0), new Vec3(16, 0, 0), + false + )); + + // STOMACH + faces.add(Faces.createRectangle( + program, texture.get(224, 96, 32, 32), color, + new Vec3(-32, -16, -16), + new Vec3(0, 32, 0), new Vec3(32, 0, 0), + false + )); + + // BOTTOM F + faces.add(Faces.createRectangle( + program, texture.get(112, 48, 32, 16), color, + new Vec3(+16, -16, -16), + new Vec3(0, 32, 0), new Vec3(-16, 0, 0), + true + )); + + // BODY L + faces.add(Faces.createRectangle( + program, texture.get(112, 16, 16, 32), color, + new Vec3(+16, +16, -16), + new Vec3(-16, 0, 0), new Vec3(0, 0, +32), + false + )); + + // BODY SIDES (left) + faces.add(Faces.createRectangle( + program, texture.get(96, 96, 32, 32), color, + new Vec3(0, +16, -16), + new Vec3(-32, 0, 0), new Vec3(0, 0, +32), + false + )); + + // QT MARK (left) + faces.add(Faces.createRectangle( + program, texture.get(16, 96, 16, 32), color, + new Vec3(-32, +16, -16), + new Vec3(-16, 0, 0), new Vec3(0, 0, +32), + false + )); + + // BODY R + faces.add(Faces.createRectangle( + program, texture.get(64, 16, 16, 32), color, + new Vec3(0, -16, -16), + new Vec3(+16, 0, 0), new Vec3(0, 0, +32), + false + )); + + // BODY SIDES (right) + faces.add(Faces.createRectangle( + program, texture.get(96, 96, 32, 32), color, + new Vec3(0, -16, -16), + new Vec3(-32, 0, 0), new Vec3(0, 0, +32), + true + )); + + // QT MARK (right) + faces.add(Faces.createRectangle( + program, texture.get(16, 96, 16, 32), color, + new Vec3(-32, -16, -16), + new Vec3(-16, 0, 0), new Vec3(0, 0, +32), + true + )); + + return new Shape( + Usage.STATIC, program, + faces.toArray(new Face[faces.size()]) + ); + } + + private static Renderable createHead(ComplexTexture texture) { + WorldRenderProgram program = WorldRenderProgram.getDefault(); + StaticModel.Builder b = StaticModel.builder(); + + // Head + b.addPart(new PppBuilder( + program, texture.getCuboidTextures(0, 64, 32) + ).setOrigin(-16, -16, 0).setSize(32).create()); + + final float hairOffset = 1f; + + // Hair + b.addPart( + new PppBuilder( + program, texture.getCuboidTextures(128, 64, 32) + ) + .setOrigin(-16 - hairOffset, -16 - hairOffset, -hairOffset) + .setSize(32 + 2*hairOffset) + .create() + ); + + // Right ear + b.addPart(new PppBuilder( + program, texture.getCuboidTextures(48, 128-80, 8) + ).setOrigin(-16 + 3, -16, 32).setSize(8).create()); + + // Left ear + b.addPart(new PppBuilder( + program, texture.getCuboidTextures(48, 128-80, 8) + ).setOrigin(-16 + 3, 16 - 8, 32).setSize(8).create()); + + // Muzzle + b.addPart(new PppBuilder( + program, BlockFace.mapToFaces( + texture.get(32, 64, 0, 0), + texture.get(32, 64, 0, 0), + texture.get(32 + 8, 64, 16, 8), + texture.get(32, 64, 0, 0), + texture.get(32, 64, 0, 0), + texture.get(32, 64, 0, 0) + ) + ).setOrigin(16, -8, 0).setSize(16, 4, 8).create()); + + // Nose + b.addPart(new PppBuilder( + program, BlockFace.mapToFaces( + texture.get(32, 64, 0, 0), + texture.get(32, 64, 0, 0), + texture.get(32 + 12, 64 + 8, 8, 4), + texture.get(32, 64, 0, 0), + texture.get(32, 64, 0, 0), + texture.get(32, 64, 0, 0) + ) + ).setOrigin(16, -4, 8).setSize(8, 4, 4).create()); + + return new StaticModel(b); + } + + private static Renderable createForeLeg(ComplexTexture texture) { + return new PppBuilder( + WorldRenderProgram.getDefault(), + texture.getCuboidTextures(160, 0, 16, 48, 16) + ).setOrigin(-8, -8, -48).setSize(16, 16, 48).create(); + } + + private static Renderable createHindLeg(ComplexTexture texture) { + return new PppBuilder( + WorldRenderProgram.getDefault(), + texture.getCuboidTextures(0, 0, 16, 48, 16) + ).setOrigin(-8, -8, -48).setSize(16, 16, 48).create(); + } + + private static Renderable createTail(ComplexTexture texture) { + WorldRenderProgram program = WorldRenderProgram.getDefault(); + StaticModel.Builder b = StaticModel.builder(); + + // Main tail + b.addPart(new PppBuilder( + program, BlockFace.mapToFaces( + texture.get(128, 96, 16, 16), + texture.get(128, 96, 16, 16), + texture.get(128, 96, 16, 32), + texture.get(128, 96, 16, 32), + texture.get(144, 96, 16, 32), + texture.get(144, 96, 16, 32) + ) + ).setOrigin(-8, -8, -32).setSize(16, 16, 32).create()); + + return new StaticModel(b); + } + + @Override + public Renderable createRenderable(EntityData entity) { + return new QuadripedModel( + entity, + + new QuadripedModel.Body(body), + new QuadripedModel.Head( + head, new Vec3(16, 0, 20), 60, 45 + ), + new QuadripedModel.Leg(foreLeg, new Vec3( 6, +8.1f, -16), 0.0f), + new QuadripedModel.Leg(foreLeg, new Vec3( 6, -8.1f, -16), 2.5f), + new QuadripedModel.Leg(hindLeg, new Vec3(-36, +8.2f, -16), 2.5f), + new QuadripedModel.Leg(hindLeg, new Vec3(-36, -8.2f, -16), 0.0f), + 1 / 96f + ); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/model/Shapes.java b/src/main/java/ru/windcorp/progressia/client/graphics/model/Shapes.java index fabc468..90624ec 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/model/Shapes.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/model/Shapes.java @@ -17,10 +17,13 @@ *******************************************************************************/ package ru.windcorp.progressia.client.graphics.model; +import java.util.Map; + import glm.vec._3.Vec3; import ru.windcorp.progressia.client.graphics.backend.Usage; import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.world.block.BlockFace; public class Shapes { @@ -149,6 +152,21 @@ public class Shapes { this.westTexture = west; } + public PppBuilder( + ShapeRenderProgram program, + Map textureMap + ) { + this( + program, + textureMap.get(BlockFace.TOP), + textureMap.get(BlockFace.BOTTOM), + textureMap.get(BlockFace.NORTH), + textureMap.get(BlockFace.SOUTH), + textureMap.get(BlockFace.EAST), + textureMap.get(BlockFace.WEST) + ); + } + public PppBuilder(ShapeRenderProgram program, Texture texture) { this(program, texture, texture, texture, texture, texture, texture); } @@ -222,6 +240,10 @@ public class Shapes { return this.setWidth(x).setDepth(y).setHeight(z); } + public PppBuilder setSize(float size) { + return this.setSize(size, size, size); + } + public Shape create() { return createParallelepiped( program, diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/texture/ComplexTexture.java b/src/main/java/ru/windcorp/progressia/client/graphics/texture/ComplexTexture.java new file mode 100644 index 0000000..d8f4c18 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/graphics/texture/ComplexTexture.java @@ -0,0 +1,66 @@ +package ru.windcorp.progressia.client.graphics.texture; + +import java.util.Map; + +import glm.vec._2.Vec2; +import ru.windcorp.progressia.common.world.block.BlockFace; + +public class ComplexTexture { + + private final TexturePrimitive primitive; + + private final float assumedWidth; + private final float assumedHeight; + + public ComplexTexture( + TexturePrimitive primitive, + int abstractWidth, int abstractHeight + ) { + this.primitive = primitive; + + this.assumedWidth = abstractWidth + * primitive.getWidth() / (float) primitive.getBufferWidth(); + + this.assumedHeight = abstractHeight + * primitive.getHeight() / (float) primitive.getBufferHeight(); + } + + public Texture get(int x, int y, int width, int height) { + return new SimpleTexture(new Sprite( + primitive, + new Vec2(x / assumedWidth, y / assumedHeight), + new Vec2(width / assumedWidth, height / assumedHeight) + )); + } + + public Map getCuboidTextures( + int x, int y, + int width, int height, int depth + ) { + return BlockFace.mapToFaces( + get( + x + depth + width, y + height + depth, + -width, -depth + ), + get( + x + depth + width + width, y + height + depth, + -width, -depth + ), + get(x + depth, y, width, height), + get( + x + depth + width + depth, y, + width, height + ), + get(x, y, depth, height), + get(x + depth + width, y, depth, height) + ); + } + + public Map getCuboidTextures( + int x, int y, + int size + ) { + return getCuboidTextures(x, y, size, size, size); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/Camera.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/Camera.java index 3eb8745..23af1e4 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/world/Camera.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/Camera.java @@ -33,6 +33,8 @@ public class Camera { private float fieldOfView; + public boolean tmp_mode = false; + public Camera(Vec3 position, float pitch, float yaw, float fieldOfView) { teleport(position); setPitch(pitch); @@ -46,6 +48,10 @@ public class Camera { applyPerspective(helper); rotateCoordinateSystem(helper); + // TODO debug + helper.pushViewTransform().translate(3.5f, 0, -0.5f); + if (tmp_mode) helper.pushViewTransform().rotateZ(PI); + applyDirection(helper); applyPosition(helper); } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java index 6f18b0e..30e4c4f 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java @@ -66,7 +66,15 @@ public class LayerWorld extends Layer { @Override protected void doRender() { + client.getLocalPlayer().setPosition(client.getCamera().getPosition()); + client.getLocalPlayer().setVelocity(velocity); + client.getLocalPlayer().getDirection().set( + -client.getCamera().getYaw(), + -client.getCamera().getPitch() + ); + client.getCamera().apply(helper); + renderWorld(); helper.reset(); @@ -156,6 +164,12 @@ public class LayerWorld extends Layer { flag = !flag; break; + case GLFW.GLFW_KEY_F5: + if (!event.isPress()) return false; + + client.getCamera().tmp_mode = !client.getCamera().tmp_mode; + break; + default: return false; } diff --git a/src/main/java/ru/windcorp/progressia/client/world/ChunkRender.java b/src/main/java/ru/windcorp/progressia/client/world/ChunkRender.java index 3bead82..badcfb7 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/ChunkRender.java +++ b/src/main/java/ru/windcorp/progressia/client/world/ChunkRender.java @@ -92,6 +92,12 @@ public class ChunkRender { model.render(renderer); renderer.popTransform(); + + getData().forEachEntity(entityData -> { + renderer.pushTransform().translate(entityData.getPosition()); + getWorld().getEntityRenderable(entityData).render(renderer); + renderer.popTransform(); + }); } private void buildModel() { diff --git a/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java b/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java index 249dd91..68b5e22 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java +++ b/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java @@ -18,19 +18,26 @@ package ru.windcorp.progressia.client.world; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.WeakHashMap; import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; +import ru.windcorp.progressia.client.world.entity.EntityRenderRegistry; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.common.world.entity.EntityData; public class WorldRender { private final WorldData data; private final Map chunks = new HashMap<>(); + private final Map entityModels = + Collections.synchronizedMap(new WeakHashMap<>()); public WorldRender(WorldData data) { this.data = data; @@ -61,5 +68,17 @@ public class WorldRender { chunk.render(renderer); } } + + public Renderable getEntityRenderable(EntityData entity) { + return entityModels.computeIfAbsent( + entity, + WorldRender::createEntityRenderable + ); + } + + private static Renderable createEntityRenderable(EntityData entity) { + return EntityRenderRegistry.getInstance().get(entity.getId()) + .createRenderable(entity); + } } diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRender.java b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRender.java new file mode 100644 index 0000000..98e1f10 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRender.java @@ -0,0 +1,15 @@ +package ru.windcorp.progressia.client.world.entity; + +import ru.windcorp.progressia.client.graphics.model.Renderable; +import ru.windcorp.progressia.common.util.Namespaced; +import ru.windcorp.progressia.common.world.entity.EntityData; + +public abstract class EntityRender extends Namespaced { + + public EntityRender(String namespace, String name) { + super(namespace, name); + } + + public abstract Renderable createRenderable(EntityData entity); + +} diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java new file mode 100644 index 0000000..e79cebe --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java @@ -0,0 +1,35 @@ +package ru.windcorp.progressia.client.world.entity; + +import java.io.IOException; + +import ru.windcorp.progressia.client.graphics.texture.TextureLoader; +import ru.windcorp.progressia.client.graphics.texture.TexturePrimitive; +import ru.windcorp.progressia.client.graphics.texture.TextureSettings; +import ru.windcorp.progressia.common.resource.ResourceManager; +import ru.windcorp.progressia.common.util.NamespacedRegistry; + +public class EntityRenderRegistry extends NamespacedRegistry { + + private static final EntityRenderRegistry INSTANCE = + new EntityRenderRegistry(); + + public static EntityRenderRegistry getInstance() { + return INSTANCE; + } + + public static TexturePrimitive getEntityTexture(String name) { + try { + return new TexturePrimitive( + TextureLoader.loadPixels( + ResourceManager.getTextureResource( + "entities/" + name + ), + new TextureSettings(false) + ).getData() + ); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/QuadripedModel.java b/src/main/java/ru/windcorp/progressia/client/world/entity/QuadripedModel.java new file mode 100644 index 0000000..14e01e9 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/QuadripedModel.java @@ -0,0 +1,195 @@ +package ru.windcorp.progressia.client.world.entity; + +import static java.lang.Math.*; + +import glm.Glm; +import glm.mat._4.Mat4; +import glm.vec._3.Vec3; +import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; +import ru.windcorp.progressia.client.graphics.model.Renderable; +import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; +import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.world.entity.EntityData; + +public class QuadripedModel implements Renderable { + + private static abstract class BodyPart { + private final Renderable renderable; + private final Vec3 translation = new Vec3(); + + public BodyPart(Renderable renderable, Vec3 joint) { + this.renderable = renderable; + if (joint != null) { +// joint.negate(this.translation); + this.translation.set(joint); + } + } + + + protected void render( + ShapeRenderHelper renderer, QuadripedModel model + ) { + renderer.pushTransform().translate(translation); + applyTransform(renderer.pushTransform(), model); + renderable.render(renderer); + renderer.popTransform(); + renderer.popTransform(); + } + + protected abstract void applyTransform(Mat4 mat, QuadripedModel model); + } + + public static class Body extends BodyPart { + public Body(Renderable renderable) { + super(renderable, null); + } + + @Override + protected void applyTransform(Mat4 mat, QuadripedModel model) { + // Do nothing + } + } + + public static class Head extends BodyPart { + private final float maxYaw; + private final float maxPitch; + + public Head( + Renderable renderable, Vec3 joint, + double maxYawDegrees, double maxPitchDegrees + ) { + super(renderable, joint); + this.maxYaw = (float) toRadians(maxYawDegrees); + this.maxPitch = (float) toRadians(maxPitchDegrees); + } + + @Override + protected void applyTransform(Mat4 mat, QuadripedModel model) { + mat.rotateZ(model.headYaw).rotateY(model.headPitch); + } + } + + public static class Leg extends BodyPart { + private final float animationOffset; + + public Leg( + Renderable renderable, Vec3 joint, + float animationOffset + ) { + super(renderable, joint); + this.animationOffset = animationOffset; + } + + @Override + protected void applyTransform(Mat4 mat, QuadripedModel model) { + mat.rotateY(sin(model.walkingFrequency * model.walkingAnimationParameter + animationOffset) * model.walkingSwing * model.velocityCoeff); + } + } + + private final EntityData entity; + + private final Body body; + private final Head head; + private final Leg leftForeLeg, rightForeLeg; + private final Leg leftHindLeg, rightHindLeg; + + private final float scale; + + private float walkingAnimationParameter = 0; + private float velocityCoeff = 0; + private float velocity = 0; + + private float walkingFrequency = 0.15f; + private float walkingSwing = (float) toRadians(30); + + private float bodyYaw = Float.NaN; + private float headYaw; + private float headPitch; + + public QuadripedModel( + EntityData entity, + + Body body, Head head, + Leg leftForeLeg, Leg rightForeLeg, + Leg leftHindLeg, Leg rightHindLeg, + + float scale + ) { + this.entity = entity; + + this.body = body; + this.head = head; + this.leftForeLeg = leftForeLeg; + this.rightForeLeg = rightForeLeg; + this.leftHindLeg = leftHindLeg; + this.rightHindLeg = rightHindLeg; + + this.scale = scale; + } + + @Override + public void render(ShapeRenderHelper renderer) { + evaluateAngles(); + accountForVelocity(); + + renderer.pushTransform().scale(scale).rotateZ(bodyYaw); + body.render(renderer, this); + + head.render(renderer, this); + + leftForeLeg.render(renderer, this); + rightForeLeg.render(renderer, this); + leftHindLeg.render(renderer, this); + rightHindLeg.render(renderer, this); + renderer.popTransform(); + } + + private void evaluateAngles() { + float globalYaw = normalizeAngle(entity.getYaw()); + + if (Float.isNaN(bodyYaw)) { + bodyYaw = globalYaw; + headYaw = 0; + } else { + headYaw = normalizeAngle(globalYaw - bodyYaw); + + if (headYaw > +head.maxYaw) { + bodyYaw += headYaw - +head.maxYaw; + headYaw = +head.maxYaw; + } else if (headYaw < -head.maxYaw) { + bodyYaw += headYaw - -head.maxYaw; + headYaw = -head.maxYaw; + } + } + + bodyYaw = normalizeAngle(bodyYaw); + + headPitch = Glm.clamp( + entity.getPitch(), + -head.maxPitch, head.maxPitch + ); + } + + private void accountForVelocity() { + // TODO switch to world time + Vec3 horizontal = Vectors.grab3(); + horizontal.set(entity.getVelocity()); + horizontal.z = 0; + + velocity = (float) (horizontal.length()); + velocityCoeff = -1 / (velocity * 1000 + 1) + 1; + walkingAnimationParameter += velocity * GraphicsInterface.getFrameLength() * 1000; + + bodyYaw += velocityCoeff * normalizeAngle( + (float) (atan2(horizontal.y, horizontal.x) - bodyYaw) + ) * min(1, GraphicsInterface.getFrameLength() * 10); + Vectors.release(horizontal); + } + + private static float normalizeAngle(float x) { + final float half = (float) (PI); + final float full = (float) (2 * PI); + return ((x + half) % full + full) % full - half; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java index f1f2bb8..b67032d 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java @@ -20,12 +20,14 @@ package ru.windcorp.progressia.common.world; import static ru.windcorp.progressia.common.world.block.BlockFace.*; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; import com.google.common.collect.Lists; +import glm.vec._3.Vec3; import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.client.world.tile.TileLocation; import ru.windcorp.progressia.common.util.SizeLimitedList; @@ -34,6 +36,7 @@ import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.world.block.BlockData; import ru.windcorp.progressia.common.world.block.BlockDataRegistry; import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.tile.TileData; import ru.windcorp.progressia.common.world.tile.TileDataRegistry; @@ -55,6 +58,9 @@ public class ChunkData { BLOCK_FACE_COUNT ]; + private final List entities = + Collections.synchronizedList(new ArrayList<>()); + public ChunkData(int x, int y, int z, WorldData world) { this.position.set(x, y, z); this.world = world; @@ -123,6 +129,10 @@ public class ChunkData { } } } + + EntityData javapony = new EntityData("Test", "Javapony"); + javapony.setPosition(new Vec3(8, 12, 16.2f)); + getEntities().add(javapony); } public BlockData getBlock(Vec3i posInChunk) { @@ -216,6 +226,10 @@ public class ChunkData { face.getId(); } + public List getEntities() { + return entities; + } + private static void checkLocalCoordinates(Vec3i posInChunk) { if (!isInBounds(posInChunk)) { throw new IllegalArgumentException( @@ -279,6 +293,10 @@ public class ChunkData { } }); } + + public void forEachEntity(Consumer action) { + getEntities().forEach(action); + } public int getX() { return position.x; diff --git a/src/main/java/ru/windcorp/progressia/common/world/block/BlockFace.java b/src/main/java/ru/windcorp/progressia/common/world/block/BlockFace.java index b9ed2cc..f33f8d7 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/block/BlockFace.java +++ b/src/main/java/ru/windcorp/progressia/common/world/block/BlockFace.java @@ -18,6 +18,7 @@ package ru.windcorp.progressia.common.world.block; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import glm.vec._3.i.Vec3i; @@ -58,6 +59,21 @@ public final class BlockFace extends BlockRelation { b.counterFace = a; } + public static ImmutableMap mapToFaces( + E top, E bottom, + E north, E south, + E east, E west + ) { + return ImmutableMap.builderWithExpectedSize(6) + .put(TOP, top) + .put(BOTTOM, bottom) + .put(NORTH, north) + .put(SOUTH, south) + .put(EAST, east) + .put(WEST, west) + .build(); + } + private static int nextId = 0; private final int id; diff --git a/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java new file mode 100644 index 0000000..1748104 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java @@ -0,0 +1,62 @@ +package ru.windcorp.progressia.common.world.entity; + +import java.util.UUID; + +import glm.vec._2.Vec2; +import glm.vec._3.Vec3; +import ru.windcorp.progressia.common.util.Namespaced; + +public class EntityData extends Namespaced { + + private final Vec3 position = new Vec3(); + private final Vec3 velocity = new Vec3(); + + private final Vec2 direction = new Vec2(); + + private UUID uuid; + + public EntityData(String namespace, String name) { + super(namespace, name); + } + + public Vec3 getPosition() { + return position; + } + + public void setPosition(Vec3 position) { + this.position.set(position); + } + + public Vec3 getVelocity() { + return velocity; + } + + public void setVelocity(Vec3 velocity) { + this.velocity.set(velocity); + } + + public Vec2 getDirection() { + return direction; + } + + public void setDirection(Vec2 direction) { + this.direction.set(direction.x, direction.y); + } + + public float getYaw() { + return getDirection().x; + } + + public float getPitch() { + return getDirection().y; + } + + public UUID getUUID() { + return uuid; + } + + public void setUUID(UUID uuid) { + this.uuid = uuid; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/entity/EntityDataRegistry.java b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityDataRegistry.java new file mode 100644 index 0000000..8457541 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityDataRegistry.java @@ -0,0 +1,13 @@ +package ru.windcorp.progressia.common.world.entity; + +import ru.windcorp.progressia.common.util.NamespacedRegistry; + +public class EntityDataRegistry extends NamespacedRegistry { + + private static final EntityDataRegistry INSTANCE = new EntityDataRegistry(); + + public static EntityDataRegistry getInstance() { + return INSTANCE; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogic.java b/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogic.java new file mode 100644 index 0000000..c3c09e6 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogic.java @@ -0,0 +1,11 @@ +package ru.windcorp.progressia.server.world.entity; + +import ru.windcorp.progressia.common.util.Namespaced; + +public class EntityLogic extends Namespaced { + + public EntityLogic(String namespace, String name) { + super(namespace, name); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogicRegistry.java b/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogicRegistry.java new file mode 100644 index 0000000..21e37f9 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogicRegistry.java @@ -0,0 +1,14 @@ +package ru.windcorp.progressia.server.world.entity; + +import ru.windcorp.progressia.common.util.NamespacedRegistry; + +public class EntityLogicRegistry extends NamespacedRegistry { + + private static final EntityLogicRegistry INSTANCE = + new EntityLogicRegistry(); + + public static EntityLogicRegistry getInstance() { + return INSTANCE; + } + +} diff --git a/src/main/resources/assets/textures/entities/javapony.png b/src/main/resources/assets/textures/entities/javapony.png new file mode 100644 index 0000000000000000000000000000000000000000..59ac59ce0e7f3f3a1a05978eda0dc2d90f1d984a GIT binary patch literal 3347 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