diff --git a/modules/io.github.xenfork.freeworld.client/src/main/java/io/github/xenfork/freeworld/client/Freeworld.java b/modules/io.github.xenfork.freeworld.client/src/main/java/io/github/xenfork/freeworld/client/Freeworld.java index 39c3ebd..f349d95 100644 --- a/modules/io.github.xenfork.freeworld.client/src/main/java/io/github/xenfork/freeworld/client/Freeworld.java +++ b/modules/io.github.xenfork.freeworld.client/src/main/java/io/github/xenfork/freeworld/client/Freeworld.java @@ -20,6 +20,7 @@ import io.github.xenfork.freeworld.world.block.BlockTypes; import io.github.xenfork.freeworld.world.entity.Entity; import io.github.xenfork.freeworld.world.entity.EntityTypes; +import org.joml.Vector2d; import org.slf4j.Logger; import overrun.marshal.Unmarshal; import overrungl.glfw.GLFW; @@ -107,10 +108,10 @@ public void start() { EntityTypes.bootstrap(); BuiltinRegistries.ENTITY_TYPE.freeze(); - camera.setPosition(1.5, 16.0, 1.5); - world = new World("New world"); player = new Entity(world, UUID.randomUUID(), EntityTypes.PLAYER); + player.position().position().set(1.5, 16.0, 1.5); + world.entities.add(player); initGL(); run(); @@ -141,7 +142,19 @@ private void onCursorPos(double x, double y) { cursorDeltaX = x - cursorX; cursorDeltaY = y - cursorY; if (disableCursor) { - camera.rotate(-cursorDeltaY * MOUSE_SENSITIVITY, -cursorDeltaX * MOUSE_SENSITIVITY); + final double pitch = -cursorDeltaY * MOUSE_SENSITIVITY; + final double yaw = -cursorDeltaX * MOUSE_SENSITIVITY; + final Vector2d rotation = player.rotation().rotation(); + final double updateX = Math.clamp(rotation.x() + pitch, -90.0, 90.0); + double updateY = rotation.y() + yaw; + + if (updateY < 0.0) { + updateY += 360.0; + } else if (updateY >= 360.0) { + updateY -= 360.0; + } + + rotation.set(updateX, updateY); } cursorX = x; cursorY = y; @@ -149,6 +162,7 @@ private void onCursorPos(double x, double y) { private void tick() { camera.preUpdate(); + world.tick(); final double speed = 0.5; double xo = 0.0; double yo = 0.0; @@ -159,7 +173,7 @@ private void tick() { if (glfw.getKey(window, GLFW.KEY_D) == GLFW.PRESS) xo += 1.0; if (glfw.getKey(window, GLFW.KEY_LEFT_SHIFT) == GLFW.PRESS) yo -= 1.0; if (glfw.getKey(window, GLFW.KEY_SPACE) == GLFW.PRESS) yo += 1.0; - camera.moveRelative(xo, yo, zo, speed); + player.velocity().velocity().set(xo, yo, zo).mul(speed); } private void initGL() { @@ -227,6 +241,10 @@ public World world() { return world; } + public Entity player() { + return player; + } + public static Freeworld getInstance() { return INSTANCE; } diff --git a/modules/io.github.xenfork.freeworld.client/src/main/java/io/github/xenfork/freeworld/client/render/Camera.java b/modules/io.github.xenfork.freeworld.client/src/main/java/io/github/xenfork/freeworld/client/render/Camera.java index af0e7af..ad51ed9 100644 --- a/modules/io.github.xenfork.freeworld.client/src/main/java/io/github/xenfork/freeworld/client/render/Camera.java +++ b/modules/io.github.xenfork.freeworld.client/src/main/java/io/github/xenfork/freeworld/client/render/Camera.java @@ -10,6 +10,10 @@ package io.github.xenfork.freeworld.client.render; +import io.github.xenfork.freeworld.world.entity.Entity; +import io.github.xenfork.freeworld.world.entity.component.PositionComponent; +import io.github.xenfork.freeworld.world.entity.component.RotationXYComponent; +import io.github.xenfork.freeworld.world.entity.system.EntitySystem; import org.joml.*; import java.lang.Math; @@ -25,42 +29,11 @@ public final class Camera { private final Vector2d rotation = new Vector2d(); private final Matrix4f viewMatrix = new Matrix4f(); - public void setPosition(double x, double y, double z) { - position.set(x, y, z); - } - - public void move(double x, double y, double z) { - position.add(x, y, z); - } - - public void moveRelative(double x, double y, double z, double speed) { - final double dest = x * x + z * z; - if (dest > 0.001) { - final double sqrt = Math.sqrt(dest); - final double invSqrt = 1.0 / sqrt; - final double yaw = Math.toRadians(rotation.y()); - - final double normalX = x * invSqrt * speed; - final double normalZ = z * invSqrt * speed; - final double sin = Math.sin(yaw); - final double cos = Math.cos(yaw); - position.x += normalX * cos + normalZ * sin; - position.z += normalZ * cos - normalX * sin; + public void moveToEntity(Entity entity) { + if (EntitySystem.filter(entity, PositionComponent.ID, RotationXYComponent.ID)) { + position.set(entity.position().position()); + rotation.set(entity.rotation().rotation()); } - position.y += y * speed; - } - - public void rotate(double pitch, double yaw) { - final double updateX = Math.clamp(rotation.x() + pitch, -90.0, 90.0); - double updateY = rotation.y() + yaw; - - if (updateY < 0.0) { - updateY += 360.0; - } else if (updateY >= 360.0) { - updateY -= 360.0; - } - - rotation.set(updateX, updateY); } public void preUpdate() { diff --git a/modules/io.github.xenfork.freeworld.client/src/main/java/io/github/xenfork/freeworld/client/render/GameRenderer.java b/modules/io.github.xenfork.freeworld.client/src/main/java/io/github/xenfork/freeworld/client/render/GameRenderer.java index b8b6098..6663b6e 100644 --- a/modules/io.github.xenfork.freeworld.client/src/main/java/io/github/xenfork/freeworld/client/render/GameRenderer.java +++ b/modules/io.github.xenfork.freeworld.client/src/main/java/io/github/xenfork/freeworld/client/render/GameRenderer.java @@ -103,6 +103,7 @@ public void render(GLStateMgr gl, double partialTick) { 1000.0f ); final Camera camera = client.camera(); + camera.moveToEntity(client.player()); camera.updateLerp(partialTick); camera.updateViewMatrix(); projectionView.mul(camera.viewMatrix()); diff --git a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/util/MathUtil.java b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/util/MathUtil.java new file mode 100644 index 0000000..cad2c93 --- /dev/null +++ b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/util/MathUtil.java @@ -0,0 +1,43 @@ +/* + * freeworld - 3D sandbox game + * Copyright (C) 2024 XenFork Union + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +package io.github.xenfork.freeworld.util; + +import org.joml.Vector3d; + +/** + * @author squid233 + * @since 0.1.0 + */ +public final class MathUtil { + private MathUtil() { + } + + public static Vector3d moveRelative(double x, double y, double z, double yawDegrees, Vector3d dest) { + final double dst = x * x + z * z; + double moveX = 0.0; + double moveY = 0.0; + double moveZ = 0.0; + if (dst > 0.001) { + final double sqrt = Math.sqrt(dst); + final double invSqrt = 1.0 / sqrt; + final double yaw = Math.toRadians(yawDegrees); + + final double normalX = x * invSqrt; + final double normalZ = z * invSqrt; + final double sin = Math.sin(yaw); + final double cos = Math.cos(yaw); + moveX += normalX * cos + normalZ * sin; + moveZ += normalZ * cos - normalX * sin; + } + moveY += y; + return dest.set(moveX, moveY, moveZ); + } +} diff --git a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/World.java b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/World.java index b2b01fc..ae37025 100644 --- a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/World.java +++ b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/World.java @@ -11,7 +11,10 @@ package io.github.xenfork.freeworld.world; import io.github.xenfork.freeworld.world.chunk.Chunk; +import io.github.xenfork.freeworld.world.entity.Entity; +import io.github.xenfork.freeworld.world.entity.system.MotionSystem; +import java.util.ArrayList; import java.util.List; /** @@ -24,11 +27,17 @@ public final class World { public final Chunk c2 = new Chunk(this, 0, 0, 1); public final Chunk c3 = new Chunk(this, 1, 0, 1); public final List chunks = List.of(c0, c1, c2, c3); + public final List entities = new ArrayList<>(); + public final MotionSystem motionSystem = new MotionSystem(); public World(String name) { chunks.forEach(Chunk::generateTerrain); } + public void tick() { + motionSystem.process(entities); + } + public Chunk createChunk(int x, int y, int z) { return new Chunk(this, x, y, z); } diff --git a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/Entity.java b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/Entity.java index 1651abf..8d165a0 100644 --- a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/Entity.java +++ b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/Entity.java @@ -10,13 +10,13 @@ package io.github.xenfork.freeworld.world.entity; +import io.github.xenfork.freeworld.core.Identifier; import io.github.xenfork.freeworld.world.World; -import io.github.xenfork.freeworld.world.entity.component.EntityComponent; -import io.github.xenfork.freeworld.world.entity.component.PositionComponent; -import io.github.xenfork.freeworld.world.entity.component.VelocityComponent; +import io.github.xenfork.freeworld.world.entity.component.*; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.UUID; /** @@ -27,34 +27,45 @@ public final class Entity { private final World world; private final UUID uuid; private final EntityType entityType; - private final Map componentMap = new HashMap<>(); + private final Map componentMap = new HashMap<>(); public Entity(World world, UUID uuid, EntityType entityType) { this.world = world; this.uuid = uuid; this.entityType = entityType; - entityType.defaultComponents().forEach(this::addComponent); + for (var supplier : entityType.defaultComponents()) { + addComponent(supplier.get()); + } } public void addComponent(EntityComponent component) { - final String name = component.componentName(); - if (componentMap.containsKey(name)) { + Objects.requireNonNull(component); + final Identifier id = component.componentId(); + if (componentMap.containsKey(id)) { return; } - componentMap.put(name, component); + componentMap.put(id, component); } @SuppressWarnings("unchecked") - public T getComponent(String name) { - return (T) componentMap.get(name); + public T getComponent(Identifier id) { + return (T) componentMap.get(id); + } + + public boolean hasComponent(Identifier id) { + return componentMap.containsKey(id); } public PositionComponent position() { - return getComponent(PositionComponent.NAME); + return getComponent(PositionComponent.ID); + } + + public RotationXYComponent rotation() { + return getComponent(RotationXYComponent.ID); } public VelocityComponent velocity() { - return getComponent(VelocityComponent.NAME); + return getComponent(VelocityComponent.ID); } public World world() { diff --git a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/EntityType.java b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/EntityType.java index 541501c..5e3cf42 100644 --- a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/EntityType.java +++ b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/EntityType.java @@ -13,6 +13,7 @@ import io.github.xenfork.freeworld.world.entity.component.EntityComponent; import java.util.List; +import java.util.function.Supplier; /** * @author squid233 @@ -20,13 +21,13 @@ */ @SuppressWarnings("ClassCanBeRecord") public final class EntityType { - private final List defaultComponents; + private final List> defaultComponents; - public EntityType(List defaultComponents) { + public EntityType(List> defaultComponents) { this.defaultComponents = defaultComponents; } - public List defaultComponents() { + public List> defaultComponents() { return defaultComponents; } } diff --git a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/EntityTypes.java b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/EntityTypes.java index 80fbdc7..ef35839 100644 --- a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/EntityTypes.java +++ b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/EntityTypes.java @@ -14,6 +14,7 @@ import io.github.xenfork.freeworld.core.registry.BuiltinRegistries; import io.github.xenfork.freeworld.core.registry.Registry; import io.github.xenfork.freeworld.world.entity.component.PositionComponent; +import io.github.xenfork.freeworld.world.entity.component.RotationXYComponent; import io.github.xenfork.freeworld.world.entity.component.VelocityComponent; import java.util.List; @@ -23,7 +24,15 @@ * @since 0.1.0 */ public final class EntityTypes { - public static final EntityType PLAYER = register(0, "player", new EntityType(List.of(new PositionComponent(), new VelocityComponent()))); + public static final EntityType PLAYER = register(0, "player", + new EntityType(List.of( + PositionComponent::new, + RotationXYComponent::new, + VelocityComponent::new + ))); + + private EntityTypes() { + } private static EntityType register(int rawId, String name, EntityType entityType) { return Registry.register(BuiltinRegistries.ENTITY_TYPE, Identifier.ofBuiltin(name), rawId, entityType); diff --git a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/component/EntityComponent.java b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/component/EntityComponent.java index 8415a9b..b798fbf 100644 --- a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/component/EntityComponent.java +++ b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/component/EntityComponent.java @@ -10,15 +10,15 @@ package io.github.xenfork.freeworld.world.entity.component; +import io.github.xenfork.freeworld.core.Identifier; + /** * @author squid233 * @since 0.1.0 */ -public sealed interface EntityComponent permits - PositionComponent, - VelocityComponent { +public sealed interface EntityComponent permits PositionComponent, RotationXYComponent, VelocityComponent { /** - * {@return a unique name of this component} + * {@return a unique identifier of this component} */ - String componentName(); + Identifier componentId(); } diff --git a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/component/PositionComponent.java b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/component/PositionComponent.java index a5d4e28..20a1f7b 100644 --- a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/component/PositionComponent.java +++ b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/component/PositionComponent.java @@ -10,6 +10,7 @@ package io.github.xenfork.freeworld.world.entity.component; +import io.github.xenfork.freeworld.core.Identifier; import org.joml.Vector3d; /** @@ -17,14 +18,14 @@ * @since 0.1.0 */ public record PositionComponent(Vector3d position) implements EntityComponent { - public static final String NAME = "position"; + public static final Identifier ID = Identifier.ofBuiltin("position"); public PositionComponent() { this(new Vector3d()); } @Override - public String componentName() { - return NAME; + public Identifier componentId() { + return ID; } } diff --git a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/component/RotationXYComponent.java b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/component/RotationXYComponent.java new file mode 100644 index 0000000..1b8fe85 --- /dev/null +++ b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/component/RotationXYComponent.java @@ -0,0 +1,31 @@ +/* + * freeworld - 3D sandbox game + * Copyright (C) 2024 XenFork Union + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +package io.github.xenfork.freeworld.world.entity.component; + +import io.github.xenfork.freeworld.core.Identifier; +import org.joml.Vector2d; + +/** + * @author squid233 + * @since 0.1.0 + */ +public record RotationXYComponent(Vector2d rotation) implements EntityComponent { + public static final Identifier ID = Identifier.ofBuiltin("rotation"); + + public RotationXYComponent() { + this(new Vector2d()); + } + + @Override + public Identifier componentId() { + return ID; + } +} diff --git a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/component/VelocityComponent.java b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/component/VelocityComponent.java index 7158097..49d72cc 100644 --- a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/component/VelocityComponent.java +++ b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/component/VelocityComponent.java @@ -10,6 +10,7 @@ package io.github.xenfork.freeworld.world.entity.component; +import io.github.xenfork.freeworld.core.Identifier; import org.joml.Vector3d; /** @@ -17,14 +18,14 @@ * @since 0.1.0 */ public record VelocityComponent(Vector3d velocity) implements EntityComponent { - public static final String NAME = "velocity"; + public static final Identifier ID = Identifier.ofBuiltin("velocity"); public VelocityComponent() { this(new Vector3d()); } @Override - public String componentName() { - return NAME; + public Identifier componentId() { + return ID; } } diff --git a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/system/EntitySystem.java b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/system/EntitySystem.java new file mode 100644 index 0000000..f4bb3ce --- /dev/null +++ b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/system/EntitySystem.java @@ -0,0 +1,35 @@ +/* + * freeworld - 3D sandbox game + * Copyright (C) 2024 XenFork Union + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +package io.github.xenfork.freeworld.world.entity.system; + +import io.github.xenfork.freeworld.core.Identifier; +import io.github.xenfork.freeworld.world.entity.Entity; + +import java.util.List; + +/** + * @author squid233 + * @since 0.1.0 + */ +public interface EntitySystem { + void process(List entities); + + static boolean filter(Entity entity, Identifier... componentIds) { + if (entity == null) return false; + + for (Identifier id : componentIds) { + if (entity.hasComponent(id)) { + return true; + } + } + return false; + } +} diff --git a/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/system/MotionSystem.java b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/system/MotionSystem.java new file mode 100644 index 0000000..5fef380 --- /dev/null +++ b/modules/io.github.xenfork.freeworld.core/src/main/java/io/github/xenfork/freeworld/world/entity/system/MotionSystem.java @@ -0,0 +1,43 @@ +/* + * freeworld - 3D sandbox game + * Copyright (C) 2024 XenFork Union + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +package io.github.xenfork.freeworld.world.entity.system; + +import io.github.xenfork.freeworld.util.MathUtil; +import io.github.xenfork.freeworld.world.entity.Entity; +import io.github.xenfork.freeworld.world.entity.component.PositionComponent; +import io.github.xenfork.freeworld.world.entity.component.RotationXYComponent; +import io.github.xenfork.freeworld.world.entity.component.VelocityComponent; +import org.joml.Vector2d; +import org.joml.Vector3d; + +import java.util.List; + +/** + * @author squid233 + * @since 0.1.0 + */ +public final class MotionSystem implements EntitySystem { + private final Vector3d movement = new Vector3d(); + + @Override + public void process(List entities) { + for (Entity entity : entities) { + if (EntitySystem.filter(entity, PositionComponent.ID, RotationXYComponent.ID, VelocityComponent.ID)) { + final Vector3d position = entity.position().position(); + final Vector2d rotation = entity.rotation().rotation(); + final Vector3d velocity = entity.velocity().velocity(); + MathUtil.moveRelative(velocity.x(), velocity.y(), velocity.z(), rotation.y(), movement); + position.add(movement); + velocity.mul(0.5, 0.5, 0.5); + } + } + } +} diff --git a/modules/io.github.xenfork.freeworld.core/src/main/java/module-info.java b/modules/io.github.xenfork.freeworld.core/src/main/java/module-info.java index 77d9ebb..96317f5 100644 --- a/modules/io.github.xenfork.freeworld.core/src/main/java/module-info.java +++ b/modules/io.github.xenfork.freeworld.core/src/main/java/module-info.java @@ -23,6 +23,7 @@ exports io.github.xenfork.freeworld.world.chunk; exports io.github.xenfork.freeworld.world.entity; exports io.github.xenfork.freeworld.world.entity.component; + exports io.github.xenfork.freeworld.world.entity.system; requires ch.qos.logback.core; requires ch.qos.logback.classic;