mirror of https://github.com/webbukkit/dynmap.git
3625 lines
170 KiB
Java
3625 lines
170 KiB
Java
package org.dynmap.hdmap;
|
|
|
|
import java.awt.image.BufferedImage;
|
|
import java.io.BufferedReader;
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.InputStreamReader;
|
|
import java.io.LineNumberReader;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.BitSet;
|
|
import java.util.Enumeration;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
import java.util.Properties;
|
|
import java.util.Set;
|
|
import java.util.TreeSet;
|
|
import java.util.zip.ZipEntry;
|
|
import java.util.zip.ZipException;
|
|
import java.util.zip.ZipFile;
|
|
|
|
import javax.imageio.ImageIO;
|
|
|
|
import org.dynmap.Color;
|
|
import org.dynmap.ConfigurationNode;
|
|
import org.dynmap.DynmapCore;
|
|
import org.dynmap.Log;
|
|
import org.dynmap.MapManager;
|
|
import org.dynmap.common.BiomeMap;
|
|
import org.dynmap.common.DynmapCommandSender;
|
|
import org.dynmap.exporter.OBJExport;
|
|
import org.dynmap.renderer.CustomColorMultiplier;
|
|
import org.dynmap.renderer.DynmapBlockState;
|
|
import org.dynmap.utils.BlockStateParser;
|
|
import org.dynmap.utils.BlockStep;
|
|
import org.dynmap.utils.BufferOutputStream;
|
|
import org.dynmap.utils.DynIntHashMap;
|
|
import org.dynmap.utils.DynmapBufferedImage;
|
|
import org.dynmap.utils.ForgeConfigFile;
|
|
import org.dynmap.utils.MapIterator;
|
|
|
|
/**
|
|
* Loader and processor class for minecraft texture packs
|
|
* Texture packs are found in dynmap/texturepacks directory, and either are either ZIP files
|
|
* or are directories whose content matches the structure of a zipped texture pack:
|
|
* misc/grasscolor.png - tone for grass color, biome sensitive (required)
|
|
* misc/foliagecolor.png - tone for leaf color, biome sensitive (required)
|
|
* custom_lava_still.png - custom still lava animation (optional)
|
|
* custom_lava_flowing.png - custom flowing lava animation (optional)
|
|
* custom_water_still.png - custom still water animation (optional)
|
|
* custom_water_flowing.png - custom flowing water animation (optional)
|
|
* misc/watercolorX.png - custom water color multiplier (optional)
|
|
* misc/swampgrasscolor.png - tone for grass color in swamps (optional)
|
|
* misc/swampfoliagecolor.png - tone for leaf color in swamps (optional)
|
|
*/
|
|
|
|
public class TexturePack {
|
|
/* Loaded texture packs */
|
|
private static HashMap<String, TexturePack> packs = new HashMap<String, TexturePack>();
|
|
private static Object packlock = new Object();
|
|
|
|
private static final String GRASSCOLOR_PNG = "misc/grasscolor.png";
|
|
private static final String GRASSCOLOR_RP_PNG = "assets/minecraft/textures/colormap/grass.png";
|
|
private static final String FOLIAGECOLOR_PNG = "misc/foliagecolor.png";
|
|
private static final String FOLIAGECOLOR_RP_PNG = "assets/minecraft/textures/colormap/foliage.png";
|
|
private static final String WATERCOLORX_PNG = "misc/watercolorX.png";
|
|
private static final String WATERCOLORX_RP_PNG = "assets/minecraft/mcpatcher/colormap/watercolorX.png";
|
|
private static final String WATERCOLORX2_RP_PNG = "assets/minecraft/mcpatcher/colormap/water.png";
|
|
private static final String CUSTOMLAVASTILL_PNG = "custom_lava_still.png";
|
|
private static final String CUSTOMLAVAFLOWING_PNG = "custom_lava_flowing.png";
|
|
private static final String CUSTOMWATERSTILL_PNG = "custom_water_still.png";
|
|
private static final String CUSTOMWATERFLOWING_PNG = "custom_water_flowing.png";
|
|
private static final String SWAMPGRASSCOLOR_PNG = "misc/swampgrasscolor.png";
|
|
private static final String SWAMPGRASSCOLOR_RP_PNG = "assets/minecraft/mcpatcher/colormap/swampgrass.png";
|
|
private static final String SWAMPFOLIAGECOLOR_PNG = "misc/swampfoliagecolor.png";
|
|
private static final String SWAMPFOLIAGECOLOR_RP_PNG = "assets/minecraft/mcpatcher/colormap/swampfoliage.png";
|
|
private static final String PINECOLOR_PNG = "misc/pinecolor.png";
|
|
private static final String PINECOLOR_RP_PNG = "assets/minecraft/mcpatcher/colormap/pine.png";
|
|
private static final String BIRCHCOLOR_PNG = "misc/birchcolor.png";
|
|
private static final String BIRCHCOLOR_RP_PNG = "assets/minecraft/mcpatcher/colormap/birch.png";
|
|
|
|
/* Color modifier codes (x1000 for value in definition file, x1000000 for internal value) */
|
|
//private static final int COLORMOD_NONE = 0;
|
|
public static final int COLORMOD_GRASSTONED = 1;
|
|
public static final int COLORMOD_FOLIAGETONED = 2;
|
|
public static final int COLORMOD_WATERTONED = 3;
|
|
public static final int COLORMOD_ROT90 = 4;
|
|
public static final int COLORMOD_ROT180 = 5;
|
|
public static final int COLORMOD_ROT270 = 6;
|
|
public static final int COLORMOD_FLIPHORIZ = 7;
|
|
public static final int COLORMOD_SHIFTDOWNHALF = 8;
|
|
public static final int COLORMOD_SHIFTDOWNHALFANDFLIPHORIZ = 9;
|
|
public static final int COLORMOD_INCLINEDTORCH = 10;
|
|
public static final int COLORMOD_GRASSSIDE = 11;
|
|
public static final int COLORMOD_CLEARINSIDE = 12;
|
|
public static final int COLORMOD_PINETONED = 13;
|
|
public static final int COLORMOD_BIRCHTONED = 14;
|
|
public static final int COLORMOD_LILYTONED = 15;
|
|
//private static final int COLORMOD_OLD_WATERSHADED = 16;
|
|
public static final int COLORMOD_MULTTONED = 17; /* Toned with colorMult or custColorMult - not biome-style */
|
|
public static final int COLORMOD_GRASSTONED270 = 18; // GRASSTONED + ROT270
|
|
public static final int COLORMOD_FOLIAGETONED270 = 19; // FOLIAGETONED + ROT270
|
|
public static final int COLORMOD_WATERTONED270 = 20; // WATERTONED + ROT270
|
|
public static final int COLORMOD_MULTTONED_CLEARINSIDE = 21; // MULTTONED + CLEARINSIDE
|
|
public static final int COLORMOD_FOLIAGEMULTTONED = 22; // FOLIAGETONED + colorMult or custColorMult
|
|
|
|
private static final int COLORMOD_MULT_FILE = 1000;
|
|
private static final int COLORMOD_MULT_INTERNAL = 1000000;
|
|
/* Special tile index values */
|
|
public static final int TILEINDEX_BLANK = -1;
|
|
private static final int TILEINDEX_GRASS = 0;
|
|
private static final int TILEINDEX_GRASSMASK = 38;
|
|
private static final int TILEINDEX_SNOW = 66;
|
|
private static final int TILEINDEX_SNOWSIDE = 68;
|
|
private static final int TILEINDEX_PISTONSIDE = 108;
|
|
private static final int TILEINDEX_GLASSPANETOP = 148;
|
|
private static final int TILEINDEX_AIRFRAME = 158;
|
|
private static final int TILEINDEX_REDSTONE_NSEW_TONE = 164;
|
|
private static final int TILEINDEX_REDSTONE_EW_TONE = 165;
|
|
private static final int TILEINDEX_EYEOFENDER = 174;
|
|
private static final int TILEINDEX_REDSTONE_NSEW = 180;
|
|
private static final int TILEINDEX_REDSTONE_EW = 181;
|
|
private static final int TILEINDEX_STATIONARYWATER = 257;
|
|
private static final int TILEINDEX_MOVINGWATER = 258;
|
|
private static final int TILEINDEX_STATIONARYLAVA = 259;
|
|
private static final int TILEINDEX_MOVINGLAVA = 260;
|
|
private static final int TILEINDEX_PISTONEXTSIDE = 261;
|
|
private static final int TILEINDEX_PISTONSIDE_EXT = 262;
|
|
private static final int TILEINDEX_PANETOP_X = 263;
|
|
private static final int TILEINDEX_AIRFRAME_EYE = 264;
|
|
private static final int TILEINDEX_WHITE = 267; // Pure white tile
|
|
private static final int MAX_TILEINDEX = 267; /* Index of last static tile definition */
|
|
|
|
/* Indexes of faces in a CHEST format tile file */
|
|
private static final int TILEINDEX_CHEST_TOP = 0;
|
|
private static final int TILEINDEX_CHEST_LEFT = 1;
|
|
private static final int TILEINDEX_CHEST_RIGHT = 2;
|
|
private static final int TILEINDEX_CHEST_FRONT = 3;
|
|
private static final int TILEINDEX_CHEST_BACK = 4;
|
|
private static final int TILEINDEX_CHEST_BOTTOM = 5;
|
|
private static final int TILEINDEX_CHEST_COUNT = 6;
|
|
|
|
/* Indexes of faces in a BIGCHEST format tile file */
|
|
private static final int TILEINDEX_BIGCHEST_TOPLEFT = 0;
|
|
private static final int TILEINDEX_BIGCHEST_TOPRIGHT = 1;
|
|
private static final int TILEINDEX_BIGCHEST_FRONTLEFT = 2;
|
|
private static final int TILEINDEX_BIGCHEST_FRONTRIGHT = 3;
|
|
private static final int TILEINDEX_BIGCHEST_LEFT = 4;
|
|
private static final int TILEINDEX_BIGCHEST_RIGHT = 5;
|
|
private static final int TILEINDEX_BIGCHEST_BACKLEFT = 6;
|
|
private static final int TILEINDEX_BIGCHEST_BACKRIGHT = 7;
|
|
private static final int TILEINDEX_BIGCHEST_BOTTOMLEFT = 8;
|
|
private static final int TILEINDEX_BIGCHEST_BOTTOMRIGHT = 9;
|
|
private static final int TILEINDEX_BIGCHEST_COUNT = 10;
|
|
|
|
/* Indexes of faces in the SIGN format tile file */
|
|
private static final int TILEINDEX_SIGN_FRONT = 0;
|
|
private static final int TILEINDEX_SIGN_BACK = 1;
|
|
private static final int TILEINDEX_SIGN_TOP = 2;
|
|
private static final int TILEINDEX_SIGN_BOTTOM = 3;
|
|
private static final int TILEINDEX_SIGN_LEFTSIDE = 4;
|
|
private static final int TILEINDEX_SIGN_RIGHTSIDE = 5;
|
|
private static final int TILEINDEX_SIGN_POSTFRONT = 6;
|
|
private static final int TILEINDEX_SIGN_POSTBACK = 7;
|
|
private static final int TILEINDEX_SIGN_POSTLEFT = 8;
|
|
private static final int TILEINDEX_SIGN_POSTRIGHT = 9;
|
|
private static final int TILEINDEX_SIGN_COUNT = 10;
|
|
|
|
/* Indexes of faces in the SKIN format tile file */
|
|
private static final int TILEINDEX_SKIN_FACEFRONT = 0;
|
|
private static final int TILEINDEX_SKIN_FACELEFT = 1;
|
|
private static final int TILEINDEX_SKIN_FACERIGHT = 2;
|
|
private static final int TILEINDEX_SKIN_FACEBACK = 3;
|
|
private static final int TILEINDEX_SKIN_FACETOP = 4;
|
|
private static final int TILEINDEX_SKIN_FACEBOTTOM = 5;
|
|
private static final int TILEINDEX_SKIN_COUNT = 6;
|
|
|
|
/* Indexes of faces in the SHULKER format tile file */
|
|
private static final int TILEINDEX_SHULKER_TOP = 0;
|
|
private static final int TILEINDEX_SHULKER_LEFT = 1;
|
|
private static final int TILEINDEX_SHULKER_RIGHT = 2;
|
|
private static final int TILEINDEX_SHULKER_FRONT = 3;
|
|
private static final int TILEINDEX_SHULKER_BACK = 4;
|
|
private static final int TILEINDEX_SHULKER_BOTTOM = 5;
|
|
private static final int TILEINDEX_SHULKER_COUNT = 6;
|
|
|
|
/* Indexes of faces in the BED format tile file */
|
|
//private static final int TILEINDEX_BED_HEAD_TOP = 0;
|
|
//private static final int TILEINDEX_BED_HEAD_BOTTOM = 1;
|
|
//private static final int TILEINDEX_BED_HEAD_LEFT = 2;
|
|
//private static final int TILEINDEX_BED_HEAD_RIGHT = 3;
|
|
//private static final int TILEINDEX_BED_HEAD_END = 4;
|
|
//private static final int TILEINDEX_BED_FOOT_TOP = 5;
|
|
//private static final int TILEINDEX_BED_FOOT_BOTTOM = 6;
|
|
//private static final int TILEINDEX_BED_FOOT_LEFT = 7;
|
|
//private static final int TILEINDEX_BED_FOOT_RIGHT = 8;
|
|
//private static final int TILEINDEX_BED_FOOT_END = 9;
|
|
//private static final int TILEINDEX_BED_HEAD_LEFTLEG_1 = 10;
|
|
//private static final int TILEINDEX_BED_HEAD_LEFTLEG_2 = 11;
|
|
//private static final int TILEINDEX_BED_HEAD_RIGHTLEG_1 = 12;
|
|
//private static final int TILEINDEX_BED_HEAD_RIGHTLEG_2 = 13;
|
|
//private static final int TILEINDEX_BED_FOOT_LEFTLEG_1 = 14;
|
|
//private static final int TILEINDEX_BED_FOOT_LEFTLEG_2 = 15;
|
|
//private static final int TILEINDEX_BED_FOOT_RIGHTLEG_1 = 16;
|
|
//private static final int TILEINDEX_BED_FOOT_RIGHTLEG_2 = 17;
|
|
private static final int TILEINDEX_BED_COUNT = 18;
|
|
|
|
public static enum TileFileFormat {
|
|
GRID,
|
|
CHEST,
|
|
BIGCHEST,
|
|
SIGN,
|
|
SKIN,
|
|
SHULKER,
|
|
CUSTOM,
|
|
TILESET,
|
|
BIOME,
|
|
BED // 1.13 bed texture
|
|
};
|
|
|
|
// Material type: used for setting advanced rendering/export characteristics for image in given file
|
|
// (e.g. reflective surfaces, index of refraction, etc)
|
|
public static enum MaterialType {
|
|
GLASS(1.5, 200, 3), // Glass material: Ni=1.5, Ns=100, illum=3
|
|
WATER(1.33, 100, 3); // Water material: Ni=1.33, Ns=95, illum=3
|
|
|
|
public final double Ni;
|
|
public final double Ns;
|
|
public final int illum;
|
|
MaterialType(double Ni, double Ns, int illum) {
|
|
this.Ni = Ni;
|
|
this.Ns = Ns;
|
|
this.illum = illum;
|
|
}
|
|
}
|
|
|
|
/* Map of 1.5 texture files to 0-255 texture indices */
|
|
private static final String[] terrain_map = {
|
|
"grass_top", "stone", "dirt", "grass_side", "wood", "stoneslab_side", "stoneslab_top", "brick",
|
|
"tnt_side", "tnt_top", "tnt_bottom", "web", "rose", "flower", "portal", "sapling",
|
|
"stonebrick", "bedrock", "sand", "gravel", "tree_side", "tree_top", "blockIron", "blockGold",
|
|
"blockDiamond", "blockEmerald", null, null, "mushroom_red", "mushroom_brown", "sapling_jungle", null,
|
|
"oreGold", "oreIron", "oreCoal", "bookshelf", "stoneMoss", "obsidian", "grass_side_overlay", "tallgrass",
|
|
null, "beacon", null, "workbench_top", "furnace_front", "furnace_side", "dispenser_front", null,
|
|
"sponge", "glass", "oreDiamond", "oreRedstone", "leaves", "leaves_opaque", "stonebricksmooth", "deadbush",
|
|
"fern", null, null, "workbench_side", "workbench_front", "furnace_front_lit", "furnace_top", "sapling_spruce",
|
|
"cloth_0", "mobSpawner", "snow", "ice", "snow_side", "cactus_top", "cactus_side", "cactus_bottom",
|
|
"clay", "reeds", "musicBlock", "jukebox_top", "waterlily", "mycel_side", "mycel_top", "sapling_birch",
|
|
"torch", "doorWood_upper", "doorIron_upper", "ladder", "trapdoor", "fenceIron", "farmland_wet", "farmland_dry",
|
|
"crops_0", "crops_1", "crops_2", "crops_3", "crops_4", "crops_5", "crops_6", "crops_7",
|
|
"lever", "doorWood_lower", "doorIron_lower", "redtorch_lit", "stonebricksmooth_mossy", "stonebricksmooth_cracked", "pumpkin_top", "hellrock",
|
|
"hellsand", "lightgem", "piston_top_sticky", "piston_top", "piston_side", "piston_bottom", "piston_inner_top", "stem_straight",
|
|
"rail_turn", "cloth_15", "cloth_7", "redtorch", "tree_spruce", "tree_birch", "pumpkin_side", "pumpkin_face",
|
|
"pumpkin_jack", "cake_top", "cake_side", "cake_inner", "cake_bottom", "mushroom_skin_red", "mushroom_skin_brown", "stem_bent",
|
|
"rail", "cloth_14", "cloth_6", "repeater", "leaves_spruce", "leaves_spruce_opaque", "bed_feet_top", "bed_head_top",
|
|
"melon_side", "melon_top", "cauldron_top", "cauldron_inner", null, "mushroom_skin_stem", "mushroom_inside", "vine",
|
|
"blockLapis", "cloth_13", "cloth_5", "repeater_lit", "thinglass_top", "bed_feet_end", "bed_feet_side", "bed_head_side",
|
|
"bed_head_end", "tree_jungle", "cauldron_side", "cauldron_bottom", "brewingStand_base", "brewingStand", "endframe_top", "endframe_side",
|
|
"oreLapis", "cloth_12", "cloth_4", "goldenRail", "redstoneDust_cross", "redstoneDust_line", "enchantment_top", "dragonEgg",
|
|
"cocoa_2", "cocoa_1", "cocoa_0", "oreEmerald", "tripWireSource", "tripWire", "endframe_eye", "whiteStone",
|
|
"sandstone_top", "cloth_11", "cloth_3", "goldenRail_powered", "redstoneDust_cross_overlay", "redstoneDust_line_overlay", "enchantment_side", "enchantment_bottom",
|
|
"commandBlock", "itemframe_back", "flowerPot", null, null, null, null, null,
|
|
"sandstone_side", "cloth_10", "cloth_2", "detectorRail", "leaves_jungle", "leaves_jungle_opaque", "wood_spruce", "wood_jungle",
|
|
"carrots_0", "carrots_1", "carrots_2", "carrots_3", "potatoes_3", null, null, null,
|
|
"sandstone_bottom", "cloth_9", "cloth_1", "redstoneLight", "redstoneLight_lit", "stonebricksmooth_carved", "wood_birch", "anvil_base",
|
|
"anvil_top_damaged_1", null, null, null, null, null, null, null,
|
|
"netherBrick", "cloth_8", "netherStalk_0", "netherStalk_1", "netherStalk_2", "sandstone_carved", "sandstone_smooth", "anvil_top",
|
|
"anvil_top_damaged_2", null, null, null, null, null, null, null,
|
|
"destroy_0", "destroy_1", "destroy_2", "destroy_3", "destroy_4", "destroy_5", "destroy_6", "destroy_7",
|
|
"destroy_8", "destroy_9", null, null, null, null, null, null,
|
|
/* Extra 1.5-based textures: starting at 256 (corresponds to TILEINDEX_ values) */
|
|
null, "water", "water_flow", "lava", "lava_flow", null, null, null,
|
|
null, "fire_0", "portal"
|
|
};
|
|
|
|
/* Map of 1.6 resource files to 0-255 texture indices */
|
|
private static final String[] terrain_rp_map = {
|
|
"grass_top", "stone", "dirt", "grass_side", "planks_oak", "stone_slab_side", "stone_slab_top", "brick",
|
|
"tnt_side", "tnt_top", "tnt_bottom", "web", "flower_rose", "flower_dandelion", "portal", "sapling_oak",
|
|
"cobblestone", "bedrock", "sand", "gravel", "log_oak", "log_oak_top", "iron_block", "gold_block",
|
|
"diamond_block", "emerald_block", null, null, "mushroom_red", "mushroom_brown", "sapling_jungle", null,
|
|
"gold_ore", "iron_ore", "coal_ore", "bookshelf", "cobblestone_mossy", "obsidian", "grass_side_overlay", "tallgrass",
|
|
null, "beacon", null, "crafting_table_top", "furnace_front_off", "furnace_side", "dispenser_front_horizontal", null,
|
|
"sponge", "glass", "diamond_ore", "redstone_ore", "leaves_oak", "leaves_oak_opaque", "stonebrick", "deadbush",
|
|
"fern", null, null, "crafting_table_side", "crafting_table_front", "furnace_front_on", "furnace_top", "sapling_spruce",
|
|
"wool_colored_white", "mob_spawner", "snow", "ice", "grass_side_snowed", "cactus_top", "cactus_side", "cactus_bottom",
|
|
"clay", "reeds", "jukebox_side", "jukebox_top", "waterlily", "mycelium_side", "mycelium_top", "sapling_birch",
|
|
"torch_on", "door_wood_upper", "door_iron_upper", "ladder", "trapdoor", "iron_bars", "farmland_wet", "farmland_dry",
|
|
"wheat_stage_0", "wheat_stage_1", "wheat_stage_2", "wheat_stage_3", "wheat_stage_4", "wheat_stage_5", "wheat_stage_6", "wheat_stage_7",
|
|
"lever", "door_wood_lower", "door_iron_lower", "redstone_torch_on", "stonebrick_mossy", "stonebrick_cracked", "pumpkin_top", "netherrack",
|
|
"soul_sand", "glowstone", "piston_top_sticky", "piston_top_normal", "piston_side", "piston_bottom", "piston_inner", "pumpkin_stem_disconnected",
|
|
"rail_normal_turned", "wool_colored_black", "wool_colored_gray", "redstone_torch_off", "log_spruce", "log_birch", "pumpkin_side", "pumpkin_face_off",
|
|
"pumpkin_face_on", "cake_top", "cake_side", "cake_inner", "cake_bottom", "mushroom_block_skin_red", "mushroom_block_skin_brown", "pumpkin_stem_connected",
|
|
"rail_normal", "wool_colored_red", "wool_colored_pink", "repeater_off", "leaves_spruce", "leaves_spruce_opaque", "bed_feet_top", "bed_head_top",
|
|
"melon_side", "melon_top", "cauldron_top", "cauldron_inner", null, "mushroom_block_skin_stem", "mushroom_block_inside", "vine",
|
|
"lapis_block", "wool_colored_green", "wool_colored_lime", "repeater_on", "glass_pane_top", "bed_feet_end", "bed_feet_side", "bed_head_side",
|
|
"bed_head_end", "log_jungle", "cauldron_side", "cauldron_bottom", "brewing_stand_base", "brewing_stand", "endframe_top", "endframe_side",
|
|
"lapis_ore", "wool_colored_brown", "wool_colored_yellow", "rail_golden", "redstone_dust_cross", "redstone_dust_line", "enchanting_table_top", "dragon_egg",
|
|
"cocoa_stage_2", "cocoa_stage_1", "cocoa_stage_0", "emerald_ore", "trip_wire_source", "trip_wire", "endframe_eye", "end_stone",
|
|
"sandstone_top", "wool_colored_blue", "wool_colored_light_blue", "rail_golden_powered", "redstone_dust_cross_overlay", "redstone_dust_line_overlay", "enchanting_table_side", "enchanting_table_bottom",
|
|
"command_block", "itemframe_background", "flower_pot", null, null, null, null, null,
|
|
"sandstone_normal", "wool_colored_purple", "wool_colored_magenta", "rail_detector", "leaves_jungle", "leaves_jungle_opaque", "planks_spruce", "planks_jungle",
|
|
"carrots_stage_0", "carrots_stage_1", "carrots_stage_2", "carrots_stage_3", "potatoes_stage_3", null, null, null,
|
|
"sandstone_bottom", "wool_colored_cyan", "wool_colored_orange", "redstone_lamp_off", "redstone_lamp_on", "stonebrick_carved", "planks_birch", "anvil_base",
|
|
"anvil_top_damaged_1", null, null, null, null, null, null, null,
|
|
"nether_brick", "wool_colored_silver", "nether_wart_stage_0", "nether_wart_stage_1", "nether_wart_stage_2", "sandstone_carved", "sandstone_smooth", "anvil_top_damaged_0",
|
|
"anvil_top_damaged_2", null, null, null, null, null, null, null,
|
|
"destroy_stage_0", "destroy_stage_1", "destroy_stage_2", "destroy_stage_3", "destroy_stage_4", "destroy_stage_5", "destroy_stage_6", "destroy_stage_7",
|
|
"destroy_stage_8", "destroy_stage_9", null, null, null, null, null, null,
|
|
/* Extra 1.5-based textures: starting at 256 (corresponds to TILEINDEX_ values) */
|
|
null, "water_still", "water_flow", "lava_still", "lava_flow", null, null, null,
|
|
null, "fire_layer_0", "portal"
|
|
};
|
|
|
|
private static class CustomTileRec {
|
|
int srcx, srcy, width, height, targetx, targety;
|
|
public CustomTileRec() {}
|
|
public CustomTileRec(int srcx, int srcy, int width, int height, int targetx, int targety) {
|
|
this.srcx = srcx; this.srcy = srcy;
|
|
this.width = width; this.height = height;
|
|
this.targetx = targetx; this.targety = targety;
|
|
}
|
|
public CustomTileRec(int srcx, int srcy, int width, int height) {
|
|
this(srcx, srcy, width, height, 0, 0);
|
|
}
|
|
}
|
|
|
|
private static int next_dynamic_tile = MAX_TILEINDEX+1;
|
|
|
|
private static class DynamicTileFile {
|
|
int idx; /* Index of tile in addonfiles */
|
|
String filename;
|
|
String modname; /* Modname associated with file, if any */
|
|
int tilecnt_x, tilecnt_y; /* Number of tiles horizontally and vertically */
|
|
int tile_to_dyntile[]; /* Mapping from tile index in tile file to dynamic ID in global tile table (terrain_argb): 0=unassigned */
|
|
TileFileFormat format;
|
|
List<CustomTileRec> cust;
|
|
String[] tilenames; /* For TILESET, array of tilenames, indexed by tile index */
|
|
boolean used; // Set to true if any active references to the file
|
|
MaterialType material; // Material type, if specified
|
|
}
|
|
private static ArrayList<DynamicTileFile> addonfiles = new ArrayList<DynamicTileFile>();
|
|
private static Map<String, DynamicTileFile> addonfilesbyname = new HashMap<String, DynamicTileFile>();
|
|
private Map<Integer, MaterialType> materialbytileid = new HashMap<Integer, MaterialType>();
|
|
private Map<Integer, String> matIDByTileID = new HashMap<Integer, String>();
|
|
private Map<String, Integer> tileIDByMatID = new HashMap<String, Integer>();
|
|
// Mods supplying their own texture files
|
|
private static HashSet<String> loadedmods = new HashSet<String>();
|
|
|
|
private static String getBlockFileName(int idx) {
|
|
if ((idx >= 0) && (idx < terrain_map.length) && (terrain_map[idx] != null)) {
|
|
return "textures/blocks/" + terrain_map[idx] + ".png";
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static String getRPFileName(int idx) {
|
|
if ((idx >= 0) && (idx < terrain_rp_map.length) && (terrain_rp_map[idx] != null)) {
|
|
return "assets/minecraft/textures/blocks/" + terrain_rp_map[idx] + ".png";
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/* Reset add-on tile data */
|
|
private static void resetFiles(DynmapCore core) {
|
|
synchronized(packlock) {
|
|
packs.clear();
|
|
}
|
|
addonfiles.clear();
|
|
addonfilesbyname.clear();
|
|
loadedmods.clear();
|
|
next_dynamic_tile = MAX_TILEINDEX+1;
|
|
|
|
/* Now, load entries for vanilla v1.6.x RP files */
|
|
for(int i = 0; i < terrain_rp_map.length; i++) {
|
|
String fn = getRPFileName(i);
|
|
if (fn != null) {
|
|
int idx = findOrAddDynamicTileFile(fn, null, 1, 1, TileFileFormat.GRID, new String[0]);
|
|
DynamicTileFile dtf = addonfiles.get(idx);
|
|
if (dtf != null) { // Fix mapping of tile ID to global table index
|
|
dtf.tile_to_dyntile[0] = i;
|
|
dtf.used = true;
|
|
}
|
|
}
|
|
}
|
|
/* Now, load entries for vanilla v1.5.x files (put second so that add-on TP overrides built in RP) */
|
|
for(int i = 0; i < terrain_map.length; i++) {
|
|
String fn = getBlockFileName(i);
|
|
if (fn != null) {
|
|
int idx = findOrAddDynamicTileFile(fn, null, 1, 1, TileFileFormat.GRID, new String[0]);
|
|
DynamicTileFile dtf = addonfiles.get(idx);
|
|
if (dtf != null) { // Fix mapping of tile ID to global table index
|
|
dtf.used = true;
|
|
dtf.tile_to_dyntile[0] = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static class LoadedImage {
|
|
int[] argb;
|
|
int width, height;
|
|
int trivial_color;
|
|
boolean isLoaded;
|
|
@SuppressWarnings("unused")
|
|
String fname, modid;
|
|
}
|
|
|
|
private int[][] tile_argb;
|
|
private int[] blank;
|
|
private int native_scale;
|
|
private CTMTexturePack ctm;
|
|
// private static BitSet hasBaseBlockColoring = new BitSet(); // Quick lookup - (blockID << 4) + blockMeta - set if custom colorizer
|
|
// private static DynIntHashMap baseBlockColoring = new DynIntHashMap(); // Base block coloring (RP independent)
|
|
// Need copy, since RP can change this....
|
|
private ColorizingData blockColoring = HDBlockStateTextureMap.getColorizingData();
|
|
|
|
private int colorMultBirch = 0x80a755; /* From ColorizerFoliage.java in MCP */
|
|
private int colorMultPine = 0x619961; /* From ColorizerFoliage.java in MCP */
|
|
private int colorMultLily = 0x208030; /* from BlockLilyPad.java in MCP */
|
|
|
|
private static final int IMG_GRASSCOLOR = 0;
|
|
private static final int IMG_FOLIAGECOLOR = 1;
|
|
private static final int IMG_CUSTOMWATERMOVING = 2;
|
|
private static final int IMG_CUSTOMWATERSTILL = 3;
|
|
private static final int IMG_CUSTOMLAVAMOVING = 4;
|
|
private static final int IMG_CUSTOMLAVASTILL = 5;
|
|
private static final int IMG_WATERCOLORX = 6;
|
|
private static final int IMG_SWAMPGRASSCOLOR = 7;
|
|
private static final int IMG_SWAMPFOLIAGECOLOR = 8;
|
|
private static final int IMG_PINECOLOR = 9;
|
|
private static final int IMG_BIRCHCOLOR = 10;
|
|
|
|
private static final int IMG_CNT = 11;
|
|
/* 0-(IMG_CNT-1) are fixed, IMG_CNT+x is dynamic file x */
|
|
private LoadedImage[] imgs;
|
|
|
|
private HashMap<Integer, TexturePack> scaled_textures;
|
|
private Object scaledlock = new Object();
|
|
|
|
public enum BlockTransparency {
|
|
OPAQUE, /* Block is opaque - blocks light - lit by light from adjacent blocks */
|
|
TRANSPARENT, /* Block is transparent - passes light - lit by light level in own block */
|
|
SEMITRANSPARENT, /* Opaque block that doesn't block all rays (steps, slabs) - use light above for face lighting on opaque blocks */
|
|
LEAVES /* Special case of transparent, to work around lighting errors in SpoutPlugin */
|
|
}
|
|
|
|
public static class ColorizingData {
|
|
private DynIntHashMap map = new DynIntHashMap();
|
|
|
|
public void setBlkStateValue(DynmapBlockState blk, Integer mapidx) {
|
|
int idx = blk.globalStateIndex;
|
|
if (mapidx == null) {
|
|
map.remove(idx);
|
|
}
|
|
else {
|
|
map.put(idx, mapidx);
|
|
}
|
|
}
|
|
public Integer getBlkStateValue(DynmapBlockState blk) {
|
|
return (Integer) map.get(blk.globalStateIndex);
|
|
}
|
|
public boolean hasBlkStateValue(DynmapBlockState blk) {
|
|
return map.containsKey(blk.globalStateIndex);
|
|
}
|
|
public void scrubValues(Integer val) {
|
|
List<Integer> badvals = map.keysWithValue(val);
|
|
for (Integer v : badvals) {
|
|
map.remove(v);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Texture map - used for accumulation of textures from different sources, keyed by lookup value
|
|
*/
|
|
public static class TextureMap {
|
|
private Map<Integer, Integer> key_to_index = new HashMap<Integer, Integer>();
|
|
private List<Integer> texture_ids = new ArrayList<Integer>();
|
|
private Map<DynmapBlockState, BitSet> states;
|
|
private BlockTransparency trans = BlockTransparency.OPAQUE;
|
|
private int colorMult = 0;
|
|
private CustomColorMultiplier custColorMult = null;
|
|
private String blockset;
|
|
|
|
public TextureMap() { }
|
|
|
|
public int addTextureByKey(int key, int textureid) {
|
|
int off = texture_ids.size(); /* Next index in array is texture index */
|
|
texture_ids.add(textureid); /* Add texture ID to list */
|
|
key_to_index.put(key, off); /* Add texture index to lookup by key */
|
|
return off;
|
|
}
|
|
}
|
|
private static HashMap<String, TextureMap> textmap_by_id = new HashMap<String, TextureMap>();
|
|
|
|
/**
|
|
* Set tile ARGB buffer at index
|
|
* @param idx - index of tile
|
|
* @param buf - buffer to be set
|
|
*/
|
|
public final void setTileARGB(int idx, int[] buf) {
|
|
if (idx >= tile_argb.length) {
|
|
tile_argb = Arrays.copyOf(tile_argb, 3*idx/2);
|
|
}
|
|
tile_argb[idx] = buf;
|
|
}
|
|
/**
|
|
* Get number of entries in tile list
|
|
* @return length of tile list
|
|
*/
|
|
public final int getTileARGBCount() {
|
|
return tile_argb.length;
|
|
}
|
|
/**
|
|
* Get tile ARGB buffer at index
|
|
* @param idx - tile index
|
|
* @return ARGB array for tile, or blank array if not found
|
|
*/
|
|
public final int[] getTileARGB(int idx) {
|
|
int[] rslt = blank;
|
|
if (idx < tile_argb.length) {
|
|
rslt = tile_argb[idx];
|
|
if (rslt == null) {
|
|
rslt = tile_argb[idx] = blank;
|
|
}
|
|
}
|
|
return rslt;
|
|
}
|
|
/**
|
|
* Add texture to texture map
|
|
*/
|
|
private static int addTextureByKey(String id, int key, int textureid) {
|
|
TextureMap idx = textmap_by_id.get(id);
|
|
if(idx == null) { /* Add empty one, if not found */
|
|
idx = new TextureMap();
|
|
textmap_by_id.put(id, idx);
|
|
}
|
|
return idx.addTextureByKey(key, textureid);
|
|
}
|
|
/**
|
|
* Add settings for texture map
|
|
*/
|
|
private static void addTextureIndex(String id, Map<DynmapBlockState, BitSet> states, BlockTransparency trans, int colorMult, CustomColorMultiplier custColorMult, String blockset) {
|
|
TextureMap idx = textmap_by_id.get(id);
|
|
if(idx == null) { /* Add empty one, if not found */
|
|
idx = new TextureMap();
|
|
textmap_by_id.put(id, idx);
|
|
}
|
|
idx.states = states;
|
|
idx.trans = trans;
|
|
idx.colorMult = colorMult;
|
|
idx.custColorMult = custColorMult;
|
|
}
|
|
/**
|
|
* Finish processing of texture indexes - add to texture maps
|
|
*/
|
|
private static void processTextureMaps() {
|
|
for(TextureMap ti : textmap_by_id.values()) {
|
|
if(ti.states.isEmpty()) continue;
|
|
int[] txtids = new int[ti.texture_ids.size()];
|
|
for(int i = 0; i < txtids.length; i++) {
|
|
txtids[i] = ti.texture_ids.get(i).intValue();
|
|
}
|
|
HDBlockStateTextureMap map = new HDBlockStateTextureMap(txtids, null, ti.colorMult, ti.custColorMult, ti.blockset, true, null, ti.trans);
|
|
map.addToTable(ti.states);
|
|
}
|
|
}
|
|
/**
|
|
* Get index of texture in texture map
|
|
* @param id - texture pack id
|
|
* @param key - key for texture
|
|
* @return index of texture, or -1 if not found
|
|
*/
|
|
public static int getTextureIndexFromTextureMap(String id, int key) {
|
|
int idx = -1;
|
|
TextureMap map = textmap_by_id.get(id);
|
|
if(map != null) {
|
|
Integer txtidx = map.key_to_index.get(key);
|
|
if(txtidx != null) {
|
|
idx = txtidx.intValue();
|
|
}
|
|
}
|
|
return idx;
|
|
}
|
|
/*
|
|
* Get count of textures in given texture map
|
|
* @param id - texture pack ID
|
|
* @return length of texture list, or -1 if error
|
|
*/
|
|
public static int getTextureMapLength(String id) {
|
|
TextureMap map = textmap_by_id.get(id);
|
|
if(map != null) {
|
|
return map.texture_ids.size();
|
|
}
|
|
return -1;
|
|
}
|
|
/**
|
|
* Get or load texture pack
|
|
* @param core - core object
|
|
* @param tpname - texture pack name
|
|
* @return loaded texture pack, or null if error
|
|
*/
|
|
public static TexturePack getTexturePack(DynmapCore core, String tpname) {
|
|
synchronized(packlock) {
|
|
TexturePack tp = packs.get(tpname);
|
|
if(tp != null)
|
|
return tp;
|
|
try {
|
|
tp = new TexturePack(core, tpname); /* Attempt to load pack */
|
|
packs.put(tpname, tp);
|
|
return tp;
|
|
} catch (FileNotFoundException fnfx) {
|
|
Log.severe("Error loading texture pack '" + tpname + "' - not found");
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Constructor for texture pack, by name
|
|
*/
|
|
private TexturePack(DynmapCore core, String tpname) throws FileNotFoundException {
|
|
File texturedir = getTexturePackDirectory(core);
|
|
/* Set up for enough files */
|
|
imgs = new LoadedImage[IMG_CNT + addonfiles.size()];
|
|
|
|
// Get texture pack
|
|
File f = new File(texturedir, tpname);
|
|
// Build loader
|
|
TexturePackLoader tpl = new TexturePackLoader(f, core);
|
|
InputStream is = null;
|
|
try {
|
|
boolean is_rp = false;
|
|
/* Check if resource pack */
|
|
is = tpl.openTPResource("pack.mcmeta");
|
|
if (is != null) {
|
|
tpl.closeResource(is);
|
|
is_rp = true;
|
|
Log.info("Loading resource pack " + f.getName());
|
|
}
|
|
else if(tpname.equals("standard")) { // Built in is RP
|
|
is_rp = true;
|
|
Log.info("Loading default resource pack");
|
|
}
|
|
else {
|
|
Log.info("Loading texture pack " + f.getName());
|
|
}
|
|
/* Load CTM support, if enabled */
|
|
if(core.isCTMSupportEnabled()) {
|
|
ctm = new CTMTexturePack(tpl, this, core, is_rp);
|
|
if(ctm.isValid() == false) {
|
|
ctm = null;
|
|
}
|
|
}
|
|
String fn;
|
|
/* Load custom colors support, if enabled */
|
|
if(core.isCustomColorsSupportEnabled()) {
|
|
fn = (is_rp?"assets/minecraft/mcpatcher/color.properties":"color.properties");
|
|
is = tpl.openTPResource(fn);
|
|
Properties p;
|
|
if (is != null) {
|
|
p = new Properties();
|
|
try {
|
|
p.load(is);
|
|
} finally {
|
|
tpl.closeResource(is);
|
|
}
|
|
processCustomColors(p);
|
|
}
|
|
}
|
|
/* Loop through dynamic files */
|
|
for(int i = 0; i < addonfiles.size(); i++) {
|
|
DynamicTileFile dtf = addonfiles.get(i);
|
|
if (dtf.used == false) { // Not used, skip it - save memory and avoid errors for downlevel mods and such
|
|
continue;
|
|
}
|
|
is = tpl.openModTPResource(dtf.filename, dtf.modname);
|
|
try {
|
|
if(dtf.format == TileFileFormat.BIOME) {
|
|
loadBiomeShadingImage(is, i+IMG_CNT, dtf.filename, dtf.modname); /* Load image file */
|
|
}
|
|
else
|
|
loadImage(is, i+IMG_CNT, dtf.filename, dtf.modname); /* Load image file */
|
|
} finally {
|
|
tpl.closeResource(is);
|
|
}
|
|
}
|
|
/* Find and load terrain */
|
|
loadTerrain(is_rp);
|
|
/* Try to find and load misc/grasscolor.png */
|
|
is = tpl.openTPResource(GRASSCOLOR_PNG, GRASSCOLOR_RP_PNG);
|
|
if (is != null) {
|
|
loadBiomeShadingImage(is, IMG_GRASSCOLOR, GRASSCOLOR_RP_PNG, "minecraft");
|
|
tpl.closeResource(is);
|
|
}
|
|
/* Try to find and load misc/foliagecolor.png */
|
|
is = tpl.openTPResource(FOLIAGECOLOR_PNG, FOLIAGECOLOR_RP_PNG);
|
|
if (is != null) {
|
|
loadBiomeShadingImage(is, IMG_FOLIAGECOLOR, FOLIAGECOLOR_RP_PNG, "minecraft");
|
|
tpl.closeResource(is);
|
|
}
|
|
/* Try to find and load misc/swampgrasscolor.png */
|
|
is = tpl.openTPResource(SWAMPGRASSCOLOR_PNG, SWAMPGRASSCOLOR_RP_PNG);
|
|
if (is != null) {
|
|
loadBiomeShadingImage(is, IMG_SWAMPGRASSCOLOR, SWAMPGRASSCOLOR_RP_PNG, "minecraft");
|
|
tpl.closeResource(is);
|
|
}
|
|
/* Try to find and load misc/swampfoliagecolor.png */
|
|
is = tpl.openTPResource(SWAMPFOLIAGECOLOR_PNG, SWAMPFOLIAGECOLOR_RP_PNG);
|
|
if (is != null) {
|
|
loadBiomeShadingImage(is, IMG_SWAMPFOLIAGECOLOR, SWAMPFOLIAGECOLOR_RP_PNG, "minecraft");
|
|
tpl.closeResource(is);
|
|
}
|
|
/* Try to find and load misc/watercolor.png */
|
|
is = tpl.openTPResource(WATERCOLORX_PNG, WATERCOLORX_RP_PNG);
|
|
if (is == null) {
|
|
/* Try to find and load colormap/water.png */
|
|
is = tpl.openTPResource(WATERCOLORX_PNG, WATERCOLORX2_RP_PNG);
|
|
}
|
|
if (is != null) {
|
|
loadBiomeShadingImage(is, IMG_WATERCOLORX, WATERCOLORX_RP_PNG, "minecraft");
|
|
tpl.closeResource(is);
|
|
}
|
|
/* Try to find pine.png */
|
|
is = tpl.openTPResource(PINECOLOR_PNG, PINECOLOR_RP_PNG);
|
|
if (is != null) {
|
|
loadBiomeShadingImage(is, IMG_PINECOLOR, PINECOLOR_RP_PNG, "minecraft");
|
|
tpl.closeResource(is);
|
|
}
|
|
/* Try to find birch.png */
|
|
is = tpl.openTPResource(BIRCHCOLOR_PNG, BIRCHCOLOR_RP_PNG);
|
|
if (is != null) {
|
|
loadBiomeShadingImage(is, IMG_BIRCHCOLOR, BIRCHCOLOR_RP_PNG, "minecraft");
|
|
tpl.closeResource(is);
|
|
}
|
|
/* Optional files - process if they exist */
|
|
is = tpl.openTPResource(CUSTOMLAVASTILL_PNG);
|
|
if (is == null) {
|
|
is = tpl.openTPResource("anim/" + CUSTOMLAVASTILL_PNG);
|
|
}
|
|
if (is != null) {
|
|
loadImage(is, IMG_CUSTOMLAVASTILL, CUSTOMLAVASTILL_PNG, "minecraft");
|
|
tpl.closeResource(is);
|
|
patchTextureWithImage(IMG_CUSTOMLAVASTILL, TILEINDEX_STATIONARYLAVA);
|
|
patchTextureWithImage(IMG_CUSTOMLAVASTILL, TILEINDEX_MOVINGLAVA);
|
|
}
|
|
is = tpl.openTPResource(CUSTOMLAVAFLOWING_PNG);
|
|
if (is == null) {
|
|
is = tpl.openTPResource("anim/" + CUSTOMLAVAFLOWING_PNG);
|
|
}
|
|
if (is != null) {
|
|
loadImage(is, IMG_CUSTOMLAVAMOVING, CUSTOMLAVAFLOWING_PNG, "minecraft");
|
|
tpl.closeResource(is);
|
|
patchTextureWithImage(IMG_CUSTOMLAVAMOVING, TILEINDEX_MOVINGLAVA);
|
|
}
|
|
is = tpl.openTPResource(CUSTOMWATERSTILL_PNG);
|
|
if (is == null) {
|
|
is = tpl.openTPResource("anim/" + CUSTOMWATERSTILL_PNG);
|
|
}
|
|
if (is != null) {
|
|
loadImage(is, IMG_CUSTOMWATERSTILL, CUSTOMWATERSTILL_PNG, "minecraft");
|
|
tpl.closeResource(is);
|
|
patchTextureWithImage(IMG_CUSTOMWATERSTILL, TILEINDEX_STATIONARYWATER);
|
|
patchTextureWithImage(IMG_CUSTOMWATERSTILL, TILEINDEX_MOVINGWATER);
|
|
}
|
|
is = tpl.openTPResource(CUSTOMWATERFLOWING_PNG);
|
|
if (is == null) {
|
|
is = tpl.openTPResource("anim/" + CUSTOMWATERFLOWING_PNG);
|
|
}
|
|
if (is != null) {
|
|
loadImage(is, IMG_CUSTOMWATERMOVING, CUSTOMWATERSTILL_PNG, "minecraft");
|
|
tpl.closeResource(is);
|
|
patchTextureWithImage(IMG_CUSTOMWATERMOVING, TILEINDEX_MOVINGWATER);
|
|
}
|
|
/* Loop through dynamic files */
|
|
for(int i = 0; i < addonfiles.size(); i++) {
|
|
DynamicTileFile dtf = addonfiles.get(i);
|
|
processDynamicImage(i, dtf.format);
|
|
}
|
|
} catch (IOException iox) {
|
|
Log.severe("Error loadling texture pack", iox);
|
|
} finally {
|
|
if (is != null) {
|
|
try { is.close(); } catch (IOException iox) {}
|
|
is = null;
|
|
}
|
|
tpl.close();
|
|
}
|
|
}
|
|
/**
|
|
* Copy subimage from portions of given image
|
|
* @param img_id - image ID of raw image
|
|
* @param from_x - top-left X
|
|
* @param from_y - top-left Y
|
|
* @param to_x - dest topleft
|
|
* @param to_y - dest topleft
|
|
* @param width - width to copy
|
|
* @param height - height to copy
|
|
* @param dest_argb - destination tile buffer
|
|
* @param dest_width - width of destination tile buffer
|
|
*/
|
|
private void copySubimageFromImage(int img_id, int from_x, int from_y, int to_x, int to_y, int width, int height, int[] dest_argb, int dest_width) {
|
|
for(int h = 0; h < height; h++) {
|
|
try {
|
|
System.arraycopy(imgs[img_id].argb, (h+from_y)*imgs[img_id].width + from_x, dest_argb, dest_width*(h+to_y) + to_x, width);
|
|
} catch (ArrayIndexOutOfBoundsException aoobx) {
|
|
Log.warning(String.format("Error copying subimage: img_id=%s, from=%d,%s, to=%d,%d, size=%d,%d, dest=%d,%d",
|
|
imgs[img_id].fname, from_x, from_y, to_x, to_y, width, height, dest_width, dest_argb.length / dest_width));
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Combine non-transparent portions of given image onto destination
|
|
* @param img_id - image ID of raw image
|
|
* @param from_x - top-left X
|
|
* @param from_y - top-left Y
|
|
* @param to_x - dest topleft
|
|
* @param to_y - dest topleft
|
|
* @param width - width to copy
|
|
* @param height - height to copy
|
|
* @param dest_argb - destination tile buffer
|
|
* @param dest_width - width of destination tile buffer
|
|
*/
|
|
private void combineSubimageFromImage(int img_id, int from_x, int from_y, int to_x, int to_y, int width, int height, int[] dest_argb, int dest_width) {
|
|
for(int h = 0; h < height; h++) {
|
|
for(int w = 0; w < width; w++) {
|
|
int src_argb = imgs[img_id].argb[(h+from_y)*imgs[img_id].width + (w+from_x)];
|
|
// Apply only solid pixels
|
|
if ( ((src_argb >> 24) & 255) == 255 ) {
|
|
dest_argb[dest_width * (h + to_y) + (w + to_x)] = src_argb;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private enum HandlePos { CENTER, LEFT, RIGHT, NONE, LEFTFRONT, RIGHTFRONT };
|
|
/**
|
|
* Make chest side image (based on chest and largechest layouts)
|
|
* @param img_id - source image ID
|
|
* @param dest_idx - destination tile index
|
|
* @param src_x - starting X of source (scaled based on 64 high)
|
|
* @param width - width to copy (scaled based on 64 high)
|
|
* @param dest_x - destination X (scaled based on 64 high)
|
|
* @param handlepos - 0=middle,1=leftedge,2=rightedge
|
|
*/
|
|
private void makeChestSideImage(int img_id, int dest_idx, int src_x, int width, int dest_x, HandlePos handlepos) {
|
|
if(dest_idx <= 0) return;
|
|
int mult = imgs[img_id].height / 64; /* Nominal height for chest images is 64 */
|
|
int[] tile = new int[16 * 16 * mult * mult]; /* Make image */
|
|
/* Copy top part */
|
|
copySubimageFromImage(img_id, src_x * mult, 14 * mult, dest_x * mult, 2 * mult, width * mult, 5 * mult, tile, 16 * mult);
|
|
/* Copy bottom part */
|
|
copySubimageFromImage(img_id, src_x * mult, 34 * mult, dest_x * mult, 7 * mult, width * mult, 9 * mult, tile, 16 * mult);
|
|
/* Handle the handle image */
|
|
if(handlepos == HandlePos.CENTER) { /* Middle */
|
|
copySubimageFromImage(img_id, 1 * mult, 1 * mult, 7 * mult, 4 * mult, 2 * mult, 4 * mult, tile, 16 * mult);
|
|
}
|
|
else if(handlepos == HandlePos.LEFT) { /* left edge */
|
|
copySubimageFromImage(img_id, 3 * mult, 1 * mult, 0 * mult, 4 * mult, 1 * mult, 4 * mult, tile, 16 * mult);
|
|
}
|
|
else if(handlepos == HandlePos.LEFTFRONT) { /* left edge - front of handle */
|
|
copySubimageFromImage(img_id, 2 * mult, 1 * mult, 0 * mult, 4 * mult, 1 * mult, 4 * mult, tile, 16 * mult);
|
|
}
|
|
else if(handlepos == HandlePos.RIGHT) { /* Right */
|
|
copySubimageFromImage(img_id, 0 * mult, 1 * mult, 15 * mult, 4 * mult, 1 * mult, 4 * mult, tile, 16 * mult);
|
|
}
|
|
else if(handlepos == HandlePos.RIGHTFRONT) { /* Right - front of handle */
|
|
copySubimageFromImage(img_id, 1 * mult, 1 * mult, 15 * mult, 4 * mult, 1 * mult, 4 * mult, tile, 16 * mult);
|
|
}
|
|
/* Put scaled result into tile buffer */
|
|
int new_argb[] = new int[native_scale*native_scale];
|
|
scaleTerrainPNGSubImage(16*mult, native_scale, tile, new_argb);
|
|
setTileARGB(dest_idx, new_argb);
|
|
}
|
|
/**
|
|
* Make chest top/bottom image (based on chest and largechest layouts)
|
|
* @param img_id - source image ID
|
|
* @param dest_idx - destination tile index
|
|
* @param src_x - starting X of source (scaled based on 64 high)
|
|
* @param src_y - starting Y of source (scaled based on 64 high)
|
|
* @param width - width to copy (scaled based on 64 high)
|
|
* @param dest_x - destination X (scaled based on 64 high)
|
|
* @param handlepos - 0=middle,1=left-edge (righttop),2=right-edge (lefttop)
|
|
*/
|
|
private void makeChestTopBottomImage(int img_id, int dest_idx, int src_x, int src_y, int width, int dest_x, HandlePos handlepos) {
|
|
if(dest_idx <= 0) return;
|
|
|
|
int mult = imgs[img_id].height / 64; /* Nominal height for chest images is 64 */
|
|
int[] tile = new int[16 * 16 * mult * mult]; /* Make image */
|
|
copySubimageFromImage(img_id, src_x * mult, src_y * mult, dest_x * mult, 1 * mult, width * mult, 14 * mult, tile, 16 * mult);
|
|
/* Handle the handle image */
|
|
if(handlepos == HandlePos.CENTER) { /* Middle */
|
|
copySubimageFromImage(img_id, 1 * mult, 0, 7 * mult, 15 * mult, 2 * mult, 1 * mult, tile, 16 * mult);
|
|
}
|
|
else if(handlepos == HandlePos.LEFT) { /* left edge */
|
|
copySubimageFromImage(img_id, 2 * mult, 0, 0 * mult, 15 * mult, 1 * mult, 1 * mult, tile, 16 * mult);
|
|
}
|
|
else if(handlepos == HandlePos.RIGHT) { /* Right */
|
|
copySubimageFromImage(img_id, 1 * mult, 0, 15 * mult, 15 * mult, 1 * mult, 1 * mult, tile, 16 * mult);
|
|
}
|
|
/* Put scaled result into tile buffer */
|
|
int new_argb[] = new int[native_scale*native_scale];
|
|
scaleTerrainPNGSubImage(16*mult, native_scale, tile, new_argb);
|
|
setTileARGB(dest_idx, new_argb);
|
|
}
|
|
/**
|
|
* Patch tiles based on image with chest-style layout
|
|
*/
|
|
private void patchChestImages(int img_id, int tile_top, int tile_bottom, int tile_front, int tile_back, int tile_left, int tile_right) {
|
|
makeChestSideImage(img_id, tile_front, 14, 14, 1, HandlePos.CENTER);
|
|
makeChestSideImage(img_id, tile_back, 42, 14, 1, HandlePos.NONE);
|
|
makeChestSideImage(img_id, tile_left, 0, 14, 1, HandlePos.RIGHT);
|
|
makeChestSideImage(img_id, tile_right, 28, 14, 1, HandlePos.LEFT);
|
|
makeChestTopBottomImage(img_id, tile_top, 14, 0, 14, 1, HandlePos.CENTER);
|
|
makeChestTopBottomImage(img_id, tile_bottom, 28, 19, 14, 1, HandlePos.CENTER);
|
|
}
|
|
/**
|
|
* Patch tiles based on image with large-chest-style layout
|
|
*/
|
|
private void patchLargeChestImages(int img_id, int tile_topright, int tile_topleft, int tile_bottomright, int tile_bottomleft, int tile_right, int tile_left, int tile_frontright, int tile_frontleft, int tile_backright, int tile_backleft) {
|
|
makeChestSideImage(img_id, tile_frontleft, 14, 15, 1, HandlePos.RIGHTFRONT);
|
|
makeChestSideImage(img_id, tile_frontright, 29, 15, 0, HandlePos.LEFTFRONT);
|
|
makeChestSideImage(img_id, tile_left, 0, 14, 1, HandlePos.RIGHT);
|
|
makeChestSideImage(img_id, tile_right, 44, 14, 1, HandlePos.LEFT);
|
|
makeChestSideImage(img_id, tile_backright, 58, 15, 1, HandlePos.NONE);
|
|
makeChestSideImage(img_id, tile_backleft, 73, 15, 0, HandlePos.NONE);
|
|
makeChestTopBottomImage(img_id, tile_topleft, 14, 0, 15, 1, HandlePos.RIGHT);
|
|
makeChestTopBottomImage(img_id, tile_topright, 29, 0, 15, 0, HandlePos.LEFT);
|
|
makeChestTopBottomImage(img_id, tile_bottomleft, 34, 19, 15, 1, HandlePos.RIGHT);
|
|
makeChestTopBottomImage(img_id, tile_bottomright, 49, 19, 15, 0, HandlePos.LEFT);
|
|
}
|
|
|
|
/**
|
|
* Make sign image (based on sign layouts)
|
|
* @param img_id - source image ID
|
|
* @param dest_idx - destination tile index
|
|
* @param src_x - starting X of source (scaled based on 32 high)
|
|
* @param src_y - starting Y of source (scaled based on 32 high)
|
|
* @param width - width to copy (scaled based on 32 high)
|
|
* @param height - height to copy (scaled based on 32 high)
|
|
*/
|
|
private void makeSignImage(int img_id, int dest_idx, int src_x, int src_y, int width, int height) {
|
|
int mult = imgs[img_id].height / 32; /* Nominal height for sign images is 32 */
|
|
int[] tile = new int[24 * 24 * mult * mult]; /* Make image (all are 24x24) */
|
|
copySubimageFromImage(img_id, src_x * mult, src_y * mult, 0, (24-height)*mult, width * mult, height * mult, tile, 24 * mult);
|
|
/* Put scaled result into tile buffer */
|
|
int new_argb[] = new int[native_scale*native_scale];
|
|
scaleTerrainPNGSubImage(24*mult, native_scale, tile, new_argb);
|
|
setTileARGB(dest_idx, new_argb);
|
|
}
|
|
|
|
private void patchSignImages(int img, int sign_front, int sign_back, int sign_top, int sign_bottom, int sign_left, int sign_right, int post_front, int post_back, int post_left, int post_right)
|
|
{
|
|
/* Load images at lower left corner of each tile */
|
|
makeSignImage(img, sign_front, 2, 2, 24, 12);
|
|
makeSignImage(img, sign_back, 28, 2, 24, 12);
|
|
makeSignImage(img, sign_top, 2, 0, 24, 2);
|
|
makeSignImage(img, sign_left, 0, 2, 2, 12);
|
|
makeSignImage(img, sign_right, 26, 2, 2, 12);
|
|
makeSignImage(img, sign_bottom, 26, 0, 24, 2);
|
|
makeSignImage(img, post_front, 0, 16, 2, 14);
|
|
makeSignImage(img, post_right, 2, 16, 2, 14);
|
|
makeSignImage(img, post_back, 4, 16, 2, 14);
|
|
makeSignImage(img, post_left, 6, 16, 2, 14);
|
|
}
|
|
|
|
/**
|
|
* Make face image (based on skin layouts)
|
|
* @param img_id - source image ID
|
|
* @param dest_idx - destination tile index
|
|
* @param src_x - starting X of source (scaled based on 32 high)
|
|
* @param src_y - starting Y of source (scaled based on 32 high)
|
|
*/
|
|
private void makeFaceImage(int img_id, int dest_idx, int src_x, int src_y) {
|
|
int mult = imgs[img_id].width / 64; /* Nominal height for skin images is 32 */
|
|
int[] tile = new int[8 * 8 * mult * mult]; /* Make image (all are 8x8) */
|
|
copySubimageFromImage(img_id, src_x * mult, src_y * mult, 0, 0, 8 * mult, 8 * mult, tile, 8 * mult);
|
|
/* Put scaled result into tile buffer */
|
|
int new_argb[] = new int[native_scale*native_scale];
|
|
scaleTerrainPNGSubImage(8 * mult, native_scale, tile, new_argb);
|
|
setTileARGB(dest_idx, new_argb);
|
|
}
|
|
|
|
private void patchSkinImages(int img, int face_front, int face_left, int face_right, int face_back, int face_top, int face_bottom)
|
|
{
|
|
makeFaceImage(img, face_front, 8, 8);
|
|
makeFaceImage(img, face_left, 16, 8);
|
|
makeFaceImage(img, face_right, 0, 8);
|
|
makeFaceImage(img, face_back, 24, 8);
|
|
makeFaceImage(img, face_top, 8, 0);
|
|
makeFaceImage(img, face_bottom, 16, 0);
|
|
}
|
|
|
|
/**
|
|
* Make shulker side image from top and bottom images (based on shulker layouts)
|
|
* @param img_id - source image ID
|
|
* @param dest_idx - destination tile index
|
|
* @param src_x - starting X of source column (scaled based on 64 high)
|
|
*/
|
|
private void makeShulkerSideImage(int img_id, int dest_idx, int src_x) {
|
|
int mult = imgs[img_id].width / 64; /* Nominal height for shulker images is 64 */
|
|
int src_y_top = 16;
|
|
int src_y_btm = 44;
|
|
int[] tile = new int[16 * 16 * mult * mult]; /* Make image (all are 16x16) */
|
|
/* Top half of the shulker */
|
|
copySubimageFromImage(img_id, src_x * mult, src_y_top * mult, 0, 0, 16 * mult, 12 * mult, tile, 16 * mult);
|
|
/* Bottom half of the shulker */
|
|
combineSubimageFromImage(img_id, src_x * mult, src_y_btm * mult, 0, 8 * mult, 16 * mult, 8 * mult, tile, 16 * mult);
|
|
/* Put scaled result into tile buffer */
|
|
int new_argb[] = new int[native_scale*native_scale];
|
|
scaleTerrainPNGSubImage(16 * mult, native_scale, tile, new_argb);
|
|
setTileARGB(dest_idx, new_argb);
|
|
}
|
|
|
|
/**
|
|
* Make shulker top/bottom image (based on shulker layouts)
|
|
* @param img_id - source image ID
|
|
* @param dest_idx - destination tile index
|
|
* @param src_x - starting X of source (scaled based on 64 high)
|
|
* @param src_y - starting Y of source (scaled based on 64 high)
|
|
*/
|
|
private void makeShulkerTopBottomImage(int img_id, int dest_idx, int src_x, int src_y) {
|
|
int mult = imgs[img_id].width / 64; /* Nominal height for shulker images is 64 */
|
|
int[] tile = new int[16 * 16 * mult * mult]; /* Make image (all are 16x16) */
|
|
copySubimageFromImage(img_id, src_x * mult, src_y * mult, 0, 0, 16 * mult, 16 * mult, tile, 16 * mult);
|
|
/* Put scaled result into tile buffer */
|
|
int new_argb[] = new int[native_scale*native_scale];
|
|
scaleTerrainPNGSubImage(16 * mult, native_scale, tile, new_argb);
|
|
setTileARGB(dest_idx, new_argb);
|
|
}
|
|
|
|
/**
|
|
* Patch tiles based on image with shulker-style layout
|
|
*/
|
|
private void patchShulkerImages(int img_id, int tile_top, int tile_bottom, int tile_front, int tile_back, int tile_left, int tile_right) {
|
|
makeShulkerSideImage(img_id, tile_front, 0);
|
|
makeShulkerSideImage(img_id, tile_back, 16);
|
|
makeShulkerSideImage(img_id, tile_left, 32);
|
|
makeShulkerSideImage(img_id, tile_right, 48);
|
|
makeShulkerTopBottomImage(img_id, tile_top, 16, 0);
|
|
makeShulkerTopBottomImage(img_id, tile_bottom, 32, 28);
|
|
}
|
|
|
|
private void patchCustomImages(int img_id, int[] imgids, List<CustomTileRec> recs, int xcnt, int ycnt)
|
|
{
|
|
int mult = imgs[img_id].height / (ycnt * 16); /* Compute scale based on nominal tile count vertically (ycnt * 16) */
|
|
for(int i = 0; i < imgids.length; i++) {
|
|
if(imgids[i] <= 0) continue;
|
|
CustomTileRec ctr = recs.get(i);
|
|
if(ctr == null) continue;
|
|
int[] tile = new int[16 * 16 * mult * mult]; /* Make image */
|
|
copySubimageFromImage(img_id, ctr.srcx * mult, ctr.srcy * mult, ctr.targetx * mult, ctr.targety * mult,
|
|
ctr.width * mult, ctr.height * mult, tile, 16 * mult);
|
|
/* Put scaled result into tile buffer */
|
|
int new_argb[] = new int[native_scale*native_scale];
|
|
scaleTerrainPNGSubImage(16*mult, native_scale, tile, new_argb);
|
|
setTileARGB(imgids[i], new_argb);
|
|
}
|
|
}
|
|
|
|
private static List<CustomTileRec> bed_patches =
|
|
Arrays.asList(new CustomTileRec[] {
|
|
// TILEINDEX_BED_HEAD_TOP
|
|
new CustomTileRec(6, 6, 16, 16),
|
|
// TILEINDEX_BED_HEAD_BOTTOM
|
|
new CustomTileRec(28, 6, 16, 16),
|
|
// TILEINDEX_BED_HEAD_LEFT
|
|
new CustomTileRec(0, 6, 6, 16),
|
|
// TILEINDEX_BED_HEAD_RIGHT
|
|
new CustomTileRec(22, 6, 6, 16),
|
|
// TILEINDEX_BED_HEAD_END
|
|
new CustomTileRec(6, 0, 16, 6),
|
|
// TILEINDEX_BED_FOOT_TOP
|
|
new CustomTileRec(6, 28, 16, 16),
|
|
// TILEINDEX_BED_FOOT_BOTTOM
|
|
new CustomTileRec(28, 28, 16, 16),
|
|
// TILEINDEX_BED_FOOT_LEFT
|
|
new CustomTileRec(0, 28, 6, 16),
|
|
// TILEINDEX_BED_FOOT_RIGHT
|
|
new CustomTileRec(22, 28, 6, 16),
|
|
// TILEINDEX_BED_FOOT_END
|
|
new CustomTileRec(22, 22, 16, 6),
|
|
// TILEINDEX_BED_HEAD_LEFTLEG_1
|
|
new CustomTileRec(50, 0, 6, 6),
|
|
// TILEINDEX_BED_HEAD_LEFTLEG_2
|
|
new CustomTileRec(56, 0, 6, 6),
|
|
// TILEINDEX_BED_HEAD_RIGHTLEG_1
|
|
new CustomTileRec(50, 6, 6, 6),
|
|
// TILEINDEX_BED_HEAD_RIGHTLEG_2
|
|
new CustomTileRec(56, 6, 6, 6),
|
|
// TILEINDEX_BED_FOOT_LEFTLEG_1
|
|
new CustomTileRec(50, 12, 6, 6),
|
|
// TILEINDEX_BED_FOOT_LEFTLEG_2
|
|
new CustomTileRec(56, 12, 6, 6),
|
|
// TILEINDEX_BED_FOOT_RIGHTLEG_1
|
|
new CustomTileRec(50, 18, 6, 6),
|
|
// TILEINDEX_BED_FOOT_RIGHTLEG_2
|
|
new CustomTileRec(56, 18, 6, 6)
|
|
});
|
|
|
|
private void patchBedImages(int img_id, int[] imgids) {
|
|
patchCustomImages(img_id, imgids, bed_patches, 64, 64);
|
|
}
|
|
|
|
/* Copy texture pack */
|
|
private TexturePack(TexturePack tp) {
|
|
this.tile_argb = Arrays.copyOf(tp.tile_argb, tp.tile_argb.length);
|
|
this.native_scale = tp.native_scale;
|
|
this.ctm = tp.ctm;
|
|
this.imgs = tp.imgs;
|
|
this.blockColoring = tp.blockColoring;
|
|
}
|
|
|
|
/* Load terrain */
|
|
private void loadTerrain(boolean is_rp) throws IOException {
|
|
int i, j;
|
|
/* Load image */
|
|
ImageIO.setUseCache(false);
|
|
tile_argb = new int[MAX_TILEINDEX][];
|
|
if (is_rp) { // If resource pack (1.6+)
|
|
native_scale = 16;
|
|
/* Loop through textures - find size of first one one */
|
|
for(i = 0; i < terrain_rp_map.length; i++) {
|
|
String fn = getRPFileName(i);
|
|
if (fn == null) continue;
|
|
DynamicTileFile dtf = addonfilesbyname.get(fn);
|
|
if (dtf == null) continue;
|
|
LoadedImage li = imgs[dtf.idx+IMG_CNT];
|
|
if(li != null) {
|
|
native_scale = li.width;
|
|
break;
|
|
}
|
|
}
|
|
blank = new int[native_scale*native_scale];
|
|
/* Now, load scaled images */
|
|
for(i = 0; i < terrain_rp_map.length; i++) {
|
|
String fn = getRPFileName(i);
|
|
if (fn == null) continue;
|
|
DynamicTileFile dtf = addonfilesbyname.get(fn);
|
|
if (dtf == null) continue;
|
|
LoadedImage li = imgs[dtf.idx + IMG_CNT];
|
|
if(li != null) {
|
|
int[] buf = new int[native_scale * native_scale];
|
|
scaleTerrainPNGSubImage(li.width, native_scale, li.argb, buf);
|
|
setTileARGB(i, buf);
|
|
}
|
|
}
|
|
}
|
|
else { /* Else, use v1.5 tile files */
|
|
native_scale = 16;
|
|
/* Loop through textures - find biggest one */
|
|
for(i = 0; i < terrain_map.length; i++) {
|
|
String fn = getBlockFileName(i);
|
|
if (fn == null) continue;
|
|
DynamicTileFile dtf = addonfilesbyname.get(fn);
|
|
if (dtf == null) continue;
|
|
LoadedImage li = imgs[dtf.idx+IMG_CNT];
|
|
if(li != null) {
|
|
if(native_scale < li.width) native_scale = li.width;
|
|
}
|
|
}
|
|
blank = new int[native_scale*native_scale];
|
|
/* Now, load scaled images */
|
|
for(i = 0; i < terrain_map.length; i++) {
|
|
String fn = getBlockFileName(i);
|
|
if (fn == null) continue;
|
|
DynamicTileFile dtf = addonfilesbyname.get(fn);
|
|
if (dtf == null) continue;
|
|
LoadedImage li = imgs[dtf.idx + IMG_CNT];
|
|
if(li != null) {
|
|
int[] buf = new int[native_scale * native_scale];
|
|
scaleTerrainPNGSubImage(li.width, native_scale, li.argb, buf);
|
|
setTileARGB(i, buf);
|
|
}
|
|
}
|
|
}
|
|
/* Now, build redstone textures with active wire color (since we're not messing with that) */
|
|
Color tc = new Color();
|
|
int[] red_nsew_tone = getTileARGB(TILEINDEX_REDSTONE_NSEW_TONE);
|
|
int[] red_nsew = getTileARGB(TILEINDEX_REDSTONE_NSEW);
|
|
int[] red_ew_tone = getTileARGB(TILEINDEX_REDSTONE_EW_TONE);
|
|
int[] red_ew = getTileARGB(TILEINDEX_REDSTONE_EW);
|
|
|
|
for(i = 0; i < native_scale*native_scale; i++) {
|
|
if(red_nsew_tone[i] != 0) {
|
|
/* Overlay NSEW redstone texture with toned wire color */
|
|
tc.setARGB(red_nsew_tone[i]);
|
|
tc.blendColor(0xFFC00000); /* Blend in red */
|
|
red_nsew[i] = tc.getARGB();
|
|
}
|
|
if(red_ew_tone[i] != 0) {
|
|
/* Overlay NSEW redstone texture with toned wire color */
|
|
tc.setARGB(red_ew_tone[i]);
|
|
tc.blendColor(0xFFC00000); /* Blend in red */
|
|
red_ew[i] = tc.getARGB();
|
|
}
|
|
}
|
|
/* Build extended piston side texture - take top 1/4 of piston side, use to make piston extension */
|
|
int[] buf = new int[native_scale*native_scale];
|
|
setTileARGB(TILEINDEX_PISTONEXTSIDE, buf);
|
|
int[] piston_side = getTileARGB(TILEINDEX_PISTONSIDE);
|
|
System.arraycopy(piston_side, 0, buf, 0, native_scale * native_scale / 4);
|
|
for(i = 0; i < native_scale/4; i++) {
|
|
for(j = 0; j < (3*native_scale/4); j++) {
|
|
buf[native_scale*(native_scale/4 + j) + (3*native_scale/8 + i)] = piston_side[native_scale*i + j];
|
|
}
|
|
}
|
|
/* Build piston side while extended (cut off top 1/4, replace with rotated top for extension */
|
|
buf = new int[native_scale*native_scale];
|
|
setTileARGB(TILEINDEX_PISTONSIDE_EXT, buf);
|
|
System.arraycopy(piston_side, native_scale*native_scale/4, buf, native_scale*native_scale/4,
|
|
3 * native_scale * native_scale / 4); /* Copy bottom 3/4 */
|
|
for(i = 0; i < native_scale/4; i++) {
|
|
for(j = 3*native_scale/4; j < native_scale; j++) {
|
|
buf[native_scale*(j - 3*native_scale/4) + (3*native_scale/8 + i)] =
|
|
piston_side[native_scale*i + j];
|
|
}
|
|
}
|
|
/* Build glass pane top in NSEW config (we use model to clip it) */
|
|
buf = new int[native_scale*native_scale];
|
|
setTileARGB(TILEINDEX_PANETOP_X, buf);
|
|
int[] glasspanetop = getTileARGB(TILEINDEX_GLASSPANETOP);
|
|
System.arraycopy(glasspanetop, 0, buf, 0, native_scale*native_scale);
|
|
for(i = native_scale*7/16; i < native_scale*9/16; i++) {
|
|
for(j = 0; j < native_scale; j++) {
|
|
buf[native_scale*i + j] = buf[native_scale*j + i];
|
|
}
|
|
}
|
|
/* Build air frame with eye overlay */
|
|
buf = new int[native_scale*native_scale];
|
|
setTileARGB(TILEINDEX_AIRFRAME_EYE, buf);
|
|
int[] airframe = getTileARGB(TILEINDEX_AIRFRAME);
|
|
int[] eyeofender = getTileARGB(TILEINDEX_EYEOFENDER);
|
|
System.arraycopy(airframe, 0, buf, 0, native_scale*native_scale);
|
|
for(i = native_scale/4; i < native_scale*3/4; i++) {
|
|
for(j = native_scale/4; j < native_scale*3/4; j++) {
|
|
buf[native_scale*i + j] = eyeofender[native_scale*i + j];
|
|
}
|
|
}
|
|
/* Build white tile */
|
|
buf = new int[native_scale*native_scale];
|
|
setTileARGB(TILEINDEX_WHITE, buf);
|
|
Arrays.fill(buf, 0xFFFFFFFF);
|
|
}
|
|
|
|
/* Load image into image array */
|
|
private void loadImage(InputStream is, int idx, String fname, String modid) throws IOException {
|
|
BufferedImage img = null;
|
|
/* Load image */
|
|
if(is != null) {
|
|
ImageIO.setUseCache(false);
|
|
try {
|
|
img = ImageIO.read(is);
|
|
} catch (IOException iox) {
|
|
}
|
|
if (img == null) {
|
|
Log.warning(String.format("Error loading image %s from module %s", fname, modid));
|
|
}
|
|
}
|
|
if(idx >= imgs.length) {
|
|
LoadedImage[] newimgs = new LoadedImage[idx+1];
|
|
System.arraycopy(imgs, 0, newimgs, 0, imgs.length);
|
|
imgs = newimgs;
|
|
}
|
|
imgs[idx] = new LoadedImage();
|
|
if (img != null) {
|
|
imgs[idx].width = img.getWidth();
|
|
imgs[idx].height = img.getHeight();
|
|
imgs[idx].argb = new int[imgs[idx].width * imgs[idx].height];
|
|
img.getRGB(0, 0, imgs[idx].width, imgs[idx].height, imgs[idx].argb, 0, imgs[idx].width);
|
|
img.flush();
|
|
imgs[idx].isLoaded = true;
|
|
}
|
|
else { // Pad with blank image
|
|
imgs[idx].width = 16;
|
|
imgs[idx].height = 16;
|
|
imgs[idx].argb = new int[imgs[idx].width * imgs[idx].height];
|
|
}
|
|
imgs[idx].fname = fname;
|
|
imgs[idx].modid = modid;
|
|
}
|
|
|
|
|
|
/* Process dynamic texture files, and patch into terrain_argb */
|
|
private void processDynamicImage(int idx, TileFileFormat format) {
|
|
DynamicTileFile dtf = addonfiles.get(idx); /* Get tile file definition */
|
|
LoadedImage li = imgs[idx+IMG_CNT];
|
|
if (li == null) return;
|
|
|
|
switch(format) {
|
|
case GRID: /* If grid format tile file */
|
|
int dim = li.width / dtf.tilecnt_x; /* Dimension of each tile */
|
|
int dim2 = li.height / dtf.tilecnt_y;
|
|
if (dim2 < dim) dim = dim2;
|
|
int old_argb[] = new int[dim*dim];
|
|
for(int x = 0; x < dtf.tilecnt_x; x++) {
|
|
for(int y = 0; y < dtf.tilecnt_y; y++) {
|
|
int tileidx = dtf.tile_to_dyntile[y*dtf.tilecnt_x + x];
|
|
if (tileidx < 0) continue;
|
|
if((tileidx >= terrain_map.length) || (terrain_map[tileidx] == null)) { /* dynamic ID? */
|
|
/* Copy source tile */
|
|
for(int j = 0; j < dim; j++) {
|
|
System.arraycopy(li.argb, (y*dim+j)*li.width + (x*dim), old_argb, j*dim, dim);
|
|
}
|
|
/* Rescale to match rest of terrain PNG */
|
|
int new_argb[] = new int[native_scale*native_scale];
|
|
scaleTerrainPNGSubImage(dim, native_scale, old_argb, new_argb);
|
|
setTileARGB(tileidx, new_argb);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case CHEST:
|
|
patchChestImages(idx+IMG_CNT, dtf.tile_to_dyntile[TILEINDEX_CHEST_TOP], dtf.tile_to_dyntile[TILEINDEX_CHEST_BOTTOM], dtf.tile_to_dyntile[TILEINDEX_CHEST_FRONT], dtf.tile_to_dyntile[TILEINDEX_CHEST_BACK], dtf.tile_to_dyntile[TILEINDEX_CHEST_LEFT], dtf.tile_to_dyntile[TILEINDEX_CHEST_RIGHT]);
|
|
break;
|
|
case BIGCHEST:
|
|
patchLargeChestImages(idx+IMG_CNT, dtf.tile_to_dyntile[TILEINDEX_BIGCHEST_TOPRIGHT], dtf.tile_to_dyntile[TILEINDEX_BIGCHEST_TOPLEFT], dtf.tile_to_dyntile[TILEINDEX_BIGCHEST_BOTTOMRIGHT], dtf.tile_to_dyntile[TILEINDEX_BIGCHEST_BOTTOMLEFT], dtf.tile_to_dyntile[TILEINDEX_BIGCHEST_RIGHT], dtf.tile_to_dyntile[TILEINDEX_BIGCHEST_LEFT], dtf.tile_to_dyntile[TILEINDEX_BIGCHEST_FRONTRIGHT], dtf.tile_to_dyntile[TILEINDEX_BIGCHEST_FRONTLEFT], dtf.tile_to_dyntile[TILEINDEX_BIGCHEST_BACKRIGHT], dtf.tile_to_dyntile[TILEINDEX_BIGCHEST_BACKLEFT]);
|
|
break;
|
|
case SIGN:
|
|
patchSignImages(idx+IMG_CNT, dtf.tile_to_dyntile[TILEINDEX_SIGN_FRONT], dtf.tile_to_dyntile[TILEINDEX_SIGN_BACK], dtf.tile_to_dyntile[TILEINDEX_SIGN_TOP], dtf.tile_to_dyntile[TILEINDEX_SIGN_BOTTOM], dtf.tile_to_dyntile[TILEINDEX_SIGN_LEFTSIDE], dtf.tile_to_dyntile[TILEINDEX_SIGN_RIGHTSIDE], dtf.tile_to_dyntile[TILEINDEX_SIGN_POSTFRONT], dtf.tile_to_dyntile[TILEINDEX_SIGN_POSTBACK], dtf.tile_to_dyntile[TILEINDEX_SIGN_POSTLEFT], dtf.tile_to_dyntile[TILEINDEX_SIGN_POSTRIGHT]);
|
|
break;
|
|
case SKIN:
|
|
patchSkinImages(idx+IMG_CNT, dtf.tile_to_dyntile[TILEINDEX_SKIN_FACEFRONT], dtf.tile_to_dyntile[TILEINDEX_SKIN_FACELEFT], dtf.tile_to_dyntile[TILEINDEX_SKIN_FACERIGHT], dtf.tile_to_dyntile[TILEINDEX_SKIN_FACEBACK], dtf.tile_to_dyntile[TILEINDEX_SKIN_FACETOP], dtf.tile_to_dyntile[TILEINDEX_SKIN_FACEBOTTOM]);
|
|
break;
|
|
case SHULKER:
|
|
patchShulkerImages(idx+IMG_CNT, dtf.tile_to_dyntile[TILEINDEX_SHULKER_TOP], dtf.tile_to_dyntile[TILEINDEX_SHULKER_BOTTOM], dtf.tile_to_dyntile[TILEINDEX_SHULKER_FRONT], dtf.tile_to_dyntile[TILEINDEX_SHULKER_BACK], dtf.tile_to_dyntile[TILEINDEX_SHULKER_LEFT], dtf.tile_to_dyntile[TILEINDEX_SHULKER_RIGHT]);
|
|
break;
|
|
case CUSTOM:
|
|
patchCustomImages(idx+IMG_CNT, dtf.tile_to_dyntile, dtf.cust, dtf.tilecnt_x, dtf.tilecnt_y);
|
|
break;
|
|
case BED:
|
|
patchBedImages(idx+IMG_CNT, dtf.tile_to_dyntile);
|
|
break;
|
|
case TILESET:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (dtf.tile_to_dyntile != null) {
|
|
for (int i = 0; i < dtf.tile_to_dyntile.length; i++) {
|
|
if (dtf.tile_to_dyntile[i] >= 0) {
|
|
if (dtf.material != null) {
|
|
materialbytileid.put(dtf.tile_to_dyntile[i], dtf.material);
|
|
}
|
|
setMatIDForTileID(dtf.filename, dtf.tile_to_dyntile[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* Load biome shading image into image array */
|
|
private void loadBiomeShadingImage(InputStream is, int idx, String fname, String modid) throws IOException {
|
|
loadImage(is, idx, fname, modid); /* Get image */
|
|
LoadedImage li = imgs[idx];
|
|
if (li.width != 256) { /* Required to be 256 x 256 */
|
|
int[] scaled = new int[256*256];
|
|
scaleTerrainPNGSubImage(li.width, 256, li.argb, scaled);
|
|
li.argb = scaled;
|
|
li.width = 256;
|
|
li.height = 256;
|
|
}
|
|
/* Get trivial color for biome-shading image */
|
|
int clr = li.argb[li.height*li.width*3/4 + li.width/2];
|
|
boolean same = true;
|
|
for(int j = 0; same && (j < li.height); j++) {
|
|
for(int i = 0; same && (i <= j); i++) {
|
|
if(li.argb[li.width*j+i] != clr)
|
|
same = false;
|
|
}
|
|
}
|
|
/* All the same - no biome lookup needed */
|
|
if(same) {
|
|
li.trivial_color = clr;
|
|
}
|
|
else { /* Else, calculate color average for lower left quadrant */
|
|
int[] clr_scale = new int[16];
|
|
scaleTerrainPNGSubImage(li.width, 4, li.argb, clr_scale);
|
|
li.trivial_color = clr_scale[9];
|
|
}
|
|
// If we didn't actually load, don't use color lookup for this (handle broken RPs like John Smith)
|
|
if (li.isLoaded == false) {
|
|
this.blockColoring.scrubValues(idx);
|
|
}
|
|
}
|
|
|
|
/* Patch image into texture table */
|
|
private void patchTextureWithImage(int image_idx, int block_idx) {
|
|
/* Now, patch in to block table */
|
|
int new_argb[] = new int[native_scale*native_scale];
|
|
scaleTerrainPNGSubImage(imgs[image_idx].width, native_scale, imgs[image_idx].argb, new_argb);
|
|
setTileARGB(block_idx, new_argb);
|
|
|
|
}
|
|
|
|
/* Get texture pack directory */
|
|
private static File getTexturePackDirectory(DynmapCore core) {
|
|
return new File(core.getDataFolder(), "texturepacks");
|
|
}
|
|
|
|
/**
|
|
* Resample terrain pack for given scale, and return copy using that scale
|
|
* @param scale - scale
|
|
* @return resampled texture pack
|
|
*/
|
|
public TexturePack resampleTexturePack(int scale) {
|
|
synchronized(scaledlock) {
|
|
if(scaled_textures == null) scaled_textures = new HashMap<Integer, TexturePack>();
|
|
TexturePack stp = scaled_textures.get(scale);
|
|
if(stp != null)
|
|
return stp;
|
|
stp = new TexturePack(this); /* Make copy */
|
|
/* Scale terrain.png, if needed */
|
|
if(stp.native_scale != scale) {
|
|
stp.native_scale = scale;
|
|
scaleTerrainPNG(stp);
|
|
}
|
|
/* Remember it */
|
|
scaled_textures.put(scale, stp);
|
|
return stp;
|
|
}
|
|
}
|
|
/**
|
|
* Scale our terrain_argb into the terrain_argb of the provided destination, matching the scale of that destination
|
|
* @param tp
|
|
*/
|
|
private void scaleTerrainPNG(TexturePack tp) {
|
|
tp.tile_argb = new int[tile_argb.length][];
|
|
/* Terrain.png is 16x16 array of images : process one at a time */
|
|
for(int idx = 0; idx < tile_argb.length; idx++) {
|
|
tp.tile_argb[idx] = new int[tp.native_scale*tp.native_scale];
|
|
scaleTerrainPNGSubImage(native_scale, tp.native_scale, getTileARGB(idx), tp.tile_argb[idx]);
|
|
}
|
|
/* Special case - some textures are used as masks - need pure alpha (00 or FF) */
|
|
makeAlphaPure(tp.tile_argb[TILEINDEX_GRASSMASK]); /* Grass side mask */
|
|
}
|
|
public static void scaleTerrainPNGSubImage(int srcscale, int destscale, int[] src_argb, int[] dest_argb) {
|
|
int nativeres = srcscale;
|
|
int res = destscale;
|
|
Color c = new Color();
|
|
/* Same size, so just copy */
|
|
if(res == nativeres) {
|
|
System.arraycopy(src_argb, 0, dest_argb, 0, dest_argb.length);
|
|
}
|
|
/* If we're scaling larger source pixels into smaller pixels, each destination pixel
|
|
* receives input from 1 or 2 source pixels on each axis
|
|
*/
|
|
else if(res > nativeres) {
|
|
int weights[] = new int[res];
|
|
int offsets[] = new int[res];
|
|
/* LCM of resolutions is used as length of line (res * nativeres)
|
|
* Each native block is (res) long, each scaled block is (nativeres) long
|
|
* Each scaled block overlaps 1 or 2 native blocks: starting with native block 'offsets[]' with
|
|
* 'weights[]' of its (res) width in the first, and the rest in the second
|
|
*/
|
|
for(int v = 0, idx = 0; v < res*nativeres; v += nativeres, idx++) {
|
|
offsets[idx] = (v/res); /* Get index of the first native block we draw from */
|
|
if((v+nativeres-1)/res == offsets[idx]) { /* If scaled block ends in same native block */
|
|
weights[idx] = nativeres;
|
|
}
|
|
else { /* Else, see how much is in first one */
|
|
weights[idx] = (offsets[idx]*res + res) - v;
|
|
}
|
|
}
|
|
/* Now, use weights and indices to fill in scaled map */
|
|
for(int y = 0; y < res; y++) {
|
|
int ind_y = offsets[y];
|
|
int wgt_y = weights[y];
|
|
for(int x = 0; x < res; x++) {
|
|
int ind_x = offsets[x];
|
|
int wgt_x = weights[x];
|
|
double accum_red = 0;
|
|
double accum_green = 0;
|
|
double accum_blue = 0;
|
|
double accum_alpha = 0;
|
|
for(int xx = 0; xx < 2; xx++) {
|
|
int wx = (xx==0)?wgt_x:(nativeres-wgt_x);
|
|
if(wx == 0) continue;
|
|
for(int yy = 0; yy < 2; yy++) {
|
|
int wy = (yy==0)?wgt_y:(nativeres-wgt_y);
|
|
if(wy == 0) continue;
|
|
/* Accumulate */
|
|
c.setARGB(src_argb[(ind_y+yy)*nativeres + ind_x + xx]);
|
|
int w = wx * wy;
|
|
double a = (double)w * (double)c.getAlpha();
|
|
accum_red += c.getRed() * a;
|
|
accum_green += c.getGreen() * a;
|
|
accum_blue += c.getBlue() * a;
|
|
accum_alpha += a;
|
|
}
|
|
}
|
|
double newalpha = accum_alpha;
|
|
if(newalpha == 0.0) newalpha = 1.0;
|
|
/* Generate weighted compnents into color */
|
|
c.setRGBA((int)(accum_red / newalpha), (int)(accum_green / newalpha),
|
|
(int)(accum_blue / newalpha), (int)(accum_alpha / (nativeres*nativeres)));
|
|
dest_argb[(y*res) + x] = c.getARGB();
|
|
}
|
|
}
|
|
}
|
|
else { /* nativeres > res */
|
|
int weights[] = new int[nativeres];
|
|
int offsets[] = new int[nativeres];
|
|
/* LCM of resolutions is used as length of line (res * nativeres)
|
|
* Each native block is (res) long, each scaled block is (nativeres) long
|
|
* Each native block overlaps 1 or 2 scaled blocks: starting with scaled block 'offsets[]' with
|
|
* 'weights[]' of its (res) width in the first, and the rest in the second
|
|
*/
|
|
for(int v = 0, idx = 0; v < res*nativeres; v += res, idx++) {
|
|
offsets[idx] = (v/nativeres); /* Get index of the first scaled block we draw to */
|
|
if((v+res-1)/nativeres == offsets[idx]) { /* If native block ends in same scaled block */
|
|
weights[idx] = res;
|
|
}
|
|
else { /* Else, see how much is in first one */
|
|
weights[idx] = (offsets[idx]*nativeres + nativeres) - v;
|
|
}
|
|
}
|
|
double accum_red[] = new double[res*res];
|
|
double accum_green[] = new double[res*res];
|
|
double accum_blue[] = new double[res*res];
|
|
double accum_alpha[] = new double[res*res];
|
|
|
|
/* Now, use weights and indices to fill in scaled map */
|
|
for(int y = 0; y < nativeres; y++) {
|
|
int ind_y = offsets[y];
|
|
int wgt_y = weights[y];
|
|
for(int x = 0; x < nativeres; x++) {
|
|
int ind_x = offsets[x];
|
|
int wgt_x = weights[x];
|
|
c.setARGB(src_argb[(y*nativeres) + x]);
|
|
for(int xx = 0; xx < 2; xx++) {
|
|
int wx = (xx==0)?wgt_x:(res-wgt_x);
|
|
if(wx == 0) continue;
|
|
for(int yy = 0; yy < 2; yy++) {
|
|
int wy = (yy==0)?wgt_y:(res-wgt_y);
|
|
if(wy == 0) continue;
|
|
double w = wx * wy;
|
|
double a = w * c.getAlpha();
|
|
accum_red[(ind_y+yy)*res + (ind_x+xx)] += c.getRed() * a;
|
|
accum_green[(ind_y+yy)*res + (ind_x+xx)] += c.getGreen() * a;
|
|
accum_blue[(ind_y+yy)*res + (ind_x+xx)] += c.getBlue() * a;
|
|
accum_alpha[(ind_y+yy)*res + (ind_x+xx)] += a;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* Produce normalized scaled values */
|
|
for(int y = 0; y < res; y++) {
|
|
for(int x = 0; x < res; x++) {
|
|
int off = (y*res) + x;
|
|
double aa = accum_alpha[off];
|
|
if(aa == 0.0) aa = 1.0;
|
|
c.setRGBA((int)(accum_red[off]/aa), (int)(accum_green[off]/aa),
|
|
(int)(accum_blue[off]/aa), (int)(accum_alpha[off] / (nativeres*nativeres)));
|
|
dest_argb[y*res + x] = c.getARGB();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private static void addFiles(List<String> tsfiles, List<String> txfiles, File dir, String path) {
|
|
File[] listfiles = dir.listFiles();
|
|
if(listfiles == null) return;
|
|
for(File f : listfiles) {
|
|
String fn = f.getName();
|
|
if(fn.equals(".") || (fn.equals(".."))) continue;
|
|
if(f.isFile()) {
|
|
if(fn.endsWith("-texture.txt")) {
|
|
txfiles.add(path + fn);
|
|
}
|
|
if(fn.endsWith("-tilesets.txt")) {
|
|
tsfiles.add(path + fn);
|
|
}
|
|
}
|
|
else if(f.isDirectory()) {
|
|
addFiles(tsfiles, txfiles, f, path + f.getName() + "/");
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Load texture pack mappings
|
|
* @param core - core object
|
|
* @param config - configuration for texture mapping
|
|
*/
|
|
public static void loadTextureMapping(DynmapCore core, ConfigurationNode config) {
|
|
File datadir = core.getDataFolder();
|
|
/* Start clean with texture packs - need to be loaded after mapping */
|
|
resetFiles(core);
|
|
/* Initialize map with blank map for all entries */
|
|
HDBlockStateTextureMap.initializeTable();
|
|
/* Load block textures (0-N) */
|
|
int i = 0;
|
|
boolean done = false;
|
|
InputStream in = null;
|
|
while (!done) {
|
|
in = TexturePack.class.getResourceAsStream("/texture_" + i + ".txt");
|
|
if(in != null) {
|
|
loadTextureFile(in, "texture_" + i + ".txt", config, core, "core");
|
|
if(in != null) { try { in.close(); } catch (IOException x) {} in = null; }
|
|
}
|
|
else {
|
|
done = true;
|
|
}
|
|
i++;
|
|
}
|
|
// Check mods to see if texture files defined there
|
|
for (String modid : core.getServer().getModList()) {
|
|
File f = core.getServer().getModContainerFile(modid); // Get mod file
|
|
if ((f != null) && f.isFile()) {
|
|
ZipFile zf = null;
|
|
in = null;
|
|
try {
|
|
zf = new ZipFile(f);
|
|
String fn = "assets/" + modid.toLowerCase() + "/dynmap-texture.txt";
|
|
ZipEntry ze = zf.getEntry(fn);
|
|
if (ze != null) {
|
|
in = zf.getInputStream(ze);
|
|
loadTextureFile(in, fn, config, core, modid);
|
|
loadedmods.add(modid); // Add to set: prevent others definitions for same mod
|
|
}
|
|
} catch (ZipException e) {
|
|
} catch (IOException e) {
|
|
} finally {
|
|
if (in != null) {
|
|
try { in.close(); } catch (IOException e) { }
|
|
in = null;
|
|
}
|
|
if (zf != null) {
|
|
try { zf.close(); } catch (IOException e) { }
|
|
zf = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Load external tile sets
|
|
File renderdir = new File(datadir, "renderdata");
|
|
ArrayList<String> tsfiles = new ArrayList<String>();
|
|
ArrayList<String> txfiles = new ArrayList<String>();
|
|
addFiles(tsfiles, txfiles, renderdir, "");
|
|
for(String fname : tsfiles) {
|
|
File custom = new File(renderdir, fname);
|
|
if(custom.canRead()) {
|
|
try {
|
|
in = new FileInputStream(custom);
|
|
loadTileSetsFile(in, custom.getPath(), config, core, HDBlockModels.getModIDFromFileName(fname));
|
|
} catch (IOException iox) {
|
|
Log.severe("Error loading " + custom.getPath() + " - " + iox);
|
|
} finally {
|
|
if(in != null) { try { in.close(); } catch (IOException x) {} in = null; }
|
|
}
|
|
}
|
|
}
|
|
// Load external texture files (before internals, to allow them to override them)
|
|
for(String fname : txfiles) {
|
|
File custom = new File(renderdir, fname);
|
|
if(custom.canRead()) {
|
|
try {
|
|
in = new FileInputStream(custom);
|
|
loadTextureFile(in, custom.getPath(), config, core, HDBlockModels.getModIDFromFileName(fname));
|
|
} catch (IOException iox) {
|
|
Log.severe("Error loading " + custom.getPath() + " - " + iox);
|
|
} finally {
|
|
if(in != null) { try { in.close(); } catch (IOException x) {} in = null; }
|
|
}
|
|
}
|
|
}
|
|
|
|
// Load internal texture files (last, so that others can override)
|
|
ZipFile zf = null;
|
|
try {
|
|
zf = new ZipFile(core.getPluginJarFile());
|
|
Enumeration<? extends ZipEntry> e = zf.entries();
|
|
while (e.hasMoreElements()) {
|
|
ZipEntry ze = e.nextElement();
|
|
String n = ze.getName();
|
|
if (!n.startsWith("renderdata/")) continue;
|
|
if (!n.endsWith("-texture.txt")) continue;
|
|
in = zf.getInputStream(ze);
|
|
if (in != null) {
|
|
loadTextureFile(in, n, config, core, HDBlockModels.getModIDFromFileName(n));
|
|
try { in.close(); } catch (IOException x) { in = null; }
|
|
}
|
|
}
|
|
} catch (IOException iox) {
|
|
Log.severe("Error processing texture files");
|
|
} finally {
|
|
if (in != null) {
|
|
try { in.close(); } catch (IOException iox) {}
|
|
in = null;
|
|
}
|
|
if (zf != null) {
|
|
try { zf.close(); } catch (IOException iox) {}
|
|
zf = null;
|
|
}
|
|
}
|
|
/* Finish processing of texture maps */
|
|
processTextureMaps();
|
|
/* Check integrity of texture mappings versus models */
|
|
for (int gidx = 0; gidx < DynmapBlockState.getGlobalIndexMax(); gidx++) {
|
|
DynmapBlockState blk = DynmapBlockState.getStateByGlobalIndex(gidx);
|
|
if (blk.isAir()) continue;
|
|
HDBlockStateTextureMap tm = HDBlockStateTextureMap.getByBlockState(blk);
|
|
if (tm == HDBlockStateTextureMap.BLANK) {
|
|
Log.verboseinfo("Block " + blk + " - no texture mapping");
|
|
}
|
|
int cnt = HDBlockModels.getNeededTextureCount(blk);
|
|
if(cnt > tm.faces.length){
|
|
Log.severe("Block " + blk + " - not enough textures for faces (" + cnt + " > " + tm.faces.length + ")");
|
|
tm.resizeFaces(cnt);
|
|
}
|
|
}
|
|
// Check to see if any blocks exist without corresponding mappings
|
|
if (core.dumpMissingBlocks()) {
|
|
String missing = "";
|
|
/* Check integrity of texture mappings versus models */
|
|
for (int gidx = 0; gidx < DynmapBlockState.getGlobalIndexMax(); gidx++) {
|
|
DynmapBlockState blk = DynmapBlockState.getStateByGlobalIndex(gidx);
|
|
if (!blk.isNotAir()) continue;
|
|
if (blk.stateIndex != 0) continue;
|
|
boolean blank = true;
|
|
for (int stateid = 0; blank && (stateid < blk.getStateCount()); stateid++) {
|
|
DynmapBlockState blk2 = blk.getState(stateid);
|
|
HDBlockStateTextureMap tm = HDBlockStateTextureMap.getByBlockState(blk2);
|
|
if (tm != HDBlockStateTextureMap.BLANK) {
|
|
blank = false;
|
|
}
|
|
}
|
|
if (blank) {
|
|
missing += blk.blockName + "\n";
|
|
}
|
|
}
|
|
if (missing.length() > 0) {
|
|
Log.warning("Blocks missing texture definition:\n" + missing);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static String getBlockName(String modid, String val) throws NumberFormatException {
|
|
char c = val.charAt(0);
|
|
if(Character.isLetter(c) || (c == '%') || (c == '&')) {
|
|
if ((c == '%') || (c == '&')) {
|
|
val = val.substring(1);
|
|
}
|
|
int off = val.indexOf('+');
|
|
if (off > 0) {
|
|
val = val.substring(0, off);
|
|
}
|
|
if (val.indexOf(':') < 0) {
|
|
val = modid + ":" + val;
|
|
}
|
|
return val;
|
|
}
|
|
else {
|
|
throw new NumberFormatException("invalid ID - " + val);
|
|
}
|
|
}
|
|
|
|
private static Integer getIntValue(Map<String,Integer> vars, String val) throws NumberFormatException {
|
|
char c = val.charAt(0);
|
|
if(Character.isLetter(c) || (c == '%') || (c == '&')) {
|
|
int off = val.indexOf('+');
|
|
int offset = 0;
|
|
if (off > 0) {
|
|
offset = Integer.valueOf(val.substring(off+1));
|
|
val = val.substring(0, off);
|
|
}
|
|
Integer v = vars.get(val);
|
|
if(v == null) {
|
|
if ((c == '%') || (c == '&')) {
|
|
vars.put(val, 0);
|
|
v = 0;
|
|
}
|
|
else {
|
|
throw new NumberFormatException("invalid ID - " + val);
|
|
}
|
|
}
|
|
if((offset != 0) && (v.intValue() > 0))
|
|
v = v.intValue() + offset;
|
|
return v;
|
|
}
|
|
else {
|
|
return Integer.valueOf(val);
|
|
}
|
|
}
|
|
|
|
private static int parseTextureIndex(HashMap<String,Integer> filetoidx, int srctxtid, String val) throws NumberFormatException {
|
|
int off = val.indexOf(':');
|
|
int txtid = -1;
|
|
if(off > 0) {
|
|
String txt = val.substring(off+1);
|
|
if(filetoidx.containsKey(txt)) {
|
|
srctxtid = filetoidx.get(txt);
|
|
}
|
|
else {
|
|
throw new NumberFormatException("Unknown attribute: " + txt);
|
|
}
|
|
txtid = Integer.valueOf(val.substring(0, off));
|
|
}
|
|
else {
|
|
txtid = Integer.valueOf(val);
|
|
}
|
|
/* Shift function code from x1000 to x1000000 for internal processing */
|
|
int funcid = (txtid / COLORMOD_MULT_FILE);
|
|
txtid = txtid - (COLORMOD_MULT_FILE * funcid);
|
|
/* If we have source texture, need to map values to dynamic ids */
|
|
if((srctxtid >= 0) && (txtid >= 0)) {
|
|
/* Map to assigned ID in global tile table: preserve modifier */
|
|
txtid =findOrAddDynamicTile(srctxtid, txtid);
|
|
}
|
|
if(srctxtid == TXTID_INVALID) {
|
|
throw new NumberFormatException("Invalid texture ID: no default terrain.png: " + val);
|
|
}
|
|
return txtid + (COLORMOD_MULT_INTERNAL * funcid);
|
|
}
|
|
/**
|
|
* Load texture pack mappings from tilesets.txt file
|
|
*/
|
|
private static void loadTileSetsFile(InputStream txtfile, String txtname, ConfigurationNode config, DynmapCore core, String blockset) {
|
|
LineNumberReader rdr = null;
|
|
DynamicTileFile tfile = null;
|
|
|
|
try {
|
|
String line;
|
|
rdr = new LineNumberReader(new BufferedReader(new InputStreamReader(txtfile)));
|
|
while((line = rdr.readLine()) != null) {
|
|
if(line.startsWith("#")) {
|
|
}
|
|
else if(line.startsWith("tileset:")) { /* Start of tileset definition */
|
|
line = line.substring(line.indexOf(':')+1);
|
|
int xdim = 16, ydim = 16;
|
|
String fname = null;
|
|
String setdir = null;
|
|
String[] toks = line.split(",");
|
|
for(String tok : toks) {
|
|
String[] v = tok.split("=");
|
|
if(v.length < 2) continue;
|
|
if(v[0].equals("xcount")) {
|
|
xdim = Integer.parseInt(v[1]);
|
|
}
|
|
else if(v[0].equals("ycount")) {
|
|
ydim = Integer.parseInt(v[1]);
|
|
}
|
|
else if(v[0].equals("setdir")) {
|
|
setdir = v[1];
|
|
}
|
|
else if(v[0].equals("filename")) {
|
|
fname = v[1];
|
|
}
|
|
}
|
|
if ((fname != null) && (setdir != null)) {
|
|
/* Register tile file */
|
|
int fid = findOrAddDynamicTileFile(fname, null, xdim, ydim, TileFileFormat.TILESET, new String[0]);
|
|
tfile = addonfiles.get(fid);
|
|
if (tfile == null) {
|
|
Log.severe("Error registering tile set " + fname + " at " + rdr.getLineNumber() + " of " + txtname);
|
|
return;
|
|
}
|
|
/* Initialize tile name map and set directory path */
|
|
tfile.tilenames = new String[tfile.tile_to_dyntile.length];
|
|
}
|
|
else {
|
|
Log.severe("Error defining tile set at " + rdr.getLineNumber() + " of " + txtname);
|
|
return;
|
|
}
|
|
}
|
|
else if(Character.isDigit(line.charAt(0))) { /* Starts with digit? tile mapping */
|
|
int split = line.indexOf('-'); /* Find first dash */
|
|
if(split < 0) continue;
|
|
String id = line.substring(0, split).trim();
|
|
String name = line.substring(split+1).trim();
|
|
String[] coord = id.split(",");
|
|
int idx = -1;
|
|
if(coord.length == 2) { /* If x,y */
|
|
idx = (Integer.parseInt(coord[1]) * tfile.tilecnt_x) + Integer.parseInt(coord[0]);
|
|
}
|
|
else if(coord.length == 1) { /* Just index */
|
|
idx = Integer.parseInt(coord[0]);
|
|
}
|
|
if((idx >= 0) && (idx < tfile.tilenames.length)) {
|
|
tfile.tilenames[idx] = name;
|
|
}
|
|
else {
|
|
Log.severe("Bad tile index - line " + rdr.getLineNumber() + " of " + txtname);
|
|
}
|
|
}
|
|
}
|
|
} catch (IOException iox) {
|
|
Log.severe("Error reading " + txtname + " - " + iox.toString());
|
|
} catch (NumberFormatException nfx) {
|
|
Log.severe("Format error - line " + rdr.getLineNumber() + " of " + txtname + ": " + nfx.getMessage());
|
|
} finally {
|
|
if(rdr != null) {
|
|
try {
|
|
rdr.close();
|
|
rdr = null;
|
|
} catch (IOException e) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private static final int TXTID_INVALID = -2;
|
|
private static final int TXTID_TERRAINPNG = -1;
|
|
/**
|
|
* Load texture pack mappings from texture.txt file
|
|
*/
|
|
private static void loadTextureFile(InputStream txtfile, String txtname, ConfigurationNode config, DynmapCore core, String blockset) {
|
|
LineNumberReader rdr = null;
|
|
int cnt = 0;
|
|
HashMap<String,Integer> filetoidx = new HashMap<String,Integer>();
|
|
HashMap<String,Integer> varvals = new HashMap<String,Integer>();
|
|
final String mcver = core.getDynmapPluginPlatformVersion();
|
|
boolean mod_cfg_needed = false;
|
|
boolean mod_cfg_loaded = false;
|
|
String modname = "minecraft"; // Default to minecraft base
|
|
String modversion = null;
|
|
String texturemod = null;
|
|
String texturepath = null;
|
|
boolean terrain_ok = true;
|
|
BlockStateParser bsp = new BlockStateParser();
|
|
Map<DynmapBlockState, BitSet> bsprslt;
|
|
try {
|
|
String line;
|
|
rdr = new LineNumberReader(new BufferedReader(new InputStreamReader(txtfile)));
|
|
while((line = rdr.readLine()) != null) {
|
|
boolean skip = false;
|
|
int lineNum = rdr.getLineNumber();
|
|
if ((line.length() > 0) && (line.charAt(0) == '[')) { // If version constrained like
|
|
int end = line.indexOf(']'); // Find end
|
|
if (end < 0) {
|
|
Log.severe("Format error - line " + lineNum + " of " + txtname + ": bad version limit");
|
|
return;
|
|
}
|
|
String vertst = line.substring(1, end);
|
|
String tver = mcver;
|
|
if (vertst.startsWith("mod:")) { // If mod version ranged
|
|
tver = modversion;
|
|
vertst = vertst.substring(4);
|
|
}
|
|
if (!HDBlockModels.checkVersionRange(tver, vertst)) {
|
|
skip = true;
|
|
}
|
|
line = line.substring(end+1);
|
|
}
|
|
if (line.startsWith("#") || line.startsWith(";")) {
|
|
skip = true;
|
|
}
|
|
// If we're skipping due to version restriction
|
|
if (skip) continue;
|
|
// Split off <type>:
|
|
int typeend = line.indexOf(':');
|
|
String typeid = "";
|
|
if (typeend >= 0) {
|
|
typeid = line.substring(0, typeend);
|
|
line = line.substring(typeend+1).trim();
|
|
}
|
|
if (typeid.equals("block")) {
|
|
// Parse block states
|
|
bsp.processLine(modname, line, lineNum, varvals);
|
|
|
|
int srctxtid = TXTID_TERRAINPNG;
|
|
if (!terrain_ok)
|
|
srctxtid = TXTID_INVALID; // Mark as not usable
|
|
int faces[] = new int[] { TILEINDEX_BLANK, TILEINDEX_BLANK, TILEINDEX_BLANK, TILEINDEX_BLANK, TILEINDEX_BLANK, TILEINDEX_BLANK };
|
|
int txtidx[] = new int[] { -1, -1, -1, -1, -1, -1 };
|
|
byte layers[] = null;
|
|
BlockTransparency trans = BlockTransparency.OPAQUE;
|
|
int colorMult = 0;
|
|
int blockColorIdx = -1;
|
|
boolean stdrot = false; // Legacy top/bottom rotation
|
|
CustomColorMultiplier custColorMult = null;
|
|
String[] args = line.split(",");
|
|
for(String a : args) {
|
|
String[] av = a.split("=");
|
|
if(av.length < 2) continue;
|
|
else if(av[0].equals("txtid")) {
|
|
if(filetoidx.containsKey(av[1]))
|
|
srctxtid = filetoidx.get(av[1]);
|
|
else
|
|
Log.severe("Format error - line " + lineNum + " of " + txtname + ": bad texture " + av[1]);
|
|
}
|
|
}
|
|
bsprslt = bsp.getMatchingStates();
|
|
|
|
if (bsprslt.size() > 0) {
|
|
for(String a : args) {
|
|
String[] av = a.split("=");
|
|
if(av.length < 2) continue;
|
|
if(av[0].equals("top") || av[0].equals("y-") || av[0].equals("face1")) {
|
|
faces[BlockStep.Y_MINUS.ordinal()] = parseTextureIndex(filetoidx, srctxtid, av[1]);
|
|
}
|
|
else if(av[0].equals("bottom") || av[0].equals("y+") || av[0].equals("face0")) {
|
|
faces[BlockStep.Y_PLUS.ordinal()] = parseTextureIndex(filetoidx, srctxtid, av[1]);
|
|
}
|
|
else if(av[0].equals("north") || av[0].equals("x+") || av[0].equals("face4")) {
|
|
faces[BlockStep.X_PLUS.ordinal()] = parseTextureIndex(filetoidx, srctxtid, av[1]);
|
|
}
|
|
else if(av[0].equals("south") || av[0].equals("x-") || av[0].equals("face5")) {
|
|
faces[BlockStep.X_MINUS.ordinal()] = parseTextureIndex(filetoidx, srctxtid, av[1]);
|
|
}
|
|
else if(av[0].equals("west") || av[0].equals("z-") || av[0].equals("face3")) {
|
|
faces[BlockStep.Z_MINUS.ordinal()] = parseTextureIndex(filetoidx, srctxtid, av[1]);
|
|
}
|
|
else if(av[0].equals("east") || av[0].equals("z+") || av[0].equals("face2")) {
|
|
faces[BlockStep.Z_PLUS.ordinal()] = parseTextureIndex(filetoidx, srctxtid, av[1]);
|
|
}
|
|
else if(av[0].startsWith("face")) {
|
|
int fid0, fid1;
|
|
String idrange = av[0].substring(4);
|
|
String[] ids = idrange.split("-");
|
|
if(ids.length > 1) {
|
|
fid0 = Integer.parseInt(ids[0]);
|
|
fid1 = Integer.parseInt(ids[1]);
|
|
}
|
|
else {
|
|
fid0 = fid1 = Integer.parseInt(ids[0]);
|
|
}
|
|
if((fid0 < 0) || (fid1 < fid0)) {
|
|
Log.severe("Texture mapping has invalid face index - " + av[1] + " - line " + lineNum + " of " + txtname);
|
|
return;
|
|
}
|
|
int faceToOrd[] = { BlockStep.Y_PLUS.ordinal(), BlockStep.Y_MINUS.ordinal(),
|
|
BlockStep.Z_PLUS.ordinal(), BlockStep.Z_MINUS.ordinal(),
|
|
BlockStep.X_PLUS.ordinal(), BlockStep.X_MINUS.ordinal()
|
|
};
|
|
int txtid = parseTextureIndex(filetoidx, srctxtid, av[1]);
|
|
for(int i = fid0; (i <= fid1) && (i < 6); i++) {
|
|
faces[faceToOrd[i]] = txtid;
|
|
}
|
|
}
|
|
else if(av[0].equals("allfaces")) {
|
|
int id = parseTextureIndex(filetoidx, srctxtid, av[1]);
|
|
for(int i = 0; i < 6; i++) {
|
|
faces[i] = id;
|
|
}
|
|
}
|
|
else if(av[0].equals("allsides")) {
|
|
int id = parseTextureIndex(filetoidx, srctxtid, av[1]);
|
|
faces[BlockStep.X_PLUS.ordinal()] = id;
|
|
faces[BlockStep.X_MINUS.ordinal()] = id;
|
|
faces[BlockStep.Z_PLUS.ordinal()] = id;
|
|
faces[BlockStep.Z_MINUS.ordinal()] = id;
|
|
}
|
|
else if(av[0].equals("topbottom")) {
|
|
faces[BlockStep.Y_MINUS.ordinal()] =
|
|
faces[BlockStep.Y_PLUS.ordinal()] = parseTextureIndex(filetoidx, srctxtid, av[1]);
|
|
}
|
|
else if(av[0].equals("blockcolor")) {
|
|
if(filetoidx.containsKey(av[1]))
|
|
blockColorIdx = filetoidx.get(av[1]);
|
|
else
|
|
Log.severe("Format error - line " + lineNum + " of " + txtname + ": bad texture " + av[1]);
|
|
}
|
|
else if(av[0].startsWith("patch")) {
|
|
int patchid0, patchid1;
|
|
String idrange = av[0].substring(5);
|
|
String[] ids = idrange.split("-");
|
|
if(ids.length > 1) {
|
|
patchid0 = Integer.parseInt(ids[0]);
|
|
patchid1 = Integer.parseInt(ids[1]);
|
|
}
|
|
else {
|
|
patchid0 = patchid1 = Integer.parseInt(ids[0]);
|
|
}
|
|
if((patchid0 < 0) || (patchid1 < patchid0)) {
|
|
Log.severe("Texture mapping has invalid patch index - " + av[1] + " - line " + lineNum + " of " + txtname);
|
|
return;
|
|
}
|
|
if(faces.length <= patchid1) {
|
|
int[] newfaces = new int[patchid1+1];
|
|
Arrays.fill(newfaces, TILEINDEX_BLANK);
|
|
System.arraycopy(faces, 0, newfaces, 0, faces.length);
|
|
faces = newfaces;
|
|
int[] newtxtidx = new int[patchid1+1];
|
|
Arrays.fill(newtxtidx, -1);
|
|
System.arraycopy(txtidx, 0, newtxtidx, 0, txtidx.length);
|
|
txtidx = newtxtidx;
|
|
}
|
|
int txtid = parseTextureIndex(filetoidx, srctxtid, av[1]);
|
|
for(int i = patchid0; i <= patchid1; i++) {
|
|
faces[i] = txtid;
|
|
}
|
|
}
|
|
else if(av[0].equals("transparency")) {
|
|
trans = BlockTransparency.valueOf(av[1]);
|
|
if(trans == null) {
|
|
trans = BlockTransparency.OPAQUE;
|
|
Log.severe("Texture mapping has invalid transparency setting - " + av[1] + " - line " + lineNum + " of " + txtname);
|
|
}
|
|
/* For leaves, base on leaf transparency setting */
|
|
if(trans == BlockTransparency.LEAVES) {
|
|
if(core.getLeafTransparency())
|
|
trans = BlockTransparency.TRANSPARENT;
|
|
else
|
|
trans = BlockTransparency.OPAQUE;
|
|
}
|
|
}
|
|
else if(av[0].equals("colorMult")) {
|
|
colorMult = (int)Long.parseLong(av[1], 16);
|
|
}
|
|
else if(av[0].equals("custColorMult")) {
|
|
try {
|
|
Class<?> cls = Class.forName(av[1]);
|
|
custColorMult = (CustomColorMultiplier)cls.getDeclaredConstructor().newInstance();
|
|
} catch (Exception x) {
|
|
Log.severe("Error loading custom color multiplier - " + av[1] + ": " + x.getMessage());
|
|
}
|
|
}
|
|
else if(av[0].equals("stdrot")) {
|
|
stdrot = av[1].equals("true");
|
|
}
|
|
}
|
|
for(String a : args) {
|
|
String[] av = a.split("=");
|
|
if(av.length < 2) continue;
|
|
if (av[0].startsWith("layer")) {
|
|
if(layers == null) {
|
|
layers = new byte[faces.length];
|
|
Arrays.fill(layers, (byte)-1);
|
|
}
|
|
String v[] = av[0].substring(5).split("-");
|
|
int id1, id2;
|
|
id1 = id2 = Integer.parseInt(v[0]);
|
|
if(v.length > 1) {
|
|
id2 = Integer.parseInt(v[1]);
|
|
}
|
|
byte val = (byte)Integer.parseInt(av[1]);
|
|
for(; id1 <= id2; id1++) {
|
|
layers[id1] = val;
|
|
}
|
|
}
|
|
}
|
|
/* If we have everything, build block */
|
|
if (bsprslt.size() > 0) {
|
|
Integer colorIndex = (blockColorIdx >= 0)?(blockColorIdx + IMG_CNT):null;
|
|
HDBlockStateTextureMap map = new HDBlockStateTextureMap(faces, layers, colorMult, custColorMult, blockset, stdrot, colorIndex, trans);
|
|
map.addToTable(bsprslt);
|
|
cnt++;
|
|
}
|
|
else {
|
|
Log.severe("Texture mapping missing required parameters = line " + lineNum + " of " + txtname);
|
|
}
|
|
}
|
|
}
|
|
else if (typeid.equals("copyblock")) {
|
|
// Parse block states
|
|
bsp.processLine(modname, line, lineNum, varvals);
|
|
|
|
String[] args = line.split(",");
|
|
String srcname = null;
|
|
int srcmeta = 0;
|
|
BlockTransparency trans = null;
|
|
for(String a : args) {
|
|
String[] av = a.split("=");
|
|
if(av.length < 2) continue;
|
|
if(av[0].equals("srcid")) {
|
|
srcname = getBlockName(modname, av[1]);
|
|
}
|
|
else if(av[0].equals("srcmeta")) {
|
|
srcmeta = getIntValue(varvals,av[1]);
|
|
}
|
|
else if(av[0].equals("transparency")) {
|
|
trans = BlockTransparency.valueOf(av[1]);
|
|
if(trans == null) {
|
|
trans = BlockTransparency.OPAQUE;
|
|
Log.severe("Texture mapping has invalid transparency setting - " + av[1] + " - line " + lineNum + " of " + txtname);
|
|
}
|
|
/* For leaves, base on leaf transparency setting */
|
|
if(trans == BlockTransparency.LEAVES) {
|
|
if(core.getLeafTransparency())
|
|
trans = BlockTransparency.TRANSPARENT;
|
|
else
|
|
trans = BlockTransparency.OPAQUE;
|
|
}
|
|
}
|
|
}
|
|
/* If we have everything, build block */
|
|
bsprslt = bsp.getMatchingStates();
|
|
|
|
if ((bsprslt.size() > 0) && (srcname != null)) {
|
|
DynmapBlockState srcblk = DynmapBlockState.getStateByNameAndIndex(srcname, srcmeta);
|
|
HDBlockStateTextureMap map = null;
|
|
if (srcblk != null) map = HDBlockStateTextureMap.getByBlockState(srcblk);
|
|
if (map == null) {
|
|
Log.severe("Copy of texture mapping failed = line " + lineNum + " of " + txtname);
|
|
}
|
|
else {
|
|
for (DynmapBlockState bblk : bsprslt.keySet()) {
|
|
BitSet stateids = bsprslt.get(bblk);
|
|
for (int stateid = stateids.nextSetBit(0); stateid >= 0; stateid = stateids.nextSetBit(stateid+1)) {
|
|
DynmapBlockState dblk2 = bblk.getState(stateid);
|
|
HDBlockStateTextureMap.copyToStateIndex(dblk2, map, trans);
|
|
}
|
|
}
|
|
cnt++;
|
|
}
|
|
}
|
|
else {
|
|
Log.severe("Texture mapping copy missing required parameters = line " + lineNum + " of " + txtname);
|
|
}
|
|
}
|
|
else if (typeid.equals("addtotexturemap")) {
|
|
int srctxtid = -1;
|
|
String mapid = null;
|
|
String[] args = line.split(",");
|
|
for (String a : args) {
|
|
String[] av = a.split("=");
|
|
if(av.length < 2) continue;
|
|
else if(av[0].equals("txtid")) {
|
|
if(filetoidx.containsKey(av[1]))
|
|
srctxtid = filetoidx.get(av[1]);
|
|
else
|
|
Log.severe("Format error - line " + lineNum + " of " + txtname);
|
|
}
|
|
else if(av[0].equals("mapid")) {
|
|
mapid = av[1];
|
|
}
|
|
}
|
|
if(mapid != null) {
|
|
for(String a : args) {
|
|
String[] av = a.split("=");
|
|
if(av.length < 2) continue;
|
|
if(av[0].startsWith("key:")) {
|
|
Integer key = getIntValue(varvals, av[0].substring(4));
|
|
if ((key != null) && (key > 0)) {
|
|
addTextureByKey(mapid, key, parseTextureIndex(filetoidx, srctxtid, av[1]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
Log.severe("Missing mapid - line " + lineNum + " of " + txtname);
|
|
}
|
|
}
|
|
else if (typeid.equals("texturemap")) {
|
|
// Parse block states
|
|
bsp.processLine(modname, line, lineNum, varvals);
|
|
|
|
String mapid = null;
|
|
BlockTransparency trans = BlockTransparency.OPAQUE;
|
|
int colorMult = 0;
|
|
CustomColorMultiplier custColorMult = null;
|
|
String[] args = line.split(",");
|
|
for(String a : args) {
|
|
String[] av = a.split("=");
|
|
if(av.length < 2) continue;
|
|
if(av[0].equals("mapid")) {
|
|
mapid = av[1];
|
|
}
|
|
else if(av[0].equals("transparency")) {
|
|
trans = BlockTransparency.valueOf(av[1]);
|
|
if(trans == null) {
|
|
trans = BlockTransparency.OPAQUE;
|
|
Log.severe("Texture mapping has invalid transparency setting - " + av[1] + " - line " + lineNum + " of " + txtname);
|
|
}
|
|
/* For leaves, base on leaf transparency setting */
|
|
if(trans == BlockTransparency.LEAVES) {
|
|
if(core.getLeafTransparency())
|
|
trans = BlockTransparency.TRANSPARENT;
|
|
else
|
|
trans = BlockTransparency.OPAQUE;
|
|
}
|
|
}
|
|
else if(av[0].equals("colorMult")) {
|
|
colorMult = Integer.valueOf(av[1], 16);
|
|
}
|
|
else if(av[0].equals("custColorMult")) {
|
|
try {
|
|
Class<?> cls = Class.forName(av[1]);
|
|
custColorMult = (CustomColorMultiplier)cls.getDeclaredConstructor().newInstance();
|
|
} catch (Exception x) {
|
|
Log.severe("Error loading custom color multiplier - " + av[1] + ": " + x.getMessage());
|
|
}
|
|
}
|
|
}
|
|
/* If we have everything, build texture map */
|
|
bsprslt = bsp.getMatchingStates();
|
|
if ((bsprslt.size() > 0) && (mapid != null)) {
|
|
addTextureIndex(mapid, bsprslt, trans, colorMult, custColorMult, blockset);
|
|
}
|
|
else {
|
|
Log.severe("Texture map missing required parameters = line " + lineNum + " of " + txtname);
|
|
}
|
|
}
|
|
else if (typeid.equals("texturefile") || typeid.equals("texture")) {
|
|
boolean istxt = typeid.equals("texture");
|
|
String[] args = line.split(",");
|
|
int xdim = 16, ydim = 16;
|
|
String fname = null;
|
|
String id = null;
|
|
TileFileFormat fmt = TileFileFormat.GRID;
|
|
MaterialType mt = null;
|
|
if(istxt) {
|
|
xdim = ydim = 1;
|
|
fmt = TileFileFormat.GRID;
|
|
}
|
|
for(String arg : args) {
|
|
String[] aval = arg.split("=");
|
|
if(aval.length < 2)
|
|
continue;
|
|
if(aval[0].equals("id")) {
|
|
id = aval[1];
|
|
if (fname == null) {
|
|
if (texturepath != null) {
|
|
fname = texturepath + id + ".png";
|
|
}
|
|
else if (texturemod != null) {
|
|
fname = "mods/" + texturemod + "/textures/blocks/" + id + ".png";
|
|
}
|
|
}
|
|
}
|
|
else if(aval[0].equals("filename"))
|
|
fname = aval[1];
|
|
else if(aval[0].equals("xcount"))
|
|
xdim = Integer.parseInt(aval[1]);
|
|
else if(aval[0].equals("ycount"))
|
|
ydim = Integer.parseInt(aval[1]);
|
|
else if(aval[0].equals("format")) {
|
|
fmt = TileFileFormat.valueOf(aval[1].toUpperCase());
|
|
if(fmt == null) {
|
|
Log.severe("Invalid format type " + aval[1] + " - line " + lineNum + " of " + txtname);
|
|
return;
|
|
}
|
|
}
|
|
else if(aval[0].equals("material")) {
|
|
mt = MaterialType.valueOf(aval[1]);
|
|
if (mt == null) {
|
|
Log.warning("Bad custom material type: " + aval[1]);
|
|
}
|
|
}
|
|
}
|
|
if((fname != null) && (id != null)) {
|
|
/* Register the file */
|
|
int fid = findOrAddDynamicTileFile(fname, modname, xdim, ydim, fmt, args);
|
|
filetoidx.put(id, fid); /* Save lookup */
|
|
if (mt != null) {
|
|
addonfiles.get(fid).material = mt;
|
|
}
|
|
}
|
|
else {
|
|
Log.severe("Format error - line " + lineNum + " of " + txtname);
|
|
return;
|
|
}
|
|
}
|
|
else if (typeid.equals("enabled")) { /* Test if texture file is enabled */
|
|
if(line.startsWith("true")) { /* We're enabled? */
|
|
/* Nothing to do - keep processing */
|
|
}
|
|
else if(line.startsWith("false")) { /* Disabled */
|
|
return; /* Quit */
|
|
}
|
|
/* If setting is not defined or false, quit */
|
|
else if(config.getBoolean(line, false) == false) {
|
|
return;
|
|
}
|
|
else {
|
|
Log.info(line + " textures enabled");
|
|
}
|
|
}
|
|
else if (typeid.equals("var")) { /* Test if variable declaration */
|
|
String args[] = line.split(",");
|
|
for(int i = 0; i < args.length; i++) {
|
|
String[] v = args[i].split("=");
|
|
if(v.length < 2) {
|
|
Log.severe("Format error - line " + lineNum + " of " + txtname);
|
|
return;
|
|
}
|
|
try {
|
|
int val = Integer.valueOf(v[1]); /* Parse default value */
|
|
int parmval = config.getInteger(v[0], val); /* Read value, with applied default */
|
|
varvals.put(v[0], parmval); /* And save value */
|
|
} catch (NumberFormatException nfx) {
|
|
Log.severe("Format error - line " + lineNum + " of " + txtname + ": " + nfx.getMessage());
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else if (typeid.equals("cfgfile")) { /* If config file */
|
|
if (!mod_cfg_loaded) {
|
|
mod_cfg_needed = true;
|
|
}
|
|
File cfgfile = new File(line);
|
|
ForgeConfigFile cfg = new ForgeConfigFile(cfgfile);
|
|
if(cfg.load()) {
|
|
cfg.addBlockIDs(varvals);
|
|
mod_cfg_needed = false;
|
|
mod_cfg_loaded = true;
|
|
}
|
|
}
|
|
else if (typeid.equals("modname")) {
|
|
String[] names = line.split(",");
|
|
boolean found = false;
|
|
for(String n : names) {
|
|
String[] ntok = n.split("[\\[\\]]");
|
|
String rng = null;
|
|
if (ntok.length > 1) {
|
|
n = ntok[0].trim();
|
|
rng = ntok[1].trim();
|
|
}
|
|
n = n.trim();
|
|
// If already supplied by mod, quit processing this file
|
|
if (loadedmods.contains(n)) {
|
|
return;
|
|
}
|
|
String modver = core.getServer().getModVersion(n);
|
|
if((modver != null) && ((rng == null) || HDBlockModels.checkVersionRange(modver, rng))) {
|
|
found = true;
|
|
Log.info(n + "[" + modver + "] textures enabled");
|
|
modname = n;
|
|
modversion = modver;
|
|
if(texturemod == null) texturemod = modname;
|
|
loadedmods.add(n);
|
|
// Prime values from block and item unique IDs
|
|
core.addModBlockItemIDs(modname, varvals);
|
|
break;
|
|
}
|
|
}
|
|
if(!found) return;
|
|
}
|
|
else if (typeid.equals("texturemod")) {
|
|
texturemod = line;
|
|
}
|
|
else if (typeid.equals("texturepath")) {
|
|
texturepath = line.trim();
|
|
if (texturepath.charAt(texturepath.length()-1) != '/') {
|
|
texturepath += "/";
|
|
}
|
|
}
|
|
else if (typeid.equals("biome")) {
|
|
String args[] = line.split(",");
|
|
int id = 0;
|
|
int grasscolormult = -1;
|
|
int foliagecolormult = -1;
|
|
int watercolormult = -1;
|
|
double rain = -1.0;
|
|
double tmp = -1.0;
|
|
for(int i = 0; i < args.length; i++) {
|
|
String[] v = args[i].split("=");
|
|
if(v.length < 2) {
|
|
Log.severe("Format error - line " + lineNum + " of " + txtname);
|
|
return;
|
|
}
|
|
if(v[0].equals("id")) {
|
|
id = getIntValue(varvals, v[1]);
|
|
}
|
|
else if(v[0].equals("grassColorMult")) {
|
|
grasscolormult = Integer.valueOf(v[1], 16);
|
|
}
|
|
else if(v[0].equals("foliageColorMult")) {
|
|
foliagecolormult = Integer.valueOf(v[1], 16);
|
|
}
|
|
else if(v[0].equals("waterColorMult")) {
|
|
watercolormult = Integer.valueOf(v[1], 16);
|
|
}
|
|
else if(v[0].equals("temp")) {
|
|
tmp = Double.parseDouble(v[1]);
|
|
}
|
|
else if(v[0].equals("rain")) {
|
|
rain = Double.parseDouble(v[1]);
|
|
}
|
|
}
|
|
if(id > 0) {
|
|
BiomeMap b = BiomeMap.byBiomeID(id); /* Find biome */
|
|
if(b == null) {
|
|
Log.severe("Format error - line " + lineNum + " of " + txtname + ": " + id);
|
|
}
|
|
else {
|
|
if(foliagecolormult != -1)
|
|
b.setFoliageColorMultiplier(foliagecolormult);
|
|
if(grasscolormult != -1)
|
|
b.setGrassColorMultiplier(grasscolormult);
|
|
if(watercolormult != -1)
|
|
b.setWaterColorMultiplier(watercolormult);
|
|
if(tmp != -1.0)
|
|
b.setTemperature(tmp);
|
|
if(rain != -1.0)
|
|
b.setRainfall(rain);
|
|
}
|
|
}
|
|
}
|
|
else if (typeid.equals("version")) {
|
|
if (!HDBlockModels.checkVersionRange(mcver, line)) {
|
|
return;
|
|
}
|
|
}
|
|
else if (typeid.equals("noterrainpng")) {
|
|
if (line.startsWith("true")) {
|
|
terrain_ok = false;
|
|
}
|
|
else {
|
|
terrain_ok = true;
|
|
}
|
|
}
|
|
}
|
|
if(mod_cfg_needed) {
|
|
Log.severe("Error loading configuration file for " + modname);
|
|
}
|
|
|
|
Log.verboseinfo("Loaded " + cnt + " texture mappings from " + txtname);
|
|
} catch (IOException iox) {
|
|
Log.severe("Error reading " + txtname + " - " + iox.toString());
|
|
} catch (NumberFormatException nfx) {
|
|
Log.severe("Format error - line " + rdr.getLineNumber() + " of " + txtname + ": " + nfx.getMessage());
|
|
} finally {
|
|
if(rdr != null) {
|
|
try {
|
|
rdr.close();
|
|
rdr = null;
|
|
} catch (IOException e) {
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* Process any block aliases */
|
|
public static void handleBlockAlias() {
|
|
Set<String> aliased = MapManager.mapman.getAliasedBlocks();
|
|
for (String an : aliased) {
|
|
String newid = MapManager.mapman.getBlockAlias(an);
|
|
if (newid.equals(an) == false) {
|
|
HDBlockStateTextureMap.remapTexture(an, newid);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read color for given subblock coordinate, with given block id and data and face
|
|
* @param ps - perspective state
|
|
* @param mapiter - map iterator
|
|
* @param rslt - color result (returned with value)
|
|
* @param blk - block state
|
|
* @param lastblocktype - last block ID
|
|
* @param ss - shader state
|
|
*/
|
|
public final void readColor(final HDPerspectiveState ps, final MapIterator mapiter, final Color rslt, final DynmapBlockState blk, final DynmapBlockState lastblocktype,
|
|
final TexturePackHDShader.ShaderState ss) {
|
|
HDBlockStateTextureMap map = HDBlockStateTextureMap.getByBlockState(blk);
|
|
BlockStep laststep = ps.getLastBlockStep();
|
|
int patchid = ps.getTextureIndex(); /* See if patch index */
|
|
int textid;
|
|
int faceindex;
|
|
if(patchid >= 0) {
|
|
faceindex = patchid;
|
|
}
|
|
else {
|
|
faceindex = laststep.ordinal();
|
|
}
|
|
try {
|
|
textid = map.faces[faceindex];
|
|
} catch (ArrayIndexOutOfBoundsException aioob) {
|
|
textid = -1;
|
|
}
|
|
if (ctm != null) {
|
|
int mod = 0;
|
|
if(textid >= COLORMOD_MULT_INTERNAL) {
|
|
mod = (textid / COLORMOD_MULT_INTERNAL) * COLORMOD_MULT_INTERNAL;
|
|
textid -= mod;
|
|
}
|
|
textid = mod + ctm.mapTexture(mapiter, blk, laststep, textid, ss);
|
|
}
|
|
readColor(ps, mapiter, rslt, blk, lastblocktype, ss, map, laststep, patchid, textid, map.stdrotate);
|
|
if(map.layers != null) { /* If layered */
|
|
/* While transparent and more layers */
|
|
while(rslt.isTransparent() && (map.layers[faceindex] >= 0)) {
|
|
faceindex = map.layers[faceindex];
|
|
textid = map.faces[faceindex];
|
|
readColor(ps, mapiter, rslt, blk, lastblocktype, ss, map, laststep, patchid, textid, map.stdrotate);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read color for given subblock coordinate, with given block id and data and face
|
|
*/
|
|
private final void readColor(final HDPerspectiveState ps, final MapIterator mapiter, final Color rslt, final DynmapBlockState blk, final DynmapBlockState lastblocktype,
|
|
final TexturePackHDShader.ShaderState ss, HDBlockStateTextureMap map, BlockStep laststep, int patchid, int textid, boolean stdrot) {
|
|
if(textid < 0) {
|
|
rslt.setTransparent();
|
|
return;
|
|
}
|
|
boolean hasblockcoloring = ss.do_biome_shading && this.blockColoring.hasBlkStateValue(blk);
|
|
// Test if we have no texture modifications
|
|
boolean simplemap = (textid < COLORMOD_MULT_INTERNAL) && (!hasblockcoloring);
|
|
int[] xyz = null;
|
|
|
|
if (simplemap) { /* If simple mapping */
|
|
int[] texture = getTileARGB(textid);
|
|
/* Get texture coordinates (U=horizontal(left=0),V=vertical(top=0)) */
|
|
int u = 0, v = 0;
|
|
/* If not patch, compute U and V */
|
|
if(patchid < 0) {
|
|
xyz = ps.getSubblockCoord();
|
|
|
|
switch(laststep) {
|
|
case X_MINUS: /* South face: U = East (Z-), V = Down (Y-) */
|
|
u = native_scale-xyz[2]-1; v = native_scale-xyz[1]-1;
|
|
break;
|
|
case X_PLUS: /* North face: U = West (Z+), V = Down (Y-) */
|
|
u = xyz[2]; v = native_scale-xyz[1]-1;
|
|
break;
|
|
case Z_MINUS: /* West face: U = South (X+), V = Down (Y-) */
|
|
u = xyz[0]; v = native_scale-xyz[1]-1;
|
|
break;
|
|
case Z_PLUS: /* East face: U = North (X-), V = Down (Y-) */
|
|
u = native_scale-xyz[0]-1; v = native_scale-xyz[1]-1;
|
|
break;
|
|
case Y_MINUS: /* U = East(Z-), V = South(X+) */
|
|
if(stdrot) {
|
|
u = xyz[0]; v = xyz[2];
|
|
}
|
|
else {
|
|
u = native_scale-xyz[2]-1; v = xyz[0];
|
|
}
|
|
break;
|
|
case Y_PLUS:
|
|
if(stdrot) {
|
|
u = native_scale-xyz[0]-1; v = xyz[2];
|
|
}
|
|
else {
|
|
u = xyz[2]; v = xyz[0];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
u = fastFloor(ps.getPatchU() * native_scale);
|
|
v = native_scale - fastFloor(ps.getPatchV() * native_scale) - 1;
|
|
}
|
|
/* Read color from texture */
|
|
try {
|
|
rslt.setARGB(texture[v*native_scale + u]);
|
|
} catch(ArrayIndexOutOfBoundsException aoobx) {
|
|
u = ((u < 0) ? 0 : ((u >= native_scale) ? (native_scale-1) : u));
|
|
v = ((v < 0) ? 0 : ((v >= native_scale) ? (native_scale-1) : v));
|
|
try {
|
|
rslt.setARGB(texture[v*native_scale + u]);
|
|
} catch(ArrayIndexOutOfBoundsException oob2) { }
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* See if not basic block texture */
|
|
int textop = textid / COLORMOD_MULT_INTERNAL;
|
|
textid = textid % COLORMOD_MULT_INTERNAL;
|
|
|
|
/* If clear-inside op, get out early */
|
|
if((textop == COLORMOD_CLEARINSIDE) || (textop == COLORMOD_MULTTONED_CLEARINSIDE)) {
|
|
DynmapBlockState lasthit = ss.getLastBlockHit(); // Last surface hit, vs last visited
|
|
/* Check if previous block is same block type as we are: surface is transparent if it is */
|
|
if (blk.matchingBaseState(lasthit) || ((blk.isWaterFilled() && lasthit.isWaterFilled()) && ps.isOnFace())) {
|
|
rslt.setTransparent();
|
|
return;
|
|
}
|
|
/* If water block, to watercolor tone op */
|
|
if (blk.isWater()) {
|
|
textop = COLORMOD_WATERTONED;
|
|
}
|
|
else if(textop == COLORMOD_MULTTONED_CLEARINSIDE) {
|
|
textop = COLORMOD_MULTTONED;
|
|
}
|
|
}
|
|
|
|
int[] texture = getTileARGB(textid);
|
|
/* Get texture coordinates (U=horizontal(left=0),V=vertical(top=0)) */
|
|
int u = 0, v = 0, tmp;
|
|
|
|
if(patchid < 0) {
|
|
if (xyz == null) xyz = ps.getSubblockCoord();
|
|
switch(laststep) {
|
|
case X_MINUS: /* South face: U = East (Z-), V = Down (Y-) */
|
|
u = native_scale-xyz[2]-1; v = native_scale-xyz[1]-1;
|
|
break;
|
|
case X_PLUS: /* North face: U = West (Z+), V = Down (Y-) */
|
|
u = xyz[2]; v = native_scale-xyz[1]-1;
|
|
break;
|
|
case Z_MINUS: /* West face: U = South (X+), V = Down (Y-) */
|
|
u = xyz[0]; v = native_scale-xyz[1]-1;
|
|
break;
|
|
case Z_PLUS: /* East face: U = North (X-), V = Down (Y-) */
|
|
u = native_scale-xyz[0]-1; v = native_scale-xyz[1]-1;
|
|
break;
|
|
case Y_MINUS: /* U = East(Z-), V = South(X+) */
|
|
if(stdrot) {
|
|
u = xyz[0]; v = xyz[2];
|
|
}
|
|
else {
|
|
u = native_scale-xyz[2]-1; v = xyz[0];
|
|
}
|
|
break;
|
|
case Y_PLUS:
|
|
if(stdrot) {
|
|
u = native_scale-xyz[0]-1; v = xyz[2];
|
|
}
|
|
else {
|
|
u = xyz[2]; v = xyz[0];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
u = fastFloor(ps.getPatchU() * native_scale);
|
|
v = native_scale - fastFloor(ps.getPatchV() * native_scale) - 1;
|
|
}
|
|
/* Handle U-V transorms before fetching color */
|
|
switch(textop) {
|
|
case COLORMOD_ROT90:
|
|
tmp = u; u = native_scale - v - 1; v = tmp;
|
|
break;
|
|
case COLORMOD_ROT180:
|
|
u = native_scale - u - 1; v = native_scale - v - 1;
|
|
break;
|
|
case COLORMOD_ROT270:
|
|
case COLORMOD_GRASSTONED270:
|
|
case COLORMOD_FOLIAGETONED270:
|
|
case COLORMOD_WATERTONED270:
|
|
tmp = u; u = v; v = native_scale - tmp - 1;
|
|
break;
|
|
case COLORMOD_FLIPHORIZ:
|
|
u = native_scale - u - 1;
|
|
break;
|
|
case COLORMOD_SHIFTDOWNHALF:
|
|
if(v < native_scale/2) {
|
|
rslt.setTransparent();
|
|
return;
|
|
}
|
|
v -= native_scale/2;
|
|
break;
|
|
case COLORMOD_SHIFTDOWNHALFANDFLIPHORIZ:
|
|
if(v < native_scale/2) {
|
|
rslt.setTransparent();
|
|
return;
|
|
}
|
|
v -= native_scale/2;
|
|
u = native_scale - u - 1;
|
|
break;
|
|
case COLORMOD_INCLINEDTORCH:
|
|
if(v >= (3*native_scale/4)) {
|
|
rslt.setTransparent();
|
|
return;
|
|
}
|
|
v += native_scale/4;
|
|
if(u < native_scale/2) u = native_scale/2-1;
|
|
if(u > native_scale/2) u = native_scale/2;
|
|
break;
|
|
case COLORMOD_GRASSSIDE:
|
|
boolean do_grass_side = false;
|
|
boolean do_snow_side = false;
|
|
if(ss.do_better_grass) {
|
|
mapiter.unstepPosition(laststep);
|
|
if (mapiter.getBlockType().isSnow())
|
|
do_snow_side = true;
|
|
if (mapiter.getBlockTypeAt(BlockStep.Y_MINUS).isGrass())
|
|
do_grass_side = true;
|
|
mapiter.stepPosition(laststep);
|
|
}
|
|
|
|
/* Check if snow above block */
|
|
if(mapiter.getBlockTypeAt(BlockStep.Y_PLUS).isSnow()) {
|
|
if(do_snow_side) {
|
|
texture = getTileARGB(TILEINDEX_SNOW); /* Snow full side block */
|
|
textid = TILEINDEX_SNOW;
|
|
}
|
|
else {
|
|
texture = getTileARGB(TILEINDEX_SNOWSIDE); /* Snow block */
|
|
textid = TILEINDEX_SNOWSIDE;
|
|
}
|
|
textop = 0;
|
|
}
|
|
else { /* Else, check the grass color overlay */
|
|
if(do_grass_side) {
|
|
texture = getTileARGB(TILEINDEX_GRASS); /* Grass block */
|
|
textid = TILEINDEX_GRASS;
|
|
textop = COLORMOD_GRASSTONED; /* Force grass toning */
|
|
}
|
|
else {
|
|
int ovclr = getTileARGB(TILEINDEX_GRASSMASK)[v*native_scale+u];
|
|
if((ovclr & 0xFF000000) != 0) { /* Hit? */
|
|
texture = getTileARGB(TILEINDEX_GRASSMASK); /* Use it */
|
|
textop = COLORMOD_GRASSTONED; /* Force grass toning */
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case COLORMOD_LILYTONED:
|
|
/* Rotate texture based on lily orientation function (from renderBlockLilyPad in RenderBlocks.jara in MCP) */
|
|
long l1 = (long)(mapiter.getX() * 0x2fc20f) ^ (long)mapiter.getZ() * 0x6ebfff5L ^ (long)mapiter.getY();
|
|
l1 = l1 * l1 * 0x285b825L + l1 * 11L;
|
|
int orientation = (int)(l1 >> 16 & 3L);
|
|
switch(orientation) {
|
|
case 0:
|
|
tmp = u; u = native_scale - v - 1; v = tmp;
|
|
break;
|
|
case 1:
|
|
u = native_scale - u - 1; v = native_scale - v - 1;
|
|
break;
|
|
case 2:
|
|
tmp = u; u = v; v = native_scale - tmp - 1;
|
|
break;
|
|
case 3:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
/* Read color from texture */
|
|
try {
|
|
rslt.setARGB(texture[v*native_scale + u]);
|
|
} catch (ArrayIndexOutOfBoundsException aioobx) {
|
|
rslt.setARGB(0);
|
|
}
|
|
|
|
int clrmult = -1;
|
|
int clralpha = 0xFF000000;
|
|
int custclrmult = -1;
|
|
// If block has custom coloring
|
|
if (hasblockcoloring) {
|
|
Integer idx = this.blockColoring.getBlkStateValue(blk);
|
|
LoadedImage img = imgs[idx.intValue()];
|
|
if (img.argb != null) {
|
|
custclrmult = mapiter.getSmoothWaterColorMultiplier(img.argb);
|
|
}
|
|
else {
|
|
hasblockcoloring = false;
|
|
}
|
|
}
|
|
if (!hasblockcoloring) {
|
|
// Switch based on texture modifier
|
|
switch(textop) {
|
|
case COLORMOD_GRASSTONED:
|
|
case COLORMOD_GRASSTONED270:
|
|
if(ss.do_biome_shading) {
|
|
if(imgs[IMG_SWAMPGRASSCOLOR] != null)
|
|
clrmult = mapiter.getSmoothColorMultiplier(imgs[IMG_GRASSCOLOR].argb, imgs[IMG_SWAMPGRASSCOLOR].argb);
|
|
else
|
|
clrmult = mapiter.getSmoothGrassColorMultiplier(imgs[IMG_GRASSCOLOR].argb);
|
|
}
|
|
else {
|
|
clrmult = imgs[IMG_GRASSCOLOR].trivial_color;
|
|
}
|
|
break;
|
|
case COLORMOD_FOLIAGETONED:
|
|
case COLORMOD_FOLIAGETONED270:
|
|
if(ss.do_biome_shading) {
|
|
if(imgs[IMG_SWAMPFOLIAGECOLOR] != null)
|
|
clrmult = mapiter.getSmoothColorMultiplier(imgs[IMG_FOLIAGECOLOR].argb, imgs[IMG_SWAMPFOLIAGECOLOR].argb);
|
|
else
|
|
clrmult = mapiter.getSmoothFoliageColorMultiplier(imgs[IMG_FOLIAGECOLOR].argb);
|
|
}
|
|
else {
|
|
clrmult = imgs[IMG_FOLIAGECOLOR].trivial_color;
|
|
}
|
|
break;
|
|
case COLORMOD_FOLIAGEMULTTONED:
|
|
if(ss.do_biome_shading) {
|
|
if(imgs[IMG_SWAMPFOLIAGECOLOR] != null)
|
|
clrmult = mapiter.getSmoothColorMultiplier(imgs[IMG_FOLIAGECOLOR].argb, imgs[IMG_SWAMPFOLIAGECOLOR].argb);
|
|
else
|
|
clrmult = mapiter.getSmoothFoliageColorMultiplier(imgs[IMG_FOLIAGECOLOR].argb);
|
|
}
|
|
else {
|
|
clrmult = imgs[IMG_FOLIAGECOLOR].trivial_color;
|
|
}
|
|
if(map.custColorMult != null) {
|
|
clrmult = ((clrmult & 0xFEFEFE) + map.custColorMult.getColorMultiplier(mapiter)) / 2;
|
|
}
|
|
else {
|
|
clrmult = ((clrmult & 0xFEFEFE) + map.colorMult) / 2;
|
|
}
|
|
break;
|
|
|
|
case COLORMOD_WATERTONED:
|
|
case COLORMOD_WATERTONED270:
|
|
if(imgs[IMG_WATERCOLORX] != null) {
|
|
if(ss.do_biome_shading) {
|
|
clrmult = mapiter.getSmoothWaterColorMultiplier(imgs[IMG_WATERCOLORX].argb);
|
|
}
|
|
else {
|
|
clrmult = imgs[IMG_WATERCOLORX].trivial_color;
|
|
}
|
|
}
|
|
else {
|
|
if(ss.do_biome_shading) {
|
|
clrmult = mapiter.getSmoothWaterColorMultiplier();
|
|
}
|
|
}
|
|
break;
|
|
case COLORMOD_BIRCHTONED:
|
|
if(ss.do_biome_shading) {
|
|
if(imgs[IMG_BIRCHCOLOR] != null)
|
|
clrmult = mapiter.getSmoothFoliageColorMultiplier(imgs[IMG_BIRCHCOLOR].argb);
|
|
else
|
|
clrmult = colorMultBirch;
|
|
}
|
|
else {
|
|
clrmult = colorMultBirch;
|
|
}
|
|
break;
|
|
case COLORMOD_PINETONED:
|
|
if(ss.do_biome_shading) {
|
|
if(imgs[IMG_PINECOLOR] != null)
|
|
clrmult = mapiter.getSmoothFoliageColorMultiplier(imgs[IMG_PINECOLOR].argb);
|
|
else
|
|
clrmult = colorMultPine;
|
|
}
|
|
else {
|
|
clrmult = colorMultPine;
|
|
}
|
|
break;
|
|
case COLORMOD_LILYTONED:
|
|
clrmult = colorMultLily;
|
|
break;
|
|
case COLORMOD_MULTTONED: /* Use color multiplier */
|
|
if(map.custColorMult != null) {
|
|
clrmult = map.custColorMult.getColorMultiplier(mapiter);
|
|
}
|
|
else {
|
|
clrmult = map.colorMult;
|
|
}
|
|
if((clrmult & 0xFF000000) != 0) {
|
|
clralpha = clrmult & 0xFF000000;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if((clrmult != -1) && (clrmult != 0)) {
|
|
rslt.blendColor(clrmult | clralpha);
|
|
}
|
|
if (hasblockcoloring && (custclrmult != -1)) {
|
|
rslt.blendColor(custclrmult | clralpha);
|
|
}
|
|
}
|
|
|
|
private static final void makeAlphaPure(int[] argb) {
|
|
for(int i = 0; i < argb.length; i++) {
|
|
if((argb[i] & 0xFF000000) != 0)
|
|
argb[i] |= 0xFF000000;
|
|
}
|
|
}
|
|
|
|
private static final int fastFloor(double f) {
|
|
return ((int)(f + 1000000000.0)) - 1000000000;
|
|
}
|
|
|
|
/**
|
|
* Get tile index, based on tile file name and relative index within tile file
|
|
* @param fname - filename
|
|
* @param idx - tile index (= (y * xdim) + x)
|
|
* @return global tile index, or -1 if not found
|
|
*/
|
|
public static int findDynamicTile(String fname, int idx) {
|
|
DynamicTileFile f;
|
|
/* Find existing, if already there */
|
|
f = addonfilesbyname.get(fname);
|
|
if (f != null) {
|
|
if ((idx >= 0) && (idx < f.tile_to_dyntile.length) && (f.tile_to_dyntile[idx] >= 0)) {
|
|
f.used = true;
|
|
return f.tile_to_dyntile[idx];
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
/**
|
|
* Add new dynmaic file definition, or return existing
|
|
*
|
|
* @param fname - filename
|
|
* @param modname - mod name
|
|
* @param xdim - x dimension
|
|
* @param ydim - y dimension
|
|
* @param fmt - tile file format
|
|
* @param args - args for file format
|
|
* @return dynamic file index
|
|
*/
|
|
public static int findOrAddDynamicTileFile(String fname, String modname, int xdim, int ydim, TileFileFormat fmt, String[] args) {
|
|
DynamicTileFile f;
|
|
/* Find existing, if already there */
|
|
f = addonfilesbyname.get(fname);
|
|
if (f != null) {
|
|
return f.idx;
|
|
}
|
|
/* Add new tile file entry */
|
|
f = new DynamicTileFile();
|
|
f.filename = fname;
|
|
f.modname = modname;
|
|
f.tilecnt_x = xdim;
|
|
f.tilecnt_y = ydim;
|
|
f.format = fmt;
|
|
f.used = false;
|
|
// Assume all biome files are used (not referred to by index)
|
|
if (fmt == TileFileFormat.BIOME) {
|
|
f.used = true;
|
|
}
|
|
switch(fmt) {
|
|
case GRID:
|
|
f.tile_to_dyntile = new int[xdim*ydim];
|
|
break;
|
|
case CHEST:
|
|
f.tile_to_dyntile = new int[TILEINDEX_CHEST_COUNT]; /* 6 images for chest tile */
|
|
break;
|
|
case BIGCHEST:
|
|
f.tile_to_dyntile = new int[TILEINDEX_BIGCHEST_COUNT]; /* 10 images for chest tile */
|
|
break;
|
|
case SIGN:
|
|
f.tile_to_dyntile = new int[TILEINDEX_SIGN_COUNT]; /* 10 images for sign tile */
|
|
break;
|
|
case SHULKER:
|
|
f.tile_to_dyntile = new int[TILEINDEX_SHULKER_COUNT]; /* 6 images for sign tile */
|
|
break;
|
|
case BED:
|
|
f.tile_to_dyntile = new int[TILEINDEX_BED_COUNT]; /* 18 images for tile */
|
|
break;
|
|
case CUSTOM:
|
|
{
|
|
List<CustomTileRec> recs = new ArrayList<CustomTileRec>();
|
|
for(String a : args) {
|
|
String[] v = a.split("=");
|
|
if(v.length != 2) continue;
|
|
if(v[0].startsWith("tile")) {
|
|
int id = 0;
|
|
try {
|
|
id = Integer.parseInt(v[0].substring(4));
|
|
} catch (NumberFormatException nfx) {
|
|
Log.warning("Bad tile ID: " + v[0]);
|
|
continue;
|
|
}
|
|
while(recs.size() <= id) {
|
|
recs.add(null);
|
|
}
|
|
CustomTileRec rec = new CustomTileRec();
|
|
try {
|
|
String[] coords = v[1].split("/");
|
|
String[] topleft = coords[0].split(":");
|
|
rec.srcx = Integer.parseInt(topleft[0]);
|
|
rec.srcy = Integer.parseInt(topleft[1]);
|
|
String[] size = coords[1].split(":");
|
|
rec.width = Integer.parseInt(size[0]);
|
|
rec.height = Integer.parseInt(size[1]);
|
|
if(coords.length >= 3) {
|
|
String[] dest = coords[2].split(":");
|
|
rec.targetx = Integer.parseInt(dest[0]);
|
|
rec.targety = Integer.parseInt(dest[1]);
|
|
}
|
|
recs.set(id, rec);
|
|
} catch (Exception x) {
|
|
Log.warning("Bad custom tile coordinate: " + v[1]);
|
|
}
|
|
}
|
|
}
|
|
f.tile_to_dyntile = new int[recs.size()];
|
|
f.cust = recs;
|
|
}
|
|
break;
|
|
case SKIN:
|
|
f.tile_to_dyntile = new int[TILEINDEX_SKIN_COUNT]; /* 6 images for skin tile */
|
|
break;
|
|
case TILESET:
|
|
f.tile_to_dyntile = new int[xdim*ydim];
|
|
break;
|
|
case BIOME:
|
|
f.tile_to_dyntile = new int[1];
|
|
break;
|
|
default:
|
|
f.tile_to_dyntile = new int[xdim*ydim];
|
|
break;
|
|
}
|
|
Arrays.fill(f.tile_to_dyntile, -1);
|
|
f.idx = addonfiles.size();
|
|
addonfiles.add(f);
|
|
addonfilesbyname.put(f.filename, f);
|
|
return f.idx;
|
|
}
|
|
/**
|
|
* Add or find dynamic tile index of given dynamic tile
|
|
* @param dynfile_idx - index of file
|
|
* @param tile_id - ID of tile within file
|
|
* @return global tile ID
|
|
*/
|
|
public static int findOrAddDynamicTile(int dynfile_idx, int tile_id) {
|
|
DynamicTileFile f = addonfiles.get(dynfile_idx);
|
|
if(f == null) {
|
|
throw new NumberFormatException("Invalid add-on file index: " + dynfile_idx);
|
|
}
|
|
if (tile_id >= f.tile_to_dyntile.length) {
|
|
throw new NumberFormatException("Invalid index " + tile_id + " for texture file " + f.filename + " on mod " + f.modname);
|
|
}
|
|
if(f.tile_to_dyntile[tile_id] < 0) { /* Not assigned yet? */
|
|
f.tile_to_dyntile[tile_id] = next_dynamic_tile;
|
|
next_dynamic_tile++; /* Allocate next ID */
|
|
}
|
|
f.used = true; // Mark file as being used
|
|
return f.tile_to_dyntile[tile_id];
|
|
}
|
|
|
|
private static final int[] smooth_water_mult = new int[10];
|
|
|
|
public static int getTextureIDAt(MapIterator mapiter, DynmapBlockState blk, BlockStep face) {
|
|
HDBlockStateTextureMap map = HDBlockStateTextureMap.getByBlockState(blk);
|
|
int idx = -1;
|
|
if (map != null) {
|
|
int sideidx = face.ordinal();
|
|
if (map.faces != null) {
|
|
if (sideidx < map.faces.length)
|
|
idx = map.faces[sideidx];
|
|
else
|
|
idx = map.faces[0];
|
|
}
|
|
}
|
|
if(idx > 0)
|
|
idx = idx % COLORMOD_MULT_INTERNAL;
|
|
return idx;
|
|
}
|
|
|
|
private static final String PALETTE_BLOCK_KEY = "palette.block.";
|
|
|
|
private void processCustomColorMap(String fname, String ids) {
|
|
// Register file name
|
|
int idx = findOrAddDynamicTileFile(fname, null, 1, 1, TileFileFormat.BIOME, new String[0]);
|
|
if(idx < 0) {
|
|
Log.info("Error registering custom color file: " + fname);
|
|
return;
|
|
}
|
|
Integer index = idx + IMG_CNT;
|
|
// Now, parse block ID list
|
|
for (String id : ids.split("\\s+")) {
|
|
String[] tok = id.split(":");
|
|
int meta = -1;
|
|
DynmapBlockState blk = null;
|
|
String blkname = null;
|
|
if (tok.length == 1) { /* Only ID */
|
|
blkname = tok[0];
|
|
blk = DynmapBlockState.getBaseStateByName(blkname);
|
|
if (blk.isAir()) {
|
|
Log.info("Bad custom color block ID: " + tok[0]);
|
|
}
|
|
}
|
|
else if (tok.length == 2) { /* ID : meta */
|
|
blkname = tok[0];
|
|
try {
|
|
meta = Integer.parseInt(tok[1]);
|
|
} catch (NumberFormatException nfx) {
|
|
Log.info("Bad custom color meta ID: " + tok[1]);
|
|
}
|
|
blk = DynmapBlockState.getStateByNameAndIndex(blkname, meta);
|
|
if (blk.isAir()) {
|
|
Log.info("Bad custom color block ID: " + tok[0] + ":" + meta);
|
|
}
|
|
}
|
|
|
|
/* Add mappings for values */
|
|
HDBlockStateTextureMap bmap = HDBlockStateTextureMap.getByBlockState(blk);
|
|
if (bmap != null) {
|
|
if (meta >= 0) {
|
|
if (blk.isNotAir()) {
|
|
this.blockColoring.setBlkStateValue(blk, index);
|
|
}
|
|
}
|
|
else if (meta == -1) { /* All meta IDs */
|
|
for (int v = 0; v < blk.getStateCount(); v++) {
|
|
DynmapBlockState b = blk.getState(v);
|
|
if (b.isNotAir()) {
|
|
this.blockColoring.setBlkStateValue(b, index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
private void processCustomColors(Properties p) {
|
|
// Loop through keys
|
|
for(String pname : p.stringPropertyNames()) {
|
|
if(!pname.startsWith(PALETTE_BLOCK_KEY))
|
|
continue;
|
|
String v = p.getProperty(pname);
|
|
String fname = pname.substring(PALETTE_BLOCK_KEY.length()).trim(); // Get filename of color map
|
|
if(fname.charAt(0) == '/') fname = fname.substring(1); // Strip leading /
|
|
if(fname.charAt(0) == '~') fname = "assets/minecraft/mcpatcher" + fname.substring(1);
|
|
processCustomColorMap(fname, v);
|
|
}
|
|
}
|
|
|
|
static {
|
|
/*
|
|
* Generate smoothed swamp multipliers (indexed by swamp biome count)
|
|
*/
|
|
Color c = new Color();
|
|
for(int i = 0; i < 10; i++) {
|
|
/* Use water color multiplier base for 1.1 (E0FFAE) */
|
|
int r = (((9-i) * 0xFF) + (i * 0xE0)) / 9;
|
|
int g = 0xFF;
|
|
int b = (((9-i) * 0xFF) + (i * 0xAE)) / 9;
|
|
c.setRGBA(r & 0xFE, g & 0xFE, b & 0xFE, 0xFF);
|
|
smooth_water_mult[i] = c.getARGB();
|
|
}
|
|
}
|
|
|
|
public int getTrivialFoliageMultiplier() {
|
|
return imgs[IMG_FOLIAGECOLOR].argb[BiomeMap.FOREST.biomeLookup()];
|
|
}
|
|
public int getTrivialGrassMultiplier() {
|
|
return imgs[IMG_GRASSCOLOR].argb[BiomeMap.FOREST.biomeLookup()];
|
|
}
|
|
public int getTrivialWaterMultiplier() {
|
|
if(imgs[IMG_WATERCOLORX] != null) {
|
|
return imgs[IMG_WATERCOLORX].argb[BiomeMap.FOREST.biomeLookup()];
|
|
}
|
|
else {
|
|
return 0xFFFFFF;
|
|
}
|
|
}
|
|
public int getCustomBlockMultiplier(DynmapBlockState blk) {
|
|
Integer idx = this.blockColoring.getBlkStateValue(blk);
|
|
if (idx != null) {
|
|
LoadedImage img = imgs[idx.intValue()];
|
|
if (img.argb != null) {
|
|
return img.argb[BiomeMap.FOREST.biomeLookup()];
|
|
}
|
|
}
|
|
return 0xFFFFFF;
|
|
}
|
|
|
|
private static class ExportedTexture {
|
|
public String filename;
|
|
public Color diffuseColor;
|
|
public String filename_a;
|
|
public MaterialType material;
|
|
}
|
|
|
|
private static class ExportedTexturePack {
|
|
Map<String, ExportedTexture> txtids = new HashMap<String, ExportedTexture>();
|
|
DynmapBufferedImage img;
|
|
OBJExport exp;
|
|
String name;
|
|
}
|
|
|
|
// Encode image as PNG and add to ZIP
|
|
private void addImageToZip(String idstr, int idx, int colormult, ExportedTexturePack etp) throws IOException {
|
|
if (etp.txtids.containsKey(idstr)) { // Already in set?
|
|
return;
|
|
}
|
|
colormult = colormult & 0xFFFFFF; // Mask multiplier
|
|
int[] argb = getTileARGB(idx); // Look up tile data
|
|
if (colormult != 0xFFFFFF) { // Non-trivial color multiplier
|
|
colormult |= 0xFF000000;
|
|
for (int i = 0; i < etp.img.argb_buf.length; i++) {
|
|
etp.img.argb_buf[i] = Color.blendColor(argb[i], colormult);
|
|
}
|
|
}
|
|
else { // Else, just copy into destination
|
|
for (int i = 0; i < etp.img.argb_buf.length; i++) {
|
|
etp.img.argb_buf[i] = argb[i];
|
|
}
|
|
}
|
|
boolean hasAlpha = false;
|
|
// Compute simple color
|
|
double r = 0.0, g = 0.0, b = 0.0, w = 0.0;
|
|
for (int i = 0; i < etp.img.argb_buf.length; i++) {
|
|
int v = etp.img.argb_buf[i];
|
|
int ww = (v >> 24) & 0xFF;
|
|
int rr = (v >> 16) & 0xFF;
|
|
int gg = (v >> 8) & 0xFF;
|
|
int bb = v & 0xFF;
|
|
r += ww * rr;
|
|
g += ww * gg;
|
|
b += ww * bb;
|
|
w += ww;
|
|
if (ww != 0xFF) { // Non-trivial alpha?
|
|
hasAlpha = true;
|
|
}
|
|
}
|
|
BufferOutputStream baos = new BufferOutputStream();
|
|
|
|
ImageIO.setUseCache(false); /* Don't use file cache - too small to be worth it */
|
|
|
|
String fname = etp.name + "/" + idstr + ".png";
|
|
etp.exp.startExportedFile(fname);
|
|
|
|
ImageIO.write(etp.img.buf_img, "png", baos);
|
|
|
|
etp.exp.addBytesToExportedFile(baos.buf, 0, baos.len);
|
|
etp.exp.finishExportedFile();
|
|
String fname_a = null;
|
|
// If has alpha, convert to gray scale for alpha image
|
|
if (hasAlpha) {
|
|
for (int i = 0; i < etp.img.argb_buf.length; i++) {
|
|
int v = etp.img.argb_buf[i];
|
|
int ww = (v >> 24) & 0xFF;
|
|
etp.img.argb_buf[i] = (ww << 24) | (ww << 16) | (ww << 8) | ww;
|
|
}
|
|
fname_a = etp.name + "/" + idstr + "_a.png";
|
|
etp.exp.startExportedFile(fname_a);
|
|
|
|
baos.reset();
|
|
ImageIO.write(etp.img.buf_img, "png", baos);
|
|
|
|
etp.exp.addBytesToExportedFile(baos.buf, 0, baos.len);
|
|
etp.exp.finishExportedFile();
|
|
}
|
|
|
|
ExportedTexture et = new ExportedTexture();
|
|
et.filename = fname;
|
|
et.filename_a = fname_a;
|
|
if (w > 0)
|
|
et.diffuseColor = new Color((int)(r / w), (int)(g / w), (int)(b / w));
|
|
else
|
|
et.diffuseColor = new Color();
|
|
et.material = getMaterialTypeByTile(idx);
|
|
etp.txtids.put(idstr, et); // Add to set
|
|
}
|
|
// Export texture pack as OBJ format material library
|
|
public void exportAsOBJMaterialLibrary(OBJExport exp, String name) throws IOException {
|
|
ExportedTexturePack etp = new ExportedTexturePack();
|
|
etp.img = DynmapBufferedImage.allocateBufferedImage(this.native_scale, this.native_scale);
|
|
etp.name = name;
|
|
etp.exp = exp;
|
|
// Get set of texture references from export
|
|
Set<String> txtids = exp.getMaterialIDs();
|
|
// Loop through them, adding the textures needed
|
|
for (String txt : txtids) {
|
|
int off = txt.lastIndexOf("__");
|
|
int mult = -1;
|
|
String txtbase = txt;
|
|
if ((off > 0) && ((off + 8) == txt.length())) {
|
|
String end = txt.substring(off+2);
|
|
try {
|
|
mult = Integer.parseInt(end, 16);
|
|
} catch (NumberFormatException x) {
|
|
Log.warning("Invalid multiplier " + end);
|
|
}
|
|
txtbase = txt.substring(0, off);
|
|
}
|
|
Integer txt_id = tileIDByMatID.get(txtbase);
|
|
int id = -1;
|
|
if (txt_id == null) {
|
|
if (txtbase.startsWith("txt") == false) {
|
|
continue;
|
|
}
|
|
try {
|
|
id = Integer.parseInt(txtbase.substring(3));
|
|
} catch (NumberFormatException x) {
|
|
Log.warning("Invalid texture ID " + txtbase);
|
|
}
|
|
}
|
|
else {
|
|
id = txt_id;
|
|
}
|
|
if (id >= 0) {
|
|
addImageToZip(txt, id, mult, etp);
|
|
}
|
|
}
|
|
// Build MTL file
|
|
exp.startExportedFile(etp.name + ".mtl");
|
|
TreeSet<String> ids = new TreeSet<String>(etp.txtids.keySet());
|
|
for (String id : ids) {
|
|
ExportedTexture et = etp.txtids.get(id);
|
|
String lines = "newmtl " + id + "\n";
|
|
lines += String.format(Locale.US, "Ka %.3f %.3f %.3f\n", (double)et.diffuseColor.getRed() / 256.0, (double)et.diffuseColor.getGreen() / 256.0, (double) et.diffuseColor.getBlue() / 256.0);
|
|
lines += String.format(Locale.US, "Kd %.3f %.3f %.3f\n", (double)et.diffuseColor.getRed() / 256.0, (double)et.diffuseColor.getGreen() / 256.0, (double) et.diffuseColor.getBlue() / 256.0);
|
|
lines += "map_Kd " + et.filename + "\n";
|
|
lines += "map_Ka " + et.filename + "\n";
|
|
if (et.filename_a != null) {
|
|
lines += "map_d " + et.filename_a + "\n";
|
|
}
|
|
if (et.material != null) {
|
|
lines += String.format(Locale.US, "Ni %.3f\n", et.material.Ni);
|
|
lines += String.format(Locale.US, "Ns %.3f\n", et.material.Ns);
|
|
lines += "Ks 0.500 0.500 0.500\n";
|
|
lines += String.format("illum %d\n", et.material.illum);
|
|
}
|
|
else {
|
|
lines += "Ks 0.000 0.000 0.000\n";
|
|
}
|
|
lines += "\n";
|
|
exp.addStringToExportedFile(lines);
|
|
}
|
|
exp.finishExportedFile();
|
|
}
|
|
private static final int[] deftxtidx = { 0, 1, 2, 3, 4, 5 };
|
|
|
|
public String[] getCurrentBlockMaterials(DynmapBlockState blk, MapIterator mapiter, int[] txtidx, BlockStep[] steps) {
|
|
HDBlockStateTextureMap map = HDBlockStateTextureMap.getByBlockState(blk);
|
|
if (txtidx == null) txtidx = deftxtidx;
|
|
String[] rslt = new String[txtidx.length]; // One for each face
|
|
boolean handlestdrot = (steps != null) && (!map.stdrotate);
|
|
Integer blockcoloring = this.blockColoring.getBlkStateValue(blk);
|
|
int custclrmult = -1;
|
|
// If block has custom coloring
|
|
if (blockcoloring != null) {
|
|
LoadedImage img = imgs[blockcoloring.intValue()];
|
|
if (img.argb != null) {
|
|
custclrmult = mapiter.getSmoothWaterColorMultiplier(img.argb);
|
|
}
|
|
else {
|
|
blockcoloring = null;
|
|
}
|
|
}
|
|
for (int patchidx = 0; patchidx < txtidx.length; patchidx++) {
|
|
int faceindex = txtidx[patchidx];
|
|
int textid = map.faces[faceindex];
|
|
int mod = textid / COLORMOD_MULT_INTERNAL;
|
|
textid = textid % COLORMOD_MULT_INTERNAL;
|
|
BlockStep step = steps[patchidx];
|
|
/* If clear-inside op, get out early */
|
|
if ((mod == COLORMOD_CLEARINSIDE) || (mod == COLORMOD_MULTTONED_CLEARINSIDE)) {
|
|
BlockStep dir = step.opposite();
|
|
/* Check if previous block is same block type as we are: surface is transparent if it is */
|
|
if (blk.matchingBaseState(mapiter.getBlockTypeAt(dir))) {
|
|
continue; // Skip: no texture
|
|
}
|
|
/* If water block, to watercolor tone op */
|
|
if (blk.isWater()) {
|
|
mod = COLORMOD_WATERTONED;
|
|
}
|
|
else if (mod == COLORMOD_MULTTONED_CLEARINSIDE) {
|
|
mod = COLORMOD_MULTTONED;
|
|
}
|
|
}
|
|
|
|
if (ctm != null) {
|
|
textid = ctm.mapTexture(mapiter, blk, step, textid, null);
|
|
}
|
|
if (textid >= 0) {
|
|
rslt[patchidx] = getMatIDForTileID(textid); // Default texture
|
|
int mult = 0xFFFFFF;
|
|
BiomeMap bio;
|
|
if (blockcoloring == null) {
|
|
|
|
switch (mod) {
|
|
case COLORMOD_GRASSTONED:
|
|
case COLORMOD_GRASSTONED270:
|
|
bio = mapiter.getBiome();
|
|
if ((bio == BiomeMap.SWAMPLAND) && (imgs[IMG_SWAMPGRASSCOLOR] != null)) {
|
|
mult = getBiomeTonedColor(imgs[IMG_SWAMPGRASSCOLOR], -1, bio, blk);
|
|
}
|
|
else {
|
|
mult = getBiomeTonedColor(imgs[IMG_GRASSCOLOR], -1, bio, blk);
|
|
}
|
|
break;
|
|
case COLORMOD_FOLIAGETONED:
|
|
case COLORMOD_FOLIAGETONED270:
|
|
case COLORMOD_FOLIAGEMULTTONED:
|
|
mult = getBiomeTonedColor(imgs[IMG_FOLIAGECOLOR], -1, mapiter.getBiome(), blk);
|
|
break;
|
|
case COLORMOD_WATERTONED:
|
|
case COLORMOD_WATERTONED270:
|
|
mult = getBiomeTonedColor(imgs[IMG_WATERCOLORX], -1, mapiter.getBiome(), blk);
|
|
break;
|
|
case COLORMOD_PINETONED:
|
|
mult = getBiomeTonedColor(imgs[IMG_PINECOLOR], colorMultPine, mapiter.getBiome(), blk);
|
|
break;
|
|
case COLORMOD_BIRCHTONED:
|
|
mult = getBiomeTonedColor(imgs[IMG_BIRCHCOLOR], colorMultBirch, mapiter.getBiome(), blk);
|
|
break;
|
|
case COLORMOD_LILYTONED:
|
|
mult = getBiomeTonedColor(null, colorMultLily, mapiter.getBiome(), blk);
|
|
break;
|
|
case COLORMOD_MULTTONED:
|
|
case COLORMOD_MULTTONED_CLEARINSIDE:
|
|
if(map.custColorMult == null) {
|
|
mult = getBiomeTonedColor(null, map.colorMult, mapiter.getBiome(), blk);
|
|
}
|
|
else {
|
|
mult = map.custColorMult.getColorMultiplier(mapiter);
|
|
}
|
|
break;
|
|
default:
|
|
mult = getBiomeTonedColor(null, -1, mapiter.getBiome(), blk);
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
mult = custclrmult;
|
|
}
|
|
if ((mult & 0xFFFFFF) != 0xFFFFFF) {
|
|
rslt[patchidx] += String.format("__%06X", mult & 0xFFFFFF);
|
|
}
|
|
if (handlestdrot && (!map.stdrotate) && ((step == BlockStep.Y_MINUS) || (step == BlockStep.Y_PLUS))) {
|
|
// Handle rotations
|
|
switch (mod) {
|
|
case COLORMOD_ROT90:
|
|
mod = COLORMOD_ROT180;
|
|
break;
|
|
case COLORMOD_ROT180:
|
|
mod = COLORMOD_ROT270;
|
|
break;
|
|
case COLORMOD_ROT270:
|
|
case COLORMOD_GRASSTONED270:
|
|
case COLORMOD_FOLIAGETONED270:
|
|
case COLORMOD_WATERTONED270:
|
|
mod = 0;
|
|
break;
|
|
default:
|
|
mod = COLORMOD_ROT90;
|
|
break;
|
|
}
|
|
}
|
|
// Handle rotations
|
|
switch (mod) {
|
|
case COLORMOD_ROT90:
|
|
rslt[patchidx] += "@" + OBJExport.ROT90;
|
|
break;
|
|
case COLORMOD_ROT180:
|
|
rslt[patchidx] += "@" + OBJExport.ROT180;
|
|
break;
|
|
case COLORMOD_ROT270:
|
|
case COLORMOD_GRASSTONED270:
|
|
case COLORMOD_FOLIAGETONED270:
|
|
case COLORMOD_WATERTONED270:
|
|
rslt[patchidx] += "@" + + OBJExport.ROT270;
|
|
break;
|
|
case COLORMOD_FLIPHORIZ:
|
|
rslt[patchidx] += "@" + OBJExport.HFLIP;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return rslt;
|
|
}
|
|
|
|
// Get biome-specific color multpliers
|
|
private int getBiomeTonedColor(LoadedImage tonemap, int defcolormult, BiomeMap biome, DynmapBlockState blk) {
|
|
int mult;
|
|
if (tonemap == null) { // No map? just use trivial
|
|
mult = defcolormult;
|
|
}
|
|
else if (tonemap.argb == null) { // No details, use trivial
|
|
mult = tonemap.trivial_color;
|
|
}
|
|
else {
|
|
mult = tonemap.argb[biome.biomeLookup()];
|
|
}
|
|
Integer idx = this.blockColoring.getBlkStateValue(blk);
|
|
if (idx != null) {
|
|
LoadedImage custimg = imgs[idx.intValue()];
|
|
if (custimg.argb != null) {
|
|
mult = Color.blendColor(mult, custimg.argb[biome.biomeLookup()]);
|
|
}
|
|
}
|
|
return mult;
|
|
}
|
|
|
|
public MaterialType getMaterialTypeByTile(int tileidx) {
|
|
return materialbytileid.get(tileidx);
|
|
}
|
|
|
|
private void setMatIDForTileID(String matid, int tileid) {
|
|
String id = matIDByTileID.get(tileid);
|
|
if (id != null) return;
|
|
id = matid;
|
|
String[] tok = id.split("/");
|
|
if (tok.length < 5) {
|
|
id = tok[tok.length-1];
|
|
}
|
|
else {
|
|
id = tok[4];
|
|
for (int i = 5; i < tok.length; i++) {
|
|
id = id + "_" + tok[i];
|
|
}
|
|
}
|
|
id = id.replace(' ', '_');
|
|
int off = id.lastIndexOf('.');
|
|
if (off > 0) {
|
|
id = id.substring(0, off);
|
|
}
|
|
int cnt = 2;
|
|
String baseid = id;
|
|
while (true) {
|
|
Integer v = tileIDByMatID.get(id);
|
|
if (v == null) { // Not defined, use ID
|
|
tileIDByMatID.put(id, tileid);
|
|
matIDByTileID.put(tileid, id);
|
|
return;
|
|
}
|
|
else if ((v != null) && (v.intValue() == tileid)) {
|
|
return;
|
|
}
|
|
id = baseid + "_" + cnt;
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
private String getMatIDForTileID(int txtid) {
|
|
String id = matIDByTileID.get(txtid);
|
|
if (id == null) {
|
|
id = "txt" + txtid;
|
|
matIDByTileID.put(txtid, id);
|
|
tileIDByMatID.put(id, txtid);
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
public static void tallyMemory(DynmapCommandSender sender) {
|
|
long packcount = 0;
|
|
long packbytecount = 0;
|
|
for (String packid : packs.keySet()) {
|
|
TexturePack p = packs.get(packid);
|
|
long scaledcount = 0;
|
|
long scaledbytecount = 0;
|
|
for (Integer scale : p.scaled_textures.keySet()) {
|
|
TexturePack sp = p.scaled_textures.get(scale);
|
|
long bytecount = 0;
|
|
long imgcount = 0;
|
|
for (int i = 0; i < sp.imgs.length; i++) {
|
|
if (sp.imgs[i] != null) {
|
|
if (sp.imgs[i].argb != null) {
|
|
bytecount += sp.imgs[i].argb.length * 4;
|
|
imgcount++;
|
|
}
|
|
}
|
|
}
|
|
sender.sendMessage("pack: " + packid + ", scale: " + scale + ", imagecount=" + imgcount + ", bytecount=" + bytecount);
|
|
scaledcount += imgcount;
|
|
scaledbytecount += bytecount;
|
|
}
|
|
sender.sendMessage("pack: " + packid + ", total: imagecount=" + scaledcount + ", bytecount=" + scaledbytecount);
|
|
packcount += scaledcount;
|
|
packbytecount += scaledbytecount;
|
|
}
|
|
sender.sendMessage("overall total: imagecount=" + packcount + ", bytecount=" + packbytecount + "(" + (packbytecount / 1024.0 / 1024.0) + "Mb)");
|
|
}
|
|
}
|