Skip to content

Commit

Permalink
Bare bones implementation of Colored Sound Particles
Browse files Browse the repository at this point in the history
  • Loading branch information
Redfan2 committed Oct 23, 2024
1 parent 662a3fe commit 8025680
Show file tree
Hide file tree
Showing 17 changed files with 245 additions and 54 deletions.
51 changes: 32 additions & 19 deletions src/main/java/ladysnake/requiem/client/particle/SoundParticle.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses>.
*
* Linking this mod statically or dynamically with second
* Linking this mod statically or dynamically with other
* modules is making a combined work based on this mod.
* Thus, the terms and conditions of the GNU General Public License cover the whole combination.
*
Expand All @@ -25,7 +25,7 @@
* and with code included in the standard release of Minecraft under All Rights Reserved (or
* modified versions of such code, with unchanged license).
* You may copy and distribute such a system following the terms of the GNU GPL for this mod
* and the licenses of the second code concerned.
* and the licenses of the other code concerned.
*
* Note that people who make modified versions of this mod are not obligated to grant
* this special exception for their modified versions; it is their choice whether to do so.
Expand All @@ -36,7 +36,9 @@

import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.VertexConsumer;
import ladysnake.requiem.api.v1.possession.PossessionComponent;
import ladysnake.requiem.client.render.RequiemRenderPhases;
import ladysnake.requiem.common.particle.RequiemSoundParticleEffect;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.particle.Particle;
import net.minecraft.client.particle.ParticleFactory;
Expand All @@ -47,7 +49,6 @@
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.particle.DefaultParticleType;
import net.minecraft.util.math.Axis;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
Expand All @@ -56,24 +57,38 @@
import org.lwjgl.opengl.GL11;
import org.quiltmc.loader.api.minecraft.ClientOnly;

