mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-23 21:38:00 +01:00
Implement proper spell rendering
This commit is contained in:
parent
23266d2f3a
commit
5011c35e15
11 changed files with 283 additions and 11 deletions
|
@ -165,7 +165,7 @@ public final class SpellTraits implements Iterable<Map.Entry<Trait, Float>> {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "[" + traits.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(",")) + "]";
|
||||
return "SpellTraits[" + traits.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(",")) + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,6 +17,7 @@ import com.minelittlepony.unicopia.client.particle.ShockwaveParticle;
|
|||
import com.minelittlepony.unicopia.client.particle.SphereParticle;
|
||||
import com.minelittlepony.unicopia.client.render.*;
|
||||
import com.minelittlepony.unicopia.client.render.entity.*;
|
||||
import com.minelittlepony.unicopia.client.render.spell.SpellRendererFactory;
|
||||
import com.minelittlepony.unicopia.entity.mob.UEntities;
|
||||
import com.minelittlepony.unicopia.item.ChameleonItem;
|
||||
import com.minelittlepony.unicopia.item.EnchantableItem;
|
||||
|
@ -182,6 +183,8 @@ public interface URenderers {
|
|||
BlockRenderLayerMap.INSTANCE.putFluids(RenderLayer.getTranslucent(), Fluids.LAVA, Fluids.FLOWING_LAVA);
|
||||
|
||||
TerraformBoatClientHelper.registerModelLayers(Unicopia.id("palm"), false);
|
||||
|
||||
SpellRendererFactory.bootstrap();
|
||||
}
|
||||
|
||||
static <T extends ParticleEffect> PendingParticleFactory<T> createFactory(ParticleSupplier<T> supplier) {
|
||||
|
|
|
@ -15,6 +15,7 @@ import com.minelittlepony.unicopia.client.gui.UHud;
|
|||
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen;
|
||||
import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate;
|
||||
import com.minelittlepony.unicopia.client.render.shader.ViewportShader;
|
||||
import com.minelittlepony.unicopia.client.render.spell.SpellEffectsRenderDispatcher;
|
||||
import com.minelittlepony.unicopia.container.*;
|
||||
import com.minelittlepony.unicopia.entity.player.PlayerCamera;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
|
@ -107,6 +108,7 @@ public class UnicopiaClient implements ClientModInitializer {
|
|||
ItemTooltipCallback.EVENT.register(new ModifierTooltipRenderer());
|
||||
|
||||
ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(ViewportShader.INSTANCE);
|
||||
ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(SpellEffectsRenderDispatcher.INSTANCE);
|
||||
|
||||
Unicopia.SIDE = () -> Optional.ofNullable(MinecraftClient.getInstance().player).map(Pony::of);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
package com.minelittlepony.unicopia.client.minelittlepony;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.minelittlepony.api.model.*;
|
||||
import com.minelittlepony.api.model.fabric.PonyModelPrepareCallback;
|
||||
import com.minelittlepony.api.model.gear.IGear;
|
||||
|
@ -13,10 +19,21 @@ import com.minelittlepony.unicopia.util.AnimationUtil;
|
|||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class Main extends MineLPDelegate implements ClientModInitializer {
|
||||
private static final Map<com.minelittlepony.api.pony.meta.Race, Race> PONY_RACE_MAPPING = new HashMap<>();
|
||||
private static final Function<com.minelittlepony.api.pony.meta.Race, Race> LOOKUP_CACHE = Util.memoize(race -> {
|
||||
return Optional.ofNullable(PONY_RACE_MAPPING.get(race))
|
||||
.or(() -> Race.REGISTRY.getOrEmpty(Unicopia.id(race.name().toLowerCase(Locale.ROOT))))
|
||||
.orElse(Race.UNSET);
|
||||
});
|
||||
|
||||
public static void registerRaceMapping(com.minelittlepony.api.pony.meta.Race minelpRace, Race unicopiaRace) {
|
||||
PONY_RACE_MAPPING.put(minelpRace, unicopiaRace);
|
||||
}
|
||||
|
||||
private boolean hookErroring;
|
||||
|
||||
|
@ -33,6 +50,14 @@ public class Main extends MineLPDelegate implements ClientModInitializer {
|
|||
IGear.register(BodyPartGear::unicornHorn);
|
||||
IGear.register(AmuletGear::new);
|
||||
IGear.register(GlassesGear::new);
|
||||
IGear.register(SpellEffectGear::new);
|
||||
|
||||
registerRaceMapping(com.minelittlepony.api.pony.meta.Race.CHANGEDLING, Race.CHANGELING);
|
||||
registerRaceMapping(com.minelittlepony.api.pony.meta.Race.ZEBRA, Race.EARTH);
|
||||
registerRaceMapping(com.minelittlepony.api.pony.meta.Race.GRYPHON, Race.PEGASUS);
|
||||
registerRaceMapping(com.minelittlepony.api.pony.meta.Race.HIPPOGRIFF, Race.PEGASUS);
|
||||
registerRaceMapping(com.minelittlepony.api.pony.meta.Race.BATPONY, Race.BAT);
|
||||
registerRaceMapping(com.minelittlepony.api.pony.meta.Race.SEAPONY, Race.UNICORN);
|
||||
}
|
||||
|
||||
private void onPonyModelPrepared(Entity entity, IModel model, ModelAttributes.Mode mode) {
|
||||
|
@ -75,7 +100,7 @@ public class Main extends MineLPDelegate implements ClientModInitializer {
|
|||
|
||||
@Override
|
||||
public Race getRace(Entity entity) {
|
||||
return IPony.getManager().getPony(entity).map(IPony::race).map(Main::toUnicopiaRace).orElse(Race.HUMAN);
|
||||
return IPony.getManager().getPony(entity).map(IPony::race).map(Main::toUnicopiaRace).orElse(Race.UNSET);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -84,14 +109,6 @@ public class Main extends MineLPDelegate implements ClientModInitializer {
|
|||
}
|
||||
|
||||
private static Race toUnicopiaRace(com.minelittlepony.api.pony.meta.Race race) {
|
||||
return switch (race) {
|
||||
case ALICORN -> Race.ALICORN;
|
||||
case CHANGELING, CHANGEDLING -> Race.CHANGELING;
|
||||
case ZEBRA, EARTH -> Race.EARTH;
|
||||
case GRYPHON, HIPPOGRIFF, PEGASUS -> Race.PEGASUS;
|
||||
case BATPONY -> Race.BAT;
|
||||
case SEAPONY, UNICORN, KIRIN -> Race.UNICORN;
|
||||
default -> Race.HUMAN;
|
||||
};
|
||||
return LOOKUP_CACHE.apply(race);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
package com.minelittlepony.unicopia.client.minelittlepony;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.minelittlepony.api.model.BodyPart;
|
||||
import com.minelittlepony.api.model.IModel;
|
||||
import com.minelittlepony.api.model.gear.IGear;
|
||||
import com.minelittlepony.client.model.IPonyModel;
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.client.render.BraceletFeatureRenderer;
|
||||
import com.minelittlepony.unicopia.client.render.spell.SpellEffectsRenderDispatcher;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.render.entity.model.EntityModel;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
class SpellEffectGear implements IGear {
|
||||
private Caster<?> caster;
|
||||
private float limbAngle;
|
||||
private float limbDistance;
|
||||
private float animationProgress;
|
||||
|
||||
@Override
|
||||
public boolean canRender(IModel model, Entity entity) {
|
||||
return Caster.of(entity).isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BodyPart getGearLocation() {
|
||||
return BodyPart.LEGS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Entity> Identifier getTexture(T entity, Context<T, ?> context) {
|
||||
return BraceletFeatureRenderer.TEXTURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <M extends EntityModel<?> & IPonyModel<?>> void transform(M model, MatrixStack matrices) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pose(IModel model, Entity entity, boolean rainboom, UUID interpolatorId, float move, float swing, float bodySwing, float ticks) {
|
||||
caster = Caster.of(entity).orElse(null);
|
||||
limbAngle = move;
|
||||
limbDistance = swing;
|
||||
animationProgress = entity.age + ticks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(MatrixStack stack, VertexConsumer consumer, int light, int overlay, float red, float green, float blue, float alpha, UUID interpolatorId) {
|
||||
SpellEffectsRenderDispatcher.INSTANCE.render(
|
||||
stack,
|
||||
MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers(),
|
||||
light, caster,
|
||||
limbAngle, limbDistance,
|
||||
MinecraftClient.getInstance().getTickDelta(), animationProgress, 0, 0
|
||||
);
|
||||
}
|
||||
}
|
|
@ -4,7 +4,9 @@ import java.util.*;
|
|||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.client.FirstPersonRendererOverrides.ArmRenderer;
|
||||
import com.minelittlepony.unicopia.client.render.spell.SpellEffectsRenderDispatcher;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.model.ModelPart;
|
||||
|
@ -37,6 +39,10 @@ public class AccessoryFeatureRenderer<
|
|||
@Override
|
||||
public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, T entity, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) {
|
||||
features.forEach(feature -> feature.render(matrices, vertexConsumers, light, entity, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch));
|
||||
|
||||
Caster.of(entity).ifPresent(caster -> {
|
||||
SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertexConsumers, light, caster, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch);
|
||||
});
|
||||
}
|
||||
|
||||
public void renderArm(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, T entity, ModelPart arm, Arm side) {
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
package com.minelittlepony.unicopia.client.render.entity;
|
||||
|
||||
import com.minelittlepony.unicopia.client.render.spell.SpellEffectsRenderDispatcher;
|
||||
import com.minelittlepony.unicopia.entity.mob.CastSpellEntity;
|
||||
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.entity.EntityRenderer;
|
||||
import net.minecraft.client.render.entity.EntityRendererFactory;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.screen.PlayerScreenHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class CastSpellEntityRenderer extends EntityRenderer<CastSpellEntity> {
|
||||
private final SpellEffectsRenderDispatcher spellRenderDispatcher = new SpellEffectsRenderDispatcher();
|
||||
|
||||
public CastSpellEntityRenderer(EntityRendererFactory.Context ctx) {
|
||||
super(ctx);
|
||||
|
@ -17,4 +21,13 @@ public class CastSpellEntityRenderer extends EntityRenderer<CastSpellEntity> {
|
|||
public Identifier getTexture(CastSpellEntity entity) {
|
||||
return PlayerScreenHandler.BLOCK_ATLAS_TEXTURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(CastSpellEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light) {
|
||||
spellRenderDispatcher.render(matrices, vertexConsumers, light, entity, 0, 0, tickDelta, getAnimationProgress(entity, tickDelta), yaw, 0);
|
||||
}
|
||||
|
||||
protected float getAnimationProgress(CastSpellEntity entity, float tickDelta) {
|
||||
return entity.age + tickDelta;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
package com.minelittlepony.unicopia.client.render.spell;
|
||||
|
||||
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||
|
||||
public interface AllSpells extends SpellPredicate<Spell> {
|
||||
AllSpells INSTANCE = spell -> true;
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
package com.minelittlepony.unicopia.client.render.spell;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.SpellContainer.Operation;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||
import com.minelittlepony.unicopia.entity.Living;
|
||||
|
||||
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.font.TextRenderer.TextLayerType;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.WorldRenderer;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.resource.ResourceManager;
|
||||
import net.minecraft.resource.SynchronousResourceReloader;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.Box;
|
||||
import net.minecraft.util.math.RotationAxis;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader, IdentifiableResourceReloadListener {
|
||||
public static final SpellEffectsRenderDispatcher INSTANCE = new SpellEffectsRenderDispatcher();
|
||||
private static final Identifier ID = Unicopia.id("spell_renderers");
|
||||
private static final Map<SpellType<?>, SpellRendererFactory<?>> REGISTRY = new HashMap<>();
|
||||
|
||||
public static <T extends Spell> void register(SpellType<T> type, SpellRendererFactory<? super T> rendererFactory) {
|
||||
REGISTRY.put(type, rendererFactory);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Map<SpellType<?>, SpellRenderer<?>> renderers = Map.of();
|
||||
private final MinecraftClient client = MinecraftClient.getInstance();
|
||||
|
||||
@Override
|
||||
public Identifier getFabricId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <S extends Spell> SpellRenderer<S> getRenderer(S spell) {
|
||||
return (SpellRenderer<S>)renderers.get(spell.getType());
|
||||
}
|
||||
|
||||
public void render(MatrixStack matrices, VertexConsumerProvider vertices, int light, Caster<?> caster, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) {
|
||||
caster.getSpellSlot().forEach(spell -> {
|
||||
var renderer = getRenderer(spell);
|
||||
if (renderer != null) {
|
||||
renderer.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch);
|
||||
}
|
||||
return Operation.SKIP;
|
||||
}, false);
|
||||
|
||||
if (client.getEntityRenderDispatcher().shouldRenderHitboxes() && !client.hasReducedDebugInfo()) {
|
||||
renderHotspot(matrices, vertices, caster, animationProgress);
|
||||
renderSpellDebugInfo(matrices, vertices, caster, light);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload(ResourceManager manager) {
|
||||
renderers = REGISTRY.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().create()));
|
||||
}
|
||||
|
||||
private void renderSpellDebugInfo(MatrixStack matrices, VertexConsumerProvider vertices, Caster<?> caster, int light) {
|
||||
matrices.push();
|
||||
matrices.multiply(client.getEntityRenderDispatcher().getRotation());
|
||||
float scale = 0.0125F;
|
||||
if (caster instanceof Living) {
|
||||
matrices.scale(scale, scale, scale);
|
||||
} else {
|
||||
matrices.scale(-scale, -scale, scale);
|
||||
}
|
||||
float g = MinecraftClient.getInstance().options.getTextBackgroundOpacity(0.25f);
|
||||
int j = (int)(g * 255.0f) << 24;
|
||||
|
||||
List<Text> debugLines = Stream.concat(
|
||||
Stream.of(
|
||||
caster.asEntity().getDisplayName().copy().append(" (" + Registries.ENTITY_TYPE.getId(caster.asEntity().getType()) + ")"),
|
||||
caster.getMaster() != null ? Text.literal("Master: ").append(caster.getMaster().getDisplayName()) : Text.empty()
|
||||
),
|
||||
caster.getSpellSlot().stream(AllSpells.INSTANCE, false).flatMap(spell ->
|
||||
Stream.of(
|
||||
Text.literal("UUID: " + spell.getUuid()),
|
||||
Text.literal("|>Type: ").append(Text.literal(spell.getType().getId().toString()).styled(s -> s.withColor(spell.getType().getColor()))),
|
||||
Text.of("|>Traits: " + spell.getTraits()),
|
||||
Text.literal("|>HasRenderer: ").append(Text.literal((getRenderer(spell) != null) + "").formatted(getRenderer(spell) != null ? Formatting.GREEN : Formatting.RED))
|
||||
)
|
||||
)
|
||||
).toList();
|
||||
|
||||
int spacing = client.textRenderer.fontHeight + 1;
|
||||
int height = spacing * debugLines.size();
|
||||
int top = -height;
|
||||
int left = (int)caster.asEntity().getWidth() * 64;
|
||||
|
||||
for (Text line : debugLines) {
|
||||
client.textRenderer.draw(line, left += 1, top += spacing, 0xFFFFFFFF, false, matrices.peek().getPositionMatrix(), vertices, TextLayerType.NORMAL, j, light);
|
||||
}
|
||||
matrices.pop();
|
||||
}
|
||||
|
||||
private void renderHotspot(MatrixStack matrices, VertexConsumerProvider vertices, Caster<?> caster, float animationProgress) {
|
||||
Box boundingBox = Box.of(caster.getOriginVector(), 1, 1, 1);
|
||||
|
||||
Vec3d pos = caster.getOriginVector();
|
||||
|
||||
double x = - pos.x;
|
||||
double y = - pos.y;
|
||||
double z = - pos.z;
|
||||
|
||||
VertexConsumer buffer = vertices.getBuffer(RenderLayer.getLines());
|
||||
|
||||
for (float i = -1; i < 1; i += 0.2F) {
|
||||
matrices.push();
|
||||
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(i * animationProgress));
|
||||
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(i * animationProgress));
|
||||
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(i * animationProgress));
|
||||
matrices.scale(i, i, i);
|
||||
WorldRenderer.drawBox(matrices, buffer, boundingBox.offset(x, y, z), 1, 0, 0, 1);
|
||||
matrices.pop();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package com.minelittlepony.unicopia.client.render.spell;
|
||||
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
|
||||
public interface SpellRenderer<T extends Spell> {
|
||||
void render(MatrixStack matrices, VertexConsumerProvider vertices, T spell, Caster<?> caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package com.minelittlepony.unicopia.client.render.spell;
|
||||
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||
|
||||
public interface SpellRendererFactory<T extends Spell> {
|
||||
SpellRenderer<T> create();
|
||||
|
||||
static void bootstrap() {
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue