added advanced texture support
This commit is contained in:
parent
ab97315910
commit
2d4f820f04
1
.idea/modules.xml
generated
1
.idea/modules.xml
generated
@ -3,6 +3,7 @@
|
||||
<component name="ProjectModuleManager">
|
||||
<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/PixelPics.PixelPic.main.iml" filepath="$PROJECT_DIR$/.idea/modules/PixelPics.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>
|
||||
</component>
|
||||
|
@ -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<JarEntry> 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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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()),
|
||||
|
@ -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,29 +11,48 @@ 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<Material, Map<BlockData, Model>> modelMap = new HashMap<>();
|
||||
private final Set<String> 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<String> blockTextures = getBlockTextures(currentMaterial);
|
||||
for (String blockTexture : blockTextures) {
|
||||
System.out.println(modelMap);
|
||||
|
||||
File blockDir = new File(Main.getInstance().getDataFolder(), "models/block");
|
||||
for (File file : Objects.requireNonNull(blockDir.listFiles())) {
|
||||
addModelFromFile(file);
|
||||
}
|
||||
|
||||
try {
|
||||
registerModel(currentMaterial, AbstractModel.Builder.createSimple(getTextureArray(blockTexture)).build());
|
||||
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
|
||||
public Model getModel(Block block) {
|
||||
@ -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<String> 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<String> results = new ArrayList<>();
|
||||
for (String variant : possibleVariants) {
|
||||
try {
|
||||
if(!Arrays.deepEquals(getTextureArray(variant), new int[0][0])) {
|
||||
results.add(variant);
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
} catch (Exception ignored) { }
|
||||
}
|
||||
return results;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
BIN
src/main/resources/colormap/foliage.png
Normal file
BIN
src/main/resources/colormap/foliage.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
src/main/resources/colormap/grass.png
Normal file
BIN
src/main/resources/colormap/grass.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
Loading…
x
Reference in New Issue
Block a user