Changes to how shapes are handled and align the particles spawned by portals to the portal's orientation

This commit is contained in:
Sollace 2022-10-01 23:53:18 +02:00
parent 63481f9c64
commit 39e97818e7
17 changed files with 199 additions and 261 deletions

View file

@ -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);
});
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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());

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

@ -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();

View file

@ -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

View file

@ -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);
}

View file

@ -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))));
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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));
}
};
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}