/* ** 2011 April 5 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. */ package info.ata4.bspsrc.modules.texture; import info.ata4.bsplib.BspFileReader; import info.ata4.bspsrc.modules.ModuleRead; import info.ata4.log.LogUtils; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.io.FilenameUtils; /** * Decompiling module to create Texture objects from texture data and to fix * texture strings. * * Based on texture building part of Vmex.writeside(), Vmex.fixtexturenames() * and BSP.gettex() * * @author Nico Bergemann <barracuda415 at yahoo.de> */ public class TextureSource extends ModuleRead { // logger private static final Logger L = LogUtils.getLogger(); // regex patterns for texture name fixing private final Pattern originPattern = Pattern.compile("_(-?\\d+)_(-?\\d+)_(-?\\d+)?$"); // cubemap position private final Pattern wvtPatchPattern = Pattern.compile("_wvt_patch$"); // world vertex patch private final Pattern waterPatchPattern = Pattern.compile("_depth_(-?\\d+)$"); // water texture patch private final Pattern mapPattern; // ID mappings private Map<Integer, Set<Integer>> cubemapToSideList = new HashMap<>(); private Map<Integer, Integer> texnameToCubemap = new HashMap<>(); private List<String> texnamesFixed = new ArrayList<>(); // settings private boolean fixTextureNames; private boolean fixToolTextures; public TextureSource(BspFileReader reader) { super(reader); mapPattern = Pattern.compile("^maps/" + reader.getBspFile().getName() + "/"); reader.loadTexInfo(); reader.loadTexData(); reader.loadCubemaps(); processTextureNames(); } /** * Converts environment-mapped texture names to original texture names and * performs some cleanups. It also assigns cubemap IDs to texname IDs which * is later used to create the "sides" properties of env_cubemap entities. * * Following operations will be performed: * - chop "maps/mapname/" from start of names * - chop "_n_n_n or _n_n_n_depth_n from end of names */ private void processTextureNames() { for (int i = 0; i < bsp.texnames.size(); i++) { String textureOld = bsp.texnames.get(i); String textureNew = textureOld; textureNew = canonizeTextureName(textureNew); // search for "maps/<mapname>" prefix Matcher matcher = mapPattern.matcher(textureNew); if (matcher.find()) { // remove it textureNew = matcher.replaceFirst(""); // search for "_wvt_patch" suffix matcher = wvtPatchPattern.matcher(textureNew); if (matcher.find()) { // remove it textureNew = matcher.replaceFirst(""); } // search for "_depth_xxx" suffix matcher = waterPatchPattern.matcher(textureNew); if (matcher.find()) { // remove it textureNew = matcher.replaceFirst(""); } // search for origin coordinates matcher = originPattern.matcher(textureNew); if (matcher.find()) { // get origin int cx = Integer.valueOf(matcher.group(1)); int cy = Integer.valueOf(matcher.group(2)); int cz = Integer.valueOf(matcher.group(3)); setCubemapForTexname(i, cx, cy, cz); // remove origin coordinates textureNew = matcher.replaceFirst(""); } } // log differences if (!textureNew.equalsIgnoreCase(textureOld)) { L.log(Level.FINEST, "{0} -> {1}", new Object[] {textureOld, textureNew}); } texnamesFixed.add(textureNew); } } private void setCubemapForTexname(int itexname, int cx, int cy, int cz) { // search for cubemap with these coordinates for (int i = 0; i < bsp.cubemaps.size(); i++) { int[] origin = bsp.cubemaps.get(i).origin; if (cx == origin[0] || cy == origin[1] || cz == origin[2]) { if (L.isLoggable(Level.FINEST)) { L.log(Level.FINEST, "TN: {0} C: {1}", new Object[]{itexname, i}); } // set cubemap index used by this texdata/texname texnameToCubemap.put(itexname, i); return; } } L.log(Level.FINER, "Couldn''t find cubemap for coordinates ({0}, {1}, {2})", new Object[]{cx, cy, cz}); } public TextureBuilder getTextureBuilder() { return new TextureBuilder(this, bsp); } public void addBrushSideID(int itexname, int side) { Integer icubemap = texnameToCubemap.get(itexname); if (icubemap == null) { // not environment mapped return; } Set<Integer> sides = cubemapToSideList.get(icubemap); // create new side list if required if (sides == null) { sides = new HashSet<>(); cubemapToSideList.put(icubemap, sides); } sides.add(side); } public Set<Integer> getBrushSidesForCubemap(int icubemap) { return cubemapToSideList.get(icubemap); } /** * Returns the texture name string for a texinfo index. * * @param itexinfo texinfo index * @return texture name string */ public String getTextureName(short itexinfo) { try { int ti = bsp.texinfos.get(itexinfo).texdata; int td = bsp.texdatas.get(ti).texname; if (fixTextureNames) { return texnamesFixed.get(td); } else { return bsp.texnames.get(td); } } catch (ArrayIndexOutOfBoundsException ex) { return ToolTexture.SKIP; } } public String canonizeTextureName(String textureNew) { // convert to lower case textureNew = textureNew.toLowerCase(); // fix separators textureNew = FilenameUtils.separatorsToUnix(textureNew); return textureNew; } public List<String> getFixedTextureNames() { return Collections.unmodifiableList(texnamesFixed); } public boolean isFixTextureNames() { return fixTextureNames; } public void setFixTextureNames(boolean fixTextureNames) { this.fixTextureNames = fixTextureNames; } public boolean isFixToolTextures() { return fixToolTextures; } public void setFixToolTextures(boolean fixToolTextures) { this.fixToolTextures = fixToolTextures; } }