Fixed disk particles and add improved physics for the dark vortex

This commit is contained in:
Sollace 2021-12-26 16:11:34 +02:00
parent 9460957c56
commit 76506665bc
10 changed files with 199 additions and 178 deletions

View file

@ -5,7 +5,9 @@ import com.minelittlepony.unicopia.ability.magic.spell.ProjectileSpell;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
import com.minelittlepony.unicopia.particle.MagicParticleEffect; import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.util.MagicalDamageSource; import com.minelittlepony.unicopia.util.MagicalDamageSource;
import com.minelittlepony.unicopia.util.shape.Sphere; import com.minelittlepony.unicopia.util.shape.Sphere;
@ -22,11 +24,15 @@ public class AttractiveSpell extends ShieldSpell implements ProjectileSpell {
@Override @Override
public void generateParticles(Caster<?> source) { public void generateParticles(Caster<?> source) {
int range = 4 + (source.getLevel().get() * 2); double range = getDrawDropOffRange(source) + 10;
Vec3d pos = source.getOriginVector();
source.spawnParticles(new Sphere(false, range), range * 9, p -> { source.spawnParticles(getOrigin(source), new Sphere(false, range), 7, p -> {
source.addParticle(new MagicParticleEffect(getType().getColor()), p, p.subtract(pos)); source.addParticle(
new FollowingParticleEffect(UParticles.HEALTH_DRAIN, source.getEntity(), 0.4F)
.withChild(new MagicParticleEffect(getType().getColor())),
p,
Vec3d.ZERO
);
}); });
} }
@ -36,8 +42,8 @@ public class AttractiveSpell extends ShieldSpell implements ProjectileSpell {
} }
@Override @Override
protected boolean isValidTarget(Entity entity) { protected boolean isValidTarget(Caster<?> source, Entity entity) {
return getTraits().get(Trait.FOCUS) > 10 ? entity instanceof ItemEntity : super.isValidTarget(entity); return getTraits().get(Trait.FOCUS) > 10 ? entity instanceof ItemEntity : super.isValidTarget(source, entity);
} }
@Override @Override
@ -55,7 +61,7 @@ public class AttractiveSpell extends ShieldSpell implements ProjectileSpell {
source.getEntity().damage(MagicalDamageSource.create("vortex"), 4); source.getEntity().damage(MagicalDamageSource.create("vortex"), 4);
} }
applyForce(source.getOriginVector(), target, -force, 0); applyForce(getOrigin(source), target, -force, 0);
float maxVel = !isFriendlyTogether(source) ? 1 : 1.6f; float maxVel = !isFriendlyTogether(source) ? 1 : 1.6f;

View file

