cleanup and introducing plugin structure

This commit is contained in:
Elias Müller 2025-03-19 17:40:18 +01:00
parent 0103a654c3
commit ddbe59a5ac
9 changed files with 201 additions and 154 deletions

1
.idea/modules.xml generated
View File

@ -3,6 +3,7 @@
<component name="ProjectModuleManager"> <component name="ProjectModuleManager">
<modules> <modules>
<module fileurl="file://$PROJECT_DIR$/.idea/modules/PixelPic.main.iml" filepath="$PROJECT_DIR$/.idea/modules/PixelPic.main.iml" /> <module fileurl="file://$PROJECT_DIR$/.idea/modules/PixelPic.main.iml" filepath="$PROJECT_DIR$/.idea/modules/PixelPic.main.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/eu.mhsl.minecraft.pixelpic.PixelPic.main.iml" filepath="$PROJECT_DIR$/.idea/modules/eu.mhsl.minecraft.pixelpic.PixelPic.main.iml" />
</modules> </modules>
</component> </component>
</project> </project>

View File

@ -11,4 +11,8 @@
</configuration> </configuration>
</facet> </facet>
</component> </component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module> </module>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="FacetManager">
<facet type="minecraft" name="Minecraft">
<configuration>
<autoDetectTypes>
<platformType>PAPER</platformType>
<platformType>ADVENTURE</platformType>
</autoDetectTypes>
<projectReimportVersion>1</projectReimportVersion>
</configuration>
</facet>
</component>
</module>

View File

@ -1,6 +1,5 @@
package eu.mhsl.minecraft.pixelpic; package eu.mhsl.minecraft.pixelpic;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.map.MapCanvas; import org.bukkit.map.MapCanvas;
import org.bukkit.map.MapRenderer; import org.bukkit.map.MapRenderer;
@ -10,23 +9,19 @@ import org.jetbrains.annotations.NotNull;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
public class ImageMapRenderer extends MapRenderer { public class ImageMapRenderer extends MapRenderer {
public final int IMAGE_SIZE = 128; public static final int IMAGE_SIZE = 128;
private final BufferedImage image;
private BufferedImage image; private boolean alreadyRendered = false;
private final int x;
private final int y;
public ImageMapRenderer(BufferedImage image) { public ImageMapRenderer(BufferedImage image) {
this(image, 0, 0); this(image, 0, 0);
} }
public ImageMapRenderer(BufferedImage image, int x, int y) { public ImageMapRenderer(BufferedImage image, int x, int y) {
this.x = x; this.image = recalculateInput(image, x, y);
this.y = y;
recalculateInput(image);
} }
public void recalculateInput(BufferedImage input) { public static BufferedImage recalculateInput(BufferedImage input, int x, int y) {
if (x * IMAGE_SIZE > input.getWidth() || y * IMAGE_SIZE > input.getHeight()) if (x * IMAGE_SIZE > input.getWidth() || y * IMAGE_SIZE > input.getHeight())
throw new RuntimeException(String.format("Input image mus match a multiple of x and y with %d", IMAGE_SIZE)); throw new RuntimeException(String.format("Input image mus match a multiple of x and y with %d", IMAGE_SIZE));
@ -37,14 +32,15 @@ public class ImageMapRenderer extends MapRenderer {
int y2 = (int) (double) Math.min(input.getHeight(), ((y + 1) * IMAGE_SIZE)); int y2 = (int) (double) Math.min(input.getHeight(), ((y + 1) * IMAGE_SIZE));
if (x2 - x1 <= 0 || y2 - y1 <= 0) if (x2 - x1 <= 0 || y2 - y1 <= 0)
return; throw new RuntimeException("Invalid Image dimensions!");
this.image = input.getSubimage(x1, y1, x2 - x1, y2 - y1); return input.getSubimage(x1, y1, x2 - x1, y2 - y1);
} }
@Override @Override
public void render(@NotNull MapView map, @NotNull MapCanvas canvas, @NotNull Player player) { public void render(@NotNull MapView map, @NotNull MapCanvas canvas, @NotNull Player player) {
if(image == null) return; if(this.alreadyRendered) return;
Bukkit.getScheduler().runTaskLater(Main.getInstance(), () -> canvas.drawImage(0, 0, image), 2L); canvas.drawImage(0, 0, this.image);
this.alreadyRendered = true;
} }
} }

View File

@ -1,120 +1,83 @@
package eu.mhsl.minecraft.pixelpic; package eu.mhsl.minecraft.pixelpic;
import eu.mhsl.minecraft.pixelpic.commands.PixelPicCommand;
import eu.mhsl.minecraft.pixelpic.render.render.DefaultScreenRenderer; import eu.mhsl.minecraft.pixelpic.render.render.DefaultScreenRenderer;
import eu.mhsl.minecraft.pixelpic.render.render.Renderer; import eu.mhsl.minecraft.pixelpic.render.render.Renderer;
import eu.mhsl.minecraft.pixelpic.render.render.Resolution;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.MapMeta;
import org.bukkit.map.MapView;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
public final class Main extends JavaPlugin { public final class Main extends JavaPlugin {
private static Main instance; private static Main instance;
private Renderer screenRenderer; private Renderer screenRenderer;
@Override @Override
public void onEnable() { public void onEnable() {
this.instance = this; instance = this;
this.screenRenderer = new DefaultScreenRenderer(); Bukkit.getPluginCommand("pixelPic").setExecutor(new PixelPicCommand());
Bukkit.getPluginCommand("test").setExecutor((sender, command, label, args) -> { //
if(!(sender instanceof Player player)) return false; // Bukkit.getPluginCommand("test2").setExecutor((sender, command, label, args) -> {
// if(!(sender instanceof Player player)) return false;
Resolution.Pixels pixels = Resolution.Pixels._128P; // Bukkit.broadcast(Component.text("HI"));
Resolution.AspectRatio aspectRatio = Resolution.AspectRatio._1_1; //
Resolution resolution = new Resolution(pixels, aspectRatio); // Resolution.Pixels pixels = Resolution.Pixels._256P;
BufferedImage image = screenRenderer.render((Player) sender, resolution); // Resolution.AspectRatio aspectRatio = Resolution.AspectRatio._1_1;
Bukkit.broadcast(Component.text(image.toString())); // Resolution resolution = new Resolution(pixels, aspectRatio);
// BufferedImage image = screenRenderer.render((Player) sender, resolution);
File file = new File(getDataFolder(), "Bild" + ".png"); // Bukkit.broadcast(Component.text(image.toString()));
try { //
getDataFolder().mkdir(); // File file = new File(getDataFolder(), "Bild" + ".png");
ImageIO.write(image, "png", file); // try {
} catch (Exception e) { // getDataFolder().mkdir();
return true; // ImageIO.write(image, "png", file);
} // } catch (Exception e) {
// return true;
ItemStack map = new ItemStack(Material.FILLED_MAP, 1); // }
MapMeta meta = (MapMeta) map.getItemMeta(); //
// ItemStack map = new ItemStack(Material.FILLED_MAP, 1);
MapView mapView = Bukkit.createMap(Bukkit.getWorlds().getFirst()); // MapMeta meta = (MapMeta) map.getItemMeta();
mapView.addRenderer(new ImageMapRenderer(image)); // MapView mapView = Bukkit.createMap(Bukkit.getWorlds().getFirst());
// mapView.addRenderer(new ImageMapRenderer(image, 0, 0));
meta.setMapView(mapView); // meta.setMapView(mapView);
map.setItemMeta(meta); // map.setItemMeta(meta);
// player.getInventory().addItem(map);
player.getInventory().addItem(map); //
player.updateInventory(); // ItemStack map2 = new ItemStack(Material.FILLED_MAP, 1);
// MapMeta meta1 = (MapMeta) map2.getItemMeta();
return true; // MapView mapView4 = Bukkit.createMap(Bukkit.getWorlds().getFirst());
}); // mapView4.addRenderer(new ImageMapRenderer(image, 1, 0));
// meta1.setMapView(mapView4);
Bukkit.getPluginCommand("test2").setExecutor((sender, command, label, args) -> { // map2.setItemMeta(meta1);
if(!(sender instanceof Player player)) return false; // player.getInventory().addItem(map2);
Bukkit.broadcast(Component.text("HI")); //
// ItemStack map3 = new ItemStack(Material.FILLED_MAP, 1);
Resolution.Pixels pixels = Resolution.Pixels._256P; // MapMeta meta2 = (MapMeta) map3.getItemMeta();
Resolution.AspectRatio aspectRatio = Resolution.AspectRatio._1_1; // MapView mapView3 = Bukkit.createMap(Bukkit.getWorlds().getFirst());
Resolution resolution = new Resolution(pixels, aspectRatio); // mapView3.addRenderer(new ImageMapRenderer(image, 0, 1));
BufferedImage image = screenRenderer.render((Player) sender, resolution); // meta2.setMapView(mapView3);
Bukkit.broadcast(Component.text(image.toString())); // map3.setItemMeta(meta2);
// player.getInventory().addItem(map3);
File file = new File(getDataFolder(), "Bild" + ".png"); //
try { // ItemStack map4 = new ItemStack(Material.FILLED_MAP, 1);
getDataFolder().mkdir(); // MapMeta meta3 = (MapMeta) map4.getItemMeta();
ImageIO.write(image, "png", file); // MapView mapView2 = Bukkit.createMap(Bukkit.getWorlds().getFirst());
} catch (Exception e) { // mapView2.addRenderer(new ImageMapRenderer(image, 1, 1));
return true; // meta3.setMapView(mapView2);
} // map4.setItemMeta(meta3);
// player.getInventory().addItem(map4);
ItemStack map = new ItemStack(Material.FILLED_MAP, 1); //
MapMeta meta = (MapMeta) map.getItemMeta(); // player.updateInventory();
MapView mapView = Bukkit.createMap(Bukkit.getWorlds().getFirst()); //
mapView.addRenderer(new ImageMapRenderer(image, 0, 0)); // return true;
meta.setMapView(mapView); // });
map.setItemMeta(meta);
player.getInventory().addItem(map);
ItemStack map2 = new ItemStack(Material.FILLED_MAP, 1);
MapMeta meta1 = (MapMeta) map2.getItemMeta();
MapView mapView4 = Bukkit.createMap(Bukkit.getWorlds().getFirst());
mapView4.addRenderer(new ImageMapRenderer(image, 1, 0));
meta1.setMapView(mapView4);
map2.setItemMeta(meta1);
player.getInventory().addItem(map2);
ItemStack map3 = new ItemStack(Material.FILLED_MAP, 1);
MapMeta meta2 = (MapMeta) map3.getItemMeta();
MapView mapView3 = Bukkit.createMap(Bukkit.getWorlds().getFirst());
mapView3.addRenderer(new ImageMapRenderer(image, 0, 1));
meta2.setMapView(mapView3);
map3.setItemMeta(meta2);
player.getInventory().addItem(map3);
ItemStack map4 = new ItemStack(Material.FILLED_MAP, 1);
MapMeta meta3 = (MapMeta) map4.getItemMeta();
MapView mapView2 = Bukkit.createMap(Bukkit.getWorlds().getFirst());
mapView2.addRenderer(new ImageMapRenderer(image, 1, 1));
meta3.setMapView(mapView2);
map4.setItemMeta(meta3);
player.getInventory().addItem(map4);
player.updateInventory();
return true;
});
} }
@Override @Override
public void onDisable() { public void onDisable() {
// Plugin shutdown logic }
public Renderer getScreenRenderer() {
if(this.screenRenderer == null) this.screenRenderer = new DefaultScreenRenderer();
return this.screenRenderer;
} }
public static Main getInstance() { public static Main getInstance() {

View File

@ -0,0 +1,56 @@
package eu.mhsl.minecraft.pixelpic.commands;
import eu.mhsl.minecraft.pixelpic.ImageMapRenderer;
import eu.mhsl.minecraft.pixelpic.Main;
import eu.mhsl.minecraft.pixelpic.render.render.Resolution;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.MapMeta;
import org.bukkit.map.MapView;
import org.jetbrains.annotations.NotNull;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
public class PixelPicCommand implements CommandExecutor {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String @NotNull [] args) {
if(!(sender instanceof Player player))
throw new IllegalStateException("Dieser Command kann nur von einem Spieler ausgeführt werden!");
Resolution.Pixels pixels = Resolution.Pixels._128P;
Resolution.AspectRatio aspectRatio = Resolution.AspectRatio._1_1;
Resolution resolution = new Resolution(pixels, aspectRatio);
BufferedImage image = Main.getInstance().getScreenRenderer().render(player, resolution);
File file = new File(Main.getInstance().getDataFolder(), "Bild" + ".png");
try {
Main.getInstance().getDataFolder().mkdir();
ImageIO.write(image, "png", file);
} catch (Exception e) {
return true;
}
ItemStack map = new ItemStack(Material.FILLED_MAP, 1);
MapMeta meta = (MapMeta) map.getItemMeta();
MapView mapView = Bukkit.createMap(Bukkit.getWorlds().getFirst());
mapView.getRenderers().forEach(mapView::removeRenderer);
mapView.addRenderer(new ImageMapRenderer(image));
meta.setMapView(mapView);
map.setItemMeta(meta);
player.getInventory().addItem(map);
player.updateInventory();
return true;
}
}

View File

@ -15,14 +15,20 @@ import org.bukkit.block.data.BlockData;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
public class DefaultRaytracer implements Raytracer { public class DefaultRaytracer implements Raytracer {
private final int maxDistance;
private static final int MAX_DISTANCE = 300; private final int reflectionDepth;
private static final int REFLECTION_DEPTH = 10;
private final ModelRegistry textureRegistry; private final ModelRegistry textureRegistry;
private Block reflectedBlock; private Block reflectedBlock;
public DefaultRaytracer() { public DefaultRaytracer() {
this(300, 10);
}
public DefaultRaytracer(int maxDistance, int reflectionDepth) {
this.maxDistance = maxDistance;
this.reflectionDepth = reflectionDepth;
this.textureRegistry = new DefaultModelRegistry(); this.textureRegistry = new DefaultModelRegistry();
this.textureRegistry.initialize(); this.textureRegistry.initialize();
@ -31,7 +37,7 @@ public class DefaultRaytracer implements Raytracer {
@Override @Override
public int trace(World world, Vector point, Vector direction) { public int trace(World world, Vector point, Vector direction) {
return trace(world, point, direction, REFLECTION_DEPTH); return trace(world, point, direction, reflectionDepth);
} }
private int trace(World world, Vector point, Vector direction, int reflectionDepth) { private int trace(World world, Vector point, Vector direction, int reflectionDepth) {
@ -52,19 +58,10 @@ public class DefaultRaytracer implements Raytracer {
Material occlusionMaterial = null; Material occlusionMaterial = null;
BlockData occlusionData = null; BlockData occlusionData = null;
for (int i = 0; i < MAX_DISTANCE; i++) { for (int i = 0; i < maxDistance; i++) {
if (!iterator.hasNext()) { if (!iterator.hasNext()) break;
break;
}
Block block = iterator.next(); Block block = iterator.next();
if (block == null) { if (reflectedBlock != null && reflectedBlock.equals(block)) continue;
continue;
}
if (reflectedBlock != null && reflectedBlock.equals(block)) {
continue;
}
reflectedBlock = null; reflectedBlock = null;
Material material = block.getType(); Material material = block.getType();
@ -75,20 +72,30 @@ public class DefaultRaytracer implements Raytracer {
} }
Model textureModel = textureRegistry.getModel(block); Model textureModel = textureRegistry.getModel(block);
Intersection currentIntersection = Intersection.of(MathUtil.toVector(iterator.getIntersectionFace()), Intersection currentIntersection = Intersection.of(
i == 0 ? point : iterator.getIntersectionPoint(), direction); MathUtil.toVector(iterator.getIntersectionFace()),
i == 0 ? point : iterator.getIntersectionPoint(),
direction
);
Intersection newIntersection = textureModel.intersect(block, currentIntersection); Intersection newIntersection = textureModel.intersect(block, currentIntersection);
if (newIntersection == null) { if (newIntersection == null) continue;
continue;
}
int color = newIntersection.getColor(); int color = newIntersection.getColor();
if (!reflected && textureModel.getReflectionFactor() > 0 && reflectionDepth > 0 && (color >> 24) != 0) { if (!reflected && textureModel.getReflectionFactor() > 0 && reflectionDepth > 0 && (color >> 24) != 0) {
reflectedBlock = block; reflectedBlock = block;
reflectionColor = trace(world, newIntersection.getPoint(), MathUtil.reflectVector(point, direction, reflectionColor = trace(
newIntersection.getPoint(), newIntersection.getNormal()), reflectionDepth - 1); world,
newIntersection.getPoint(),
MathUtil.reflectVector(
point,
direction,
newIntersection.getPoint(),
newIntersection.getNormal()
),
reflectionDepth - 1
);
reflectionFactor = textureModel.getReflectionFactor(); reflectionFactor = textureModel.getReflectionFactor();
reflected = true; reflected = true;
} }
@ -102,9 +109,7 @@ public class DefaultRaytracer implements Raytracer {
if (textureModel.isOccluding()) { if (textureModel.isOccluding()) {
BlockData data = block.getBlockData(); BlockData data = block.getBlockData();
if (material == occlusionMaterial && data.equals(occlusionData)) { if (material == occlusionMaterial && data.equals(occlusionData)) continue;
continue;
}
occlusionMaterial = material; occlusionMaterial = material;
occlusionData = data; occlusionData = data;
@ -113,13 +118,8 @@ public class DefaultRaytracer implements Raytracer {
occlusionData = null; occlusionData = null;
} }
if (transparencyStart != null && textureModel.getTransparencyFactor() > 0) { if (transparencyStart != null && textureModel.getTransparencyFactor() > 0) continue;
continue; if ((color >> 24) == 0) continue;
}
if ((color >> 24) == 0) {
continue;
}
baseColor = color; baseColor = color;
finalIntersection = newIntersection.getPoint(); finalIntersection = newIntersection.getPoint();
@ -127,13 +127,22 @@ public class DefaultRaytracer implements Raytracer {
} }
if (transparencyStart != null) { if (transparencyStart != null) {
baseColor = MathUtil.weightedColorSum(baseColor, transparencyColor, transparencyFactor, (1 baseColor = MathUtil.weightedColorSum(
baseColor,
transparencyColor,
transparencyFactor,
(1
- transparencyFactor) - transparencyFactor)
* (1 + transparencyStart.distance(finalIntersection == null ? transparencyStart : finalIntersection) * (1 + transparencyStart.distance(finalIntersection == null ? transparencyStart : finalIntersection)
/ 5.0)); / 5.0));
} }
if (reflected) { if (reflected) {
baseColor = MathUtil.weightedColorSum(baseColor, reflectionColor, 1 - reflectionFactor, reflectionFactor); baseColor = MathUtil.weightedColorSum(
baseColor,
reflectionColor,
1 - reflectionFactor,
reflectionFactor
);
} }
return baseColor & 0xFFFFFF; return baseColor & 0xFFFFFF;

View File

@ -5,6 +5,7 @@ import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.util.BlockIterator; import org.bukkit.util.BlockIterator;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
public class BlockRaytracer extends BlockIterator { public class BlockRaytracer extends BlockIterator {
@ -32,14 +33,16 @@ public class BlockRaytracer extends BlockIterator {
public Vector getIntersectionPoint() { public Vector getIntersectionPoint() {
BlockFace lastFace = getIntersectionFace(); BlockFace lastFace = getIntersectionFace();
Vector planeNormal = new Vector(lastFace.getModX(), lastFace.getModY(), lastFace.getModZ()); Vector planeNormal = new Vector(lastFace.getModX(), lastFace.getModY(), lastFace.getModZ());
Vector planePoint = lastBlock.getLocation().add(0.5, 0.5, 0.5).toVector() Vector planePoint = lastBlock.getLocation()
.add(planeNormal.clone().multiply(0.5)); .add(0.5, 0.5, 0.5)
.toVector()
.add(planeNormal.clone().multiply(0.5));
return MathUtil.getLinePlaneIntersection(position, direction, planePoint, planeNormal, true); return MathUtil.getLinePlaneIntersection(position, direction, planePoint, planeNormal, true);
} }
@Override @Override
public Block next() { public @NotNull Block next() {
Block currentBlock = super.next(); Block currentBlock = super.next();
currentFace = lastBlock == null ? BlockFace.SELF : currentBlock.getFace(lastBlock); currentFace = lastBlock == null ? BlockFace.SELF : currentBlock.getFace(lastBlock);

View File

@ -3,5 +3,6 @@ version: '1.0-SNAPSHOT'
main: eu.mhsl.minecraft.pixelpic.Main main: eu.mhsl.minecraft.pixelpic.Main
api-version: '1.21' api-version: '1.21'
commands: commands:
test: pixelPic:
test2: permission: "pixelpic.use"
usage: "pixelpic take"