diff --git a/.idea/modules.xml b/.idea/modules.xml
index 0fe66a1..987850f 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -3,6 +3,7 @@
+
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/Main.java b/src/main/java/eu/mhsl/minecraft/pixelpic/Main.java
index 7a3d31e..0e04da3 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelpic/Main.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/Main.java
@@ -6,9 +6,16 @@ import eu.mhsl.minecraft.pixelpic.render.render.Renderer;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.java.JavaPlugin;
-import java.util.Objects;
+import java.io.*;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
public final class Main extends JavaPlugin {
private static Main instance;
@@ -17,10 +24,27 @@ public final class Main extends JavaPlugin {
@Override
public void onEnable() {
instance = this;
+ extractJsonResources();
+
Bukkit.getPluginCommand("pixelPic").setExecutor(new PixelPicCommand());
Bukkit.getPluginCommand("test").setExecutor((sender, command, label, args) -> {
+ Material.getMaterial("acacia_button");
Bukkit.broadcast(Component.text(Material.STONE.getBlockTranslationKey().replace("block.minecraft.", "")));
+
+ if(!(sender instanceof Player player))
+ throw new IllegalStateException("Dieser Command kann nur von einem Spieler ausgeführt werden!");
+
+ File blockDir = new File(getDataFolder(), "models/block");
+ for (File file : blockDir.listFiles()) {
+ String blockName = file.getName().substring(0, file.getName().lastIndexOf('.'));
+ Material material = Material.getMaterial(blockName.toUpperCase());
+ System.out.println(material);
+ if(material == null) {
+ System.out.println(blockName);
+ }
+ }
+
return true;
});
}
@@ -29,6 +53,49 @@ public final class Main extends JavaPlugin {
public void onDisable() {
}
+ public void extractJsonResources() {
+ String resourcePath = "models/block/"; // Pfad im JAR
+ File outputDir = new File(getDataFolder(), resourcePath);
+ if (outputDir.exists()) return;
+ outputDir.mkdirs();
+
+ try {
+ URL jarUrl = getClass().getProtectionDomain().getCodeSource().getLocation();
+ File jarFile = new File(jarUrl.toURI());
+
+ try (JarFile jar = new JarFile(jarFile)) {
+ Enumeration entries = jar.entries();
+
+ while (entries.hasMoreElements()) {
+ JarEntry entry = entries.nextElement();
+ String entryName = entry.getName();
+
+ // Nur JSON-Dateien im gewünschten Ordner
+ if (entryName.startsWith(resourcePath) && entryName.endsWith(".json")) {
+ InputStream in = getResource(entryName);
+ if (in == null) continue;
+
+ File outFile = new File(getDataFolder(), entryName);
+ outFile.getParentFile().mkdirs(); // Ordnerstruktur sicherstellen
+
+ try (OutputStream out = new FileOutputStream(outFile)) {
+ byte[] buffer = new byte[1024];
+ int len;
+ while ((len = in.read(buffer)) != -1) {
+ out.write(buffer, 0, len);
+ }
+ System.out.println("Extrahiert: " + entryName);
+ }
+
+ in.close();
+ }
+ }
+ }
+ } catch (IOException | URISyntaxException e) {
+ e.printStackTrace();
+ }
+ }
+
public Renderer getScreenRenderer() {
if(this.screenRenderer == null) this.screenRenderer = new DefaultScreenRenderer();
return this.screenRenderer;
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/render/raytrace/AdvancedRaytracer.java b/src/main/java/eu/mhsl/minecraft/pixelpic/render/raytrace/AdvancedRaytracer.java
new file mode 100644
index 0000000..b58a4e4
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/raytrace/AdvancedRaytracer.java
@@ -0,0 +1,151 @@
+package eu.mhsl.minecraft.pixelpic.render.raytrace;
+
+import eu.mhsl.minecraft.pixelpic.render.model.Model;
+import eu.mhsl.minecraft.pixelpic.render.registry.AdvancedModelRegistry;
+import eu.mhsl.minecraft.pixelpic.render.registry.ModelRegistry;
+import eu.mhsl.minecraft.pixelpic.render.util.BlockRaytracer;
+import eu.mhsl.minecraft.pixelpic.render.util.Intersection;
+import eu.mhsl.minecraft.pixelpic.render.util.MathUtil;
+import org.bukkit.Color;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.block.Biome;
+import org.bukkit.block.Block;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.util.Vector;
+
+public class AdvancedRaytracer implements Raytracer {
+ private final int maxDistance;
+ private final int reflectionDepth;
+
+ private final AdvancedModelRegistry textureRegistry;
+ private Block reflectedBlock;
+
+ public AdvancedRaytracer() {
+ this(300, 10);
+ }
+
+ public AdvancedRaytracer(int maxDistance, int reflectionDepth) {
+ this.maxDistance = maxDistance;
+ this.reflectionDepth = reflectionDepth;
+
+ this.textureRegistry = new AdvancedModelRegistry();
+ this.textureRegistry.initialize();
+
+ this.reflectedBlock = null;
+ }
+
+ @Override
+ public int trace(World world, Vector point, Vector direction) {
+ return trace(world, point, direction, reflectionDepth);
+ }
+
+ private int trace(World world, Vector point, Vector direction, int reflectionDepth) {
+ Location loc = point.toLocation(world);
+ loc.setDirection(direction);
+ BlockRaytracer iterator = new BlockRaytracer(loc);
+ int baseColor = Color.fromRGB(65, 89, 252).asRGB();
+ Vector finalIntersection = null;
+
+ int reflectionColor = 0;
+ double reflectionFactor = 0;
+ boolean reflected = false;
+
+ Vector transparencyStart = null;
+ int transparencyColor = 0;
+ double transparencyFactor = 0;
+
+ Material occlusionMaterial = null;
+ BlockData occlusionData = null;
+
+ for (int i = 0; i < maxDistance; i++) {
+ if (!iterator.hasNext()) break;
+ Block block = iterator.next();
+ if (reflectedBlock != null && reflectedBlock.equals(block)) continue;
+ reflectedBlock = null;
+
+ Material material = block.getType();
+ if (material == Material.AIR) {
+ occlusionMaterial = null;
+ occlusionData = null;
+ continue;
+ }
+
+ Model textureModel = textureRegistry.getModel(block.getType(), block.getBlockData(), block.getTemperature(), block.getHumidity());
+ Intersection currentIntersection = Intersection.of(
+ MathUtil.toVector(iterator.getIntersectionFace()),
+ i == 0 ? point : iterator.getIntersectionPoint(),
+ direction
+ );
+ Intersection newIntersection = textureModel.intersect(block, currentIntersection);
+
+ if (newIntersection == null) continue;
+
+ int color = newIntersection.getColor();
+
+ if (!reflected && textureModel.getReflectionFactor() > 0 && reflectionDepth > 0 && (color >> 24) != 0) {
+ reflectedBlock = block;
+ reflectionColor = trace(
+ world,
+ newIntersection.getPoint(),
+ MathUtil.reflectVector(
+ point,
+ direction,
+ newIntersection.getPoint(),
+ newIntersection.getNormal()
+ ),
+ reflectionDepth - 1
+ );
+ reflectionFactor = textureModel.getReflectionFactor();
+ reflected = true;
+ }
+
+ if (transparencyStart == null && textureModel.getTransparencyFactor() > 0) {
+ transparencyStart = newIntersection.getPoint();
+ transparencyColor = newIntersection.getColor();
+ transparencyFactor = textureModel.getTransparencyFactor();
+ }
+
+ if (textureModel.isOccluding()) {
+ BlockData data = block.getBlockData();
+
+ if (material == occlusionMaterial && data.equals(occlusionData)) continue;
+
+ occlusionMaterial = material;
+ occlusionData = data;
+ } else {
+ occlusionMaterial = null;
+ occlusionData = null;
+ }
+
+ if (transparencyStart != null && textureModel.getTransparencyFactor() > 0) continue;
+ if ((color >> 24) == 0) continue;
+
+ baseColor = color;
+ finalIntersection = newIntersection.getPoint();
+ break;
+ }
+
+ if (transparencyStart != null) {
+ baseColor = MathUtil.weightedColorSum(
+ baseColor,
+ transparencyColor,
+ transparencyFactor,
+ (1
+ - transparencyFactor)
+ * (1 + transparencyStart.distance(finalIntersection == null ? transparencyStart : finalIntersection)
+ / 5.0));
+ }
+ if (reflected) {
+ baseColor = MathUtil.weightedColorSum(
+ baseColor,
+ reflectionColor,
+ 1 - reflectionFactor,
+ reflectionFactor
+ );
+ }
+
+ return baseColor & 0xFFFFFF;
+ }
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/render/raytrace/DefaultRaytracer.java b/src/main/java/eu/mhsl/minecraft/pixelpic/render/raytrace/DefaultRaytracer.java
index 621e0a0..7851007 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelpic/render/raytrace/DefaultRaytracer.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/raytrace/DefaultRaytracer.java
@@ -10,6 +10,7 @@ import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
+import org.bukkit.block.Biome;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.util.Vector;
@@ -71,6 +72,7 @@ public class DefaultRaytracer implements Raytracer {
continue;
}
+ Biome biome = block.getBiome();
Model textureModel = textureRegistry.getModel(block);
Intersection currentIntersection = Intersection.of(
MathUtil.toVector(iterator.getIntersectionFace()),
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/render/registry/AdvancedModelRegistry.java b/src/main/java/eu/mhsl/minecraft/pixelpic/render/registry/AdvancedModelRegistry.java
index a8d8004..80f9218 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelpic/render/registry/AdvancedModelRegistry.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/registry/AdvancedModelRegistry.java
@@ -1,5 +1,7 @@
package eu.mhsl.minecraft.pixelpic.render.registry;
+import com.google.gson.Gson;
+import eu.mhsl.minecraft.pixelpic.Main;
import eu.mhsl.minecraft.pixelpic.render.model.AbstractModel;
import eu.mhsl.minecraft.pixelpic.render.model.Model;
import org.bukkit.Color;
@@ -9,28 +11,47 @@ import org.bukkit.block.data.BlockData;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
-import java.io.IOException;
-import java.io.InputStream;
+import java.io.*;
import java.net.URL;
import java.util.*;
import static eu.mhsl.minecraft.pixelpic.render.registry.DefaultModelRegistry.TEXTURE_SIZE;
public class AdvancedModelRegistry implements ModelRegistry {
+ private final Gson gson = new Gson();
+
private final Map> modelMap = new HashMap<>();
+ private final Set tintedBlocks = Set.of("grass", "grass_block", "leaves", "oak_leaves", "water", "vine", "sugar_cane");
+
+ public record BlockInfo(String parent, BlockTextures textures){}
+ public record BlockTextures(
+ String texture,
+ String bottom,
+ String top,
+ String all,
+ String particle,
+ String end,
+ String side,
+ String cross,
+ String rail,
+ String overlay
+ ){}
@Override
public void initialize() {
-// registerModel(Material.STONE, AbstractModel.Builder.createSimple(getTextureArray(Material.STONE.getBlockTranslationKey().replace("block.minecraft.", ""))).build());
- for (Material currentMaterial : Material.values()) {
- if(!currentMaterial.isBlock()) continue;
- List blockTextures = getBlockTextures(currentMaterial);
- for (String blockTexture : blockTextures) {
- try {
- registerModel(currentMaterial, AbstractModel.Builder.createSimple(getTextureArray(blockTexture)).build());
- } catch (Exception ignored) { }
- }
+ System.out.println(modelMap);
+
+ File blockDir = new File(Main.getInstance().getDataFolder(), "models/block");
+ for (File file : Objects.requireNonNull(blockDir.listFiles())) {
+ addModelFromFile(file);
}
+
+ try {
+ registerModel(Material.LAVA, AbstractModel.Builder.createSimple(getTextureArray("lava_still"))
+ .transparency(0.15).reflection(0.05).occlusion().build());
+ registerModel(Material.WATER, AbstractModel.Builder.createSimple(getTextureArray("water_still"))
+ .transparency(0.60).reflection(0.1).occlusion().build());
+ } catch (Exception ignored) { }
}
@Override
@@ -40,6 +61,10 @@ public class AdvancedModelRegistry implements ModelRegistry {
@Override
public Model getModel(Material material, BlockData blockData) {
+ return getModel(material, blockData, 0.8, 0.4);
+ }
+
+ public Model getModel(Material material, BlockData blockData, double temperature, double humidity) {
return modelMap.computeIfAbsent(material, key -> new HashMap<>()).getOrDefault(blockData,
blockData == null ? getDefaultModel()
: modelMap.get(material).getOrDefault(null, getDefaultModel()));
@@ -55,39 +80,54 @@ public class AdvancedModelRegistry implements ModelRegistry {
.put(null, blockModel);
}
- private List getBlockTextures(Material material) {
- if(!material.isBlock()) return new ArrayList<>();
- String blockName = material.name().toLowerCase();
- try {
- if(!Arrays.deepEquals(getTextureArray(blockName), new int[0][0])) {
- return List.of(blockName);
+ private void addModelFromFile(File file) {
+ String blockName = file.getName().substring(0, file.getName().lastIndexOf('.'));
+ Material material = Material.getMaterial(blockName.toUpperCase());
+ if(material == null) return;
+
+ Model model = getModelFromFile(file);
+ if(model == null) return;
+
+ registerModel(material, model);
+ }
+
+ private Model getModelFromFile(File file) {
+ try (Reader reader = new FileReader(file)) {
+ BlockInfo blockInfo = gson.fromJson(reader, BlockInfo.class);
+
+ if(blockInfo.textures.all != null) {
+ return AbstractModel.Builder.createSimple(
+ getTextureArray(blockInfo.textures.all.substring(blockInfo.textures.all.lastIndexOf('/') + 1))
+ ).build();
+ }
+ if(blockInfo.textures.cross != null) {
+ return AbstractModel.Builder.createCross(
+ getTextureArray(blockInfo.textures.cross.substring(blockInfo.textures.cross.lastIndexOf('/') + 1))
+ ).build();
+ }
+ if(blockInfo.textures.side != null && blockInfo.textures.bottom != null && blockInfo.textures.top != null) {
+ return AbstractModel.Builder.createMulti(
+ getTextureArray(blockInfo.textures.top.substring(blockInfo.textures.top.lastIndexOf('/') + 1)),
+ getTextureArray(blockInfo.textures.side.substring(blockInfo.textures.side.lastIndexOf('/') + 1)),
+ getTextureArray(blockInfo.textures.bottom.substring(blockInfo.textures.bottom.lastIndexOf('/') + 1))
+ ).build();
+ }
+ if(blockInfo.textures.side != null && blockInfo.textures.end != null) {
+ return AbstractModel.Builder.createMulti(
+ getTextureArray(blockInfo.textures.end.substring(blockInfo.textures.end.lastIndexOf('/') + 1)),
+ getTextureArray(blockInfo.textures.side.substring(blockInfo.textures.side.lastIndexOf('/') + 1)),
+ getTextureArray(blockInfo.textures.end.substring(blockInfo.textures.end.lastIndexOf('/') + 1))
+ ).build();
}
} catch (Exception e) {
- String[] possibleVariants = {
- blockName + "_top",
- blockName + "_side",
- blockName + "_bottom",
- blockName + "_front",
- blockName + "_back",
- blockName + "_left",
- blockName + "_right"
- };
- List results = new ArrayList<>();
- for (String variant : possibleVariants) {
- try {
- if(!Arrays.deepEquals(getTextureArray(variant), new int[0][0])) {
- results.add(variant);
- }
- } catch (Exception ignored) { }
- }
- return results;
+ System.out.println(e.getMessage());
}
- return new ArrayList<>();
+ return null;
}
private int[][] getTextureArray(String textureName) {
int[][] texture = new int[TEXTURE_SIZE][TEXTURE_SIZE];
- BufferedImage img = null;
+ BufferedImage img;
URL url = this.getClass().getClassLoader().getResource(String.format("textures/block/%s.png", textureName));
if (url == null) {
throw new RuntimeException("Block Texture Resource not found.");
@@ -100,10 +140,18 @@ public class AdvancedModelRegistry implements ModelRegistry {
for (int pixelY = 0; pixelY < TEXTURE_SIZE; pixelY++) {
for (int pixelX = 0; pixelX < TEXTURE_SIZE; pixelX++) {
- texture[pixelY][pixelX] = img.getRGB(pixelX, pixelY);
+ texture[TEXTURE_SIZE - 1 - pixelY][TEXTURE_SIZE - 1 - pixelX] = img.getRGB(pixelX, pixelY);
}
}
return texture;
}
+
+ private int tintPixel(int baseColor, int tintColor) {
+ int a = (baseColor >> 24) & 0xFF;
+ int r = ((baseColor >> 16) & 0xFF) * ((tintColor >> 16) & 0xFF) / 255;
+ int g = ((baseColor >> 8) & 0xFF) * ((tintColor >> 8) & 0xFF) / 255;
+ int b = (baseColor & 0xFF) * (tintColor & 0xFF) / 255;
+ return (a << 24) | (r << 16) | (g << 8) | b;
+ }
}
diff --git a/src/main/resources/colormap/foliage.png b/src/main/resources/colormap/foliage.png
new file mode 100644
index 0000000..d58fce2
Binary files /dev/null and b/src/main/resources/colormap/foliage.png differ
diff --git a/src/main/resources/colormap/grass.png b/src/main/resources/colormap/grass.png
new file mode 100644
index 0000000..5b94654
Binary files /dev/null and b/src/main/resources/colormap/grass.png differ