From 0103a654c37813d837b7aa6c8625c7766ae09f48 Mon Sep 17 00:00:00 2001
From: lars <larslukasneuhaus@gmx.de>
Date: Tue, 18 Mar 2025 21:34:22 +0100
Subject: [PATCH] basic image generation based on WinX64/bukkit-screenshot

---
 .../minecraft/pixelpic/ImageMapRenderer.java  |  50 ++++++
 .../java/eu/mhsl/minecraft/pixelpic/Main.java | 108 ++++++++++-
 .../pixelpic/render/model/AbstractModel.java  | 108 +++++++++++
 .../pixelpic/render/model/CrossModel.java     |  91 ++++++++++
 .../pixelpic/render/model/Model.java          |  15 ++
 .../pixelpic/render/model/MultiModel.java     |  61 +++++++
 .../render/model/OctahedronModel.java         | 100 +++++++++++
 .../pixelpic/render/model/SimpleModel.java    |  58 ++++++
 .../pixelpic/render/model/SphereModel.java    | 128 +++++++++++++
 .../pixelpic/render/model/StaticModel.java    |  57 ++++++
 .../render/raytrace/DefaultRaytracer.java     | 141 +++++++++++++++
 .../pixelpic/render/raytrace/Raytracer.java   |   9 +
 .../render/registry/DefaultModelRegistry.java | 169 ++++++++++++++++++
 .../render/registry/ModelRegistry.java        |  23 +++
 .../render/render/DefaultScreenRenderer.java  |  87 +++++++++
 .../pixelpic/render/render/Renderer.java      |  10 ++
 .../pixelpic/render/render/Resolution.java    |  62 +++++++
 .../pixelpic/render/util/BlockRaytracer.java  |  48 +++++
 .../pixelpic/render/util/Intersection.java    |  42 +++++
 .../pixelpic/render/util/MathUtil.java        |  68 +++++++
 src/main/resources/plugin.yml                 |   3 +
 src/main/resources/terrain.png                | Bin 0 -> 36672 bytes
 22 files changed, 1437 insertions(+), 1 deletion(-)
 create mode 100644 src/main/java/eu/mhsl/minecraft/pixelpic/ImageMapRenderer.java
 create mode 100644 src/main/java/eu/mhsl/minecraft/pixelpic/render/model/AbstractModel.java
 create mode 100644 src/main/java/eu/mhsl/minecraft/pixelpic/render/model/CrossModel.java
 create mode 100644 src/main/java/eu/mhsl/minecraft/pixelpic/render/model/Model.java
 create mode 100644 src/main/java/eu/mhsl/minecraft/pixelpic/render/model/MultiModel.java
 create mode 100644 src/main/java/eu/mhsl/minecraft/pixelpic/render/model/OctahedronModel.java
 create mode 100644 src/main/java/eu/mhsl/minecraft/pixelpic/render/model/SimpleModel.java
 create mode 100644 src/main/java/eu/mhsl/minecraft/pixelpic/render/model/SphereModel.java
 create mode 100644 src/main/java/eu/mhsl/minecraft/pixelpic/render/model/StaticModel.java
 create mode 100644 src/main/java/eu/mhsl/minecraft/pixelpic/render/raytrace/DefaultRaytracer.java
 create mode 100644 src/main/java/eu/mhsl/minecraft/pixelpic/render/raytrace/Raytracer.java
 create mode 100644 src/main/java/eu/mhsl/minecraft/pixelpic/render/registry/DefaultModelRegistry.java
 create mode 100644 src/main/java/eu/mhsl/minecraft/pixelpic/render/registry/ModelRegistry.java
 create mode 100644 src/main/java/eu/mhsl/minecraft/pixelpic/render/render/DefaultScreenRenderer.java
 create mode 100644 src/main/java/eu/mhsl/minecraft/pixelpic/render/render/Renderer.java
 create mode 100644 src/main/java/eu/mhsl/minecraft/pixelpic/render/render/Resolution.java
 create mode 100644 src/main/java/eu/mhsl/minecraft/pixelpic/render/util/BlockRaytracer.java
 create mode 100644 src/main/java/eu/mhsl/minecraft/pixelpic/render/util/Intersection.java
 create mode 100644 src/main/java/eu/mhsl/minecraft/pixelpic/render/util/MathUtil.java
 create mode 100644 src/main/resources/terrain.png

diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/ImageMapRenderer.java b/src/main/java/eu/mhsl/minecraft/pixelpic/ImageMapRenderer.java
new file mode 100644
index 0000000..3461005
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/ImageMapRenderer.java
@@ -0,0 +1,50 @@
+package eu.mhsl.minecraft.pixelpic;
+
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.map.MapCanvas;
+import org.bukkit.map.MapRenderer;
+import org.bukkit.map.MapView;
+import org.jetbrains.annotations.NotNull;
+
+import java.awt.image.BufferedImage;
+
+public class ImageMapRenderer extends MapRenderer {
+    public final int IMAGE_SIZE = 128;
+
+    private BufferedImage image;
+    private final int x;
+    private final int y;
+
+    public ImageMapRenderer(BufferedImage image) {
+        this(image, 0, 0);
+    }
+
+    public ImageMapRenderer(BufferedImage image, int x, int y) {
+        this.x = x;
+        this.y = y;
+        recalculateInput(image);
+    }
+
+    public void recalculateInput(BufferedImage input) {
+        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));
+
+        int x1 = (int) (double) (x * IMAGE_SIZE);
+        int y1 = (int) (double) (y * IMAGE_SIZE);
+
+        int x2 = (int) (double) Math.min(input.getWidth(), ((x + 1) * IMAGE_SIZE));
+        int y2 = (int) (double) Math.min(input.getHeight(), ((y + 1) * IMAGE_SIZE));
+
+        if (x2 - x1 <= 0 || y2 - y1 <= 0)
+            return;
+
+        this.image = input.getSubimage(x1, y1, x2 - x1, y2 - y1);
+    }
+
+    @Override
+    public void render(@NotNull MapView map, @NotNull MapCanvas canvas, @NotNull Player player) {
+        if(image == null) return;
+        Bukkit.getScheduler().runTaskLater(Main.getInstance(), () -> canvas.drawImage(0, 0, image), 2L);
+    }
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/Main.java b/src/main/java/eu/mhsl/minecraft/pixelpic/Main.java
index 1ccf6c9..25beb2a 100644
--- a/src/main/java/eu/mhsl/minecraft/pixelpic/Main.java
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/Main.java
@@ -1,17 +1,123 @@
 package eu.mhsl.minecraft.pixelpic;
 
