Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package io.ix0rai.rainglow.data;

import com.mojang.blaze3d.vertex.VertexConsumer;

public class ColorOverlayVertexConsumer implements VertexConsumer {
private final VertexConsumer base;
private final float r;
private final float g;
private final float b;

public ColorOverlayVertexConsumer(VertexConsumer base, float r, float g, float b) {
this.base = base;
this.r = r;
this.g = g;
this.b = b;
}

// Vertex
@Override
public VertexConsumer xyz(float f, float g, float h) {
return base.xyz(f, g, h);
}

@Override
public VertexConsumer color(int red, int green, int blue, int alpha) {
// Adjust Alpha value to stop my eyes from blowing out
// Change the float value to control transparency (For outer slime layer)
int adjustedAlpha = (int)(alpha * 0.9f);

return base.color(Math.min(255, (int)(red * r)), Math.min(255, (int)(green * g)), Math.min(255, (int)(blue * b)), adjustedAlpha);
}

// Texture
@Override
public VertexConsumer uv0(float f, float g) {
return base.uv0(f, g);
}

// Overlay
@Override
public VertexConsumer uv1(int i, int j) {
return base.uv1(i, j);
}

// Light
@Override
public VertexConsumer uv2(int i, int j) {
return base.uv2(i, j);
}

@Override
public VertexConsumer normal(float x, float y, float z) {
return base.normal(x, y, z);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,16 @@ public interface EntityRenderStateTracker {
* @return The UUID of the associated entity, or null if not set
*/
UUID rainglow$getEntityUuid();

/**
* Set the Rainbow Rendering for the associated entity
* @param isRainbow boolean value to toggle the rainbow rendering
*/
void rainglow$setRainbow(boolean isRainbow);

/**
* Get the Rainbow Rendering for the associated entity
* @return boolean for entity should have the rainbow rendering
*/
boolean rainglow$isRainbow();
}
74 changes: 74 additions & 0 deletions src/main/java/io/ix0rai/rainglow/data/RainbowManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package io.ix0rai.rainglow.data;

import net.minecraft.client.MinecraftClient;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;

import java.util.UUID;

// TODO: Might need the client environment tag, but should only run client side
public class RainbowManager {
public static LivingEntity findEntityByUuid(ClientWorld world, UUID uuid) {
for (Entity entity : world.getEntities()) {
if (entity.getUuid().equals(uuid)) {
return (LivingEntity) entity;
}
}

return null;
}

@SuppressWarnings("DataFlowIssue")
public static int getRainbowColor(UUID entityUuid) {
long worldTime = MinecraftClient.getInstance().world.getTime();

int hashCode = entityUuid.hashCode();
float entityOffset = (hashCode & 0x7FFFFFFF) % 1000 / 1000.0f;
float smoothHue = getHue(worldTime, entityOffset);

return smoothedHsvToRgb(smoothHue);
}

public static float getHue(long worldTime, float entityOffset) {
// Change this value to increase or decrease transition time, lower is quicker
final int cycleDuration = 1000;

// Progress within cycle (0.0 to 1.0)
float progress = (worldTime % cycleDuration) / (float)cycleDuration;
float smoothHue = (float)(0.5 + 0.5 * Math.sin(progress * 2 * Math.PI));

smoothHue = (smoothHue + entityOffset) % 1.0f;
return smoothHue;
}

public static int smoothedHsvToRgb(float h) {
h = h % 1.0f;
if (h < 0) h += 1.0f;

float hueSix = h * 6.0f;
int hueSixCategory = (int)hueSix;
float hueSixRemainder = hueSix - hueSixCategory;

float p = 0.0f;
float q = (1.0f - (hueSixRemainder));
float t = (1.0f - ((1.0f - hueSixRemainder)));

float r, g, b;

switch (hueSixCategory % 6) {
case 0: r = (float) 1.0; g = t; b = p; break;
case 1: r = q; g = (float) 1.0; b = p; break;
case 2: r = p; g = (float) 1.0; b = t; break;
case 3: r = p; g = q; b = (float) 1.0; break;
case 4: r = t; g = p; b = (float) 1.0; break;
default: r = (float) 1.0; g = p; b = q; break;
}

int ri = Math.min(255, Math.max(0, (int)(r * 255)));
int gi = Math.min(255, Math.max(0, (int)(g * 255)));
int bi = Math.min(255, Math.max(0, (int)(b * 255)));

return (0xFF << 24) | (ri << 16) | (gi << 8) | bi;
}
}
8 changes: 7 additions & 1 deletion src/main/java/io/ix0rai/rainglow/data/RainglowEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,13 @@ public static RainglowEntity get(Entity entity) {
}

@Nullable
public Identifier overrideTexture(UUID uuid, World world) {
public Identifier overrideTexture(UUID uuid, World world, boolean rainbowState) {
if (rainbowState) {
// Set the texture to light gray, best for overlaying colours as white is a bit too bright
Identifier texture = RainglowColour.LIGHT_GRAY.getTexture(this);
return texture != null ? texture : this.getDefaultTexture();
}

RainglowColour colour = Rainglow.getColour(uuid, world, this);

// Returning null will just use default texture, no need for extra checks
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ public void getTexture(class_9996 state, CallbackInfoReturnable<Identifier> cir)
if (entityUuid != null) {
ClientWorld world = MinecraftClient.getInstance().world;
if (world != null) {
boolean rainbowState = ((EntityRenderStateTracker) state).rainglow$isRainbow();

RainglowEntity type = RainglowEntity.ALLAY;
Identifier texture = type.overrideTexture(entityUuid, world);
Identifier texture = type.overrideTexture(entityUuid, world, rainbowState);
cir.setReturnValue(texture != null ? texture : type.getDefaultTexture());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
public class EntityRenderStateMixin implements EntityRenderStateTracker {
@Unique
private UUID entityUuid;
@Unique
private boolean isRainbow;

@Override
public void rainglow$setEntity(Entity entity) {
Expand All @@ -26,4 +28,14 @@ public class EntityRenderStateMixin implements EntityRenderStateTracker {
public UUID rainglow$getEntityUuid() {
return this.entityUuid;
}

@Override
public void rainglow$setRainbow(boolean isRainbow) {
this.isRainbow = isRainbow;
}

@Override
public boolean rainglow$isRainbow() {
return isRainbow;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ public class EntityRendererMixin<T extends Entity, S extends class_10017> {
private void updateRenderState(T entity, S state, float f, CallbackInfo ci) {
if (state instanceof EntityRenderStateTracker) {
((EntityRenderStateTracker) state).rainglow$setEntity(entity);

// TODO: This is a placeholder, better to add a new variant.
if (entity.hasCustomName()) {
((EntityRenderStateTracker) state).rainglow$setRainbow(entity.getCustomName().getString().contains("rainbow"));
} else {
// Reset it just in case
((EntityRenderStateTracker) state).rainglow$setRainbow(false);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ public void getTexture(class_10069 state, CallbackInfoReturnable<Identifier> cir
if (entityUuid != null) {
ClientWorld world = MinecraftClient.getInstance().world;
if (world != null) {
boolean rainbowState = ((EntityRenderStateTracker) state).rainglow$isRainbow();

RainglowEntity type = RainglowEntity.GLOW_SQUID;
Identifier texture = type.overrideTexture(entityUuid, world);
Identifier texture = type.overrideTexture(entityUuid, world, rainbowState);
cir.setReturnValue(texture != null ? texture : type.getDefaultTexture());
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package io.ix0rai.rainglow.mixin.client;

import io.ix0rai.rainglow.data.EntityRenderStateTracker;
import io.ix0rai.rainglow.data.RainbowManager;
import io.ix0rai.rainglow.data.RainglowEntity;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.entity.EntityRenderer;
import net.minecraft.client.render.entity.EntityRendererFactory;
import net.minecraft.client.render.entity.LivingEntityRenderer;
import net.minecraft.client.render.entity.feature.FeatureRendererContext;
import net.minecraft.client.render.entity.model.EntityModel;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.LivingEntity;
import net.minecraft.unmapped.C_ozphnurq;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import java.util.UUID;

@Mixin(LivingEntityRenderer.class)
public abstract class LivingEntityRendererMixin<T extends LivingEntity, S extends C_ozphnurq, M extends EntityModel<? super S>> extends EntityRenderer<T, S> implements FeatureRendererContext<S, M> {
protected LivingEntityRendererMixin(EntityRendererFactory.Context ctx) {
super(ctx);
}

@Inject(method = "method_62484", at = @At("RETURN"), cancellable = true)
private void getMixColour(S state, CallbackInfoReturnable<Integer> cir) {
// Only if this entity uses the State Tracker and has valid UUID
if (state instanceof EntityRenderStateTracker) {
UUID entityUuid = ((EntityRenderStateTracker) state).rainglow$getEntityUuid();
if (entityUuid != null) {
boolean rainbowState = ((EntityRenderStateTracker) state).rainglow$isRainbow();
if (rainbowState) {
ClientWorld world = MinecraftClient.getInstance().world;
if (world != null) {
LivingEntity entity = RainbowManager.findEntityByUuid(world, entityUuid);
if (RainglowEntity.get(entity) != null) {
int colour = RainbowManager.getRainbowColor(entityUuid);
cir.setReturnValue(colour);
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ public void getTexture(class_10067 state, CallbackInfoReturnable<Identifier> cir
if (entityUuid != null) {
ClientWorld world = MinecraftClient.getInstance().world;
if (world != null) {
boolean rainbowState = ((EntityRenderStateTracker) state).rainglow$isRainbow();

RainglowEntity type = RainglowEntity.SLIME;
Identifier texture = type.overrideTexture(entityUuid, world);
Identifier texture = type.overrideTexture(entityUuid, world, rainbowState);
cir.setReturnValue(texture != null ? texture : type.getDefaultTexture());
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package io.ix0rai.rainglow.mixin.client;

import com.mojang.blaze3d.vertex.VertexConsumer;
import io.ix0rai.rainglow.data.ColorOverlayVertexConsumer;
import io.ix0rai.rainglow.data.EntityRenderStateTracker;
import io.ix0rai.rainglow.data.RainbowManager;
import io.ix0rai.rainglow.data.RainglowEntity;
import net.minecraft.class_10067;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.entity.SlimeEntityRenderer;
import net.minecraft.client.render.entity.feature.SlimeOverlayFeatureRenderer;
import net.minecraft.client.render.entity.model.SlimeEntityModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
Expand Down Expand Up @@ -44,6 +49,34 @@ public class SlimeOverlayFeatureRendererMixin {
return overrideTexture != null ? RenderLayer.getEntityTranslucent(overrideTexture) : RenderLayer.getEntityTranslucent(SlimeEntityRenderer.TEXTURE);
}

@Redirect(method = "render*", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/entity/model/SlimeEntityModel;method_60879(Lnet/minecraft/client/util/math/MatrixStack;Lcom/mojang/blaze3d/vertex/VertexConsumer;II)V"))
private void rainglow$renderWithColour(SlimeEntityModel model, MatrixStack matrices, VertexConsumer vertices, int light, int overlay, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, class_10067 state, float f, float g) {
if (state instanceof EntityRenderStateTracker) {
UUID entityUuid = ((EntityRenderStateTracker) state).rainglow$getEntityUuid();
if (entityUuid != null && ((EntityRenderStateTracker) state).rainglow$isRainbow()) {
ClientWorld world = MinecraftClient.getInstance().world;
if (world != null) {
LivingEntity entity = RainbowManager.findEntityByUuid(world, entityUuid);
if (RainglowEntity.get(entity) != null) {
int rainbowColor = RainbowManager.getRainbowColor(entityUuid);

float r = ((rainbowColor >> 16) & 0xFF) / 255.0F;
float g1 = ((rainbowColor >> 8) & 0xFF) / 255.0F;
float b = (rainbowColor & 0xFF) / 255.0F;

VertexConsumer coloredVertices = new ColorOverlayVertexConsumer(vertices, r, g1, b);

model.method_60879(matrices, coloredVertices, light, overlay);
return;
}
}
}
}

// Default
model.method_60879(matrices, vertices, light, overlay);
}

@Unique
private Identifier getOverrideTexture(class_10067 state) {
if (state instanceof EntityRenderStateTracker) {
Expand All @@ -52,7 +85,8 @@ private Identifier getOverrideTexture(class_10067 state) {
ClientWorld world = MinecraftClient.getInstance().world;
if (world != null) {
try {
return RainglowEntity.SLIME.overrideTexture(entityUuid, world);
boolean rainbowState = ((EntityRenderStateTracker) state).rainglow$isRainbow();
return RainglowEntity.SLIME.overrideTexture(entityUuid, world, rainbowState);
} catch (Exception e) {
// ignore any errors and just let return null for default textures
}
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/rainglow.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"client.EntityRenderStateMixin",
"client.GlowParticleMixin",
"client.GlowSquidEntityRendererMixin",
"client.LivingEntityRendererMixin",
"client.SlimeEntityRendererMixin",
"client.SlimeOverlayFeatureRendererMixin",
"client.SlimeParticleMixin",
Expand Down