308 lines
13 KiB
Java
308 lines
13 KiB
Java
|
package at.petrak.hexcasting.client.render;
|
||
|
|
||
|
import at.petrak.hexcasting.api.block.HexBlockEntity;
|
||
|
import at.petrak.hexcasting.api.casting.math.HexPattern;
|
||
|
import at.petrak.hexcasting.common.blocks.akashic.BlockAkashicBookshelf;
|
||
|
import at.petrak.hexcasting.common.blocks.akashic.BlockEntityAkashicBookshelf;
|
||
|
import at.petrak.hexcasting.common.blocks.circles.BlockEntitySlate;
|
||
|
import at.petrak.hexcasting.common.blocks.circles.BlockSlate;
|
||
|
import com.mojang.blaze3d.platform.NativeImage;
|
||
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||
|
import com.mojang.math.Matrix3f;
|
||
|
import com.mojang.math.Matrix4f;
|
||
|
import com.mojang.math.Vector3f;
|
||
|
import net.minecraft.client.Minecraft;
|
||
|
import net.minecraft.client.renderer.GameRenderer;
|
||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||
|
import net.minecraft.client.renderer.RenderType;
|
||
|
import net.minecraft.client.renderer.texture.DynamicTexture;
|
||
|
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||
|
import net.minecraft.resources.ResourceLocation;
|
||
|
import net.minecraft.util.Tuple;
|
||
|
import net.minecraft.world.level.block.state.BlockState;
|
||
|
import net.minecraft.world.level.block.state.properties.AttachFace;
|
||
|
import net.minecraft.world.phys.Vec2;
|
||
|
|
||
|
import java.awt.*;
|
||
|
import java.awt.image.BufferedImage;
|
||
|
import java.util.HashMap;
|
||
|
import java.util.List;
|
||
|
import java.util.stream.Collectors;
|
||
|
|
||
|
public class PatternTextureManager {
|
||
|
|
||
|
//TODO: remove if not needed anymore for comparison
|
||
|
public static boolean useTextures = true;
|
||
|
public static int repaintIndex = 0;
|
||
|
|
||
|
public static int resolutionByBlockSize = 512;
|
||
|
public static int paddingByBlockSize = 50;
|
||
|
public static int circleRadiusByBlockSize = 8;
|
||
|
public static int scaleLimit = 16;
|
||
|
|
||
|
private static HashMap<String, ResourceLocation> patternTextures = new HashMap<>();
|
||
|
|
||
|
public static String getPointsKey(List<Vec2> zappyPoints)
|
||
|
{
|
||
|
return zappyPoints.stream()
|
||
|
.map(p -> String.format("(%f,%f)", p.x, p.y))
|
||
|
.collect(Collectors.joining(";"));
|
||
|
}
|
||
|
|
||
|
public static HexPatternPoints generateHexPatternPoints(HexBlockEntity tile, HexPattern pattern, float flowIrregular)
|
||
|
{
|
||
|
var stupidHash = tile.getBlockPos().hashCode();
|
||
|
var lines1 = pattern.toLines(1, Vec2.ZERO);
|
||
|
var zappyPoints = RenderLib.makeZappy(lines1, RenderLib.findDupIndices(pattern.positions()),
|
||
|
10, 0.5f, 0f, flowIrregular, 0f, 1f, stupidHash);
|
||
|
return new HexPatternPoints(zappyPoints);
|
||
|
}
|
||
|
|
||
|
public static void renderPatternForScroll(String pointsKey, PoseStack ps, MultiBufferSource bufSource, int light, List<Vec2> zappyPoints, int blockSize, boolean showStrokeOrder)
|
||
|
{
|
||
|
renderPattern(pointsKey, ps, bufSource, light, zappyPoints, blockSize, showStrokeOrder, false, true, false,false, true,-1);
|
||
|
}
|
||
|
public static void renderPatternForSlate(BlockEntitySlate tile, HexPattern pattern, PoseStack ps, MultiBufferSource buffer, int light, BlockState bs)
|
||
|
{
|
||
|
if(tile.points == null)
|
||
|
tile.points = generateHexPatternPoints(tile, pattern, 0.2f);
|
||
|
|
||
|
boolean isOnWall = bs.getValue(BlockSlate.ATTACH_FACE) == AttachFace.WALL;
|
||
|
boolean isOnCeiling = bs.getValue(BlockSlate.ATTACH_FACE) == AttachFace.CEILING;
|
||
|
int facing = bs.getValue(BlockSlate.FACING).get2DDataValue();
|
||
|
|
||
|
renderPatternForBlockEntity(tile.points, ps, buffer, light, isOnWall, isOnCeiling, true, facing);
|
||
|
}
|
||
|
public static void renderPatternForAkashicBookshelf(BlockEntityAkashicBookshelf tile, HexPattern pattern, PoseStack ps, MultiBufferSource buffer, int light, BlockState bs)
|
||
|
{
|
||
|
if(tile.points == null)
|
||
|
tile.points = generateHexPatternPoints(tile, pattern, 0f);
|
||
|
|
||
|
int facing = bs.getValue(BlockAkashicBookshelf.FACING).get2DDataValue();
|
||
|
renderPatternForBlockEntity(tile.points, ps, buffer, light, true, false, false, facing);
|
||
|
}
|
||
|
|
||
|
public static void renderPatternForBlockEntity(HexPatternPoints points, PoseStack ps, MultiBufferSource buffer, int light, boolean isOnWall, boolean isOnCeiling, boolean isSlate, int facing)
|
||
|
{
|
||
|
var oldShader = RenderSystem.getShader();
|
||
|
ps.pushPose();
|
||
|
RenderSystem.setShader(GameRenderer::getPositionTexShader);
|
||
|
renderPattern(points.pointsKey, ps, buffer, light, points.zappyPoints, 1, false, true, isOnWall, isOnCeiling, isSlate, false, facing);
|
||
|
ps.popPose();
|
||
|
RenderSystem.setShader(() -> oldShader);
|
||
|
}
|
||
|
|
||
|
public static void renderPattern(String pointsKey, PoseStack ps, MultiBufferSource bufSource, int light, List<Vec2> zappyPoints, int blockSize, boolean showStrokeOrder, boolean useFullSize, boolean isOnWall, boolean isOnCeiling, boolean isSlate, boolean isScroll, int facing)
|
||
|
{
|
||
|
ps.pushPose();
|
||
|
|
||
|
PoseStack.Pose last = ps.last();
|
||
|
Matrix4f mat = last.pose();
|
||
|
Matrix3f normal = last.normal();
|
||
|
|
||
|
float x = blockSize, y = blockSize, z = (-1f / 16f) - 0.01f;
|
||
|
float nx = 0, ny = 0, nz = 0;
|
||
|
|
||
|
//TODO: refactor this mess of a method
|
||
|
|
||
|
if(isOnWall)
|
||
|
{
|
||
|
if(isScroll)
|
||
|
{
|
||
|
ps.translate(-blockSize / 2f, -blockSize / 2f, 1f / 32f);
|
||
|
nz = -1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ps.mulPose(Vector3f.ZP.rotationDegrees(180));
|
||
|
|
||
|
if(isSlate)
|
||
|
{
|
||
|
if(facing == 0)
|
||
|
ps.translate(0,-1,0);
|
||
|
if(facing == 1)
|
||
|
ps.translate(-1,-1,0);
|
||
|
if(facing == 2)
|
||
|
ps.translate(-1,-1,1);
|
||
|
if(facing == 3)
|
||
|
ps.translate(0,-1,1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
z = -0.01f;
|
||
|
if(facing == 0)
|
||
|
ps.translate(0,-1,1);
|
||
|
if(facing == 1)
|
||
|
ps.translate(0,-1,0);
|
||
|
if(facing == 2)
|
||
|
ps.translate(-1,-1,0);
|
||
|
if(facing == 3)
|
||
|
ps.translate(-1,-1,1);
|
||
|
}
|
||
|
|
||
|
if(facing == 0)
|
||
|
ps.mulPose(Vector3f.YP.rotationDegrees(180));
|
||
|
if(facing == 1)
|
||
|
ps.mulPose(Vector3f.YP.rotationDegrees(270));
|
||
|
if(facing == 3)
|
||
|
ps.mulPose(Vector3f.YP.rotationDegrees(90));
|
||
|
|
||
|
if(facing == 0 || facing == 2)
|
||
|
nz = -1;
|
||
|
if(facing == 1 || facing == 3)
|
||
|
nx = -1;
|
||
|
ps.translate(0,0,0);
|
||
|
}
|
||
|
}
|
||
|
else //slates on the floor or ceiling
|
||
|
{
|
||
|
if(facing == 0)
|
||
|
ps.translate(0,0,0);
|
||
|
if(facing == 1)
|
||
|
ps.translate(1,0,0);
|
||
|
if(facing == 2)
|
||
|
ps.translate(1,0,1);
|
||
|
if(facing == 3)
|
||
|
ps.translate(0,0,1);
|
||
|
ps.mulPose(Vector3f.YP.rotationDegrees(facing*-90));
|
||
|
|
||
|
if(isOnCeiling)
|
||
|
{
|
||
|
ps.mulPose(Vector3f.XP.rotationDegrees(-90));
|
||
|
ps.translate(0,-1,1);
|
||
|
}
|
||
|
else
|
||
|
ps.mulPose(Vector3f.XP.rotationDegrees(90));
|
||
|
nz = -1;
|
||
|
}
|
||
|
|
||
|
int lineWidth = 16;
|
||
|
int outerColor = 0xB4B4BE;//0xff_c8c8d2;
|
||
|
int innerColor = 0x2A2A2A;//0xc8_322b33;
|
||
|
if(isScroll)
|
||
|
{
|
||
|
lineWidth = 20;
|
||
|
outerColor = 0xDEDEDE;//0xff_d2c8c8;
|
||
|
innerColor = 0x343434;//0xc8_322b33;
|
||
|
}
|
||
|
|
||
|
ResourceLocation texture = getTexture(zappyPoints, pointsKey, blockSize, showStrokeOrder, lineWidth, useFullSize, new Color(innerColor), new Color(outerColor));
|
||
|
VertexConsumer verts = bufSource.getBuffer(RenderType.entityCutout(texture));
|
||
|
|
||
|
vertex(mat, normal, light, verts, 0, 0, z, 0, 0, nx, ny, nz);
|
||
|
vertex(mat, normal, light, verts, 0, y, z, 0, 1, nx, ny, nz);
|
||
|
vertex(mat, normal, light, verts, x, y, z, 1, 1, nx, ny, nz);
|
||
|
vertex(mat, normal, light, verts, x, 0, z, 1, 0, nx, ny, nz);
|
||
|
|
||
|
ps.popPose();
|
||
|
}
|
||
|
|
||
|
private static void vertex(Matrix4f mat, Matrix3f normal, int light, VertexConsumer verts, float x, float y, float z,
|
||
|
float u, float v, float nx, float ny, float nz) {
|
||
|
verts.vertex(mat, x, y, z)
|
||
|
.color(0xffffffff)
|
||
|
.uv(u, v).overlayCoords(OverlayTexture.NO_OVERLAY).uv2(light)
|
||
|
.normal(normal, nx, ny, nz)
|
||
|
.endVertex();
|
||
|
}
|
||
|
|
||
|
public static ResourceLocation getTexture(List<Vec2> points, String pointsKey, int blockSize, boolean showsStrokeOrder, int lineWidth, boolean useFullSize, Color innerColor, Color outerColor) {
|
||
|
if (patternTextures.containsKey(pointsKey))
|
||
|
return patternTextures.get(pointsKey);
|
||
|
return createTexture(points, pointsKey, blockSize, showsStrokeOrder, lineWidth, useFullSize, innerColor, outerColor);
|
||
|
}
|
||
|
|
||
|
public static ResourceLocation createTexture(List<Vec2> points, String pointsKey, int blockSize, boolean showsStrokeOrder, int lineWidth, boolean useFullSize, Color innerColor, Color outerColor)
|
||
|
{
|
||
|
int resolution = resolutionByBlockSize * blockSize;
|
||
|
int padding = paddingByBlockSize * blockSize;
|
||
|
|
||
|
double minX = Double.MAX_VALUE, maxX = Double.MIN_VALUE, minY = Double.MAX_VALUE, maxY = Double.MIN_VALUE;
|
||
|
for (Vec2 point : points)
|
||
|
{
|
||
|
minX = Math.min(minX, point.x);
|
||
|
maxX = Math.max(maxX, point.x);
|
||
|
minY = Math.min(minY, point.y);
|
||
|
maxY = Math.max(maxY, point.y);
|
||
|
}
|
||
|
|
||
|
double rangeX = maxX - minX;
|
||
|
double rangeY = maxY - minY;
|
||
|
|
||
|
double scale = Math.min((resolution - 2 * padding) / rangeX, (resolution - 2 * padding) / rangeY);
|
||
|
|
||
|
double limit = blockSize * scaleLimit;
|
||
|
if (!useFullSize && scale > limit)
|
||
|
scale = limit;
|
||
|
|
||
|
double offsetX = ((resolution - 2 * padding) - rangeX * scale) / 2;
|
||
|
double offsetY = ((resolution - 2 * padding) - rangeY * scale) / 2;
|
||
|
|
||
|
BufferedImage img = new BufferedImage(resolution, resolution, BufferedImage.TYPE_INT_ARGB);
|
||
|
Graphics2D g2d = img.createGraphics();
|
||
|
|
||
|
g2d.setColor(outerColor);
|
||
|
g2d.setStroke(new BasicStroke((blockSize * 5f / 3f) * lineWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
|
||
|
drawLines(g2d, points, minX, minY, scale, offsetX, offsetY, padding);
|
||
|
|
||
|
g2d.setColor(innerColor);
|
||
|
g2d.setStroke(new BasicStroke((blockSize * 2f / 3f) * lineWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
|
||
|
drawLines(g2d, points, minX, minY, scale, offsetX, offsetY, padding);
|
||
|
|
||
|
|
||
|
if (showsStrokeOrder) {
|
||
|
g2d.setColor(new Color(0xff_d77b5b));
|
||
|
Tuple<Integer, Integer> point = getTextureCoordinates(points.get(0), minX, minY, scale, offsetX, offsetY, padding);
|
||
|
int spotRadius = circleRadiusByBlockSize * blockSize;
|
||
|
drawHexagon(g2d, point.getA(), point.getB(), spotRadius);
|
||
|
}
|
||
|
|
||
|
g2d.dispose();
|
||
|
|
||
|
NativeImage nativeImage = new NativeImage(img.getWidth(), img.getHeight(), true);
|
||
|
for (int y = 0; y < img.getHeight(); y++)
|
||
|
for (int x = 0; x < img.getWidth(); x++)
|
||
|
nativeImage.setPixelRGBA(x, y, img.getRGB(x, y));
|
||
|
|
||
|
DynamicTexture dynamicTexture = new DynamicTexture(nativeImage);
|
||
|
ResourceLocation resourceLocation = Minecraft.getInstance().getTextureManager().register("hex_pattern_texture_" + points.hashCode() + "_" + repaintIndex + ".png", dynamicTexture);
|
||
|
patternTextures.put(pointsKey, resourceLocation);
|
||
|
return resourceLocation;
|
||
|
}
|
||
|
|
||
|
private static void drawLines(Graphics2D g2d, List<Vec2> points, double minX, double minY, double scale, double offsetX, double offsetY, int padding) {
|
||
|
for (int i = 0; i < points.size() - 1; i++) {
|
||
|
Tuple<Integer, Integer> pointFrom = getTextureCoordinates(points.get(i), minX, minY, scale, offsetX, offsetY, padding);
|
||
|
Tuple<Integer, Integer> pointTo = getTextureCoordinates(points.get(i+1), minX, minY, scale, offsetX, offsetY, padding);
|
||
|
g2d.drawLine(pointFrom.getA(), pointFrom.getB(), pointTo.getA(), pointTo.getB());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static Tuple<Integer, Integer> getTextureCoordinates(Vec2 point, double minX, double minY, double scale, double offsetX, double offsetY, int padding) {
|
||
|
int x = (int) ((point.x - minX) * scale + offsetX) + padding;
|
||
|
int y = (int) ((point.y - minY) * scale + offsetY) + padding;
|
||
|
return new Tuple(x, y);
|
||
|
}
|
||
|
|
||
|
private static void drawHexagon(Graphics2D g2d, int x, int y, int radius) {
|
||
|
int fracOfCircle = 6;
|
||
|
Polygon hexagon = new Polygon();
|
||
|
|
||
|
for (int i = 0; i < fracOfCircle; i++) {
|
||
|
double theta = (i / (double) fracOfCircle) * Math.PI * 2;
|
||
|
int hx = (int) (x + Math.cos(theta) * radius);
|
||
|
int hy = (int) (y + Math.sin(theta) * radius);
|
||
|
hexagon.addPoint(hx, hy);
|
||
|
}
|
||
|
|
||
|
g2d.fill(hexagon);
|
||
|
}
|
||
|
|
||
|
public static void repaint() {
|
||
|
repaintIndex++;
|
||
|
patternTextures.clear();
|
||
|
}
|
||
|
}
|