mirror of
https://github.com/Sollace/Unicopia.git
synced 2025-02-07 22:16:44 +01:00
Changes to how shapes are handled and align the particles spawned by portals to the portal's orientation
This commit is contained in:
parent
63481f9c64
commit
39e97818e7
17 changed files with 199 additions and 261 deletions
|
@ -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<Attachment> 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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<Entity> 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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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<Vec3d> particleSpawner) {
|
||||
default void spawnParticles(PointGenerator area, int count, Consumer<Vec3d> particleSpawner) {
|
||||
spawnParticles(getOriginVector(), area, count, particleSpawner);
|
||||
}
|
||||
|
||||
default void spawnParticles(Vec3d pos, Shape area, int count, Consumer<Vec3d> particleSpawner) {
|
||||
area.randomPoints(count, getReferenceWorld().random)
|
||||
.map(point -> point.add(pos))
|
||||
.forEach(particleSpawner);
|
||||
default void spawnParticles(Vec3d pos, PointGenerator area, int count, Consumer<Vec3d> particleSpawner) {
|
||||
area.offset(pos).randomPoints(count, getReferenceWorld().random).forEach(particleSpawner);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<BlockPos> 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))));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<BlockPos> getBlockPositions();
|
||||
|
||||
@Override
|
||||
public double getVolumeOfSpawnableSpace() {
|
||||
return source.getVolumeOfSpawnableSpace() + other.getVolumeOfSpawnableSpace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3d computePoint(Random rand) {
|
||||
return source.computePoint(rand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Vec3d> 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<BlockPos> getBlockPositions() {
|
||||
return source.getBlockPositions().map(pos -> pos.add(offset.x, offset.y, offset.z));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<BlockPos> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue