diff --git a/common/src/main/java/org/valkyrienskies/mod/mixin/client/MixinCamera.java b/common/src/main/java/org/valkyrienskies/mod/mixin/client/MixinCamera.java index d8ccabe22..edd303059 100644 --- a/common/src/main/java/org/valkyrienskies/mod/mixin/client/MixinCamera.java +++ b/common/src/main/java/org/valkyrienskies/mod/mixin/client/MixinCamera.java @@ -128,6 +128,19 @@ private void setRotationWithShipTransform(final float yaw, final float pitch, fi this.left.transform(this.rotation); } + @Unique + public void addRotation(final Quaterniondc quaterniondc) { + this.xRot += (float) quaterniondc.x(); + this.yRot += (float) quaterniondc.y(); + VectorConversionsMCKt.set(this.rotation, quaterniondc); + this.forwards.set(0.0F, 0.0F, 1.0F); + this.forwards.transform(this.rotation); + this.up.set(0.0F, 1.0F, 0.0F); + this.up.transform(this.rotation); + this.left.set(1.0F, 0.0F, 0.0F); + this.left.transform(this.rotation); + } + /** * When in third person, do not block the camera on the ship the player is mounted to */ diff --git a/common/src/main/java/org/valkyrienskies/mod/mixin/client/player/MixinPlayer.java b/common/src/main/java/org/valkyrienskies/mod/mixin/client/player/MixinPlayer.java index c572558ad..504d56995 100644 --- a/common/src/main/java/org/valkyrienskies/mod/mixin/client/player/MixinPlayer.java +++ b/common/src/main/java/org/valkyrienskies/mod/mixin/client/player/MixinPlayer.java @@ -12,6 +12,7 @@ import net.minecraft.world.phys.AABB; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.valkyrienskies.mod.common.entity.ShipMountingEntity; @@ -40,4 +41,18 @@ private List redirectAiStep(final Level instance, final Entity entity, f return getEntities.call(instance, entity, aabb); } + + @Unique + public void setPostion(float x, float y, float z) { + this.xo = x; + this.yo = y; + this.zo = z; + } + + @Unique + public void offsetPostion(float x, float y, float z) { + this.xo += x; + this.yo += y; + this.zo += z; + } } diff --git a/common/src/main/java/org/valkyrienskies/mod/mixin/client/renderer/MixinGameRenderer.java b/common/src/main/java/org/valkyrienskies/mod/mixin/client/renderer/MixinGameRenderer.java index 22578544f..70ceb45bf 100644 --- a/common/src/main/java/org/valkyrienskies/mod/mixin/client/renderer/MixinGameRenderer.java +++ b/common/src/main/java/org/valkyrienskies/mod/mixin/client/renderer/MixinGameRenderer.java @@ -29,6 +29,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.valkyrienskies.core.api.ships.ClientShip; import org.valkyrienskies.core.apigame.world.ClientShipWorldCore; +import org.valkyrienskies.mod.api.PositionGravity; import org.valkyrienskies.mod.client.IVSCamera; import org.valkyrienskies.mod.common.IShipObjectWorldClientProvider; import org.valkyrienskies.mod.common.VSGameUtilsKt; @@ -238,69 +239,95 @@ private void postRender(final float tickDelta, final long startTime, final boole target = "Lnet/minecraft/client/renderer/LevelRenderer;prepareCullFrustum(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/world/phys/Vec3;Lcom/mojang/math/Matrix4f;)V" ) ) - private void setupCameraWithMountedShip(final LevelRenderer instance, final PoseStack ignore, final Vec3 vec3, + private void moveCamera( final LevelRenderer instance, final PoseStack ignore, final Vec3 vec3, final Matrix4f matrix4f, final Operation prepareCullFrustum, final float partialTicks, - final long finishTimeNano, final PoseStack matrixStack) { + final long finishTimeNano, final PoseStack matrixStack){ final ClientLevel clientLevel = minecraft.level; final Entity player = minecraft.player; - if (clientLevel == null || player == null) { - prepareCullFrustum.call(instance, matrixStack, vec3, matrix4f); - return; - } + final Camera camera = this.mainCamera; final ClientShip playerShipMountedTo = VSGameUtilsKt.getShipObjectEntityMountedTo(clientLevel, player); - if (playerShipMountedTo == null) { - prepareCullFrustum.call(instance, matrixStack, vec3, matrix4f); - return; - } - final Entity playerVehicle = player.getVehicle(); - if (playerVehicle == null) { + + if (clientLevel == null || player == null || camera == null) { prepareCullFrustum.call(instance, matrixStack, vec3, matrix4f); return; } - // Update [matrixStack] to mount the camera to the ship - final Vector3dc inShipPos = - VSGameUtilsKt.getPassengerPos(playerVehicle, player.getMyRidingOffset(), partialTicks); + if (playerShipMountedTo == null) { + // if you aren't mounted on a ship + final Vector3d force = new PositionGravity().playerEffect(minecraft.player); + if (force.length() >= 10){ + final Quaternion forceDirection = + new Quaternion(0.0F, (float) force.x, (float) force.y, (float) force.z); - final Camera camera = this.mainCamera; - if (camera == null) { - prepareCullFrustum.call(instance, matrixStack, vec3, matrix4f); - return; - } + ((IVSCamera) camera).moveCamera( + this.minecraft.level, + this.minecraft.getCameraEntity() == null ? this.minecraft.player : this.minecraft.getCameraEntity(), + !this.minecraft.options.getCameraType().isFirstPerson(), + this.minecraft.options.getCameraType().isMirrored(), + partialTicks + ); + + matrixStack.mulPose(forceDirection); - ((IVSCamera) camera).setupWithShipMounted( - this.minecraft.level, - this.minecraft.getCameraEntity() == null ? this.minecraft.player : this.minecraft.getCameraEntity(), - !this.minecraft.options.getCameraType().isFirstPerson(), - this.minecraft.options.getCameraType().isMirrored(), - partialTicks, - playerShipMountedTo, - inShipPos - ); + // We also need to recompute [inverseViewRotationMatrix] after updating [matrixStack] + { + final Matrix3f matrix3f = matrixStack.last().normal().copy(); + if (matrix3f.invert()) { + RenderSystem.setInverseViewRotationMatrix(matrix3f); + } + } - // Apply the ship render transform to [matrixStack] - final Quaternion invShipRenderRotation = VectorConversionsMCKt.toMinecraft( - playerShipMountedTo.getRenderTransform().getShipToWorldRotation().conjugate(new Quaterniond())); - matrixStack.mulPose(invShipRenderRotation); + final double fov = this.getFov(camera, partialTicks, true); - // We also need to recompute [inverseViewRotationMatrix] after updating [matrixStack] - { - final Matrix3f matrix3f = matrixStack.last().normal().copy(); - if (matrix3f.invert()) { - RenderSystem.setInverseViewRotationMatrix(matrix3f); + prepareCullFrustum.call(instance, matrixStack, camera.getPosition(), + this.getProjectionMatrix(Math.max(fov, this.minecraft.options.fov))); + } + } else { + // if you're mounted on a ship + final Entity playerVehicle = player.getVehicle(); + if (playerVehicle == null) { + prepareCullFrustum.call(instance, matrixStack, vec3, matrix4f); + return; } - } - // Camera FOV changes based on the position of the camera, so recompute FOV to account for the change of camera - // position. - final double fov = this.getFov(camera, partialTicks, true); + // Update [matrixStack] to mount the camera to the ship + final Vector3dc inShipPos = + VSGameUtilsKt.getPassengerPos(playerVehicle, player.getMyRidingOffset(), partialTicks); - // Use [camera.getPosition()] instead of [vec3] because mounting the player to the ship has changed the camera - // position. - prepareCullFrustum.call(instance, matrixStack, camera.getPosition(), - this.getProjectionMatrix(Math.max(fov, this.minecraft.options.fov))); + ((IVSCamera) camera).setupWithShipMounted( + this.minecraft.level, + this.minecraft.getCameraEntity() == null ? this.minecraft.player : this.minecraft.getCameraEntity(), + !this.minecraft.options.getCameraType().isFirstPerson(), + this.minecraft.options.getCameraType().isMirrored(), + partialTicks, + playerShipMountedTo, + inShipPos + ); + + // Apply the ship render transform to [matrixStack] + final Quaternion invShipRenderRotation = VectorConversionsMCKt.toMinecraft( + playerShipMountedTo.getRenderTransform().getShipToWorldRotation().conjugate(new Quaterniond())); + matrixStack.mulPose(invShipRenderRotation); + + // We also need to recompute [inverseViewRotationMatrix] after updating [matrixStack] + { + final Matrix3f matrix3f = matrixStack.last().normal().copy(); + if (matrix3f.invert()) { + RenderSystem.setInverseViewRotationMatrix(matrix3f); + } + } + + // Camera FOV changes based on the position of the camera, so recompute FOV to account for the change of camera + // position. + final double fov = this.getFov(camera, partialTicks, true); + + // Use [camera.getPosition()] instead of [vec3] because mounting the player to the ship has changed the camera + // position. + prepareCullFrustum.call(instance, matrixStack, camera.getPosition(), + this.getProjectionMatrix(Math.max(fov, this.minecraft.options.fov))); + } } // endregion } diff --git a/common/src/main/kotlin/org/valkyrienskies/mod/api/PositionGravity.kt b/common/src/main/kotlin/org/valkyrienskies/mod/api/PositionGravity.kt new file mode 100644 index 000000000..04480ffc5 --- /dev/null +++ b/common/src/main/kotlin/org/valkyrienskies/mod/api/PositionGravity.kt @@ -0,0 +1,123 @@ +package org.valkyrienskies.mod.api + +import net.minecraft.world.entity.LivingEntity +import net.minecraft.world.entity.player.Player +import net.minecraft.world.phys.Vec3 +import org.joml.Vector3d +import org.joml.Vector3dc +import org.valkyrienskies.core.api.ships.ServerShip +import org.valkyrienskies.core.util.x +import org.valkyrienskies.core.util.y +import org.valkyrienskies.core.util.z +import org.valkyrienskies.mod.common.entity.MobWeights + +class PositionGravity { + private lateinit var instances: Map + fun PositionGravity( + pos: Vector3dc, + mass: Double + ) { + instances + Pair(pos, mass) + } + + fun removePosition( + pos: Vector3dc + ) { + instances - pos + } + + fun entityEffect( + entity: LivingEntity + ): Vector3d { + lateinit var forces: List + instances.forEach{ + val distance: Double = + entity.getPosition(0.0F).distanceTo(Vec3(it.key!!.x, it.key!!.x, it.key!!.x)) + Vector3d( + force( + entity.deltaMovement.x, distance, + MobWeights.NORMAL_PLAYER.weight, it.value, + entity.position().x - it.key!!.x + ), + force( + entity.deltaMovement.y, distance, + MobWeights.NORMAL_PLAYER.weight, it.value, + entity.position().y - it.key!!.y + ), + force( + entity.deltaMovement.z, distance, + MobWeights.NORMAL_PLAYER.weight, it.value, + entity.position().z - it.key!!.z + ) + ) + } + lateinit var totalForce: Vector3d + forces.forEach { + totalForce.add(it.x, it.y, it.z) + } + return totalForce + } + + fun playerEffect( + player: Player + ): Vector3d { + lateinit var forces: List + instances.forEach{ + val distance: Double = + player.getPosition(0.0F).distanceTo(Vec3(it.key!!.x, it.key!!.x, it.key!!.x)) + Vector3d( + force( + player.deltaMovement.x, distance, + MobWeights.NORMAL_PLAYER.weight, it.value, + player.position().x - it.key!!.x + ), + force( + player.deltaMovement.y, distance, + MobWeights.NORMAL_PLAYER.weight, it.value, + player.position().y - it.key!!.y + ), + force( + player.deltaMovement.z, distance, + MobWeights.NORMAL_PLAYER.weight, it.value, + player.position().z - it.key!!.z + ) + ) + } + lateinit var totalForce: Vector3d + forces.forEach { + totalForce.add(it.x, it.y, it.z) + } + return totalForce + } + fun shipEffect( + ship: ServerShip + ): Vector3d { + lateinit var forces: List + instances.forEach { + val distance: Double = ship.inertiaData.centerOfMassInShip.distance(it.key) + forces + Vector3d( + force( + ship.velocity.x, distance, ship.inertiaData.mass, it.value, + ship.inertiaData.centerOfMassInShip.x - it.key!!.x + ), + force( + ship.velocity.y, distance, ship.inertiaData.mass, it.value, + ship.inertiaData.centerOfMassInShip.y - it.key!!.y + ), + force( + ship.velocity.z, distance, ship.inertiaData.mass, it.value, + ship.inertiaData.centerOfMassInShip.z - it.key!!.z + ) + ) + } + lateinit var totalForce: Vector3d + forces.forEach { + totalForce.add(it.x, it.y, it.z) + } + return totalForce + } + + private fun force(axis: Double, distance: Double, mass1: Double, mass2: Double, axisDistance: Double): Double { + return axis - ((mass1 * axisDistance) / distance) + } +} diff --git a/common/src/main/kotlin/org/valkyrienskies/mod/api/math/EaseFloat.kt b/common/src/main/kotlin/org/valkyrienskies/mod/api/math/EaseFloat.kt new file mode 100644 index 000000000..8d6bccb83 --- /dev/null +++ b/common/src/main/kotlin/org/valkyrienskies/mod/api/math/EaseFloat.kt @@ -0,0 +1,89 @@ +package org.valkyrienskies.mod.api.math + +import org.valkyrienskies.mod.api.math.EaseFloat.EaseType.EASE_IN_EXPO +import org.valkyrienskies.mod.api.math.EaseFloat.EaseType.EASE_IN_OUT_EXPO +import org.valkyrienskies.mod.api.math.EaseFloat.EaseType.EASE_IN_OUT_QUAD +import org.valkyrienskies.mod.api.math.EaseFloat.EaseType.EASE_IN_OUT_SINE +import org.valkyrienskies.mod.api.math.EaseFloat.EaseType.EASE_IN_QUAD +import org.valkyrienskies.mod.api.math.EaseFloat.EaseType.EASE_IN_SINE +import org.valkyrienskies.mod.api.math.EaseFloat.EaseType.EASE_OUT_EXPO +import org.valkyrienskies.mod.api.math.EaseFloat.EaseType.EASE_OUT_QUAD +import org.valkyrienskies.mod.api.math.EaseFloat.EaseType.EASE_OUT_SINE + +class EaseFloat { + private lateinit var type: EaseType + private var speed: Float = 0.0f + private var target: Float = 0.0f + private var current: Float = 0.0f + private var start: Float = 0.0f + private var eased: Float = 0.0f + private var done: Boolean = false + + fun EaseFloat(speed: Float, type: EaseType) { + this.type = type + this.speed = speed + } + + fun tick() { + var mult: Float + if (start < target && current < target) { + mult = (target - start) + + } else if (start > target && current > target) { + mult = (start - target) + + } else { + done = true + return + } + done = false + val percent = current / mult + + current += speed + + when (type) { + EASE_IN_SINE -> eased = EaseHelper.easeInSine(percent) * mult + EASE_OUT_SINE -> eased = EaseHelper.easeOutSine(percent) * mult + EASE_IN_OUT_SINE -> eased = EaseHelper.easeInOutSine(percent) * mult + EASE_IN_QUAD -> eased = EaseHelper.easeInQuad(percent) * mult + EASE_OUT_QUAD -> eased = EaseHelper.easeOutQuad(percent) * mult + EASE_IN_OUT_QUAD -> eased = EaseHelper.easeInOutQuad(percent) * mult + EASE_IN_EXPO -> eased = EaseHelper.easeInExpo(percent) * mult + EASE_OUT_EXPO -> eased = EaseHelper.easeOutExpo(percent) * mult + EASE_IN_OUT_EXPO -> eased = EaseHelper.easeInOutExpo(percent) * mult + else -> throw RuntimeException() + } + + } + + fun setTarget(target: Float) { + done = false + + this.target = target + this.start = this.current + } + + fun setSpeed(speed: Float) { + this.speed = speed + } + + fun isDone(): Boolean { + return done + } + + fun getEased(): Float { + return eased + } + + enum class EaseType { + EASE_IN_SINE, + EASE_OUT_SINE, + EASE_IN_OUT_SINE, + EASE_IN_QUAD, + EASE_OUT_QUAD, + EASE_IN_OUT_QUAD, + EASE_IN_EXPO, + EASE_OUT_EXPO, + EASE_IN_OUT_EXPO + } +} diff --git a/common/src/main/kotlin/org/valkyrienskies/mod/api/math/EaseHelper.kt b/common/src/main/kotlin/org/valkyrienskies/mod/api/math/EaseHelper.kt new file mode 100644 index 000000000..3231c89eb --- /dev/null +++ b/common/src/main/kotlin/org/valkyrienskies/mod/api/math/EaseHelper.kt @@ -0,0 +1,72 @@ +package org.valkyrienskies.mod.api.math + +import kotlin.math.pow + +object EaseHelper { + /** + * Ease In Sine + Ease Out Sine + Ease In/Out Sine + */ + //Sine In + fun easeInSine(x: Float): Float { + return (1 - Math.cos(x * Math.PI / 2)).toFloat() + } + + //Sine Out + fun easeOutSine(x: Float): Float { + return Math.sin(x * Math.PI / 2).toFloat() + } + + //Sine In/Out + fun easeInOutSine(x: Float): Float { + return (-(Math.cos(Math.PI * x) - 1) / 2).toFloat() + } + + /** + * Ease In Quad + Ease Out Quad + Ease In/Out Quad + */ + //Quad In + fun easeInQuad(x: Float): Float { + return x * x + } + + //Quad Out + fun easeOutQuad(x: Float): Float { + return 1 - (1 - x) * (1 - x) + } + + //Quad In/Out + fun easeInOutQuad(x: Float): Float { + return if (x < 0.5) 2 * x * x else (1 - Math.pow((-2 * x + 2).toDouble(), 2.0) / 2).toFloat() + } + + /** + * Ease In Exponential + Ease Out Exponential + Ease In/Out Exponential + */ + + //Exponential In + fun easeInExpo(x: Float): Float { + return if (x.toInt() == 0) { + 0.0f + } else { + ((10 * x - 10).pow(2)) + } + } + + //Exponential Out + fun easeOutExpo(x: Float): Float { + return if (x == 1.0f) { 1.0f } else {(1 - 2.0.pow(-10 * x.toInt())).toFloat()} + } + + //Exponential In/Out + fun easeInOutExpo(x: Float): Float { + return if (x == 0.0f) { + 0.0f + } else if (x == 1.0f) { + 1.0f + } else if (x < 0.5) { + 2.0.pow(20 * x.toInt() - 10).toFloat() / 2 + } else{ + (2 - 2.0.pow(-20 * x.toInt() + 10)).toFloat() / 2 + } + } +} diff --git a/common/src/main/kotlin/org/valkyrienskies/mod/client/IVSCamera.kt b/common/src/main/kotlin/org/valkyrienskies/mod/client/IVSCamera.kt index 569ec9770..c5968b275 100644 --- a/common/src/main/kotlin/org/valkyrienskies/mod/client/IVSCamera.kt +++ b/common/src/main/kotlin/org/valkyrienskies/mod/client/IVSCamera.kt @@ -1,5 +1,6 @@ package org.valkyrienskies.mod.client +import net.minecraft.client.multiplayer.ClientLevel import net.minecraft.world.entity.Entity import net.minecraft.world.level.BlockGetter import org.joml.Vector3dc @@ -15,4 +16,12 @@ interface IVSCamera { shipMountedTo: ClientShip, inShipPlayerPosition: Vector3dc ) + + fun moveCamera( + level: ClientLevel, + renderViewEntity: Entity, + thirdPerson: Boolean, + thirdPersonReverse: Boolean, + partialTicks: Float, + ) } diff --git a/common/src/main/kotlin/org/valkyrienskies/mod/common/CentrifugalForce.kt b/common/src/main/kotlin/org/valkyrienskies/mod/common/CentrifugalForce.kt new file mode 100644 index 000000000..65ea3d9e9 --- /dev/null +++ b/common/src/main/kotlin/org/valkyrienskies/mod/common/CentrifugalForce.kt @@ -0,0 +1,5 @@ +package org.valkyrienskies.mod.common + +class CentrifugalForce { + //TODO: apply forces to player +} diff --git a/common/src/main/kotlin/org/valkyrienskies/mod/common/block/TestGravityBlock.kt b/common/src/main/kotlin/org/valkyrienskies/mod/common/block/TestGravityBlock.kt new file mode 100644 index 000000000..5fa2414c9 --- /dev/null +++ b/common/src/main/kotlin/org/valkyrienskies/mod/common/block/TestGravityBlock.kt @@ -0,0 +1,29 @@ +package org.valkyrienskies.mod.common.block + + +import net.minecraft.core.BlockPos +import net.minecraft.world.level.Level +import net.minecraft.world.level.block.Block +import net.minecraft.world.level.block.SoundType +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.level.material.Material +import org.valkyrienskies.mod.api.PositionGravity +import org.valkyrienskies.mod.common.util.toJOMLD + +object TestGravityBlock : Block( + Properties.of(Material.METAL).strength(1.0f, 120.0f).sound(SoundType.METAL) +) { + override fun onPlace( + blockState: BlockState, level: Level, blockPos: BlockPos, blockState2: BlockState, bl: Boolean + ) { + super.onPlace(blockState, level, blockPos, blockState2, bl) + PositionGravity().PositionGravity(blockPos.toJOMLD(), 580.0) + } + + override fun onRemove( + blockState: BlockState, level: Level, blockPos: BlockPos, blockState2: BlockState, bl: Boolean + ) { + super.onRemove(blockState, level, blockPos, blockState2, bl) + PositionGravity().removePosition(blockPos.toJOMLD()) + } + } diff --git a/common/src/main/kotlin/org/valkyrienskies/mod/common/entity/MobWeights.java b/common/src/main/kotlin/org/valkyrienskies/mod/common/entity/MobWeights.java new file mode 100644 index 000000000..43707b56a --- /dev/null +++ b/common/src/main/kotlin/org/valkyrienskies/mod/common/entity/MobWeights.java @@ -0,0 +1,10 @@ +package org.valkyrienskies.mod.common.entity; + +public enum MobWeights { + NORMAL_PLAYER(58.0); + + public double weight; + MobWeights(final Double weight) { + this.weight = weight; + } +}