@ -1,11 +1,11 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect; package com.minelittlepony.unicopia.ability.magic.spell.effect;
import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.magic.Affine;
import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.Situation; import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.particle.MagicParticleEffect; import com.minelittlepony.unicopia.particle.DiskParticleEffect;
import com.minelittlepony.unicopia.particle.SphereParticleEffect; import com.minelittlepony.unicopia.particle.SphereParticleEffect;
import com.minelittlepony.unicopia.util.MagicalDamageSource; import com.minelittlepony.unicopia.util.MagicalDamageSource;
import com.minelittlepony.unicopia.util.PosHelper; import com.minelittlepony.unicopia.util.PosHelper;
@ -13,9 +13,15 @@ import com.minelittlepony.unicopia.util.shape.Sphere;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.explosion.Explosion; import net.minecraft.util.math.Vec3f;
/**
* More powerful version of the vortex spell which creates a black hole
*/
public class DarkVortexSpell extends AttractiveSpell { public class DarkVortexSpell extends AttractiveSpell {
public static final SpellTraits DEFAULT_TRAITS = new SpellTraits.Builder() public static final SpellTraits DEFAULT_TRAITS = new SpellTraits.Builder()
.with(Trait.CHAOS, 5) .with(Trait.CHAOS, 5)
@ -24,6 +30,9 @@ public class DarkVortexSpell extends AttractiveSpell {
.with(Trait.DARKNESS, 100) .with(Trait.DARKNESS, 100)
.build(); .build();
private static final Vec3d SPHERE_OFFSET = new Vec3d(0, 2, 0);
private int age = 0;
private float accumulatedMass = 0; private float accumulatedMass = 0;
protected DarkVortexSpell(SpellType<?> type, SpellTraits traits) { protected DarkVortexSpell(SpellType<?> type, SpellTraits traits) {
@ -42,88 +51,122 @@ public class DarkVortexSpell extends AttractiveSpell {
return true; return true;
} }
if (!source.isClient() && source.getWorld().random.nextInt(200) == 0) { age++;
accumulatedMass += 0.001F * (1 + getTraits().get(Trait.STRENGTH, 70, 120) - 70); setDirty();
if (age % 20 == 0) {
source.getWorld().playSound(null, source.getOrigin(), SoundEvents.AMBIENT_SOUL_SAND_VALLEY_MOOD, SoundCategory.AMBIENT, 1, 1);
} }
if (accumulatedMass > 20 * getTraits().get(Trait.POWER, 1, 60) / 10F) {
if (!source.isClient()) {
Vec3d pos = source.getOriginVector();
source.getWorld().createExplosion(
source.getMaster(),
MagicalDamageSource.create("super_nova"),
null,
pos.getX(), pos.getY(), pos.getZ(), 17, true, Explosion.DestructionType.DESTROY
);
}
return false;
}
return super.tick(source, situation); return super.tick(source, situation);
} }
@Override
public boolean isFriendlyTogether(Affine other) {
return accumulatedMass < 150 && super.isFriendlyTogether(other);
}
@Override
protected boolean isValidTarget(Caster<?> source, Entity entity) {
return getAttractiveForce(source, entity) > 0;
}
@Override @Override
public void generateParticles(Caster<?> source) { public void generateParticles(Caster<?> source) {
int range = 4 + (source.getLevel().get() * 2); super.generateParticles(source);
Vec3d pos = source.getOriginVector();
source.spawnParticles(new Sphere(false, range), range * 9, p -> { float radius = (float)getEventHorizonRadius();
source.addParticle(new MagicParticleEffect(getType().getColor()), p, p.subtract(pos));
});
float radius = 1 + (float)getDrawDropOffRange(source) / 2;
particlEffect.ifAbsent(getUuid(), source, spawner -> { particlEffect.ifAbsent(getUuid(), source, spawner -> {
spawner.addParticle(new SphereParticleEffect(getType().getColor(), 0.99F, radius), source.getOriginVector(), Vec3d.ZERO); spawner.addParticle(new SphereParticleEffect(getType().getColor(), 0.99F, radius, SPHERE_OFFSET), source.getOriginVector(), Vec3d.ZERO);
}).ifPresent(p -> { }).ifPresent(p -> {
p.setAttribute(0, radius); p.setAttribute(0, radius);
}); });
if (source.getEntity().age % 20 == 0) { source.spawnParticles(ParticleTypes.SMOKE, 3);
source.playSound(USounds.AMBIENT_WIND_GUST, 1, 1);
if (age % 11 == 0) {
source.addParticle(new DiskParticleEffect(Vec3f.ZERO, 1, radius + 1), getOrigin(source), Vec3d.ZERO);
} }
} }
@Override @Override
public double getDrawDropOffRange(Caster<?> caster) { public double getDrawDropOffRange(Caster<?> source) {
return accumulatedMass + (caster.getLevel().get() * 2); return getEventHorizonRadius() * 20;
}
@Override
protected Vec3d getOrigin(Caster<?> source) {
return source.getOriginVector().add(SPHERE_OFFSET);
} }
@Override @Override
protected long applyEntities(Caster<?> source) { protected long applyEntities(Caster<?> source) {
if (!source.isClient()) { if (!source.isClient()) {
PosHelper.getAllInRegionMutable(source.getOrigin(), new Sphere(false, 1 + ((int)getDrawDropOffRange(source) / 2F))).forEach(i -> {
if (!source.getWorld().isAir(i) && source.getWorld().random.nextInt(120) == 0) { double radius = getEventHorizonRadius();
source.getWorld().breakBlock(i, false);
accumulatedMass++; if (radius > 3) {
Vec3d origin = getOrigin(source);
PosHelper.getAllInRegionMutable(source.getOrigin(), new Sphere(false, radius)).forEach(i -> {
CatapultSpell.createBlockEntity(source.getWorld(), i, e -> {
applyRadialEffect(source, e, e.getPos().distanceTo(origin), radius);
});
setDirty(); setDirty();
} });
}); }
} }
return super.applyEntities(source); return super.applyEntities(source);
} }
// 1. force decreases with distance: distance scale 1 -> 0
// 2. max force (at dist 0) is taken from accumulated mass
// 3. force reaches 0 at distance of drawDropOffRange
private double getEventHorizonRadius() {
return Math.sqrt(Math.max(0.001, getMass() - 10));
}
private double getAttractiveForce(Caster<?> source, Entity target) {
return (getMass() * getMass(target)) / Math.pow(getOrigin(source).distanceTo(target.getPos()), 2);
}
private double getMass() {
float pulse = (float)Math.sin(age * 6) / 8F;
return 10 + Math.min(15, Math.min(0.5F + pulse, (float)Math.exp(age) / 8F - 90) + pulse + accumulatedMass / 10F);
}
private double getMass(Entity entity) {
return entity.getWidth() * entity.getHeight();
}
@Override @Override
protected void applyRadialEffect(Caster<?> source, Entity target, double distance, double radius) { protected void applyRadialEffect(Caster<?> source, Entity target, double distance, double radius) {
if (distance < 1) { if (distance <= getEventHorizonRadius()) {
accumulatedMass += 1 + getTraits().get(Trait.CHAOS, 0, 2); accumulatedMass += getMass(target);
target.damage(MagicalDamageSource.create("black_hole"), Integer.MAX_VALUE); target.damage(MagicalDamageSource.create("black_hole"), Integer.MAX_VALUE);
target.discard();
} else { } else {
super.applyRadialEffect(source, target, distance, radius); double force = getAttractiveForce(source, target);
target.setVelocity(target.getVelocity().multiply(Math.min(1, 1 - force)));
applyForce(getOrigin(source), target, -force, 0);
} }
} }
@Override @Override
public void toNBT(NbtCompound compound) { public void toNBT(NbtCompound compound) {
super.toNBT(compound); super.toNBT(compound);
compound.putInt("age", age);
compound.putFloat("accumulatedMass", accumulatedMass); compound.putFloat("accumulatedMass", accumulatedMass);
} }
@Override @Override
public void fromNBT(NbtCompound compound) { public void fromNBT(NbtCompound compound) {
super.fromNBT(compound); super.fromNBT(compound);
age = compound.getInt("age");
accumulatedMass = compound.getFloat("accumulatedMass"); accumulatedMass = compound.getFloat("accumulatedMass");
} }
} }

View file

@ -57,13 +57,14 @@ public class ShieldSpell extends AbstractSpell {
protected void generateParticles(Caster<?> source) { protected void generateParticles(Caster<?> source) {
float radius = (float)getDrawDropOffRange(source); float radius = (float)getDrawDropOffRange(source);
Vec3d origin = getOrigin(source);
source.spawnParticles(new Sphere(true, radius), (int)(radius * 6), pos -> { source.spawnParticles(origin, new Sphere(true, radius), (int)(radius * 6), pos -> {
source.addParticle(new MagicParticleEffect(getType().getColor()), pos, Vec3d.ZERO); source.addParticle(new MagicParticleEffect(getType().getColor()), pos, Vec3d.ZERO);
}); });
particlEffect.ifAbsent(getUuid(), source, spawner -> { particlEffect.ifAbsent(getUuid(), source, spawner -> {
spawner.addParticle(new SphereParticleEffect(getType().getColor(), 0.3F, radius), source.getOriginVector(), Vec3d.ZERO); spawner.addParticle(new SphereParticleEffect(getType().getColor(), 0.3F, radius), origin, Vec3d.ZERO);
}).ifPresent(p -> { }).ifPresent(p -> {
p.setAttribute(0, radius); p.setAttribute(0, radius);
p.setAttribute(1, getType().getColor()); p.setAttribute(1, getType().getColor());
@ -111,7 +112,7 @@ public class ShieldSpell extends AbstractSpell {
return (min + (source.getLevel().get() * 2)) / multiplier; return (min + (source.getLevel().get() * 2)) / multiplier;
} }
protected boolean isValidTarget(Entity entity) { protected boolean isValidTarget(Caster<?> source, Entity entity) {
return (entity instanceof LivingEntity return (entity instanceof LivingEntity
|| entity instanceof TntEntity || entity instanceof TntEntity
|| entity instanceof FallingBlockEntity || entity instanceof FallingBlockEntity
@ -125,21 +126,25 @@ public class ShieldSpell extends AbstractSpell {
protected long applyEntities(Caster<?> source) { protected long applyEntities(Caster<?> source) {
double radius = getDrawDropOffRange(source); double radius = getDrawDropOffRange(source);
Vec3d origin = source.getOriginVector(); Vec3d origin = getOrigin(source);
targetSelecter.getEntities(source, radius, this::isValidTarget).forEach(i -> { targetSelecter.getEntities(source, radius, this::isValidTarget).forEach(i -> {
try { try {
applyRadialEffect(source, i, i.getPos().distanceTo(origin), radius); applyRadialEffect(source, i, i.getPos().distanceTo(origin), radius);
} catch (Throwable e) { } catch (Throwable e) {
Unicopia.LOGGER.error("Error updating shield effect", e); Unicopia.LOGGER.error("Error updating radial effect", e);
} }
}); });
return targetSelecter.getTotalDamaged(); return targetSelecter.getTotalDamaged();
} }
protected Vec3d getOrigin(Caster<?> source) {
return source.getOriginVector();
}
protected void applyRadialEffect(Caster<?> source, Entity target, double distance, double radius) { protected void applyRadialEffect(Caster<?> source, Entity target, double distance, double radius) {
Vec3d pos = source.getOriginVector(); Vec3d pos = getOrigin(source);
if (ProjectileUtil.isFlyingProjectile(target)) { if (ProjectileUtil.isFlyingProjectile(target)) {
if (!ProjectileUtil.isProjectileThrownBy(target, source.getMaster())) { if (!ProjectileUtil.isProjectileThrownBy(target, source.getMaster())) {

View file

@ -3,7 +3,7 @@ package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.UUID; import java.util.UUID;
import java.util.function.Predicate; import java.util.function.BiPredicate;
import java.util.stream.Stream; import java.util.stream.Stream;
import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.EquinePredicates;
@ -24,7 +24,7 @@ public class TargetSelecter {
this.spell = spell; this.spell = spell;
} }
public Stream<Entity> getEntities(Caster<?> source, double radius, Predicate<Entity> filter) { public Stream<Entity> getEntities(Caster<?> source, double radius, BiPredicate<Caster<?>, Entity> filter) {
targets.values().removeIf(Target::tick); targets.values().removeIf(Target::tick);
Entity owner = source.getMaster(); Entity owner = source.getMaster();
@ -32,12 +32,13 @@ public class TargetSelecter {
boolean ownerIsValid = spell.isFriendlyTogether(source) && (EquinePredicates.PLAYER_UNICORN.test(owner) && owner.isSneaking()); boolean ownerIsValid = spell.isFriendlyTogether(source) && (EquinePredicates.PLAYER_UNICORN.test(owner) && owner.isSneaking());
return source.findAllEntitiesInRange(radius) return source.findAllEntitiesInRange(radius)
.filter(entity -> !entity.isRemoved())
.filter(entity -> { .filter(entity -> {
return !FriendshipBraceletItem.isComrade(source, entity) return !FriendshipBraceletItem.isComrade(source, entity)
&& !SpellPredicate.IS_SHIELD_LIKE.isOn(entity) && !SpellPredicate.IS_SHIELD_LIKE.isOn(entity)
&& !(ownerIsValid && (Pony.equal(entity, owner) || owner.isConnectedThroughVehicle(entity))); && !(ownerIsValid && (Pony.equal(entity, owner) || owner.isConnectedThroughVehicle(entity)));
}) })
.filter(filter) .filter(e -> filter.test(source, e))
.map(i -> { .map(i -> {
targets.computeIfAbsent(i.getUuid(), Target::new); targets.computeIfAbsent(i.getUuid(), Target::new);
return i; return i;

View file

@ -1,21 +0,0 @@
package com.minelittlepony.unicopia.client.particle;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.Matrix4f;
public class DiskModel extends SphereModel {
@Override
public void render(MatrixStack.Entry matrices, VertexConsumer vertexWriter, int light, int overlay, float r, float g, float b, float a) {
Matrix4f model = matrices.getPositionMatrix();
final double num_rings = 30;
final double zenithIncrement = Math.PI / num_rings;
double radius = 1;
for (double zenith = 0; zenith < Math.PI; zenith += zenithIncrement) {
drawVertex(model, vertexWriter, radius, zenith, Math.PI, light, overlay, r, g, b, a);
}
}
}

View file

@ -1,60 +1,25 @@
package com.minelittlepony.unicopia.client.particle; package com.minelittlepony.unicopia.client.particle;
import com.minelittlepony.unicopia.client.render.RenderLayers;
import com.minelittlepony.unicopia.particle.SphereParticleEffect; import com.minelittlepony.unicopia.particle.SphereParticleEffect;
import com.minelittlepony.unicopia.util.ColorHelper;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.world.ClientWorld; import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.math.Quaternion; import net.minecraft.util.math.Quaternion;
public class DiskParticle extends SphereParticle { public class DiskParticle extends SphereParticle {
private static final DiskModel MODEL = new DiskModel(); private final Quaternion rotation;
protected float rotX;
protected float rotY;
protected float rotZ;
public DiskParticle(SphereParticleEffect effect, ClientWorld w, double x, double y, double z, double rX, double rY, double rZ) { public DiskParticle(SphereParticleEffect effect, ClientWorld w, double x, double y, double z, double rX, double rY, double rZ) {
super(effect, w, x, y, z, 0, 0, 0); super(effect, w, x, y, z, 0, 0, 0);
rotation = new Quaternion((float)rX, (float)rY, (float)rZ, true);
rotX = (float)rX;
rotY = (float)rY;
rotZ = (float)rZ;
} }
@Override @Override
public void buildGeometry(VertexConsumer vertexConsumer, Camera camera, float tickDelta) { protected void renderModel(MatrixStack matrices, VertexConsumer buffer, float scale, float tickDelta, int light) {
if (colorAlpha <= 0 || radius <= 0) { matrices.multiply(rotation);
return; SphereModel.DISK.render(matrices, buffer, 1, light, scale, 1, 1, 1, 1);
}
float[] color = ColorHelper.changeSaturation(colorRed, colorGreen, colorBlue, 4);
RenderSystem.setShaderColor(color[0], color[1], color[2], colorAlpha / 3F);
VertexConsumerProvider.Immediate immediate = MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers();
VertexConsumer buffer = immediate.getBuffer(RenderLayers.getMagicGlow());
int light = getBrightness(tickDelta);
MatrixStack matrices = new MatrixStack();
matrices.push();
matrices.translate(x, y, z);
matrices.multiply(new Quaternion(rotX, rotY, rotZ, true));
matrices.scale(radius, radius, radius);
MODEL.render(matrices, buffer, 1, light, 1, 1, 1, 1);
matrices.pop();
immediate.draw();
RenderSystem.setShaderColor(1, 1, 1, 1);
} }
} }

View file

@ -31,9 +31,6 @@ public class FollowingParticle extends NoRenderParticle {
public Particle scale(float scale) { public Particle scale(float scale) {
this.scale *= scale; this.scale *= scale;
super.scale(scale); super.scale(scale);
if (particle != null) {
particle.scale(scale);
}
return this; return this;
} }

View file

@ -6,54 +6,58 @@ import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.Vector4f; import net.minecraft.util.math.Vector4f;
public class SphereModel { public class SphereModel {
public void render(MatrixStack matrices, VertexConsumer vertexWriter, int light, int overlay, float r, float g, float b, float a) { protected static final double pi = Math.PI;
render(matrices.peek(), vertexWriter, light, overlay, r, g, b, a); protected static final double two_pi = pi * 2F;
public static final SphereModel SPHERE = new SphereModel(40, 40, two_pi);
public static final SphereModel DISK = new SphereModel(40, 2, pi);
private final double num_rings;
private final double num_sectors;
private final double azimuthRange;
final double zenithIncrement;
final double azimuthIncrement;
public SphereModel(double rings, double sectors, double azimuthRange) {
this.num_rings = rings;
this.num_sectors = sectors;
this.azimuthRange = azimuthRange;
zenithIncrement = pi / num_rings;
azimuthIncrement = two_pi / num_sectors;
} }
public void render(MatrixStack.Entry matrices, VertexConsumer vertexWriter, int light, int overlay, float r, float g, float b, float a) { public final void render(MatrixStack matrices, VertexConsumer vertexWriter, int light, int overlay, float radius, float r, float g, float b, float a) {
if (radius <= 0) {
return;
}
Matrix4f model = matrices.getPositionMatrix(); Matrix4f position = matrices.peek().getPositionMatrix();
final double num_rings = 40;
final double num_sectors = 40;
final double pi = Math.PI;
final double two_pi = Math.PI * 2F;
final double zenithIncrement = Math.PI / num_rings;
final double azimuthIncrement = two_pi / num_sectors;
double radius = 1;
for (double zenith = -pi; zenith < pi; zenith += zenithIncrement) { for (double zenith = -pi; zenith < pi; zenith += zenithIncrement) {
for (double azimuth = -two_pi; azimuth < two_pi; azimuth += azimuthIncrement) { for (double azimuth = -azimuthRange; azimuth < azimuthRange; azimuth += azimuthIncrement) {
drawQuad(model, vertexWriter, radius, zenith, azimuth, zenithIncrement, azimuthIncrement, light, overlay, r, g, b, a); drawQuad(position, vertexWriter, radius, zenith, azimuth, light, overlay, r, g, b, a);
} }
} }
} }
protected void drawQuad(Matrix4f model, VertexConsumer vertexWriter, private void drawQuad(Matrix4f model, VertexConsumer vertexWriter,
double radius, double zenith, double azimuth, double radius, double zenith, double azimuth,
double zenithIncrement, double azimuthIncrement,
int light, int overlay, float r, float g, float b, float a) { int light, int overlay, float r, float g, float b, float a) {
drawVertex(model, vertexWriter, convertToCartesianCoord(radius, zenith, azimuth), light, overlay, r, g, b, a);
drawVertex(model, vertexWriter, radius, zenith, azimuth, light, overlay, r, g, b, a); drawVertex(model, vertexWriter, convertToCartesianCoord(radius, zenith + zenithIncrement, azimuth), light, overlay, r, g, b, a);
drawVertex(model, vertexWriter, convertToCartesianCoord(radius, zenith + zenithIncrement, azimuth + azimuthIncrement), light, overlay, r, g, b, a);
drawVertex(model, vertexWriter, radius, zenith + zenithIncrement, azimuth, light, overlay, r, g, b, a); drawVertex(model, vertexWriter, convertToCartesianCoord(radius, zenith, azimuth + azimuthIncrement), light, overlay, r, g, b, a);
drawVertex(model, vertexWriter, radius, zenith + zenithIncrement, azimuth + azimuthIncrement, light, overlay, r, g, b, a);
drawVertex(model, vertexWriter, radius, zenith, azimuth + azimuthIncrement, light, overlay, r, g, b, a);
} }
public static void drawVertex(Matrix4f model, VertexConsumer vertexWriter, private static void drawVertex(Matrix4f model, VertexConsumer vertexWriter, Vector4f position, int light, int overlay, float r, float g, float b, float a) {
double radius, double zenith, double azimuth,
int light, int overlay, float r, float g, float b, float a) {
Vector4f position = convertToCartesianCoord(radius, zenith, azimuth);
position.transform(model); position.transform(model);
vertexWriter.vertex(position.getX(), position.getY(), position.getZ(), r, g, b, a, 0, 0, overlay, light, 0, 0, 0); vertexWriter.vertex(position.getX(), position.getY(), position.getZ(), r, g, b, a, 0, 0, overlay, light, 0, 0, 0);
} }
public static Vector4f convertToCartesianCoord(double r, double theta, double phi) { private static Vector4f convertToCartesianCoord(double r, double theta, double phi) {
double x = r * Math.sin(theta) * Math.cos(phi); double x = r * Math.sin(theta) * Math.cos(phi);
double y = r * Math.sin(theta) * Math.sin(phi); double y = r * Math.sin(theta) * Math.sin(phi);
double z = r * Math.cos(theta); double z = r * Math.cos(theta);

View file

@ -9,6 +9,7 @@ import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.world.ClientWorld; import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.client.render.RenderLayers; import com.minelittlepony.unicopia.client.render.RenderLayers;
@ -33,7 +34,7 @@ public class SphereParticle extends Particle implements Attachment {
private Optional<Link> link = Optional.empty(); private Optional<Link> link = Optional.empty();
private static final SphereModel MODEL = new SphereModel(); private final SphereParticleEffect parameters;
public SphereParticle(SphereParticleEffect parameters, ClientWorld w, double x, double y, double z, double vX, double vY, double vZ) { public SphereParticle(SphereParticleEffect parameters, ClientWorld w, double x, double y, double z, double vX, double vY, double vZ) {
this(parameters, w, x, y, z); this(parameters, w, x, y, z);
@ -45,6 +46,7 @@ public class SphereParticle extends Particle implements Attachment {
public SphereParticle(SphereParticleEffect parameters, ClientWorld w, double x, double y, double z) { public SphereParticle(SphereParticleEffect parameters, ClientWorld w, double x, double y, double z) {
super(w, x, y, z); super(w, x, y, z);
this.parameters = parameters;
this.radius = parameters.getRadius(); this.radius = parameters.getRadius();
this.colorRed = parameters.getColor().getX() / 255F; this.colorRed = parameters.getColor().getX() / 255F;
this.colorGreen = parameters.getColor().getY() / 255F; this.colorGreen = parameters.getColor().getY() / 255F;
@ -96,11 +98,12 @@ public class SphereParticle extends Particle implements Attachment {
if (link.isPresent()) { if (link.isPresent()) {
link.flatMap(Link::get).map(Caster::getEntity).ifPresentOrElse(e -> { link.flatMap(Link::get).map(Caster::getEntity).ifPresentOrElse(e -> {
setPos(e.getX(), e.getY() + 0.5, e.getZ()); Vec3d offset = parameters.getOffset();
setPos(e.getX() + offset.getX(), e.getY() + offset.getY(), e.getZ() + offset.getZ());
prevPosX = e.lastRenderX; prevPosX = e.lastRenderX + offset.getX();
prevPosY = e.lastRenderY + 0.5; prevPosY = e.lastRenderY + offset.getY();
prevPosZ = e.lastRenderZ; prevPosZ = e.lastRenderZ + offset.getZ();
}, this::detach); }, this::detach);
if (steps-- > 0) { if (steps-- > 0) {
@ -118,18 +121,12 @@ public class SphereParticle extends Particle implements Attachment {
return; return;
} }
float lerpedRad = MathHelper.lerp(tickDelta, prevRadius, radius);
float[] color = ColorHelper.changeSaturation(colorRed, colorGreen, colorBlue, 4); float[] color = ColorHelper.changeSaturation(colorRed, colorGreen, colorBlue, 4);
RenderSystem.setShaderColor(color[0], color[1], color[2], colorAlpha / 3F); RenderSystem.setShaderColor(color[0], color[1], color[2], colorAlpha / 3F);
VertexConsumerProvider.Immediate immediate = MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers(); VertexConsumerProvider.Immediate immediate = MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers();
VertexConsumer buffer = immediate.getBuffer(RenderLayers.getMagicGlow()); VertexConsumer buffer = immediate.getBuffer(RenderLayers.getMagicGlow());
float thickness = 0.05F;
int light = getBrightness(tickDelta);
MatrixStack matrices = new MatrixStack(); MatrixStack matrices = new MatrixStack();
matrices.push(); matrices.push();
@ -139,22 +136,10 @@ public class SphereParticle extends Particle implements Attachment {
MathHelper.lerp(tickDelta, prevPosZ, z) - camera.getPos().z MathHelper.lerp(tickDelta, prevPosZ, z) - camera.getPos().z
); );
float scale = lerpedRad + thickness; float scale = MathHelper.lerp(tickDelta, prevRadius, radius);
if (scale > 0) { renderModel(matrices, buffer, scale, tickDelta, getBrightness(tickDelta));
matrices.push();
matrices.scale(scale, scale, scale);
MODEL.render(matrices, buffer, light, 1, 1, 1, 1, 0.8F);
matrices.pop();
}
scale = lerpedRad - thickness;
if (scale > 0) {
matrices.scale(scale, scale, scale);
MODEL.render(matrices, buffer, light, 1, 1, 1, 1, 1);
}
matrices.pop(); matrices.pop();
immediate.draw(); immediate.draw();
@ -163,5 +148,14 @@ public class SphereParticle extends Particle implements Attachment {
RenderSystem.setShaderColor(1, 1, 1, 1); RenderSystem.setShaderColor(1, 1, 1, 1);
} }
protected void renderModel(MatrixStack matrices, VertexConsumer buffer, float lerpedRad, float tickDelta, int light) {
float thickness = 0.05F;
matrices.push();
SphereModel.SPHERE.render(matrices, buffer, light, 1, lerpedRad + thickness, 1, 1, 1, 0.8F);
matrices.pop();
SphereModel.SPHERE.render(matrices, buffer, light, 1, lerpedRad - thickness, 1, 1, 1, 1);
}
} }

View file

@ -10,6 +10,7 @@ import net.minecraft.particle.AbstractDustParticleEffect;
import net.minecraft.particle.ParticleEffect; import net.minecraft.particle.ParticleEffect;
import net.minecraft.particle.ParticleType; import net.minecraft.particle.ParticleType;
import net.minecraft.network.PacketByteBuf; import net.minecraft.network.PacketByteBuf;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.Vec3f; import net.minecraft.util.math.Vec3f;
import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.Registry;
@ -17,12 +18,16 @@ public class SphereParticleEffect implements ParticleEffect {
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public static final Factory<SphereParticleEffect> FACTORY = ParticleFactoryHelper.of(SphereParticleEffect::new, SphereParticleEffect::new); public static final Factory<SphereParticleEffect> FACTORY = ParticleFactoryHelper.of(SphereParticleEffect::new, SphereParticleEffect::new);
private static final Vec3d DEFAULT_OFFSET = new Vec3d(0, 0.5, 0);
private final Vec3f color; private final Vec3f color;
private final float alpha; private final float alpha;
private final float radius; private final float radius;
private Vec3d offset = Vec3d.ZERO;
protected SphereParticleEffect(ParticleType<? extends SphereParticleEffect> type, StringReader reader) throws CommandSyntaxException { protected SphereParticleEffect(ParticleType<? extends SphereParticleEffect> type, StringReader reader) throws CommandSyntaxException {
this(AbstractDustParticleEffect.readColor(reader), ParticleFactoryHelper.readFloat(reader), ParticleFactoryHelper.readFloat(reader)); this(AbstractDustParticleEffect.readColor(reader), ParticleFactoryHelper.readFloat(reader), ParticleFactoryHelper.readFloat(reader), ParticleFactoryHelper.readVector(reader));
} }
protected SphereParticleEffect(ParticleType<? extends SphereParticleEffect> type, PacketByteBuf buf) { protected SphereParticleEffect(ParticleType<? extends SphereParticleEffect> type, PacketByteBuf buf) {
@ -30,15 +35,28 @@ public class SphereParticleEffect implements ParticleEffect {
} }
public SphereParticleEffect(int tint, float alpha, float rad) { public SphereParticleEffect(int tint, float alpha, float rad) {
this(new Vec3f(Color.r(tint), Color.g(tint), Color.b(tint)), alpha, rad); this(tint, alpha, rad, DEFAULT_OFFSET);
} }
public SphereParticleEffect(Vec3f color, float alpha, float rad) { public SphereParticleEffect(Vec3f color, float alpha, float rad) {
this(color, alpha, rad, DEFAULT_OFFSET);
}
public SphereParticleEffect(int tint, float alpha, float rad, Vec3d offset) {
this(new Vec3f(Color.r(tint), Color.g(tint), Color.b(tint)), alpha, rad, offset);
}
public SphereParticleEffect(Vec3f color, float alpha, float rad, Vec3d offset) {
this.color = color; this.color = color;
this.offset = offset;
this.alpha = alpha; this.alpha = alpha;
this.radius = rad; this.radius = rad;
} }
public Vec3d getOffset() {
return offset;
}
public Vec3f getColor() { public Vec3f getColor() {
return color; return color;
} }
@ -63,10 +81,19 @@ public class SphereParticleEffect implements ParticleEffect {
buf.writeFloat(color.getZ()); buf.writeFloat(color.getZ());
buf.writeFloat(alpha); buf.writeFloat(alpha);
buf.writeFloat(radius); buf.writeFloat(radius);
buf.writeDouble(offset.getX());
buf.writeDouble(offset.getY());
buf.writeDouble(offset.getZ());
} }
@Override @Override
public String asString() { public String asString() {
return String.format(Locale.ROOT, "%s %.2f %.2f %.2f %.2f", Registry.PARTICLE_TYPE.getId(getType()), color.getX(), color.getY(), color.getZ(), alpha, radius); return String.format(Locale.ROOT, "%s %.2f %.2f %.2f %.2f %.2f %.2f %.2f",
Registry.PARTICLE_TYPE.getId(getType()),
color.getX(), color.getY(), color.getZ(),
alpha,
radius,
offset.getX(), offset.getY(), offset.getZ()
);
} }
} }