+import eu.mhsl.minecraft.pixelpic.render.render.DefaultScreenRenderer;
+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.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 javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.File;
+
 public final class Main extends JavaPlugin {
+    private static Main instance;
+    private Renderer screenRenderer;
 
     @Override
     public void onEnable() {
-        // Plugin startup logic
+        this.instance = this;
+        this.screenRenderer = new DefaultScreenRenderer();
+        Bukkit.getPluginCommand("test").setExecutor((sender, command, label, args) -> {
+            if(!(sender instanceof Player player)) return false;
 
+            Resolution.Pixels pixels = Resolution.Pixels._128P;
+            Resolution.AspectRatio aspectRatio = Resolution.AspectRatio._1_1;
+            Resolution resolution = new Resolution(pixels, aspectRatio);
+            BufferedImage image = screenRenderer.render((Player) sender, resolution);
+            Bukkit.broadcast(Component.text(image.toString()));
+
+            File file = new File(getDataFolder(), "Bild" + ".png");
+            try {
+                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.addRenderer(new ImageMapRenderer(image));
+
+            meta.setMapView(mapView);
+            map.setItemMeta(meta);
+
+            player.getInventory().addItem(map);
+            player.updateInventory();
+
+            return true;
+        });
+
+        Bukkit.getPluginCommand("test2").setExecutor((sender, command, label, args) -> {
+            if(!(sender instanceof Player player)) return false;
+            Bukkit.broadcast(Component.text("HI"));
+
+            Resolution.Pixels pixels = Resolution.Pixels._256P;
+            Resolution.AspectRatio aspectRatio = Resolution.AspectRatio._1_1;
+            Resolution resolution = new Resolution(pixels, aspectRatio);
+            BufferedImage image = screenRenderer.render((Player) sender, resolution);
+            Bukkit.broadcast(Component.text(image.toString()));
+
+            File file = new File(getDataFolder(), "Bild" + ".png");
+            try {
+                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.addRenderer(new ImageMapRenderer(image, 0, 0));
+            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
     public void onDisable() {
         // Plugin shutdown logic
     }
+
+    public static Main getInstance() {
+        return instance;
+    }
 }
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/AbstractModel.java b/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/AbstractModel.java
new file mode 100644
index 0000000..af5d8ef
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/AbstractModel.java
@@ -0,0 +1,108 @@
+package eu.mhsl.minecraft.pixelpic.render.model;
+
+import com.google.common.base.Preconditions;
+
+import eu.mhsl.minecraft.pixelpic.render.model.MultiModel.MultiModelBuilder;
+import eu.mhsl.minecraft.pixelpic.render.model.CrossModel.CrossModelBuilder;
+import eu.mhsl.minecraft.pixelpic.render.model.StaticModel.StaticModelBuilder;
+import eu.mhsl.minecraft.pixelpic.render.model.OctahedronModel.OctahedronModelBuilder;
+import eu.mhsl.minecraft.pixelpic.render.model.SphereModel.SphereModelBuilder;
+
+
+public abstract class AbstractModel implements Model {
+
+    final int textureSize;
+    final int[][] texture;
+
+    private final double transparencyFactor;
+    private final double reflectionFactor;
+    private final boolean occluding;
+
+    AbstractModel(int[][] texture, double transparencyFactor, double reflectionFactor,
+                  boolean occluding) {
+        Preconditions.checkNotNull(texture);
+        Preconditions.checkArgument(texture.length > 0, "texture cannot be empty");
+        Preconditions.checkArgument(texture.length == texture[0].length, "texture must be a square array");
+
+        this.textureSize = texture.length;
+        this.texture = texture;
+
+        this.transparencyFactor = transparencyFactor;
+        this.reflectionFactor = reflectionFactor;
+        this.occluding = occluding;
+    }
+
+    @Override
+    public double getTransparencyFactor() {
+        return transparencyFactor;
+    }
+
+    @Override
+    public double getReflectionFactor() {
+        return reflectionFactor;
+    }
+
+    @Override
+    public boolean isOccluding() {
+        return occluding;
+    }
+
+    public static abstract class Builder {
+
+        final int[][] texture;
+
+        double transparencyFactor;
+        double reflectionFactor;
+        boolean occluding;
+
+        Builder(int[][] texture) {
+            this.texture = texture;
+
+            this.transparencyFactor = 0;
+            this.reflectionFactor = 0;
+            this.occluding = false;
+        }
+
+        public static SimpleModel.SimpleModelBuilder createSimple(int[][] texture) {
+            return new SimpleModel.SimpleModelBuilder(texture);
+        }
+
+        public static MultiModelBuilder createMulti(int[][] topTexture, int[][] sideTexture,
+                                                               int[][] bottomTexture) {
+            return new MultiModelBuilder(topTexture, sideTexture, bottomTexture);
+        }
+
+        public static StaticModelBuilder createStatic(int color) {
+            return new StaticModelBuilder(color);
+        }
+
+        public static CrossModelBuilder createCross(int[][] texture) {
+            return new CrossModelBuilder(texture);
+        }
+
+        public static SphereModelBuilder createSphere(int[][] texture) {
+            return new SphereModelBuilder(texture);
+        }
+
+        public static OctahedronModelBuilder createOctahedron(int[][] texture) {
+            return new OctahedronModelBuilder(texture);
+        }
+
+        public Builder transparency(double transparencyFactor) {
+            this.transparencyFactor = transparencyFactor;
+            return this;
+        }
+
+        public Builder reflection(double reflectionFactor) {
+            this.reflectionFactor = reflectionFactor;
+            return this;
+        }
+
+        public Builder occlusion() {
+            this.occluding = true;
+            return this;
+        }
+
+        public abstract Model build();
+    }
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/CrossModel.java b/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/CrossModel.java
new file mode 100644
index 0000000..ae5b62f
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/CrossModel.java
@@ -0,0 +1,91 @@
+package eu.mhsl.minecraft.pixelpic.render.model;
+
+import eu.mhsl.minecraft.pixelpic.render.util.Intersection;
+import eu.mhsl.minecraft.pixelpic.render.util.MathUtil;
+import org.bukkit.block.Block;
+import org.bukkit.util.Vector;
+
+public class CrossModel extends AbstractModel {
+
+    private static final Vector NORMAL_ONE = new Vector(1, 0, 1).normalize();
+    private static final Vector NORMAL_TWO = new Vector(-1, 0, 1).normalize();
+
+    private static final Vector POINT_ONE = new Vector(1, 0, 0);
+    private static final Vector POINT_TWO = new Vector(1, 0, 1);
+
+    private CrossModel(int[][] texture, double transparencyFactor, double reflectionFactor,
+                       boolean occluding) {
+        super(texture, transparencyFactor, reflectionFactor, occluding);
+    }
+
+    @Override
+    public Intersection intersect(Block block, Intersection currentIntersection) {
+        Vector linePoint = currentIntersection.getPoint();
+        Vector lineDirection = currentIntersection.getDirection();
+
+        Vector blockPoint = block.getLocation().toVector();
+        Vector planePoint = block.getLocation().add(0.5, 0, 0.5).toVector();
+
+        double distance = Double.POSITIVE_INFINITY;
+        int color = 0;
+        Vector target = null;
+
+        Vector intersectionOne = MathUtil.getLinePlaneIntersection(linePoint, lineDirection, planePoint, NORMAL_ONE,
+                true);
+        if (intersectionOne != null) {
+            intersectionOne.subtract(blockPoint);
+            if (isInsideBlock(intersectionOne)) {
+                color = getColor(intersectionOne, POINT_ONE);
+                distance = linePoint.distanceSquared(intersectionOne.add(blockPoint));
+                target = intersectionOne;
+            }
+        }
+
+        Vector intersectionTwo = MathUtil.getLinePlaneIntersection(linePoint, lineDirection, planePoint, NORMAL_TWO,
+                true);
+        if (intersectionTwo != null) {
+            intersectionTwo.subtract(blockPoint);
+            if (isInsideBlock(intersectionTwo)) {
+                int colorTwo = getColor(intersectionTwo, POINT_TWO);
+                double distanceTwo = linePoint.distanceSquared(intersectionTwo.add(blockPoint));
+                if ((distanceTwo < distance && (colorTwo >> 24) != 0) || (color >> 24) == 0) {
+                    target = intersectionTwo;
+                    color = colorTwo;
+                }
+            }
+        }
+
+        if (target == null) {
+            target = linePoint;
+        }
+
+        return Intersection.of(currentIntersection.getNormal(), target, lineDirection, color);
+    }
+
+    private boolean isInsideBlock(Vector vec) {
+        return vec.getX() >= 0 && vec.getZ() < 1 && vec.getY() >= 0 && vec.getY() < 1 && vec.getZ() >= 0
+                && vec.getZ() < 1;
+    }
+
+    private int getColor(Vector vec, Vector base) {
+        double xOffset = Math.sqrt(Math.pow(vec.getX() - base.getX(), 2) + Math.pow(vec.getZ() - base.getZ(), 2));
+        double yOffset = vec.getY();
+
+        int pixelY = (int) Math.floor(yOffset * textureSize);
+        int pixelX = (int) Math.floor(xOffset / Math.sqrt(2) * textureSize);
+
+        return texture[pixelY][pixelX];
+    }
+
+    public static class CrossModelBuilder extends Builder {
+
+        CrossModelBuilder(int[][] texture) {
+            super(texture);
+        }
+
+        @Override
+        public CrossModel build() {
+            return new CrossModel(texture, transparencyFactor, reflectionFactor, occluding);
+        }
+    }
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/Model.java b/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/Model.java
new file mode 100644
index 0000000..830e288
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/Model.java
@@ -0,0 +1,15 @@
+package eu.mhsl.minecraft.pixelpic.render.model;
+
+import eu.mhsl.minecraft.pixelpic.render.util.Intersection;
+import org.bukkit.block.Block;
+
+public interface Model {
+
+	Intersection intersect(Block block, Intersection currentIntersection);
+
+	double getTransparencyFactor();
+
+	double getReflectionFactor();
+
+	boolean isOccluding();
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/MultiModel.java b/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/MultiModel.java
new file mode 100644
index 0000000..6af8049
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/MultiModel.java
@@ -0,0 +1,61 @@
+package eu.mhsl.minecraft.pixelpic.render.model;
+
+import eu.mhsl.minecraft.pixelpic.render.util.Intersection;
+import org.bukkit.block.Block;
+import org.bukkit.util.Vector;
+
+public class MultiModel extends SimpleModel {
+
+	private final int[][] topTexture;
+	private final int[][] bottomTexture;
+
+	private MultiModel(int[][] topTexture, int[][] sideTexture, int[][] bottomTexture,
+					   double transparencyFactor, double reflectionFactor, boolean occluding) {
+		super(sideTexture, transparencyFactor, reflectionFactor, occluding);
+
+		this.topTexture = topTexture;
+		this.bottomTexture = bottomTexture;
+	}
+
+	@Override
+	public Intersection intersect(Block block, Intersection currentIntersection) {
+		if (!currentIntersection.getNormal().equals(UP) && !currentIntersection.getNormal().equals(DOWN)) {
+			return super.intersect(block, currentIntersection);
+		}
+
+		Vector normal = currentIntersection.getNormal();
+		Vector point = currentIntersection.getPoint();
+		Vector direction = currentIntersection.getDirection();
+
+		double yOffset = point.getX() - (int) point.getX();
+		double xOffset = point.getZ() - (int) point.getZ();
+
+		int pixelY = (int) Math.floor((yOffset < 0 ? yOffset + 1 : yOffset) * textureSize);
+		int pixelX = (int) Math.floor((xOffset < 0 ? xOffset + 1 : xOffset) * textureSize);
+
+		if (normal.equals(UP)) {
+			return Intersection.of(normal, point, direction, topTexture[pixelY][pixelX]);
+		} else {
+			return Intersection.of(normal, point, direction, bottomTexture[pixelY][pixelX]);
+		}
+	}
+
+	public static class MultiModelBuilder extends SimpleModelBuilder {
+
+		private final int[][] topTexture;
+		private final int[][] bottomTexture;
+
+		MultiModelBuilder(int[][] topTexture, int[][] sideTexture, int[][] bottomTexture) {
+			super(sideTexture);
+
+			this.topTexture = topTexture;
+			this.bottomTexture = bottomTexture;
+		}
+
+		@Override
+		public MultiModel build() {
+			return new MultiModel(topTexture, texture, bottomTexture, transparencyFactor,
+					reflectionFactor, occluding);
+		}
+	}
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/OctahedronModel.java b/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/OctahedronModel.java
new file mode 100644
index 0000000..5cdd731
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/OctahedronModel.java
@@ -0,0 +1,100 @@
+package eu.mhsl.minecraft.pixelpic.render.model;
+
+import eu.mhsl.minecraft.pixelpic.render.util.Intersection;
+import eu.mhsl.minecraft.pixelpic.render.util.MathUtil;
+import org.bukkit.block.Block;
+import org.bukkit.util.Vector;
+
+public class OctahedronModel extends AbstractModel {
+
+    private static final double RADIUS = 0.5;
+
+    private static final Vector[] NORMALS = new Vector[]{new Vector(-1, -1, -1), new Vector(-1, -1, 1),
+            new Vector(-1, 1, -1), new Vector(-1, 1, 1), new Vector(1, -1, -1), new Vector(1, -1, 1),
+            new Vector(1, 1, -1), new Vector(1, 1, 1)};
+
+    private OctahedronModel(int[][] texture, double transparencyFactor, double reflectionFactor,
+                            boolean occluding) {
+        super(texture, transparencyFactor, reflectionFactor, occluding);
+    }
+
+    @Override
+    public Intersection intersect(Block block, Intersection currentIntersection) {
+        Vector linePoint = currentIntersection.getPoint();
+        Vector lineDirection = currentIntersection.getDirection();
+        Vector blockPoint = block.getLocation().toVector();
+        Vector centerPoint = blockPoint.clone().add(new Vector(0.5, 0.5, 0.5));
+
+        Vector lastIntersection = null;
+        double lastDistance = Double.POSITIVE_INFINITY;
+        for (int i = 0; i < 8; i++) {
+            Vector planePoint = new Vector(i < 4 ? -0.5 : 0.5, 0, 0).add(centerPoint);
+            Vector planeNormal = NORMALS[i];
+
+            Vector intersection = MathUtil.getLinePlaneIntersection(linePoint, lineDirection, planePoint, planeNormal,
+                    false);
+            if (intersection == null) {
+                continue;
+            }
+
+            if (!isInsideBlock(blockPoint, planeNormal, intersection)) {
+                continue;
+            }
+
+            double distance = intersection.distance(linePoint);
+            if (distance < lastDistance) {
+                lastIntersection = intersection;
+                lastDistance = distance;
+            }
+        }
+
+        if (lastIntersection == null) {
+            return currentIntersection;
+        }
+
+        double dist = linePoint.distance(centerPoint);
+        double minDist = dist - RADIUS;
+        double maxDist = dist + RADIUS;
+        double factor = (lastDistance - minDist) / (maxDist - minDist);
+
+        double yOffset = lastIntersection.getX() - (int) lastIntersection.getX();
+        double xOffset = lastIntersection.getZ() - (int) lastIntersection.getZ();
+
+        int pixelY = (int) Math.floor((yOffset < 0 ? yOffset + 1 : yOffset) * textureSize);
+        int pixelX = (int) Math.floor((xOffset < 0 ? xOffset + 1 : xOffset) * textureSize);
+
+        return Intersection.of(currentIntersection.getNormal(), lastIntersection, lineDirection,
+                0xFF000000 | MathUtil.weightedColorSum(texture[pixelY][pixelX], 0, 1 - factor, factor));
+    }
+
+    private boolean isInsideBlock(Vector blockPoint, Vector planeNormal, Vector intersection) {
+        intersection = intersection.clone().subtract(blockPoint);
+
+        if (intersection.getX() < 0 || intersection.getX() >= 1 || intersection.getY() < 0 || intersection.getY() >= 1
+                || intersection.getZ() < 0 || intersection.getZ() >= 1) {
+            return false;
+        }
+
+        boolean posX = planeNormal.getX() >= 0;
+        boolean posY = planeNormal.getY() >= 0;
+        boolean posZ = planeNormal.getZ() >= 0;
+
+        boolean blockX = intersection.getX() >= 0.5;
+        boolean blockY = intersection.getY() >= 0.5;
+        boolean blockZ = intersection.getZ() >= 0.5;
+
+        return posX == blockX && posY == blockY && posZ == blockZ;
+    }
+
+    public static class OctahedronModelBuilder extends Builder {
+
+        OctahedronModelBuilder(int[][] texture) {
+            super(texture);
+        }
+
+        @Override
+        public Model build() {
+            return new OctahedronModel(texture, transparencyFactor, reflectionFactor, occluding);
+        }
+    }
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/SimpleModel.java b/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/SimpleModel.java
new file mode 100644
index 0000000..b74c797
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/SimpleModel.java
@@ -0,0 +1,58 @@
+package eu.mhsl.minecraft.pixelpic.render.model;
+
+import eu.mhsl.minecraft.pixelpic.render.util.Intersection;
+import org.bukkit.block.Block;
+import org.bukkit.util.Vector;
+
+public class SimpleModel extends AbstractModel {
+
+    static final Vector UP = new Vector(0, 1, 0);
+    static final Vector DOWN = new Vector(0, -1, 0);
+    private static final Vector NORTH = new Vector(0, 0, -1);
+    private static final Vector SOUTH = new Vector(0, 0, 1);
+    private static final Vector EAST = new Vector(1, 0, 0);
+    private static final Vector WEST = new Vector(-1, 0, 0);
+
+    SimpleModel(int[][] texture, double transparencyFactor, double reflectionFactor,
+                boolean occluding) {
+        super(texture, transparencyFactor, reflectionFactor, occluding);
+    }
+
+    @Override
+    public Intersection intersect(Block block, Intersection currentIntersection) {
+        double yOffset;
+        double xOffset;
+
+        Vector normal = currentIntersection.getNormal();
+        Vector point = currentIntersection.getPoint();
+        Vector direction = currentIntersection.getDirection();
+
+        if (normal.equals(NORTH) || normal.equals(SOUTH)) {
+            yOffset = point.getY() - (int) point.getY();
+            xOffset = point.getX() - (int) point.getX();
+        } else if (normal.equals(EAST) || normal.equals(WEST)) {
+            yOffset = point.getY() - (int) point.getY();
+            xOffset = point.getZ() - (int) point.getZ();
+        } else {
+            yOffset = point.getX() - (int) point.getX();
+            xOffset = point.getZ() - (int) point.getZ();
+        }
+
+        int pixelY = (int) Math.floor((yOffset < 0 ? yOffset + 1 : yOffset) * textureSize);
+        int pixelX = (int) Math.floor((xOffset < 0 ? xOffset + 1 : xOffset) * textureSize);
+
+        return Intersection.of(normal, point, direction, texture[pixelY][pixelX]);
+    }
+
+    public static class SimpleModelBuilder extends Builder {
+
+        protected SimpleModelBuilder(int[][] texture) {
+            super(texture);
+        }
+
+        @Override
+        public Model build() {
+            return new SimpleModel(texture, transparencyFactor, reflectionFactor, occluding);
+        }
+    }
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/SphereModel.java b/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/SphereModel.java
new file mode 100644
index 0000000..f67c162
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/SphereModel.java
@@ -0,0 +1,128 @@
+package eu.mhsl.minecraft.pixelpic.render.model;
+
+import eu.mhsl.minecraft.pixelpic.render.util.Intersection;
+import eu.mhsl.minecraft.pixelpic.render.util.MathUtil;
+import org.bukkit.Location;
+import org.bukkit.block.Block;
+import org.bukkit.util.Vector;
+
+public class SphereModel extends AbstractModel {
+
+    private final double radius;
+    private final Vector offset;
+
+    private SphereModel(int[][] texture, double transparencyFactor, double reflectionFactor,
+                        boolean occluding, double radius, Vector offset) {
+        super(texture, transparencyFactor, reflectionFactor, occluding);
+
+        this.radius = radius;
+        this.offset = offset;
+    }
+
+    @Override
+    public Intersection intersect(Block block, Intersection currentIntersection) {
+        Vector linePoint = currentIntersection.getPoint();
+        Vector lineDirection = currentIntersection.getDirection();
+        Vector blockPoint = block.getLocation().toVector();
+        Vector centerPoint = block.getLocation().add(0.5, 0.5, 0.5).add(offset).toVector();
+
+        double a = lineDirection.dot(lineDirection);
+        double b = 2 * (linePoint.dot(lineDirection) - centerPoint.dot(lineDirection));
+        double c = linePoint.dot(linePoint) - 2 * centerPoint.dot(linePoint) + centerPoint.dot(centerPoint)
+                - Math.pow(radius, 2);
+
+        double delta = Math.pow(b, 2) - 4 * a * c;
+        if (delta < 0) {
+            return Intersection.of(currentIntersection.getNormal(), linePoint, lineDirection);
+        }
+
+        double dist = linePoint.distance(centerPoint);
+        double minDist = dist - radius;
+        double maxDist = dist + radius;
+
+        if (delta == 0) {
+            double t = -b / (2 * a);
+            Vector intersection = lineDirection.clone().add(lineDirection.clone().multiply(t));
+            if (!isInsideBlock(blockPoint, intersection)) {
+                return currentIntersection;
+            }
+            double currentDist = intersection.distance(linePoint);
+            double factor = (currentDist - minDist) / (maxDist - minDist);
+            Vector normal = intersection.clone().subtract(centerPoint).normalize();
+            return Intersection.of(normal, intersection, lineDirection, getColor(centerPoint, intersection, factor));
+        }
+
+        double deltaSqrt = Math.sqrt(delta);
+
+        double tOne = (-b + deltaSqrt) / (2 * a);
+        double tTwo = (-b - deltaSqrt) / (2 * a);
+
+        Vector intersectionOne = linePoint.clone().add(lineDirection.clone().multiply(tOne));
+        Vector intersectionTwo = linePoint.clone().add(lineDirection.clone().multiply(tTwo));
+
+        boolean first = intersectionOne.distanceSquared(linePoint) < intersectionTwo.distanceSquared(linePoint);
+        double currentDist = (first ? intersectionOne : intersectionTwo).distance(linePoint);
+        double factor = (currentDist - minDist) / (maxDist - minDist);
+        if (first && isInsideBlock(blockPoint, intersectionOne)) {
+            Vector normal = intersectionOne.clone().subtract(centerPoint).normalize();
+            return Intersection.of(normal, intersectionOne, lineDirection,
+                    getColor(centerPoint, intersectionOne, factor));
+        } else if (isInsideBlock(blockPoint, intersectionTwo)) {
+            Vector normal = intersectionTwo.clone().subtract(centerPoint).normalize();
+            return Intersection.of(normal, intersectionTwo, lineDirection,
+                    getColor(centerPoint, intersectionTwo, factor));
+        } else {
+            return currentIntersection;
+        }
+    }
+
+    private int getColor(Vector base, Vector intersection, double factor) {
+        Location loc = base.toLocation(null);
+        loc.setDirection(intersection.clone().subtract(base).normalize());
+
+        double perimeter = Math.round(2 * Math.PI * radius);
+        double yawDiv = 360 / perimeter;
+        double pitchDiv = 180 / perimeter;
+
+        int pixelX = (int) ((loc.getYaw() % yawDiv) / (yawDiv / textureSize));
+        int pixelY = (int) (((loc.getPitch() + 90) % pitchDiv) / (pitchDiv / textureSize));
+
+        return 0xFF000000 | MathUtil.weightedColorSum(texture[pixelY][pixelX], 0, 1 - factor, factor);
+    }
+
+    private boolean isInsideBlock(Vector blockPoint, Vector intersection) {
+        intersection = intersection.clone().subtract(blockPoint);
+
+        return intersection.getX() >= 0 && intersection.getX() < 1 && intersection.getY() >= 0
+                && intersection.getY() < 1 && intersection.getZ() >= 0 && intersection.getZ() < 1;
+    }
+
+    public static class SphereModelBuilder extends Builder {
+
+        private double radius;
+        private Vector offset;
+
+        SphereModelBuilder(int[][] texture) {
+            super(texture);
+
+            this.radius = 0.5;
+            this.offset = new Vector();
+        }
+
+        public SphereModelBuilder radius(double radius) {
+            this.radius = radius;
+            return this;
+        }
+
+        public SphereModelBuilder offset(Vector offset) {
+            this.offset = offset.clone();
+            return this;
+        }
+
+        @Override
+        public Model build() {
+            return new SphereModel(texture, transparencyFactor, reflectionFactor, occluding, radius,
+                    offset);
+        }
+    }
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/StaticModel.java b/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/StaticModel.java
new file mode 100644
index 0000000..75c7be3
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/model/StaticModel.java
@@ -0,0 +1,57 @@
+package eu.mhsl.minecraft.pixelpic.render.model;
+
+import eu.mhsl.minecraft.pixelpic.render.model.AbstractModel.Builder;
+import eu.mhsl.minecraft.pixelpic.render.util.Intersection;
+import org.bukkit.block.Block;
+
+public class StaticModel implements Model {
+
+    private final int color;
+    private final double transparencyFactor;
+    private final double reflectionFactor;
+    private final boolean occluding;
+
+    private StaticModel(int color, double transparencyFactor, double reflectionFactor, boolean occluding) {
+        this.color = color;
+        this.transparencyFactor = transparencyFactor;
+        this.reflectionFactor = reflectionFactor;
+        this.occluding = occluding;
+    }
+
+    @Override
+    public Intersection intersect(Block block, Intersection currentIntersection) {
+        return Intersection.of(currentIntersection.getNormal(), currentIntersection.getPoint(),
+                currentIntersection.getDirection(), color);
+    }
+
+    @Override
+    public double getTransparencyFactor() {
+        return transparencyFactor;
+    }
+
+    @Override
+    public double getReflectionFactor() {
+        return reflectionFactor;
+    }
+
+    @Override
+    public boolean isOccluding() {
+        return occluding;
+    }
+
+    public static class StaticModelBuilder extends Builder {
+
+        private final int color;
+
+        StaticModelBuilder(int color) {
+            super(new int[1][1]);
+
+            this.color = color;
+        }
+
+        @Override
+        public StaticModel build() {
+            return new StaticModel(color, transparencyFactor, reflectionFactor, occluding);
+        }
+    }
+}
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
new file mode 100644
index 0000000..134a9f5
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/raytrace/DefaultRaytracer.java
@@ -0,0 +1,141 @@
+package eu.mhsl.minecraft.pixelpic.render.raytrace;
+
+import eu.mhsl.minecraft.pixelpic.render.model.Model;
+import eu.mhsl.minecraft.pixelpic.render.registry.DefaultModelRegistry;
+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.Block;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.util.Vector;
+
+public class DefaultRaytracer implements Raytracer {
+
+    private static final int MAX_DISTANCE = 300;
+    private static final int REFLECTION_DEPTH = 10;
+
+    private final ModelRegistry textureRegistry;
+    private Block reflectedBlock;
+
+    public DefaultRaytracer() {
+        this.textureRegistry = new DefaultModelRegistry();
+        this.textureRegistry.initialize();
+
+        this.reflectedBlock = null;
+    }
+
+    @Override
+    public int trace(World world, Vector point, Vector direction) {
+        return trace(world, point, direction, REFLECTION_DEPTH);
+    }
+
+    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 < MAX_DISTANCE; i++) {
+            if (!iterator.hasNext()) {
+                break;
+            }
+
+            Block block = iterator.next();
+            if (block == null) {
+                continue;
+            }
+
+            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);
+            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/Raytracer.java b/src/main/java/eu/mhsl/minecraft/pixelpic/render/raytrace/Raytracer.java
new file mode 100644
index 0000000..fdb5d85
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/raytrace/Raytracer.java
@@ -0,0 +1,9 @@
+package eu.mhsl.minecraft.pixelpic.render.raytrace;
+
+import org.bukkit.World;
+import org.bukkit.util.Vector;
+
+public interface Raytracer {
+
+	int trace(World world, Vector point, Vector direction);
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/render/registry/DefaultModelRegistry.java b/src/main/java/eu/mhsl/minecraft/pixelpic/render/registry/DefaultModelRegistry.java
new file mode 100644
index 0000000..641342b
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/registry/DefaultModelRegistry.java
@@ -0,0 +1,169 @@
+package eu.mhsl.minecraft.pixelpic.render.registry;
+
+import eu.mhsl.minecraft.pixelpic.render.model.AbstractModel.Builder;
+import eu.mhsl.minecraft.pixelpic.render.model.Model;
+import org.bukkit.Color;
+import org.bukkit.Material;
+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.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+public class DefaultModelRegistry implements ModelRegistry {
+
+    private static final String IMAGE_RESOURCE = "terrain.png";
+    private static final int TEXTURE_SIZE = 16;
+
+    private final Map<Material, Map<BlockData, Model>> modelMap;
+    private BufferedImage textures;
+
+    public DefaultModelRegistry() {
+        this.modelMap = new HashMap<>();
+    }
+
+    @Override
+    public void initialize() {
+        URL url = this.getClass().getClassLoader().getResource(IMAGE_RESOURCE);
+        if (url == null) {
+            throw new RuntimeException("Default resource \"terrain.png\" is missing");
+        }
+        try (InputStream input = url.openConnection().getInputStream()) {
+            this.textures = ImageIO.read(input);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+        registerModel(Material.GRASS_BLOCK, Builder.createMulti(textureIndex(0, 0), textureIndex(0, 3), textureIndex(0, 2)).build());
+        registerModel(Material.STONE, Builder.createSimple(textureIndex(0, 1)).build());
+        registerModel(Material.DIRT, Builder.createSimple(textureIndex(0, 2)).build());
+        registerModel(Material.OAK_PLANKS, Builder.createSimple(textureIndex(0, 4)).build());
+        registerModel(Material.SPRUCE_PLANKS,
+                Builder.createSimple(textureIndex(0, 4)).build());
+        registerModel(Material.BIRCH_PLANKS,
+                Builder.createSimple(textureIndex(0, 4)).build());
+        registerModel(Material.JUNGLE_PLANKS,
+                Builder.createSimple(textureIndex(0, 4)).build());
+        registerModel(Material.ACACIA_PLANKS,
+                Builder.createSimple(textureIndex(0, 4)).build());
+        registerModel(Material.DARK_OAK_PLANKS,
+                Builder.createSimple(textureIndex(0, 4)).build());
+        registerModel(Material.BRICK, Builder.createSimple(textureIndex(0, 7)).build());
+        registerModel(Material.TNT, Builder.createMulti(textureIndex(0, 9),
+                textureIndex(0, 8), textureIndex(0, 10)).build());
+        registerModel(Material.WATER, Builder.createStatic(0xFF000000 | Color.fromRGB(0, 5, 60).asRGB())
+                .transparency(0.60).reflection(0.1).occlusion().build());
+        registerModel(Material.DIAMOND_BLOCK,
+                Builder.createSimple(textureIndex(3, 3)).reflection(0.75).build());
+        registerModel(Material.POPPY, Builder.createCross(textureIndex(0, 12)).build());
+        registerModel(Material.DANDELION, Builder.createCross(textureIndex(0, 13)).build());
+        registerModel(Material.OAK_SAPLING,
+                Builder.createCross(textureIndex(0, 15)).build());
+        registerModel(Material.SPRUCE_SAPLING,
+                Builder.createCross(textureIndex(0, 15)).build());
+        registerModel(Material.BIRCH_SAPLING,
+                Builder.createCross(textureIndex(0, 15)).build());
+        registerModel(Material.JUNGLE_SAPLING,
+                Builder.createCross(textureIndex(0, 15)).build());
+        registerModel(Material.ACACIA_SAPLING,
+                Builder.createCross(textureIndex(0, 15)).build());
+        registerModel(Material.DARK_OAK_SAPLING,
+                Builder.createCross(textureIndex(0, 15)).build());
+
+        registerModel(Material.COBBLESTONE,
+                Builder.createSimple(textureIndex(1, 0)).build());
+        registerModel(Material.BEDROCK, Builder.createSimple(textureIndex(1, 1)).build());
+        registerModel(Material.SAND, Builder.createSimple(textureIndex(1, 2)).build());
+        registerModel(Material.GRAVEL, Builder.createSimple(textureIndex(1, 3)).build());
+        registerModel(Material.OAK_LOG, Builder.createMulti(textureIndex(1, 5),
+                textureIndex(1, 4), textureIndex(1, 5)).build());
+        registerModel(Material.SPRUCE_LOG, Builder.createMulti(textureIndex(1, 5),
+                textureIndex(1, 4), textureIndex(1, 5)).build());
+        registerModel(Material.BIRCH_LOG, Builder.createMulti(textureIndex(1, 5),
+                textureIndex(1, 4), textureIndex(1, 5)).build());
+        registerModel(Material.JUNGLE_LOG, Builder.createMulti(textureIndex(1, 5),
+                textureIndex(1, 4), textureIndex(1, 5)).build());
+        registerModel(Material.ACACIA_LOG, Builder.createMulti(textureIndex(1, 5),
+                textureIndex(1, 4), textureIndex(1, 5)).build());
+        registerModel(Material.DARK_OAK_LOG, Builder.createMulti(textureIndex(1, 5),
+                textureIndex(1, 4), textureIndex(1, 5)).build());
+        registerModel(Material.OAK_WOOD, Builder.createSimple(textureIndex(1, 4)).build());
+        registerModel(Material.SPRUCE_WOOD,
+                Builder.createSimple(textureIndex(1, 4)).build());
+        registerModel(Material.BIRCH_WOOD, Builder.createSimple(textureIndex(1, 4)).build());
+        registerModel(Material.JUNGLE_WOOD,
+                Builder.createSimple(textureIndex(1, 4)).build());
+        registerModel(Material.ACACIA_WOOD,
+                Builder.createSimple(textureIndex(1, 4)).build());
+        registerModel(Material.DARK_OAK_WOOD,
+                Builder.createSimple(textureIndex(1, 4)).build());
+        registerModel(Material.OAK_LEAVES, Builder.createSimple(textureIndex(1, 6)).build());
+        registerModel(Material.SPRUCE_LEAVES,
+                Builder.createSimple(textureIndex(1, 6)).build());
+        registerModel(Material.BIRCH_LEAVES,
+                Builder.createSimple(textureIndex(1, 6)).build());
+        registerModel(Material.JUNGLE_LEAVES,
+                Builder.createSimple(textureIndex(1, 6)).build());
+        registerModel(Material.ACACIA_LEAVES,
+                Builder.createSimple(textureIndex(1, 6)).build());
+        registerModel(Material.DARK_OAK_LEAVES,
+                Builder.createSimple(textureIndex(1, 6)).build());
+        registerModel(Material.IRON_BLOCK,
+                Builder.createMulti(textureIndex(1, 7),
+                        textureIndex(2, 7), textureIndex(3, 7)).build());
+        registerModel(Material.GOLD_BLOCK, Builder.createMulti(textureIndex(1, 8),
+                textureIndex(2, 8), textureIndex(3, 8)).build());
+        registerModel(Material.RED_MUSHROOM,
+                Builder.createCross(textureIndex(1, 12)).build());
+        registerModel(Material.BROWN_MUSHROOM,
+                Builder.createCross(textureIndex(1, 13)).build());
+        registerModel(Material.LAVA, Builder.createSimple(textureIndex(2, 14))
+                .transparency(0.15).reflection(0.05).occlusion().build());
+
+        registerModel(Material.GOLD_ORE, Builder.createSimple(textureIndex(2, 0)).build());
+        registerModel(Material.IRON_ORE, Builder.createSimple(textureIndex(2, 1)).build());
+        registerModel(Material.COAL_ORE, Builder.createSimple(textureIndex(2, 2)).build());
+
+        registerModel(Material.GLASS,
+                Builder.createSimple(textureIndex(3, 1)).occlusion().build());
+
+        registerModel(Material.GRASS_BLOCK, Builder.createCross(textureIndex(5, 6)).build());
+        registerModel(Material.SUGAR_CANE, Builder.createCross(textureIndex(5, 5)).build());
+    }
+
+    @Override
+    public Model getModel(Material material, BlockData blockData) {
+        return modelMap.computeIfAbsent(material, key -> new HashMap<>()).getOrDefault(blockData,
+                blockData == null ? getDefaultModel()
+                        : modelMap.get(material).getOrDefault(null, getDefaultModel()));
+    }
+
+    @Override
+    public Model getDefaultModel() {
+        return Builder.createStatic(Color.PURPLE.asRGB()).build();
+    }
+
+    private void registerModel(Material material, Model blockModel) {
+        modelMap.computeIfAbsent(material, key -> new HashMap<>())
+                .put(null, blockModel);
+    }
+
+    private int[][] textureIndex(int verticalIndex, int horizontalIndex) {
+        int[][] texture = new int[TEXTURE_SIZE][TEXTURE_SIZE];
+
+        int offsetY = verticalIndex * TEXTURE_SIZE + (TEXTURE_SIZE - 1);
+        int offsetX = horizontalIndex * TEXTURE_SIZE;
+
+        for (int pixelY = 0; pixelY < TEXTURE_SIZE; pixelY++) {
+            for (int pixelX = 0; pixelX < TEXTURE_SIZE; pixelX++) {
+                texture[pixelY][pixelX] = textures.getRGB(offsetX + pixelX, offsetY - pixelY);
+            }
+        }
+
+        return texture;
+    }
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/render/registry/ModelRegistry.java b/src/main/java/eu/mhsl/minecraft/pixelpic/render/registry/ModelRegistry.java
new file mode 100644
index 0000000..d304671
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/registry/ModelRegistry.java
@@ -0,0 +1,23 @@
+package eu.mhsl.minecraft.pixelpic.render.registry;
+
+import eu.mhsl.minecraft.pixelpic.render.model.Model;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.block.data.BlockData;
+
+public interface ModelRegistry {
+
+    void initialize();
+
+    default Model getModel(Block block) {
+        return getModel(block.getType(), block.getBlockData());
+    }
+
+    default Model getModel(Material material) {
+        return getModel(material, null);
+    }
+
+    Model getModel(Material material, BlockData blockData);
+
+    Model getDefaultModel();
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/render/render/DefaultScreenRenderer.java b/src/main/java/eu/mhsl/minecraft/pixelpic/render/render/DefaultScreenRenderer.java
new file mode 100644
index 0000000..8df6ac3
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/render/DefaultScreenRenderer.java
@@ -0,0 +1,87 @@
+package eu.mhsl.minecraft.pixelpic.render.render;
+
+import eu.mhsl.minecraft.pixelpic.render.raytrace.DefaultRaytracer;
+import eu.mhsl.minecraft.pixelpic.render.raytrace.Raytracer;
+import eu.mhsl.minecraft.pixelpic.render.util.MathUtil;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+import org.bukkit.util.Vector;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.util.ArrayList;
+import java.util.List;
+
+public class DefaultScreenRenderer implements Renderer {
+
+    private static final double FOV_YAW_DEG = 53;
+    private static final double FOV_PITCH_DEG = 23;
+
+    private static final double FOV_YAW_RAD = Math.toRadians(FOV_YAW_DEG);
+    private static final double FOV_PITCH_RAD = Math.toRadians(FOV_PITCH_DEG);
+
+    private static final Vector BASE_VEC = new Vector(1, 0, 0);
+
+    private final Raytracer raytracer;
+
+    public DefaultScreenRenderer() {
+        this.raytracer = new DefaultRaytracer();
+    }
+
+    @Override
+    public BufferedImage render(Player player, Resolution resolution) {
+        int width = resolution.getWidth();
+        int height = resolution.getHeight();
+
+        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+        int[] imageData = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
+
+        World world = player.getWorld();
+        Vector linePoint = player.getEyeLocation().toVector();
+        List<Vector> rayMap = buildRayMap(player, resolution);
+        for (int i = 0; i < rayMap.size(); i++) {
+            imageData[i] = raytracer.trace(world, linePoint, rayMap.get(i));
+        }
+
+        return image;
+    }
+
+    private List<Vector> buildRayMap(Player p, Resolution resolution) {
+        Location eyeLocation = p.getEyeLocation();
+        Vector lineDirection = eyeLocation.getDirection();
+
+        double x = lineDirection.getX();
+        double y = lineDirection.getY();
+        double z = lineDirection.getZ();
+
+        double angleYaw = Math.atan2(z, x);
+        double anglePitch = Math.atan2(y, Math.sqrt(x * x + z * z));
+
+        Vector lowerLeftCorner = MathUtil.doubleYawPitchRotation(BASE_VEC, -FOV_YAW_RAD, -FOV_PITCH_RAD, angleYaw, anglePitch);
+        Vector upperLeftCorner = MathUtil.doubleYawPitchRotation(BASE_VEC, -FOV_YAW_RAD, FOV_PITCH_RAD, angleYaw, anglePitch);
+        Vector lowerRightCorner = MathUtil.doubleYawPitchRotation(BASE_VEC, FOV_YAW_RAD, -FOV_PITCH_RAD, angleYaw, anglePitch);
+        Vector upperRightCorner = MathUtil.doubleYawPitchRotation(BASE_VEC, FOV_YAW_RAD, FOV_PITCH_RAD, angleYaw, anglePitch);
+
+        int width = resolution.getWidth();
+        int height = resolution.getHeight();
+        List<Vector> rayMap = new ArrayList<>(width * height);
+
+        Vector leftFraction = upperLeftCorner.clone().subtract(lowerLeftCorner).multiply(1.0 / (height - 1));
+        Vector rightFraction = upperRightCorner.clone().subtract(lowerRightCorner).multiply(1.0 / (height - 1));
+
+        for (int pitch = 0; pitch < height; pitch++) {
+
+            Vector leftPitch = upperLeftCorner.clone().subtract(leftFraction.clone().multiply(pitch));
+            Vector rightPitch = upperRightCorner.clone().subtract(rightFraction.clone().multiply(pitch));
+            Vector yawFraction = rightPitch.clone().subtract(leftPitch).multiply(1.0 / (width - 1));
+
+            for (int yaw = 0; yaw < width; yaw++) {
+                Vector ray = leftPitch.clone().add(yawFraction.clone().multiply(yaw)).normalize();
+                rayMap.add(ray);
+            }
+        }
+
+        return rayMap;
+    }
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/render/render/Renderer.java b/src/main/java/eu/mhsl/minecraft/pixelpic/render/render/Renderer.java
new file mode 100644
index 0000000..2a47ac2
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/render/Renderer.java
@@ -0,0 +1,10 @@
+package eu.mhsl.minecraft.pixelpic.render.render;
+
+import org.bukkit.entity.Player;
+
+import java.awt.image.BufferedImage;
+
+public interface Renderer {
+
+    BufferedImage render(Player player, Resolution resolution);
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/render/render/Resolution.java b/src/main/java/eu/mhsl/minecraft/pixelpic/render/render/Resolution.java
new file mode 100644
index 0000000..2a32f2d
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/render/Resolution.java
@@ -0,0 +1,62 @@
+package eu.mhsl.minecraft.pixelpic.render.render;
+
+import com.google.common.base.Preconditions;
+
+import java.util.*;
+
+public final class Resolution {
+
+    private final int width;
+    private final int height;
+
+    public Resolution(Pixels pixels, AspectRatio aspectRatio) {
+        Preconditions.checkNotNull(pixels);
+        Preconditions.checkNotNull(aspectRatio);
+
+        this.height = pixels.height;
+        this.width = (int) Math.round(pixels.height * aspectRatio.ratio);
+    }
+
+    public Resolution(int width, int height) {
+        Preconditions.checkArgument(width > 0);
+        Preconditions.checkArgument(height > 0);
+
+        this.width = width;
+        this.height = height;
+    }
+
+    public int getWidth() {
+        return width;
+    }
+
+    public int getHeight() {
+        return height;
+    }
+
+    public enum Pixels {
+        _128P(128, "128p"),
+        _256P(256, "256p");
+
+        private final int height;
+        private final List<String> aliases;
+
+        Pixels(int height, String... aliases) {
+            this.height = height;
+            this.aliases = Collections.unmodifiableList(Arrays.asList(aliases));
+        }
+    }
+
+    public enum AspectRatio {
+        _1_1(1, "1:1"),
+        _2_1(2, "2:1"),
+        _3_2(3 / 2.0, "3:2");
+
+        private final double ratio;
+        private final List<String> aliases;
+
+        AspectRatio(double ratio, String... aliases) {
+            this.ratio = ratio;
+            this.aliases = Collections.unmodifiableList(Arrays.asList(aliases));
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/render/util/BlockRaytracer.java b/src/main/java/eu/mhsl/minecraft/pixelpic/render/util/BlockRaytracer.java
new file mode 100644
index 0000000..fbfe63e
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/util/BlockRaytracer.java
@@ -0,0 +1,48 @@
+package eu.mhsl.minecraft.pixelpic.render.util;
+
+import org.bukkit.Location;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.util.BlockIterator;
+import org.bukkit.util.Vector;
+
+public class BlockRaytracer extends BlockIterator {
+
+    private final Vector position;
+    private final Vector direction;
+
+    private Block lastBlock;
+    private BlockFace currentFace;
+
+    public BlockRaytracer(Location loc) {
+        super(loc);
+
+        this.position = loc.toVector();
+        this.direction = loc.getDirection();
+    }
+
+    public BlockFace getIntersectionFace() {
+        if (currentFace == null) {
+            throw new IllegalStateException("Called before next()");
+        }
+
+        return currentFace;
+    }
+
+    public Vector getIntersectionPoint() {
+        BlockFace lastFace = getIntersectionFace();
+        Vector planeNormal = new Vector(lastFace.getModX(), lastFace.getModY(), lastFace.getModZ());
+        Vector planePoint = lastBlock.getLocation().add(0.5, 0.5, 0.5).toVector()
+                .add(planeNormal.clone().multiply(0.5));
+
+        return MathUtil.getLinePlaneIntersection(position, direction, planePoint, planeNormal, true);
+    }
+
+    @Override
+    public Block next() {
+        Block currentBlock = super.next();
+        currentFace = lastBlock == null ? BlockFace.SELF : currentBlock.getFace(lastBlock);
+
+        return (lastBlock = currentBlock);
+    }
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/render/util/Intersection.java b/src/main/java/eu/mhsl/minecraft/pixelpic/render/util/Intersection.java
new file mode 100644
index 0000000..725b8f0
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/util/Intersection.java
@@ -0,0 +1,42 @@
+package eu.mhsl.minecraft.pixelpic.render.util;
+
+import org.bukkit.util.Vector;
+
+public final class Intersection {
+
+	private final Vector normal;
+	private final Vector point;
+	private final Vector direction;
+	private final int color;
+
+	private Intersection(Vector normal, Vector point, Vector direction, int color) {
+		this.normal = normal;
+		this.point = point;
+		this.direction = direction;
+		this.color = color;
+	}
+
+	public Vector getNormal() {
+		return normal;
+	}
+
+	public Vector getPoint() {
+		return point;
+	}
+
+	public Vector getDirection() {
+		return direction;
+	}
+
+	public int getColor() {
+		return color;
+	}
+
+	public static Intersection of(Vector normal, Vector point, Vector direction) {
+		return of(normal, point, direction, 0);
+	}
+
+	public static Intersection of(Vector normal, Vector point, Vector direction, int color) {
+		return new Intersection(normal, point, direction, color);
+	}
+}
diff --git a/src/main/java/eu/mhsl/minecraft/pixelpic/render/util/MathUtil.java b/src/main/java/eu/mhsl/minecraft/pixelpic/render/util/MathUtil.java
new file mode 100644
index 0000000..c0ed7d2
--- /dev/null
+++ b/src/main/java/eu/mhsl/minecraft/pixelpic/render/util/MathUtil.java
@@ -0,0 +1,68 @@
+package eu.mhsl.minecraft.pixelpic.render.util;
+
+import org.bukkit.Color;
+import org.bukkit.block.BlockFace;
+import org.bukkit.util.Vector;
+
+public class MathUtil {
+
+	private MathUtil() {}
+
+	public static Vector yawPitchRotation(Vector base, double angleYaw, double anglePitch) {
+		double oldX = base.getX();
+		double oldY = base.getY();
+		double oldZ = base.getZ();
+
+		double sinOne = Math.sin(angleYaw);
+		double sinTwo = Math.sin(anglePitch);
+		double cosOne = Math.cos(angleYaw);
+		double cosTwo = Math.cos(anglePitch);
+
+		double newX = oldX * cosOne * cosTwo - oldY * cosOne * sinTwo - oldZ * sinOne;
+		double newY = oldX * sinTwo + oldY * cosTwo;
+		double newZ = oldX * sinOne * cosTwo - oldY * sinOne * sinTwo + oldZ * cosOne;
+
+		return new Vector(newX, newY, newZ);
+	}
+
+	public static Vector doubleYawPitchRotation(Vector base, double firstYaw, double firstPitch, double secondYaw,
+			double secondPitch) {
+		return yawPitchRotation(yawPitchRotation(base, firstYaw, firstPitch), secondYaw, secondPitch);
+	}
+
+	public static Vector reflectVector(Vector linePoint, Vector lineDirection, Vector planePoint, Vector planeNormal) {
+		return lineDirection.clone().subtract(planeNormal.clone().multiply(2 * lineDirection.dot(planeNormal)));
+	}
+
+	public static Vector toVector(BlockFace face) {
+		return new Vector(face.getModX(), face.getModY(), face.getModZ());
+	}
+
+	public static int weightedColorSum(int rgbOne, int rgbTwo, double weightOne, double weightTwo) {
+		Color colorOne = Color.fromRGB(rgbOne & 0xFFFFFF);
+		Color colorTwo = Color.fromRGB(rgbTwo & 0xFFFFFF);
+
+		double total = weightOne + weightTwo;
+		int newRed = (int) ((colorOne.getRed() * weightOne + colorTwo.getRed() * weightTwo) / total);
+		int newGreen = (int) ((colorOne.getGreen() * weightOne + colorTwo.getGreen() * weightTwo) / total);
+		int newBlue = (int) ((colorOne.getBlue() * weightOne + colorTwo.getBlue() * weightTwo) / total);
+
+		return Color.fromRGB(newRed, newGreen, newBlue).asRGB();
+	}
+
+	public static Vector getLinePlaneIntersection(Vector linePoint, Vector lineDirection, Vector planePoint,
+			Vector planeNormal, boolean allowBackwards) {
+		double d = planePoint.dot(planeNormal);
+		double t = (d - planeNormal.dot(linePoint)) / planeNormal.dot(lineDirection);
+
+		if (t < 0 && !allowBackwards) {
+			return null;
+		}
+
+		double x = linePoint.getX() + lineDirection.getX() * t;
+		double y = linePoint.getY() + lineDirection.getY() * t;
+		double z = linePoint.getZ() + lineDirection.getZ() * t;
+
+		return new Vector(x, y, z);
+	}
+}
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index d7fb166..afd0bad 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -2,3 +2,6 @@ name: PixelPic
 version: '1.0-SNAPSHOT'
 main: eu.mhsl.minecraft.pixelpic.Main
 api-version: '1.21'
+commands:
+  test:
+  test2:
\ No newline at end of file
diff --git a/src/main/resources/terrain.png b/src/main/resources/terrain.png
new file mode 100644
index 0000000000000000000000000000000000000000..d2cf26cde0bb55a33bc0ffa10fb6cc9917821147
GIT binary patch
literal 36672
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

literal 0
HcmV?d00001