public class SoundParticle extends SpriteBillboardParticle {
private final SpriteProvider spriteProvider;
import java.awt.*;

public class SoundParticle extends SpriteBillboardParticle {
public final SpriteProvider spriteProvider;
private float red;
private float green;
private float blue;
//private final ParticleTextureSheet sheet;
//TODO: by:Redfan2: think about which VertexConsumer to use to not conflict with Darkness fog effect
private static final VertexConsumerProvider.Immediate soundVertexConsumerProvider = MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers();
private static final VertexConsumerProvider.Immediate soundVertexConsumerProvider = MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers();
//Tessellator.getInstance().getBufferBuilder()
//Doesnt render: Investigate: VertexConsumerProvider.immediate(new BufferBuilder(RequiemRenderPhases.GHOST_PARTICLE_LAYER.getExpectedBufferSize()));

public SoundParticle(ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ, SpriteProvider spriteProvider) {
public SoundParticle(ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ, SpriteProvider spriteProvider, int color) {
super(world, x, y, z, velocityX, velocityY, velocityZ);
this.spriteProvider = spriteProvider;
this.setSpriteForAge(spriteProvider);

//Testing
Color tempColor = Color.BLUE;
this.green = tempColor.getGreen();
//this.sheet=new TextureSheet(spriteProvider);
//Tessellator.getInstance().getBufferBuilder().
this.red = tempColor.getRed();
this.blue = tempColor.getBlue();
this.maxAge = 10;
this.collidesWithWorld = false;
}

@Override
public void buildGeometry(VertexConsumer vertexConsumer, Camera camera, float tickDelta) {
if (camera.getFocusedEntity() instanceof PlayerEntity) {
if (camera.getFocusedEntity() instanceof PlayerEntity player && player.getComponent(PossessionComponent.KEY).isPossessionOngoing()) {
//TODO Disabled for testing if (player.getComponent(PossessionComponent.KEY).getHost() instanceof WardenEntity) {
VertexConsumer actualConsumer = soundVertexConsumerProvider.getBuffer(RequiemRenderPhases.GHOST_PARTICLE_LAYER);

RenderSystem.disableDepthTest();
Expand Down Expand Up @@ -112,9 +127,6 @@ public void buildGeometry(VertexConsumer vertexConsumer, Camera camera, float ti
int l = 15728880;
float alpha = 1;

float red = 1f;
float green = 1f;
float blue = 1f;
actualConsumer.vertex(Vec3fs[0].x(), Vec3fs[0].y(), Vec3fs[0].z()).uv(maxU, maxV).color(red, green, blue, alpha).light(l).next();
actualConsumer.vertex(Vec3fs[1].x(), Vec3fs[1].y(), Vec3fs[1].z()).uv(maxU, minV).color(red, green, blue, alpha).light(l).next();
actualConsumer.vertex(Vec3fs[2].x(), Vec3fs[2].y(), Vec3fs[2].z()).uv(minU, minV).color(red, green, blue, alpha).light(l).next();
Expand All @@ -124,7 +136,7 @@ public void buildGeometry(VertexConsumer vertexConsumer, Camera camera, float ti
soundVertexConsumerProvider.draw();
RenderSystem.enableDepthTest();
RenderSystem.depthFunc(GL11.GL_LEQUAL);

//}
} else {
this.markDead();
}
Expand All @@ -148,19 +160,20 @@ public void tick() {
}

@ClientOnly
public static class Factory implements ParticleFactory<DefaultParticleType> {
private final SpriteProvider spriteProvider;
public static class Factory implements ParticleFactory<RequiemSoundParticleEffect> {
private final SpriteProvider factorySpriteProvider;

public Factory(SpriteProvider spriteProvider) {
this.spriteProvider = spriteProvider;
this.factorySpriteProvider = spriteProvider;
}

public Particle createParticle(DefaultParticleType defaultParticleType, ClientWorld clientWorld, double d, double e, double f, double g, double h, double i) {
return new SoundParticle(clientWorld, d, e, f, g, h, i, this.spriteProvider);
@Override
public Particle createParticle(RequiemSoundParticleEffect particleEffect, ClientWorld clientWorld, double d, double e, double f, double g, double h, double i) {
return new SoundParticle(clientWorld, d, e, f, g, h, i, this.factorySpriteProvider, particleEffect.getColor());
}
}

//Stolen using Linkie
//Stolen using Linkie from Minecraft itself as it doesn't exist on JOML Quaternions
public void hamiltonProduct(Quaternionf first, Quaternionf second) {
float var2 = first.x();
float var3 = first.y();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,22 @@

import dev.onyxstudios.cca.api.v3.component.ComponentKey;
import dev.onyxstudios.cca.api.v3.component.ComponentRegistry;
import dev.onyxstudios.cca.api.v3.component.TransientComponent;
import dev.onyxstudios.cca.api.v3.component.sync.AutoSyncedComponent;
import dev.onyxstudios.cca.api.v3.entity.PlayerComponent;
import ladysnake.requiem.Requiem;
import ladysnake.requiem.api.v1.possession.PossessionComponent;
import ladysnake.requiem.core.RequiemCore;
import net.minecraft.entity.Entity;
import net.minecraft.entity.mob.warden.WardenEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.server.network.ServerPlayerEntity;

import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

public class WardenSensedComponent implements PlayerComponent<WardenSensedComponent>, AutoSyncedComponent {
public class WardenSensedComponent implements TransientComponent, AutoSyncedComponent {
/*TODO proper implementation of syncing
*/
public static final ComponentKey<WardenSensedComponent> KEY = ComponentRegistry.getOrCreate(RequiemCore.id("warden_sensed"), WardenSensedComponent.class);
Expand All @@ -67,13 +67,13 @@ public Set<UUID> getVisible() {
}

@Override
public void readFromNbt(NbtCompound nbt) {
int size = nbt.getInt("list_size");
public void applySyncPacket(PacketByteBuf buf) {
int size = buf.readInt();
if (size > 0) {
Set<UUID> uuids = new HashSet<>();
for (int i=0; i < size; i++) {
try {
uuids.add(nbt.getUuid("warden_target_"+i));
uuids.add(buf.readUuid());
} catch (Exception error) {
Requiem.LOGGER.error("Could not read element {}","warden_target_"+i );
Requiem.LOGGER.error("Reason: {}", error.getMessage());
Expand All @@ -83,17 +83,14 @@ public void readFromNbt(NbtCompound nbt) {
}
}


@Override
public void writeToNbt(NbtCompound nbt) {
public void writeSyncPacket(PacketByteBuf buf, ServerPlayerEntity recipient) {

//TODO think about a better data format for this
//MC only seems to have a ByteArray as closest, no UUIDArray or even just StringArray in NBTCompound
nbt.putInt("list_size", entities.size());
buf.writeInt(entities.size());
for (int i = 0; i < entities.size(); i++) {
nbt.putUuid(
"warden_target_"+i,
entities.stream().toList().get(i)
);
buf.writeUuid(entities.stream().toList().get(i));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@

import com.mojang.serialization.Codec;
import ladysnake.requiem.Requiem;
import ladysnake.requiem.client.particle.SoundParticle;
import net.fabricmc.fabric.api.particle.v1.FabricParticleTypes;
import net.minecraft.particle.DefaultParticleType;
import net.minecraft.particle.ParticleType;
Expand All @@ -62,7 +61,12 @@ public Codec<WispTrailParticleEffect> getCodec() {
};
public static final DefaultParticleType OBELISK_SOUL = FabricParticleTypes.simple(false);
public static final DefaultParticleType PENANCE = FabricParticleTypes.simple(false);
public static final DefaultParticleType SOUND = FabricParticleTypes.simple(true);
public static final ParticleType<RequiemSoundParticleEffect> SOUND = new ParticleType<>(false, RequiemSoundParticleEffect.PARAMETERS_FACTORY) {
@Override
public Codec<RequiemSoundParticleEffect> getCodec() {
return RequiemSoundParticleEffect.codec(this);
}
};

public static void init() {
Registry.register(Registries.PARTICLE_TYPE, Requiem.id("attrition"), ATTRITION);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Requiem
* Copyright (C) 2017-2024 Ladysnake
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses>.
*
* Linking this mod statically or dynamically with other
* modules is making a combined work based on this mod.
* Thus, the terms and conditions of the GNU General Public License cover the whole combination.
*
* In addition, as a special exception, the copyright holders of
* this mod give you permission to combine this mod
* with free software programs or libraries that are released under the GNU LGPL
* and with code included in the standard release of Minecraft under All Rights Reserved (or
* modified versions of such code, with unchanged license).
* You may copy and distribute such a system following the terms of the GNU GPL for this mod
* and the licenses of the other code concerned.
*
* Note that people who make modified versions of this mod are not obligated to grant
* this special exception for their modified versions; it is their choice whether to do so.
* The GNU General Public License gives permission to release a modified version without this exception;
* this exception also makes it possible to release a modified version which carries forward this exception.
*/
package ladysnake.requiem.common.particle;

import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.particle.ParticleEffect;
import net.minecraft.particle.ParticleType;
import net.minecraft.registry.Registries;


public class RequiemSoundParticleEffect implements ParticleEffect {
public RequiemSoundParticleEffect(ParticleType<RequiemSoundParticleEffect> type, int color) {
this.type = type;
this.color = color;
}

public int color;
private final ParticleType<RequiemSoundParticleEffect> type;


public static final Factory<RequiemSoundParticleEffect> PARAMETERS_FACTORY = new Factory<>() {
@Override
public RequiemSoundParticleEffect read(ParticleType<RequiemSoundParticleEffect> particleType, StringReader stringReader) throws CommandSyntaxException {
stringReader.expect(' ');
int color = stringReader.readInt();
return new RequiemSoundParticleEffect(particleType, color);
}

@Override
public RequiemSoundParticleEffect read(ParticleType<RequiemSoundParticleEffect> particleType, PacketByteBuf buf) {
return new RequiemSoundParticleEffect(particleType, buf.readInt());
}
};

@Override
public void write(PacketByteBuf buf) {
buf.writeInt(color);
}

@Override
public String asString() {
return "%s %d".formatted(Registries.PARTICLE_TYPE.getId(this.getType()), this.color);
}

@Override
public ParticleType<RequiemSoundParticleEffect> getType() {
return this.type;
}

public int getColor() {
return this.color;
}

public static Codec<RequiemSoundParticleEffect> codec(ParticleType particleType) {
return RecordCodecBuilder.create(instance -> instance.group(
Codec.INT.fieldOf("color").forGetter(RequiemSoundParticleEffect::getColor)
).apply(instance, color -> new RequiemSoundParticleEffect(particleType, color)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,12 @@ private static void preventShadowRender(MatrixStack matrices, VertexConsumerProv
public void notRenderNonDetectedByWarden(Entity entity, Frustum frustum, double x, double y, double z, CallbackInfoReturnable<Boolean> info) {

/**/if (entity instanceof WardenEntity) {
//TODO: by:Redfan2: Fix posessed warden itself not rendering
info.setReturnValue(true);
}

ClientPlayerEntity player = MinecraftClient.getInstance().player;
//TODO: by:Redfan2: Fix posessed warden itself not rendering

if (requiem_camerasPossessed != null) {
//Check for posession
if (requiem_camerasPossessed instanceof WardenEntity) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,19 @@
*/
package ladysnake.requiem.mixin.common.possession.gameplay;

import ladysnake.requiem.Requiem;
import ladysnake.requiem.api.v1.possession.PossessionComponent;
import ladysnake.requiem.common.particle.RequiemParticleTypes;
import ladysnake.requiem.common.particle.RequiemSoundParticleEffect;
import net.minecraft.entity.mob.warden.WardenEntity;
import net.minecraft.network.packet.Packet;
import net.minecraft.network.packet.s2c.play.ParticleS2CPacket;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.tag.GameEventTags;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.event.GameEvent;
import org.spongepowered.asm.mixin.Mixin;
Expand All @@ -50,6 +55,7 @@
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import java.awt.*;
import java.util.List;

@Mixin(ServerWorld.class)
Expand All @@ -64,11 +70,32 @@ public abstract class ServerWorldMixin {
@Inject(method = "emitGameEvent(Lnet/minecraft/world/event/GameEvent;Lnet/minecraft/util/math/Vec3d;Lnet/minecraft/world/event/GameEvent$Context;)V",at=@At("HEAD"))
public void emitGameEvent(GameEvent event, Vec3d pos, GameEvent.Context context, CallbackInfo ci) {
if (event.isIn(GameEventTags.WARDEN_CAN_SENSE)) {
//TODO: by:Redfan2: maybe ask Tags for Color

Packet<?> packet = new ParticleS2CPacket(RequiemParticleTypes.SOUND, true, pos.getX() + .5f, pos.getY() + .5f, pos.getZ() + .5f, 0, 0, 0, 0, 1);
//Fallback
Color color = Color.white;

if (event.isIn(TagKey.of(RegistryKeys.GAME_EVENT, new Identifier(Requiem.MOD_ID,"blocks")))) {
color = Color.green;
}
if (event.isIn(TagKey.of(RegistryKeys.GAME_EVENT,new Identifier(Requiem.MOD_ID,"entities")))) {
color = Color.cyan;
}
Packet<?> packet = new ParticleS2CPacket(
new RequiemSoundParticleEffect(RequiemParticleTypes.SOUND, color.getRGB()),
true,
pos.getX() + .5f,
pos.getY() + .5f,
pos.getZ() + .5f,
0,
0,
0,
0,
1
);
for (ServerPlayerEntity player : this.getPlayers()) {
if (PossessionComponent.get(player).isPossessionOngoing() && PossessionComponent.getHost(player) instanceof WardenEntity && !(context.sourceEntity() instanceof WardenEntity)) {
this.sendToPlayerIfNearby(player, true, pos.getX() + .5f, pos.getY() + .5f, pos.getZ() + .5f, packet);
this.sendToPlayerIfNearby(player, true, pos.getX(), pos.getY(), pos.getZ(), packet);
}
}
}
Expand Down
Loading

0 comments on commit 8025680

Please sign in to comment.