Add DhApiBlockColorOverrideEvent
This commit is contained in:
+148
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.api.methods.events.abstractEvents;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
|
||||
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
|
||||
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent;
|
||||
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam;
|
||||
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
|
||||
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Performance note: this event will be fired thousands of times on concurrent threads,
|
||||
* make it thread safe and as fast as possible. <Br><Br>
|
||||
*
|
||||
* This event is fired when DH needs to convert a {@link IDhApiBlockStateWrapper}
|
||||
* into a color for rendering. This event is fired after DH attempts to determine
|
||||
* the color itself (using the base color, tinting, etc.). <Br>
|
||||
* Using this event will override the tinting config for this block
|
||||
* unless you re-implement that logic yourself.
|
||||
* <Br><Br>
|
||||
*
|
||||
* This event will only trigger for {@link IDhApiBlockStateWrapper}s that have been registered
|
||||
* via {@link DhApiBlockStateWrapperCreatedEvent.EventParam#setAllowApiColorOverride(boolean)}.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2026-04-14
|
||||
* @since API 6.0.0
|
||||
* @see IDhApiBlockStateWrapper
|
||||
*/
|
||||
public abstract class DhApiBlockColorOverrideEvent implements IDhApiEvent<DhApiBlockColorOverrideEvent.EventParam>
|
||||
{
|
||||
public abstract void blockStateWrapperCreated(DhApiEventParam<EventParam> event);
|
||||
|
||||
|
||||
//=========================//
|
||||
// internal DH API methods //
|
||||
//=========================//
|
||||
|
||||
@Override
|
||||
public final void fireEvent(DhApiEventParam<EventParam> event) { this.blockStateWrapperCreated(event); }
|
||||
|
||||
|
||||
//==================//
|
||||
// parameter object //
|
||||
//==================//
|
||||
|
||||
public static class EventParam implements IDhApiEventParam
|
||||
{
|
||||
private IDhApiLevelWrapper levelWrapper;
|
||||
private IDhApiBlockStateWrapper blockStateWrapper = null;
|
||||
private int colorAsInt = -1;
|
||||
private int blockPosX = 0, blockPosY = 0, blockPosZ = 0;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public EventParam() {}
|
||||
|
||||
public void update(
|
||||
IDhApiLevelWrapper levelWrapper,
|
||||
IDhApiBlockStateWrapper blockStateWrapper,
|
||||
int colorAsInt,
|
||||
int blockPosX, int blockPosY, int blockPosZ)
|
||||
{
|
||||
this.levelWrapper = levelWrapper;
|
||||
this.blockStateWrapper = blockStateWrapper;
|
||||
this.colorAsInt = colorAsInt;
|
||||
|
||||
this.blockPosX = blockPosX;
|
||||
this.blockPosY = blockPosY;
|
||||
this.blockPosZ = blockPosZ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// getters/setters //
|
||||
//=================//
|
||||
|
||||
public IDhApiBlockStateWrapper getBlockStateWrapper() { return this.blockStateWrapper; }
|
||||
|
||||
public IDhApiLevelWrapper getLevelWrapper() { return levelWrapper; }
|
||||
|
||||
public int getColorAsInt() { return this.colorAsInt; }
|
||||
public int getRed() { return ColorUtil.getRed(this.colorAsInt); }
|
||||
public int getGreen() { return ColorUtil.getGreen(this.colorAsInt); }
|
||||
public int getBlue() { return ColorUtil.getBlue(this.colorAsInt); }
|
||||
public void setColor(int red, int green, int blue) throws IllegalArgumentException
|
||||
{
|
||||
ColorUtil.throwIfColorValueOutOfIntRange("red", red);
|
||||
ColorUtil.throwIfColorValueOutOfIntRange("green", green);
|
||||
ColorUtil.throwIfColorValueOutOfIntRange("blue", blue);
|
||||
|
||||
this.colorAsInt = ColorUtil.rgbToInt(red, green, blue);
|
||||
}
|
||||
|
||||
/** @return the block's X value in the world */
|
||||
public int getBlockPosX() { return blockPosX; }
|
||||
/** @return the block's Y value in the world */
|
||||
public int getBlockPosY() { return blockPosY; }
|
||||
/** @return the block's Z value in the world */
|
||||
public int getBlockPosZ() { return blockPosZ; }
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the same instance of this event.
|
||||
* Copying this event isn't supported
|
||||
* since the internal parameters must be mutated
|
||||
* by API users in order to be tracked by DH's internal
|
||||
* logic.
|
||||
*/
|
||||
@Override
|
||||
public EventParam copy() { return this; }
|
||||
|
||||
@Override
|
||||
public boolean getCopyBeforeFire() { return false; }
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+13
-5
@@ -61,11 +61,12 @@ public abstract class DhApiBlockStateWrapperCreatedEvent implements IDhApiEvent<
|
||||
* Note: modifying this object won't change anything
|
||||
* a new wrapper will be created after this event finishes.
|
||||
*/
|
||||
public final IDhApiBlockStateWrapper blockStateWrapper;
|
||||
private final IDhApiBlockStateWrapper blockStateWrapper;
|
||||
|
||||
private boolean overridesSet = false;
|
||||
private EDhApiBlockMaterial blockMaterial = null;
|
||||
private Integer opacity = null;
|
||||
private Boolean allowApiColorOverride = null;
|
||||
|
||||
|
||||
|
||||
@@ -73,10 +74,7 @@ public abstract class DhApiBlockStateWrapperCreatedEvent implements IDhApiEvent<
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public EventParam(IDhApiBlockStateWrapper blockStateWrapper)
|
||||
{
|
||||
this.blockStateWrapper = blockStateWrapper;
|
||||
}
|
||||
public EventParam(IDhApiBlockStateWrapper blockStateWrapper) { this.blockStateWrapper = blockStateWrapper; }
|
||||
|
||||
|
||||
|
||||
@@ -84,6 +82,8 @@ public abstract class DhApiBlockStateWrapperCreatedEvent implements IDhApiEvent<
|
||||
// getters/setters //
|
||||
//=================//
|
||||
|
||||
public IDhApiBlockStateWrapper getBlockStateWrapper() { return this.blockStateWrapper; }
|
||||
|
||||
/** if set this will override the value currently set in the given {@link IDhApiBlockStateWrapper} */
|
||||
public void setBlockMaterial(EDhApiBlockMaterial blockMaterial)
|
||||
{
|
||||
@@ -100,6 +100,14 @@ public abstract class DhApiBlockStateWrapperCreatedEvent implements IDhApiEvent<
|
||||
}
|
||||
public Integer getOpacity() { return this.opacity; }
|
||||
|
||||
/** if set to true this {@link IDhApiBlockStateWrapper} will trigger {@link DhApiBlockColorOverrideEvent} */
|
||||
public void setAllowApiColorOverride(boolean allowApiColorOverride)
|
||||
{
|
||||
this.allowApiColorOverride = allowApiColorOverride;
|
||||
this.overridesSet = true;
|
||||
}
|
||||
public Boolean getAllowApiColorOverride() { return this.allowApiColorOverride; }
|
||||
|
||||
/** If true then one or more options for this block were set to be changed */
|
||||
public boolean getOverridesSet() { return this.overridesSet; }
|
||||
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.coreapi.util;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* Handles the bit-wise math used when
|
||||
* dealing with colors stored as integers. <br><br>
|
||||
*
|
||||
* Minecraft color format is: 0xAA BB GG RR <br>
|
||||
* DH mod color format is: 0xAA RR GG BB <br>
|
||||
* OpenGL RGBA format native order: 0xRR GG BB AA <br>
|
||||
* OpenGL RGBA format Java Order: 0xAA BB GG RR <br>
|
||||
*
|
||||
* @author Cola
|
||||
* @author Leonardo Amato
|
||||
* @version 2023-5-15
|
||||
*/
|
||||
public class ColorUtil
|
||||
{
|
||||
|
||||
public static final int INVISIBLE = argbToInt(0, 0, 0, 0);
|
||||
|
||||
public static final int BLACK = rgbToInt(0, 0, 0);
|
||||
public static final int WHITE = rgbToInt(255, 255, 255);
|
||||
public static final int RED = rgbToInt(255, 0, 0);
|
||||
public static final int DARK_RED = rgbToInt(100, 0, 0);
|
||||
public static final int GREEN = rgbToInt(0, 255, 0);
|
||||
public static final int DARK_GREEN = rgbToInt(80, 140, 80);
|
||||
public static final int BLUE = rgbToInt(0, 0, 255);
|
||||
public static final int YELLOW = rgbToInt(255, 255, 0);
|
||||
public static final int CYAN = rgbToInt(0, 255, 255);
|
||||
public static final int MAGENTA = rgbToInt(255, 0, 255);
|
||||
public static final int ORANGE = rgbToInt(255, 128, 0);
|
||||
public static final int DARK_ORANGE = rgbToInt(125, 62, 0);
|
||||
public static final int TAN = rgbToInt(183, 165, 119);
|
||||
public static final int PINK = rgbToInt(255, 128, 128);
|
||||
public static final int HOT_PINK = rgbToInt(255, 105, 180);
|
||||
public static final int GRAY = rgbToInt(128, 128, 128);
|
||||
public static final int LIGHT_GRAY = rgbToInt(192, 192, 192);
|
||||
public static final int DARK_GRAY = rgbToInt(64, 64, 64);
|
||||
public static final int BROWN = rgbToInt(68, 46, 24);
|
||||
public static final int LIGHT_BROWN = rgbToInt(130, 112, 67);
|
||||
public static final int PURPLE = rgbToInt(128, 0, 128);
|
||||
|
||||
|
||||
|
||||
public static int rgbToInt(int red, int green, int blue) { return (0xFF << 24) | (red << 16) | (green << 8) | blue; }
|
||||
public static int argbToInt(int alpha, int red, int green, int blue) { return (alpha << 24) | (red << 16) | (green << 8) | blue; }
|
||||
public static int argbToInt(float alpha, float red, float green, float blue) { return argbToInt((int) (alpha * 255f), (int) (red * 255f), (int) (green * 255f), (int) (blue * 255f)); }
|
||||
|
||||
|
||||
|
||||
/** Returns a value between 0 and 255 */
|
||||
public static int getAlpha(int color) { return (color >>> 24) & 0xFF; }
|
||||
/** @param newAlpha should be a value between 0 and 255 */
|
||||
public static int setAlpha(int color, int newAlpha) { return (newAlpha << 24) | (getRed(color) << 16) | (getGreen(color) << 8) | getBlue(color); }
|
||||
|
||||
/** Returns a value between 0 and 255 */
|
||||
public static int getRed(int color) { return (color >> 16) & 0xFF; }
|
||||
/** @param newRed should be a value between 0 and 255 */
|
||||
public static int setRed(int color, int newRed) { return (getAlpha(color) << 24) | (newRed << 16) | (getGreen(color) << 8) | getBlue(color); }
|
||||
|
||||
/** Returns a value between 0 and 255 */
|
||||
public static int getGreen(int color) { return (color >> 8) & 0xFF; }
|
||||
/** @param newGreen should be a value between 0 and 255 */
|
||||
public static int setGreen(int color, int newGreen) { return (getAlpha(color) << 24) | (getRed(color) << 16) | (newGreen << 8) | getBlue(color); }
|
||||
|
||||
/** Returns a value between 0 and 255 */
|
||||
public static int getBlue(int color) { return color & 0xFF; }
|
||||
/** @param newBlue should be a value between 0 and 255 */
|
||||
public static int setBlue(int color, int newBlue) { return (getAlpha(color) << 24) | (getRed(color) << 16) | (getGreen(color) << 8) | newBlue; }
|
||||
|
||||
/** @throws IllegalArgumentException if the given int value is out of the range 0 - 255 (exclusive) */
|
||||
public static void throwIfColorValueOutOfIntRange(String colorName, int value) throws IllegalArgumentException
|
||||
{
|
||||
if (value < 0
|
||||
|| value > 255)
|
||||
{
|
||||
throw new IllegalArgumentException("["+colorName+"] with the value ["+value+"] is out of the expected range 0 - 255 (exclusive).");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static int applyShade(int color, int shade)
|
||||
{
|
||||
if (shade < 0)
|
||||
return (getAlpha(color) << 24) | (Math.max(getRed(color) + shade, 0) << 16) | (Math.max(getGreen(color) + shade, 0) << 8) | Math.max(getBlue(color) + shade, 0);
|
||||
else
|
||||
return (getAlpha(color) << 24) | (Math.min(getRed(color) + shade, 255) << 16) | (Math.min(getGreen(color) + shade, 255) << 8) | Math.min(getBlue(color) + shade, 255);
|
||||
}
|
||||
|
||||
public static int applyShade(int color, float shade)
|
||||
{
|
||||
if (shade < 1)
|
||||
return (getAlpha(color) << 24) | ((int) Math.max(getRed(color) * shade, 0) << 16) | ((int) Math.max(getGreen(color) * shade, 0) << 8) | (int) Math.max(getBlue(color) * shade, 0);
|
||||
else
|
||||
return (getAlpha(color) << 24) | ((int) Math.min(getRed(color) * shade, 255) << 16) | ((int) Math.min(getGreen(color) * shade, 255) << 8) | (int) Math.min(getBlue(color) * shade, 255);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Multiply ARGB with RGB colors */
|
||||
public static int multiplyARGBwithRGB(int argb, int rgb)
|
||||
{
|
||||
return ((getAlpha(argb) << 24) | ((getRed(argb) * getRed(rgb) / 255) << 16)
|
||||
| ((getGreen(argb) * getGreen(rgb) / 255) << 8) | (getBlue(argb) * getBlue(rgb) / 255));
|
||||
}
|
||||
|
||||
/** Multiply 2 ARGB colors */
|
||||
public static int multiplyARGBwithARGB(int color1, int color2)
|
||||
{
|
||||
return ((getAlpha(color1) * getAlpha(color2) / 255) << 24) | ((getRed(color1) * getRed(color2) / 255) << 16) | ((getGreen(color1) * getGreen(color2) / 255) << 8) | (getBlue(color1) * getBlue(color2) / 255);
|
||||
}
|
||||
|
||||
/**
|
||||
* Below 2 functions are from: https://stackoverflow.com/questions/13806483/increase-or-decrease-color-saturation
|
||||
* Alpha in [0.0,1.0], hue in [0.0,360.0], Sat in [0.0,1.0], Value in [0.0,1.0]
|
||||
*/
|
||||
public static float[] argbToAhsv(int color)
|
||||
{
|
||||
float a = getAlpha(color) / 255f;
|
||||
float r = getRed(color) / 255f;
|
||||
float g = getGreen(color) / 255f;
|
||||
float b = getBlue(color) / 255f;
|
||||
float h, s, v;
|
||||
float min = Math.min(Math.min(r, g), b);
|
||||
float max = Math.max(Math.max(r, g), b);
|
||||
|
||||
v = max;
|
||||
float delta = max - min;
|
||||
if (max != 0f)
|
||||
s = delta / max; // s
|
||||
else
|
||||
{
|
||||
// r = g = b = 0 // s = 0, v is undefined
|
||||
return new float[]{a, 0f, 0f, 0f};
|
||||
}
|
||||
if (delta == 0f)
|
||||
{
|
||||
h = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (r == max) h = (g - b) / delta; // between yellow & magenta
|
||||
else if (g == max) h = 2f + (b - r) / delta; // between cyan & yellow
|
||||
else h = 4f + (r - g) / delta; // between magenta & cyan
|
||||
h *= 60f; // degrees
|
||||
if (h < 0f)
|
||||
h += 360f;
|
||||
}
|
||||
return new float[]{a, h, s, v};
|
||||
}
|
||||
/** Alpha in [0.0,1.0], hue in [0.0,360.0], Sat in [0.0,1.0], Value in [0.0,1.0] */
|
||||
public static int ahsvToArgb(float a, float h, float s, float v)
|
||||
{
|
||||
if (a > 1.f) a = 1.f;
|
||||
if (h > 360.f) h -= 350.f;
|
||||
if (s > 1.f) s = 1.f;
|
||||
if (v > 1.f) v = 1.f;
|
||||
|
||||
if (s == 0f)
|
||||
{
|
||||
// achromatic (grey)
|
||||
return ColorUtil.argbToInt(a, v, v, v);
|
||||
}
|
||||
h /= 60f;
|
||||
int i = (int) (Math.floor(h));
|
||||
float f = h - i; // factorial part of h
|
||||
float p = v * (1f - s);
|
||||
float q = v * (1f - s * f);
|
||||
float t = v * (1f - s * (1f - f));
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
return ColorUtil.argbToInt(a, v, t, p);
|
||||
case 1:
|
||||
return ColorUtil.argbToInt(a, q, v, p);
|
||||
case 2:
|
||||
return ColorUtil.argbToInt(a, p, v, t);
|
||||
case 3:
|
||||
return ColorUtil.argbToInt(a, p, q, v);
|
||||
case 4:
|
||||
return ColorUtil.argbToInt(a, t, p, v);
|
||||
default:
|
||||
return ColorUtil.argbToInt(a, v, p, q); // case 5
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the hex value for the Alpha, Red, Green, and Blue channels. */
|
||||
public static String toHexString(int color)
|
||||
{
|
||||
return "A:" + Integer.toHexString(getAlpha(color)) +
|
||||
",R:" + Integer.toHexString(getRed(color)) +
|
||||
",G:" + Integer.toHexString(getGreen(color)) +
|
||||
",B:" + Integer.toHexString(getBlue(color));
|
||||
}
|
||||
|
||||
/** Returns the int value (0-255) for the Alpha, Red, Green, and Blue channels. */
|
||||
public static String toString(int color)
|
||||
{
|
||||
return "A:" + getAlpha(color) +
|
||||
",R:" + getRed(color) +
|
||||
",G:" + getGreen(color) +
|
||||
",B:" + getBlue(color);
|
||||
}
|
||||
|
||||
public static Color toColorObjRGB(int color) { return new Color(getRed(color), getGreen(color), getBlue(color)); }
|
||||
public static Color toColorObjARGB(int color) { return new Color(getRed(color), getGreen(color), getBlue(color), getAlpha(color)); }
|
||||
|
||||
public static int toColorInt(Color color) { return argbToInt(color.getAlpha(), color.getRed(), color.getGreen(), color.getBlue()); }
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user