package net.minecraftforge.client.model.techne; import java.awt.Dimension; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipInputStream; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import cpw.mods.fml.common.FMLLog; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.client.Minecraft; import net.minecraft.client.model.ModelBase; import net.minecraft.client.model.ModelRenderer; import net.minecraft.client.resources.IResource; import net.minecraft.util.ResourceLocation; import net.minecraftforge.client.model.IModelCustom; import net.minecraftforge.client.model.ModelFormatException; /** * Techne model importer, based on iChun's Hats importer */ @SideOnly(Side.CLIENT) public class TechneModel extends ModelBase implements IModelCustom { public static final List<String> cubeTypes = Arrays.asList( "d9e621f7-957f-4b77-b1ae-20dcd0da7751", "de81aa14-bd60-4228-8d8d-5238bcd3caaa" ); private String fileName; private Map<String, byte[]> zipContents = new HashMap<String, byte[]>(); private Map<String, ModelRenderer> parts = new LinkedHashMap<String, ModelRenderer>(); private String texture = null; private Dimension textureDims = null; private int textureName; private boolean textureNameSet = false; public TechneModel(ResourceLocation resource) throws ModelFormatException { this.fileName = resource.toString(); try { IResource res = Minecraft.getMinecraft().getResourceManager().getResource(resource); loadTechneModel(res.getInputStream()); } catch (IOException e) { throw new ModelFormatException("IO Exception reading model format", e); } } private void loadTechneModel(InputStream stream) throws ModelFormatException { try { ZipInputStream zipInput = new ZipInputStream(stream); ZipEntry entry; while ((entry = zipInput.getNextEntry()) != null) { byte[] data = new byte[(int) entry.getSize()]; // For some reason, using read(byte[]) makes reading stall upon reaching a 0x1E byte int i = 0; while (zipInput.available() > 0 && i < data.length) { data[i++] = (byte)zipInput.read(); } zipContents.put(entry.getName(), data); } byte[] modelXml = zipContents.get("model.xml"); if (modelXml == null) { throw new ModelFormatException("Model " + fileName + " contains no model.xml file"); } DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); Document document = documentBuilder.parse(new ByteArrayInputStream(modelXml)); NodeList nodeListTechne = document.getElementsByTagName("Techne"); if (nodeListTechne.getLength() < 1) { throw new ModelFormatException("Model " + fileName + " contains no Techne tag"); } NodeList nodeListModel = document.getElementsByTagName("Model"); if (nodeListModel.getLength() < 1) { throw new ModelFormatException("Model " + fileName + " contains no Model tag"); } NamedNodeMap modelAttributes = nodeListModel.item(0).getAttributes(); if (modelAttributes == null) { throw new ModelFormatException("Model " + fileName + " contains a Model tag with no attributes"); } Node modelTexture = modelAttributes.getNamedItem("texture"); if (modelTexture != null) { texture = modelTexture.getTextContent(); } NodeList textureDim = document.getElementsByTagName("TextureSize"); if (textureDim.getLength() > 0) { try { String[] tmp = textureDim.item(0).getTextContent().split(","); if (tmp.length == 2) { this.textureDims = new Dimension(Integer.parseInt(tmp[0]), Integer.parseInt(tmp[1])); } } catch (NumberFormatException e) { throw new ModelFormatException("Model " + fileName + " contains a TextureSize tag with invalid data"); } } NodeList shapes = document.getElementsByTagName("Shape"); for (int i = 0; i < shapes.getLength(); i++) { Node shape = shapes.item(i); NamedNodeMap shapeAttributes = shape.getAttributes(); if (shapeAttributes == null) { throw new ModelFormatException("Shape #" + (i + 1) + " in " + fileName + " has no attributes"); } Node name = shapeAttributes.getNamedItem("name"); String shapeName = null; if (name != null) { shapeName = name.getNodeValue(); } if (shapeName == null) { shapeName = "Shape #" + (i + 1); } String shapeType = null; Node type = shapeAttributes.getNamedItem("type"); if (type != null) { shapeType = type.getNodeValue(); } if (shapeType != null && !cubeTypes.contains(shapeType)) { FMLLog.warning("Model shape [" + shapeName + "] in " + fileName + " is not a cube, ignoring"); continue; } try { boolean mirrored = false; String[] offset = new String[3]; String[] position = new String[3]; String[] rotation = new String[3]; String[] size = new String[3]; String[] textureOffset = new String[2]; NodeList shapeChildren = shape.getChildNodes(); for (int j = 0; j < shapeChildren.getLength(); j++) { Node shapeChild = shapeChildren.item(j); String shapeChildName = shapeChild.getNodeName(); String shapeChildValue = shapeChild.getTextContent(); if (shapeChildValue != null) { shapeChildValue = shapeChildValue.trim(); if (shapeChildName.equals("IsMirrored")) { mirrored = !shapeChildValue.equals("False"); } else if (shapeChildName.equals("Offset")) { offset = shapeChildValue.split(","); } else if (shapeChildName.equals("Position")) { position = shapeChildValue.split(","); } else if (shapeChildName.equals("Rotation")) { rotation = shapeChildValue.split(","); } else if (shapeChildName.equals("Size")) { size = shapeChildValue.split(","); } else if (shapeChildName.equals("TextureOffset")) { textureOffset = shapeChildValue.split(","); } } } // That's what the ModelBase subclassing is needed for ModelRenderer cube = new ModelRenderer(this, Integer.parseInt(textureOffset[0]), Integer.parseInt(textureOffset[1])); cube.mirror = mirrored; cube.addBox(Float.parseFloat(offset[0]), Float.parseFloat(offset[1]), Float.parseFloat(offset[2]), Integer.parseInt(size[0]), Integer.parseInt(size[1]), Integer.parseInt(size[2])); cube.setRotationPoint(Float.parseFloat(position[0]), Float.parseFloat(position[1]) - 23.4F, Float.parseFloat(position[2])); cube.rotateAngleX = (float)Math.toRadians(Float.parseFloat(rotation[0])); cube.rotateAngleY = (float)Math.toRadians(Float.parseFloat(rotation[1])); cube.rotateAngleZ = (float)Math.toRadians(Float.parseFloat(rotation[2])); if (this.textureDims != null) { cube.setTextureSize((int)textureDims.getWidth(), (int)textureDims.getHeight()); } parts.put(shapeName, cube); } catch (NumberFormatException e) { FMLLog.warning("Model shape [" + shapeName + "] in " + fileName + " contains malformed integers within its data, ignoring"); e.printStackTrace(); } } } catch (ZipException e) { throw new ModelFormatException("Model " + fileName + " is not a valid zip file"); } catch (IOException e) { throw new ModelFormatException("Model " + fileName + " could not be read", e); } catch (ParserConfigurationException e) { // hush } catch (SAXException e) { throw new ModelFormatException("Model " + fileName + " contains invalid XML", e); } } private void bindTexture() { /* TODO: Update to 1.6 if (texture != null) { if (!textureNameSet) { try { byte[] textureEntry = zipContents.get(texture); if (textureEntry == null) { throw new ModelFormatException("Model " + fileName + " has no such texture " + texture); } BufferedImage image = ImageIO.read(new ByteArrayInputStream(textureEntry)); textureName = Minecraft.getMinecraft().renderEngine.allocateAndSetupTexture(image); textureNameSet = true; } catch (ZipException e) { throw new ModelFormatException("Model " + fileName + " is not a valid zip file"); } catch (IOException e) { throw new ModelFormatException("Texture for model " + fileName + " could not be read", e); } } if (textureNameSet) { GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureName); Minecraft.getMinecraft().renderEngine.resetBoundTexture(); } } */ } @Override public String getType() { return "tcn"; } @Override public void renderAll() { bindTexture(); for (ModelRenderer part : parts.values()) { part.renderWithRotation(1.0F); } } @Override public void renderPart(String partName) { ModelRenderer part = parts.get(partName); if (part != null) { bindTexture(); part.renderWithRotation(1.0F); } } @Override public void renderOnly(String... groupNames) { bindTexture(); for (ModelRenderer part : parts.values()) { for (String groupName : groupNames) { if (groupName.equalsIgnoreCase(part.boxName)) { part.render(1.0f); } } } } @Override public void renderAllExcept(String... excludedGroupNames) { for (ModelRenderer part : parts.values()) { boolean skipPart=false; for (String excludedGroupName : excludedGroupNames) { if (excludedGroupName.equalsIgnoreCase(part.boxName)) { skipPart=true; } } if(!skipPart) { part.render(1.0f); } } } }