From 39e97818e7b3ce529dcc9762647e526d4880e34e Mon Sep 17 00:00:00 2001 From: Sollace Date: Sat, 1 Oct 2022 23:53:18 +0200 Subject: [PATCH] Changes to how shapes are handled and align the particles spawned by portals to the portal's orientation --- .../ability/magic/spell/PlaceableSpell.java | 2 +- .../magic/spell/RainboomAbilitySpell.java | 9 +- .../magic/spell/effect/DarkVortexSpell.java | 3 +- .../ability/magic/spell/effect/FireSpell.java | 3 +- .../ability/magic/spell/effect/IceSpell.java | 8 +- .../magic/spell/effect/PortalSpell.java | 20 +++- .../client/particle/RunesParticle.java | 8 ++ .../unicopia/particle/ParticleHandle.java | 2 + .../unicopia/particle/ParticleSource.java | 10 +- .../unicopia/particle/ParticleUtils.java | 42 +++---- .../unicopia/util/PosHelper.java | 8 -- .../unicopia/util/shape/Cylinder.java | 38 +------ .../unicopia/util/shape/Line.java | 56 ++-------- .../unicopia/util/shape/PointGenerator.java | 40 +++---- .../unicopia/util/shape/RotatedShape.java | 51 +++++++++ .../unicopia/util/shape/Shape.java | 57 ++++------ .../unicopia/util/shape/Sphere.java | 103 ++++++------------ 17 files changed, 199 insertions(+), 261 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/util/shape/RotatedShape.java diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java index 84864883..09f88b0b 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java @@ -75,6 +75,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS @Override public boolean tick(Caster source, Situation situation) { + if (situation == Situation.BODY) { if (!source.isClient()) { @@ -174,7 +175,6 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS public Optional getParticleEffectAttachment(Caster source) { return particlEffect.update(getUuid(), source, spawner -> { - source.getOriginVector().add(0, 5, 0); spawner.addParticle(new OrientedBillboardParticleEffect(UParticles.MAGIC_RUNES, pitch + 90, yaw), Vec3d.ZERO, Vec3d.ZERO); }); } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RainboomAbilitySpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RainboomAbilitySpell.java index bf95f944..d3643ab8 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RainboomAbilitySpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RainboomAbilitySpell.java @@ -8,7 +8,6 @@ import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect; import com.minelittlepony.unicopia.particle.ParticleHandle; import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.util.MagicalDamageSource; -import com.minelittlepony.unicopia.util.PosHelper; import com.minelittlepony.unicopia.util.shape.Shape; import com.minelittlepony.unicopia.util.shape.Sphere; @@ -27,8 +26,8 @@ import net.minecraft.world.GameRules; */ public class RainboomAbilitySpell extends AbstractSpell { - private final int rad = 5; - private final Shape effect_range = new Sphere(false, rad); + private static final int RADIUS = 5; + private static final Shape EFFECT_RANGE = new Sphere(false, RADIUS); private final ParticleHandle particlEffect = new ParticleHandle(); @@ -64,10 +63,10 @@ public class RainboomAbilitySpell extends AbstractSpell { return false; } - source.findAllEntitiesInRange(rad).forEach(e -> { + source.findAllEntitiesInRange(RADIUS).forEach(e -> { e.damage(MagicalDamageSource.create("rainboom", source).setBreakSunglasses(), 6); }); - PosHelper.getAllInRegionMutable(source.getOrigin(), effect_range).forEach(pos -> { + EFFECT_RANGE.offset(source.getOrigin()).getBlockPositions().forEach(pos -> { BlockState state = source.getReferenceWorld().getBlockState(pos); if (state.isIn(UTags.FRAGILE) && canBreak(pos, owner)) { owner.world.breakBlock(pos, true); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java index 7f15c9a5..e11ca88d 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java @@ -16,7 +16,6 @@ import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.ProjectileDelegate; import com.minelittlepony.unicopia.util.MagicalDamageSource; -import com.minelittlepony.unicopia.util.PosHelper; import com.minelittlepony.unicopia.util.shape.Sphere; import net.minecraft.block.BlockState; @@ -156,7 +155,7 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileSpell if (radius > 2) { Vec3d origin = getOrigin(source); - PosHelper.getAllInRegionMutable(source.getOrigin(), new Sphere(false, radius)).forEach(i -> { + new Sphere(false, radius).offset(origin).getBlockPositions().forEach(i -> { if (!canAffect(source, i)) { return; } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/FireSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/FireSpell.java index 6caa93a1..9fcb24ed 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/FireSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/FireSpell.java @@ -13,7 +13,6 @@ import com.minelittlepony.unicopia.block.state.StateMaps; import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.util.MagicalDamageSource; -import com.minelittlepony.unicopia.util.PosHelper; import com.minelittlepony.unicopia.util.VecHelper; import com.minelittlepony.unicopia.util.shape.Sphere; @@ -67,7 +66,7 @@ public class FireSpell extends AbstractAreaEffectSpell implements ProjectileSpel generateParticles(source); } - return PosHelper.getAllInRegionMutable(source.getOrigin(), new Sphere(false, Math.max(0, 4 + getTraits().get(Trait.POWER)))).reduce(false, + return new Sphere(false, Math.max(0, 4 + getTraits().get(Trait.POWER))).offset(source.getOrigin()).getBlockPositions().reduce(false, (r, i) -> source.canModifyAt(i) && applyBlocks(source.getReferenceWorld(), i), (a, b) -> a || b) || applyEntities(null, source.getReferenceWorld(), source.getOriginVector()); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/IceSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/IceSpell.java index e9943532..0ad75241 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/IceSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/IceSpell.java @@ -31,8 +31,8 @@ public class IceSpell extends AbstractSpell { .with(Trait.ICE, 15) .build(); - private final int rad = 3; - private final Shape outerRange = new Sphere(false, rad); + private static final int RADIUS = 3; + private static final Shape OUTER_RANGE = new Sphere(false, RADIUS); protected IceSpell(CustomisedSpellType type) { super(type); @@ -42,10 +42,10 @@ public class IceSpell extends AbstractSpell { public boolean tick(Caster source, Situation situation) { boolean submerged = source.getEntity().isSubmergedInWater() || source.getEntity().isSubmergedIn(FluidTags.LAVA); - long blocksAffected = PosHelper.getAllInRegionMutable(source.getOrigin(), outerRange).filter(i -> { + long blocksAffected = OUTER_RANGE.offset(source.getOrigin()).getBlockPositions().filter(i -> { if (source.canModifyAt(i) && applyBlockSingle(source.getEntity(), source.getReferenceWorld(), i, situation)) { - if (submerged & source.getOrigin().isWithinDistance(i, rad - 1)) { + if (submerged & source.getOrigin().isWithinDistance(i, RADIUS - 1)) { BlockState state = source.getReferenceWorld().getBlockState(i); if (state.isIn(BlockTags.ICE) || state.isOf(Blocks.OBSIDIAN)) { source.getReferenceWorld().setBlockState(i, Blocks.AIR.getDefaultState(), Block.NOTIFY_NEIGHBORS); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java index 2be2caf5..90350a6d 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java @@ -13,7 +13,7 @@ import com.minelittlepony.unicopia.entity.CastSpellEntity; import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.particle.*; import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; -import com.minelittlepony.unicopia.util.shape.Sphere; +import com.minelittlepony.unicopia.util.shape.*; import net.minecraft.block.Block; import net.minecraft.block.Blocks; @@ -32,6 +32,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme .with(Trait.KNOWLEDGE, 1) .with(Trait.ORDER, 25) .build(); + private static final Shape PARTICLE_AREA = new Sphere(true, 2, 1, 1, 0); private final EntityReference teleportationTarget = new EntityReference<>(); @@ -42,6 +43,8 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme private float pitch; private float yaw; + private PointGenerator particleArea = PARTICLE_AREA; + protected PortalSpell(CustomisedSpellType type) { super(type); } @@ -57,7 +60,6 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme if (situation == Situation.GROUND) { if (source.isClient()) { - Vec3d origin = source.getOriginVector(); ParticleEffect effect = teleportationTarget.getPosition() @@ -67,13 +69,13 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme }) .orElse(ParticleTypes.ELECTRIC_SPARK); - source.spawnParticles(origin, new Sphere(true, 2, 1, 1, 1), 1, pos -> { + source.spawnParticles(origin, particleArea, 5, pos -> { source.addParticle(effect, pos, Vec3d.ZERO); }); teleportationTarget.getPosition().ifPresentOrElse(position -> { particleEffect.update(getUuid(), source, spawner -> { - spawner.addParticle(new SphereParticleEffect(UParticles.DISK, getType().getColor(), 0.8F, 2, new Vec3d(pitch, yaw, 0)), source.getOriginVector(), Vec3d.ZERO); + spawner.addParticle(new SphereParticleEffect(UParticles.DISK, getType().getColor(), 0.8F, 1.8F, new Vec3d(pitch, yaw, 0)), source.getOriginVector(), Vec3d.ZERO); }); }, () -> { particleEffect.destroy(); @@ -100,8 +102,6 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme entry.pitch = pitch; entry.yaw = yaw; } - - } return !isDead(); @@ -160,6 +160,10 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme public void setOrientation(float pitch, float yaw) { this.pitch = pitch; this.yaw = yaw; + particleArea = PARTICLE_AREA.rotate( + pitch * MathHelper.RADIANS_PER_DEGREE, + (180 - yaw) * MathHelper.RADIANS_PER_DEGREE + ); setDirty(); } @@ -202,6 +206,10 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme teleportationTarget.fromNBT(compound.getCompound("teleportationTarget")); pitch = compound.getFloat("pitch"); yaw = compound.getFloat("yaw"); + particleArea = PARTICLE_AREA.rotate( + pitch * MathHelper.RADIANS_PER_DEGREE, + (180 - yaw) * MathHelper.RADIANS_PER_DEGREE + ); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java index 5eaf31b7..0df431ae 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java @@ -88,7 +88,15 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme if (key == ATTR_RADIUS) { targetSize = value.floatValue(); } + if (key == ATTR_PITCH) { + rotation = new Quaternion(0, 0, 0, 1); + rotation.hamiltonProduct(Vec3f.POSITIVE_Y.getDegreesQuaternion(value.floatValue())); + } + if (key == ATTR_YAW) { + rotation.hamiltonProduct(Vec3f.POSITIVE_X.getDegreesQuaternion(180 - value.floatValue())); + } } + @Override public float getScale(float tickDelta) { return MathHelper.lerp(tickDelta, prevBaseSize, baseSize) * super.getScale(tickDelta); diff --git a/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java b/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java index fd32b560..f10f3174 100644 --- a/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java +++ b/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java @@ -88,6 +88,8 @@ public class ParticleHandle { int ATTR_RADIUS = 0; int ATTR_COLOR = 1; int ATTR_OPACITY = 2; + int ATTR_PITCH = 3; + int ATTR_YAW = 4; boolean isStillAlive(); diff --git a/src/main/java/com/minelittlepony/unicopia/particle/ParticleSource.java b/src/main/java/com/minelittlepony/unicopia/particle/ParticleSource.java index 6997f440..796aa5a4 100644 --- a/src/main/java/com/minelittlepony/unicopia/particle/ParticleSource.java +++ b/src/main/java/com/minelittlepony/unicopia/particle/ParticleSource.java @@ -2,7 +2,7 @@ package com.minelittlepony.unicopia.particle; import java.util.function.Consumer; -import com.minelittlepony.unicopia.util.shape.Shape; +import com.minelittlepony.unicopia.util.shape.PointGenerator; import net.minecraft.entity.Entity; import net.minecraft.particle.ParticleEffect; @@ -29,14 +29,12 @@ public interface ParticleSource extends ParticleSpawner { ParticleUtils.spawnParticles(particleId, getEntity(), count); } - default void spawnParticles(Shape area, int count, Consumer particleSpawner) { + default void spawnParticles(PointGenerator area, int count, Consumer particleSpawner) { spawnParticles(getOriginVector(), area, count, particleSpawner); } - default void spawnParticles(Vec3d pos, Shape area, int count, Consumer particleSpawner) { - area.randomPoints(count, getReferenceWorld().random) - .map(point -> point.add(pos)) - .forEach(particleSpawner); + default void spawnParticles(Vec3d pos, PointGenerator area, int count, Consumer particleSpawner) { + area.offset(pos).randomPoints(count, getReferenceWorld().random).forEach(particleSpawner); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/particle/ParticleUtils.java b/src/main/java/com/minelittlepony/unicopia/particle/ParticleUtils.java index 7f71ef13..0f77a9c3 100644 --- a/src/main/java/com/minelittlepony/unicopia/particle/ParticleUtils.java +++ b/src/main/java/com/minelittlepony/unicopia/particle/ParticleUtils.java @@ -1,7 +1,6 @@ package com.minelittlepony.unicopia.particle; -import com.minelittlepony.unicopia.util.shape.Shape; -import com.minelittlepony.unicopia.util.shape.Sphere; +import com.minelittlepony.unicopia.util.shape.*; import net.minecraft.entity.Entity; import net.minecraft.particle.ParticleEffect; @@ -12,30 +11,33 @@ import net.minecraft.world.World; /** * Utility for spawning particles. */ -public final class ParticleUtils { +public interface ParticleUtils { - public static void spawnParticles(ParticleEffect effect, Entity entity, int count) { - double halfDist = Math.abs(entity.getStandingEyeHeight() / 1.5); - double middle = entity.getBoundingBox().minY + halfDist; - - Shape shape = new Sphere(false, Math.abs((float)halfDist + entity.getWidth())); - - shape.randomPoints(count, entity.world.random).forEach(point -> { - spawnParticle(entity.world, effect, - entity.getX() + point.x, - middle + point.y, - entity.getZ() + point.z, - 0, 0, 0); - }); + static PointGenerator getShapeFor(Entity entity) { + final double halfDist = Math.abs(entity.getStandingEyeHeight() / 1.5); + final double middle = entity.getBoundingBox().minY + halfDist; + return new Sphere(false, Math.abs((float)halfDist + entity.getWidth())).offset(new Vec3d(entity.getX(), middle, entity.getZ())); } - public static void spawnParticle(World world, ParticleEffect effect, Vec3d pos, Vec3d vel) { + static void spawnParticles(ParticleEffect effect, Entity entity, int count) { + spawnParticles(entity.world, getShapeFor(entity), effect, count); + } + + static void spawnParticles(ParticleEffect effect, World world, Vec3d origin, int count) { + spawnParticles(world, Sphere.UNIT_SPHERE.offset(origin), effect, count); + } + + static void spawnParticles(World world, PointGenerator points, ParticleEffect effect, int count) { + points.randomPoints(count, world.random).forEach(point -> spawnParticle(world, effect, point, Vec3d.ZERO)); + } + + static void spawnParticle(World world, ParticleEffect effect, Vec3d pos, Vec3d vel) { spawnParticle(world, effect, pos.x, pos.y, pos.z, vel.x, vel.y, vel.z); } - public static void spawnParticle(World world, ParticleEffect effect, double x, double y, double z, double vX, double vY, double vZ) { - if (world instanceof ServerWorld) { - ((ServerWorld)world).spawnParticles(effect, x, y, z, 1, vX, vY, vZ, 0); + static void spawnParticle(World world, ParticleEffect effect, double x, double y, double z, double vX, double vY, double vZ) { + if (world instanceof ServerWorld sw) { + sw.spawnParticles(effect, x, y, z, 1, vX, vY, vZ, 0); } else { world.addParticle(effect, x, y, z, vX, vY, vZ); } diff --git a/src/main/java/com/minelittlepony/unicopia/util/PosHelper.java b/src/main/java/com/minelittlepony/unicopia/util/PosHelper.java index bbc7c22f..587e1bbb 100644 --- a/src/main/java/com/minelittlepony/unicopia/util/PosHelper.java +++ b/src/main/java/com/minelittlepony/unicopia/util/PosHelper.java @@ -10,7 +10,6 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; import com.google.common.collect.Lists; -import com.minelittlepony.unicopia.util.shape.Shape; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; @@ -66,11 +65,4 @@ public interface PosHelper { } }, false); } - - static Stream getAllInRegionMutable(BlockPos origin, Shape shape) { - return BlockPos.stream( - origin.add(new BlockPos(shape.getLowerBound())), - origin.add(new BlockPos(shape.getUpperBound())) - ).filter(pos -> shape.isPointInside(Vec3d.of(pos.subtract(origin)))); - } } diff --git a/src/main/java/com/minelittlepony/unicopia/util/shape/Cylinder.java b/src/main/java/com/minelittlepony/unicopia/util/shape/Cylinder.java index b68ed3df..7caf4195 100644 --- a/src/main/java/com/minelittlepony/unicopia/util/shape/Cylinder.java +++ b/src/main/java/com/minelittlepony/unicopia/util/shape/Cylinder.java @@ -14,9 +14,6 @@ public class Cylinder implements Shape { private final double height; private final double rad; - private float yaw = 0; - private float pitch = 0; - private final double volume; /** @@ -68,42 +65,17 @@ public class Cylinder implements Shape { return Math.PI * (stretchX * rad * stretchZ * rad) * height; } - @Override - public double getXOffset() { - return 0; - } - - @Override - public double getYOffset() { - return 0; - } - - @Override - public double getZOffset() { - return 0; - } - @Override public Vec3d computePoint(Random rand) { double y = MathHelper.nextDouble(rand, 0, height); double pheta = MathHelper.nextDouble(rand, 0, Math.PI * 2); double rho = hollow && Math.abs(y) != height/2 ? rad : MathHelper.nextDouble(rand, 0, rad); - return new Vec3d(rho * Math.cos(pheta) * stretchX, y, rho * Math.sin(pheta) * stretchZ) - .rotateY(yaw) - .rotateX(pitch); - } - - @Override - public Cylinder setRotation(float u, float v) { - yaw = u; - pitch = v; - return this; + return new Vec3d(rho * Math.cos(pheta) * stretchX, y, rho * Math.sin(pheta) * stretchZ); } @Override public boolean isPointInside(Vec3d point) { - point = point.rotateY(-yaw).rotateX(-pitch); point = new Vec3d(point.x / stretchX, point.y, point.z / stretchZ); double y = Math.abs(point.y); if (y < height/2) { @@ -115,15 +87,11 @@ public class Cylinder implements Shape { @Override public Vec3d getLowerBound() { - return new Vec3d(-rad * stretchX, 0, -rad * stretchZ) - .rotateY(yaw) - .rotateX(pitch); + return new Vec3d(-rad * stretchX, 0, -rad * stretchZ); } @Override public Vec3d getUpperBound() { - return new Vec3d(-rad * stretchX, height, -rad * stretchZ) - .rotateY(yaw) - .rotateX(pitch); + return new Vec3d(-rad * stretchX, height, -rad * stretchZ); } } \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/util/shape/Line.java b/src/main/java/com/minelittlepony/unicopia/util/shape/Line.java index 806198c3..f345ea23 100644 --- a/src/main/java/com/minelittlepony/unicopia/util/shape/Line.java +++ b/src/main/java/com/minelittlepony/unicopia/util/shape/Line.java @@ -10,18 +10,15 @@ import net.minecraft.util.math.random.Random; */ public class Line implements Shape { - double len; + private final double len; - double dX; - double dY; - double dZ; + private final double dX; + private final double dY; + private final double dZ; - double sX; - double sY; - double sZ; - - private float yaw = 0; - private float pitch = 0; + private final double sX; + private final double sY; + private final double sZ; /** * Creates a line with a given length, starting point, and gradient represented @@ -64,57 +61,24 @@ public class Line implements Shape { return len; } - @Override - public double getXOffset() { - return sX; - } - - @Override - public double getYOffset() { - return sY; - } - - @Override - public double getZOffset() { - return sZ; - } - @Override public Vec3d computePoint(Random rand) { double distance = MathHelper.nextDouble(rand, 0, len); - return new Vec3d(dX, dY, dZ) - .multiply(distance) - .add(sX, sY, sZ) - .rotateY(yaw) - .rotateX(pitch); - } - - @Override - public Line setRotation(float u, float v) { - yaw = u; - pitch = v; - return this; + return new Vec3d(dX, dY, dZ).multiply(distance).add(sX, sY, sZ); } @Override public boolean isPointInside(Vec3d point) { - point = point.rotateY(-yaw).rotateX(-pitch); - return point.x/dX == point.y/dY && point.x/dX == point.z/dZ; } @Override public Vec3d getLowerBound() { - return new Vec3d(sX, sY, sZ) - .rotateY(yaw) - .rotateX(pitch); + return new Vec3d(sX, sY, sZ); } @Override public Vec3d getUpperBound() { - return new Vec3d(sX + dX, sY + dY, sZ + dZ) - .multiply(len) - .rotateY(yaw) - .rotateX(pitch); + return new Vec3d(sX + dX, sY + dY, sZ + dZ).multiply(len); } } diff --git a/src/main/java/com/minelittlepony/unicopia/util/shape/PointGenerator.java b/src/main/java/com/minelittlepony/unicopia/util/shape/PointGenerator.java index e133bc56..71d90f4e 100644 --- a/src/main/java/com/minelittlepony/unicopia/util/shape/PointGenerator.java +++ b/src/main/java/com/minelittlepony/unicopia/util/shape/PointGenerator.java @@ -7,9 +7,7 @@ import java.util.function.Consumer; import java.util.stream.Stream; import java.util.stream.StreamSupport; -import com.google.common.collect.Streams; - -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.*; import net.minecraft.util.math.random.Random; /** @@ -56,30 +54,16 @@ public interface PointGenerator { }, false); } - default PointGenerator union(PointGenerator other) { - final PointGenerator source = this; - return new PointGenerator() { + /** + * Returns a stream of block positions. + */ + Stream getBlockPositions(); - @Override - public double getVolumeOfSpawnableSpace() { - return source.getVolumeOfSpawnableSpace() + other.getVolumeOfSpawnableSpace(); - } - - @Override - public Vec3d computePoint(Random rand) { - return source.computePoint(rand); - } - - @Override - public Stream randomPoints(int n, Random rand) { - double total = this.getVolumeOfSpawnableSpace(); - - return Streams.concat( - source.randomPoints((int)(n * (source.getVolumeOfSpawnableSpace() / total)), rand), - other.randomPoints((int)(n * (other.getVolumeOfSpawnableSpace() / total)), rand) - ); - } - }; + /** + * Returns a new point generator where all of its points are offset by the specified amount. + */ + default PointGenerator offset(Vec3i offset) { + return offset(Vec3d.of(offset)); } /** @@ -99,6 +83,10 @@ public interface PointGenerator { return source.computePoint(rand).add(offset); } + @Override + public Stream getBlockPositions() { + return source.getBlockPositions().map(pos -> pos.add(offset.x, offset.y, offset.z)); + } }; } } diff --git a/src/main/java/com/minelittlepony/unicopia/util/shape/RotatedShape.java b/src/main/java/com/minelittlepony/unicopia/util/shape/RotatedShape.java new file mode 100644 index 00000000..4f17db2b --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/util/shape/RotatedShape.java @@ -0,0 +1,51 @@ +package com.minelittlepony.unicopia.util.shape; + +import net.minecraft.util.math.*; +import net.minecraft.util.math.random.Random; + +final class RotatedShape implements Shape { + private final float pitch; + private final float yaw; + + private final Shape original; + + RotatedShape(Shape original, float pitch, float yaw) { + this.original = original; + this.pitch = pitch; + this.yaw = yaw; + } + + @Override + public double getVolumeOfSpawnableSpace() { + return original.getVolumeOfSpawnableSpace(); + } + + @Override + public Vec3d computePoint(Random rand) { + return original.computePoint(rand).rotateX(pitch).rotateY(yaw); + } + + @Override + public Vec3d getLowerBound() { + return original.getLowerBound().rotateX(pitch).rotateY(yaw); + } + + @Override + public Vec3d getUpperBound() { + return original.getUpperBound().rotateX(pitch).rotateY(yaw); + } + + @Override + public boolean isPointInside(Vec3d point) { + return original.isPointInside(point.rotateY(-yaw).rotateX(-pitch)); + } + + @Override + public Shape rotate(float pitch, float yaw) { + if (pitch == 0 && yaw == 0) { + return this; + } + + return new RotatedShape(this, this.pitch + pitch, this.yaw + yaw); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/util/shape/Shape.java b/src/main/java/com/minelittlepony/unicopia/util/shape/Shape.java index e4336326..cb6a38ba 100644 --- a/src/main/java/com/minelittlepony/unicopia/util/shape/Shape.java +++ b/src/main/java/com/minelittlepony/unicopia/util/shape/Shape.java @@ -1,5 +1,8 @@ package com.minelittlepony.unicopia.util.shape; +import java.util.stream.Stream; + +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; /** @@ -7,38 +10,6 @@ import net.minecraft.util.math.Vec3d; *Interface for a 3d shape, used for spawning particles in a designated area (or anything else you need shapes for). */ public interface Shape extends PointGenerator { - - /** - * Rotates this shape around it's center. - * - * @param u Rotate yaw - * @param v Rotate pitch - * - * @return This Shape - */ - Shape setRotation(float u, float v); - - /** - * X offset from the shape's origin. - * - * @return X - */ - double getXOffset(); - - /** - * Y offset from the shape's origin. - * - * @return Y - */ - double getYOffset(); - - /** - * Z offset from the shape's origin. - * - * @return Z - */ - double getZOffset(); - /** * Gets the lower bounds of the region occupied by this shape. */ @@ -51,7 +22,27 @@ public interface Shape extends PointGenerator { /** * Checks if the given point is on the edge, or if not hollow the inside, of this shape. - * @return */ boolean isPointInside(Vec3d point); + + /** + * Returns a stream of all block positions that fit inside this shape. + */ + @Override + default Stream getBlockPositions() { + return BlockPos.stream( + new BlockPos(getLowerBound()), + new BlockPos(getUpperBound()) + ).filter(pos -> isPointInside(Vec3d.ofCenter(pos))); + } + + /** + * Returns a new shape with after applying additional rotation. + */ + default Shape rotate(float pitch, float yaw) { + if (pitch == 0 && yaw == 0) { + return this; + } + return new RotatedShape(this, pitch, yaw); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/util/shape/Sphere.java b/src/main/java/com/minelittlepony/unicopia/util/shape/Sphere.java index ecf666cd..d44f2c12 100644 --- a/src/main/java/com/minelittlepony/unicopia/util/shape/Sphere.java +++ b/src/main/java/com/minelittlepony/unicopia/util/shape/Sphere.java @@ -9,14 +9,12 @@ import net.minecraft.util.math.random.Random; * */ public class Sphere implements Shape { + public static final Sphere UNIT_SPHERE = new Sphere(false, 1); protected final Vec3d stretch; private final boolean hollow; private final double rad; - private float yaw = 0; - private float pitch = 0; - private final double volume; /** @@ -56,7 +54,7 @@ public class Sphere implements Shape { private double computeSpawnableSpace() { if (hollow) { - if (stretch.x == stretch.x && stretch.y == stretch.z) { + if (stretch.x == stretch.y && stretch.y == stretch.z) { double radius = rad * stretch.x; return 4 * Math.PI * radius * radius; } @@ -65,6 +63,40 @@ public class Sphere implements Shape { return computeEllipsoidVolume(rad, stretch); } + @Override + public Vec3d computePoint(Random rand) { + double rad = this.rad; + + if (!hollow) { + rad = MathHelper.nextDouble(rand, 0, rad); + } + + double z = MathHelper.nextDouble(rand, -rad, rad); + double phi = MathHelper.nextDouble(rand, 0, Math.PI * 2); + double theta = Math.asin(z / rad); + + return new Vec3d(rad * Math.cos(theta) * Math.cos(phi) * stretch.x, rad * Math.cos(theta) * Math.sin(phi) * stretch.y, z * stretch.z); + } + + @Override + public boolean isPointInside(Vec3d point) { + point = new Vec3d(point.x / stretch.x, point.y / stretch.y, point.z / stretch.z); + + double dist = point.length(); + + return hollow ? dist == rad : dist <= rad; + } + + @Override + public Vec3d getLowerBound() { + return new Vec3d(-rad * stretch.x, -rad * stretch.y, -rad * stretch.z); + } + + @Override + public Vec3d getUpperBound() { + return new Vec3d(rad * stretch.x, rad * stretch.y, rad * stretch.z); + } + public static double computeEllipsoidArea(double rad, Vec3d stretch) { double p = 1.6075; double result = Math.pow(rad * stretch.x, p) * Math.pow(rad * stretch.y, p); @@ -81,67 +113,4 @@ public class Sphere implements Shape { result *= (rad * stretch.z); return result; } - - @Override - public double getXOffset() { - return 0; - } - - @Override - public double getYOffset() { - return 0; - } - - @Override - public double getZOffset() { - return 0; - } - - @Override - public Vec3d computePoint(Random rand) { - double rad = this.rad; - - if (!hollow) { - rad = MathHelper.nextDouble(rand, 0, rad); - } - - double z = MathHelper.nextDouble(rand, -rad, rad); - double phi = MathHelper.nextDouble(rand, 0, Math.PI * 2); - double theta = Math.asin(z / rad); - - return new Vec3d(rad * Math.cos(theta) * Math.cos(phi) * stretch.x, rad * Math.cos(theta) * Math.sin(phi) * stretch.y, z * stretch.z) - .rotateY(yaw) - .rotateX(pitch); - } - - @Override - public Sphere setRotation(float u, float v) { - yaw = u; - pitch = v; - return this; - } - - @Override - public boolean isPointInside(Vec3d point) { - point = point.rotateY(-yaw).rotateX(-pitch); - point = new Vec3d(point.x / stretch.x, point.y / stretch.y, point.z / stretch.z); - - double dist = point.length(); - - return hollow ? dist == rad : dist <= rad; - } - - @Override - public Vec3d getLowerBound() { - return new Vec3d(-rad * stretch.x, -rad * stretch.y, -rad * stretch.z) - .rotateY(yaw) - .rotateX(pitch); - } - - @Override - public Vec3d getUpperBound() { - return new Vec3d(rad * stretch.x, rad * stretch.y, rad * stretch.z) - .rotateY(yaw) - .rotateX(pitch); - } }