/*
* This file is part of Matter Overdrive
* Copyright (c) 2015., Simeon Radivoev, All rights reserved.
*
* Matter Overdrive is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Matter Overdrive is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Matter Overdrive. If not, see <http://www.gnu.org/licenses>.
*/
package matteroverdrive.world;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.List;
/**
* Created by Simeon on 11/17/2015.
*/
public abstract class MOImageGen
{
public static HashMap<Block,Integer> worldGenerationBlockColors = new HashMap<>();
private HashMap<Integer,BlockMapping> blockMap;
protected ResourceLocation texture;
private List<int[][]> layers;
private int textureWidth;
private int textureHeight;
private int layerCount;
protected int placeNotify;
protected final int layerWidth;
protected final int layerHeight;
protected final Random localRandom;
public MOImageGen(ResourceLocation texture,int layerWidth,int layerHeight)
{
localRandom = new Random();
blockMap = new HashMap<>();
this.layerWidth = layerWidth;
this.layerHeight = layerHeight;
setTexture(texture);
}
public void placeBlock(World world,int color,int x,int y,int z,int layer,Random random,int placeNotify)
{
Block block = getBlockFromColor(color,random);
int meta = getMetaFromColor(color,random);
if (block != null)
{
world.setBlock(x, y, z, block, meta, placeNotify);
onBlockPlace(world,block,x,y,z,random,color);
}
}
public abstract void onBlockPlace(World world,Block block,int x,int y,int z,Random random,int color);
public Block getBlockFromColor(int color,Random random)
{
BlockMapping blockMapping = blockMap.get(color & 0xffffff);
if (blockMapping != null) {
return blockMapping.getBlock(random);
}
return null;
}
public int getMetaFromColor(int color,Random random)
{
return 0;
}
public void generateFromImage(World world,Random random,int startX,int startY,int startZ,int layer,int placeNotify)
{
if (layers != null && layers.size() > 0)
{
for (BlockMapping blockMapping : blockMap.values())
{
blockMapping.reset(localRandom);
}
generateFromImage(world, random, startX, Math.min(startY, world.getHeight() - layerCount), startZ, layers, layer, placeNotify);
}
}
public void generateFromImage(World world, Random random, int startX, int startY, int startZ,List<int[][]> layers,int layer,int placeNotify)
{
for (int x = 0; x < layerWidth; x++) {
for (int z = 0; z < layerHeight; z++) {
placeBlock(world, layers.get(layer)[x][z], startX + x, startY + layer, startZ + z, layer,random,placeNotify);
}
}
}
public static void generateFromImage(World world, int startX, int startY, int startZ,int layerWidth,int layerHeight,List<int[][]> layers,Map<Integer,Block> blockMap)
{
for (int layer = 0; layer < layers.size(); layer++) {
for (int x = 0; x < layerWidth; x++) {
for (int z = 0; z < layerHeight; z++) {
int color = layers.get(layer)[x][z];
Color c = new Color(color,true);
int alpha = c.getAlpha();
Block block = blockMap.get(color & 0xffffff);
int meta = 255-alpha;
if (block != null)
{
world.setBlock(startX+x, startY + layer, startZ+z, block, meta, 2);
}
}
}
}
}
public boolean isOnSolidGround(World world,int x,int y,int z,int leaway)
{
return isPointOnSolidGround(world,x,y,z,leaway) && isPointOnSolidGround(world,x+layerWidth,y,z,leaway) && isPointOnSolidGround(world,x+layerWidth,y,z+layerHeight,leaway) && isPointOnSolidGround(world,x,y,z+layerHeight,leaway);
}
public boolean isPointOnSolidGround(World world,int x,int y,int z,int leaway)
{
for (int i = 0; i < leaway;i++)
{
if (isBlockSolid(world,x,y-i,z))
{
return true;
}
}
return false;
}
public boolean canFit(World world,int x,int y,int z)
{
return !isBlockSolid(world,x,y+layerCount,z) && !isBlockSolid(world,x+layerWidth,y+layerCount,z) && !isBlockSolid(world,x+layerWidth,y+layerCount,z+layerHeight) && !isBlockSolid(world,x,y+layerCount,z+layerHeight);
}
public boolean isBlockSolid(World world,int x,int y,int z)
{
Block block = world.getBlock(x,y,z);
if (block == Blocks.log || block == Blocks.log2 && block == Blocks.leaves2 || block == Blocks.leaves)
{
return false;
}
return block.isBlockSolid(world,x,y,z,ForgeDirection.UP.ordinal());
}
private boolean inAirFloatRange(World world,int x,int y,int z,int maxAirRange)
{
for (int i = 0; i < maxAirRange;i++)
{
if (isBlockSolid(world,x,y-i,z) && !isBlockSolid(world,x,y-i+1,z))
{
return true;
}
}
return false;
}
protected boolean colorsMatch(int color0,int color1)
{
return (color0 & 0xffffff) == (color1 & 0xffffff);
}
public void manageTextureLoading()
{
if (layers == null || layers.size() == 0)
{
loadTexture(getTexture());
}
}
private void loadTexture(ResourceLocation textureLocation) throws RuntimeException {
try {
String path = "/assets/"+textureLocation.getResourceDomain()+"/" + textureLocation.getResourcePath();
InputStream imageStream = getClass().getResourceAsStream(path);
BufferedImage image = ImageIO.read(imageStream);
textureWidth = image.getWidth();
textureHeight = image.getHeight();
layerCount = (image.getWidth() / layerWidth) * (image.getHeight() / layerHeight);
for (int i = 0; i < layerCount; i++) {
layers.add(new int[layerWidth][layerHeight]);
}
convertTo2DWithoutUsingGetRGB(image,layerWidth,layerHeight,textureWidth,layers);
} catch (IOException e) {
e.printStackTrace();
}
}
public static List<int[][]> loadTexture(File textureLocation, int layerWidth,int layerHeight) {
try {
BufferedImage image = ImageIO.read(textureLocation);
int textureWidth = image.getWidth();
int textureHeight = image.getHeight();
int layerCount = (image.getWidth() / layerWidth) * (image.getHeight() / layerHeight);
List<int[][]> layers = new ArrayList<>();
for (int i = 0; i < layerCount; i++) {
layers.add(new int[layerWidth][layerHeight]);
}
convertTo2DWithoutUsingGetRGB(image,layerWidth,layerHeight,textureWidth,layers);
return layers;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private static void convertTo2DWithoutUsingGetRGB(BufferedImage image,int layerWidth,int layerHeight,int textureWidth,List<int[][]> layers) {
final byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
final boolean hasAlphaChannel = image.getAlphaRaster() != null;
if (hasAlphaChannel) {
final int pixelLength = 4;
for (int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += pixelLength) {
int argb = 0;
argb += (((int) pixels[pixel] & 0xff) << 24); // alpha
argb += ((int) pixels[pixel + 1] & 0xff); // blue
argb += (((int) pixels[pixel + 2] & 0xff) << 8); // green
argb += (((int) pixels[pixel + 3] & 0xff) << 16); // red
int layerIndex = Math.floorDiv(col,layerWidth) + ((textureWidth/layerWidth) * (Math.floorDiv(row,layerHeight)));
layers.get(layerIndex)[col % layerWidth][row % layerHeight] = argb;
col++;
if (col == textureWidth) {
col = 0;
row++;
}
}
} else {
final int pixelLength = 3;
for (int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += pixelLength) {
int argb = 0;
argb += -16777216; // 255 alpha
argb += ((int) pixels[pixel] & 0xff); // blue
argb += (((int) pixels[pixel + 1] & 0xff) << 8); // green
argb += (((int) pixels[pixel + 2] & 0xff) << 16); // red
int layerIndex = Math.floorDiv(col,layerWidth) + ((textureWidth/layerWidth) * (Math.floorDiv(row,layerHeight)));
layers.get(layerIndex)[col % layerWidth][row % layerHeight] = argb;
col++;
if (col == textureWidth) {
col = 0;
row++;
}
}
}
}
private static void transpose(int[][] m)
{
for (int i = 0; i < m.length; i++) {
for (int j = i; j < m[0].length; j++) {
int x = m[i][j];
m[i][j] = m[j][i];
m[j][i] = x;
}
}
}
public static void swapRows(int[][] m) {
for (int i = 0, k = m.length - 1; i < k; ++i, --k) {
int[] x = m[i];
m[i] = m[k];
m[k] = x;
}
}
public static void rotateByNinetyToLeft(int[][] m) {
transpose(m);
swapRows(m);
}
public static void rotateByNinetyToRight(int[][] m) {
swapRows(m);
transpose(m);
}
public void rotateByNinetyToLeft()
{
layers.forEach(MOImageGen::rotateByNinetyToLeft);
}
public int getRedFromColor(int color)
{
return color >> 16 & 255;
}
public int getGreenFromColor(int color)
{
return color >> 8 & 255;
}
public int getBlueFromColor(int color)
{
return color >> 0 & 255;
}
public int getAlphaFromColor(int color)
{
return color >> 24 & 255;
}
public int getColorAt(int x,int y,int layer)
{
if (x < textureWidth && y < textureHeight)
{
return layers.get(layer)[textureHeight][textureWidth];
}
return 0;
}
public int getTextureWidth()
{
return textureWidth;
}
public int getTextureHeight()
{
return textureHeight;
}
public int getLayerCount(){return layerCount;}
public ResourceLocation getTexture()
{
return texture;
}
public void addMapping(int color,Block... blocks)
{
this.addMapping(color,new BlockMapping(blocks));
}
public void addMapping(int color,boolean noise,Block... blocks)
{
this.addMapping(color,new BlockMapping(noise,blocks));
}
public void addMapping(int color,BlockMapping blockMapping)
{
blockMap.put(color,blockMapping);
}
public BlockMapping getMapping(int color)
{
return blockMap.get(color);
}
public void setTexture(ResourceLocation textureLocation)
{
this.texture = textureLocation;
if (layers == null)
layers = new ArrayList<>();
else
layers.clear();
}
public static class BlockMapping
{
private Block[] blocks;
private boolean noise;
private int lastSelected;
public BlockMapping(boolean noise,Block... blocks)
{
this.blocks = blocks;
this.noise = noise;
}
public BlockMapping(Block... blocks)
{
this.blocks = blocks;
}
public void reset(Random random)
{
if (!noise)
{
lastSelected = random.nextInt(blocks.length);
}
}
public Block getBlock(Random random)
{
if (noise)
{
return blocks[random.nextInt(blocks.length)];
}else
{
return blocks[lastSelected];
}
}
public Block[] getBlocks()
{
return blocks;
}
public boolean isNoise()
{
return noise;
}
}
}