From 27d922d4708f5033ecf18a8ab62e9dc166a5ce9e Mon Sep 17 00:00:00 2001 From: richardg867 Date: Sun, 21 Apr 2013 21:32:23 -0300 Subject: [PATCH 1/2] Techne model loader (incomplete for debugging) --- .../client/model/AdvancedModelLoader.java | 4 +- .../client/model/techne/TechneModel.java | 308 ++++++++++++++++++ .../model/techne/TechneModelLoader.java | 34 ++ 3 files changed, 345 insertions(+), 1 deletion(-) create mode 100644 client/net/minecraftforge/client/model/techne/TechneModel.java create mode 100644 client/net/minecraftforge/client/model/techne/TechneModelLoader.java diff --git a/client/net/minecraftforge/client/model/AdvancedModelLoader.java b/client/net/minecraftforge/client/model/AdvancedModelLoader.java index d2e888237..b29ca1cdc 100644 --- a/client/net/minecraftforge/client/model/AdvancedModelLoader.java +++ b/client/net/minecraftforge/client/model/AdvancedModelLoader.java @@ -6,6 +6,7 @@ import java.util.Collection; import java.util.Map; import net.minecraftforge.client.model.obj.ObjModelLoader; +import net.minecraftforge.client.model.techne.TechneModelLoader; import com.google.common.collect.Maps; @@ -52,7 +53,7 @@ public class AdvancedModelLoader { FMLLog.severe("The resource name %s is not valid", resourceName); throw new IllegalArgumentException("The resource name is not valid"); } - String suffix = resourceName.substring(i); + String suffix = resourceName.substring(i + 1); IModelCustomLoader loader = instances.get(suffix); if (loader == null) { @@ -78,5 +79,6 @@ public class AdvancedModelLoader { static { registerModelHandler(new ObjModelLoader()); + registerModelHandler(new TechneModelLoader()); } } diff --git a/client/net/minecraftforge/client/model/techne/TechneModel.java b/client/net/minecraftforge/client/model/techne/TechneModel.java new file mode 100644 index 000000000..4e5565080 --- /dev/null +++ b/client/net/minecraftforge/client/model/techne/TechneModel.java @@ -0,0 +1,308 @@ +package net.minecraftforge.client.model.techne; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URL; +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.ZipFile; +import java.util.zip.ZipInputStream; + +import javax.imageio.ImageIO; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.lwjgl.opengl.GL11; +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.renderer.RenderEngine; +import net.minecraftforge.client.model.IModelCustom; +import net.minecraftforge.client.model.ModelFormatException; + +@SideOnly(Side.CLIENT) +public class TechneModel extends ModelBase implements IModelCustom { + public static final List cubeTypes = Arrays.asList( + "d9e621f7-957f-4b77-b1ae-20dcd0da7751", + "de81aa14-bd60-4228-8d8d-5238bcd3caaa" + ); + + private String fileName; + private Map zipFile = new HashMap(); + + private Map parts = new LinkedHashMap(); + private String texture = null; + private int textureName; + private boolean textureNameSet = false; + + public TechneModel(String fileName, URL resource) throws ModelFormatException + { + this.fileName = fileName; + loadTechneModel(resource); + } + + private void loadTechneModel(URL fileURL) throws ModelFormatException + { + try + { + ZipInputStream zipInput = new ZipInputStream(fileURL.openStream()); + + ZipEntry entry; + while ((entry = zipInput.getNextEntry()) != null) + { + byte[] data = new byte[(int) entry.getSize()]; + zipInput.read(data); + zipFile.put(entry.getName(), data); + } + + byte[] modelXml = zipFile.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 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); + } + + Node type = shapeAttributes.getNamedItem("type"); + if (type == null) + { + throw new ModelFormatException("Shape [" + shapeName + "] in " + fileName + " has no type"); + } + String shapeType = type.getNodeValue(); + if (shapeType == null) + { + throw new ModelFormatException("Shape [" + shapeName + "] in " + fileName + " has an invalid type"); + } + if (!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])); + + 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() + { + if (texture != null) + { + if (!textureNameSet) + { + + try + { + byte[] textureEntry = zipFile.get(texture); + if (textureEntry == null) + { + throw new ModelFormatException("Model " + fileName + " has no such texture " + texture); + } + System.out.println(textureEntry.length); + 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) + { + try + { + FileOutputStream fileOutput = new FileOutputStream("techne_texture_debug.png"); + fileOutput.write(zipFile.get(texture)); + fileOutput.flush(); + fileOutput.close(); + } + catch (Throwable e1) + { + } + + 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) + { + bindTexture(); + + ModelRenderer part = parts.get(partName); + if (part != null) + { + part.renderWithRotation(1.0F); + } + } +} diff --git a/client/net/minecraftforge/client/model/techne/TechneModelLoader.java b/client/net/minecraftforge/client/model/techne/TechneModelLoader.java new file mode 100644 index 000000000..0c9f182c8 --- /dev/null +++ b/client/net/minecraftforge/client/model/techne/TechneModelLoader.java @@ -0,0 +1,34 @@ +package net.minecraftforge.client.model.techne; + +import java.net.URL; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +import net.minecraftforge.client.model.IModelCustom; +import net.minecraftforge.client.model.IModelCustomLoader; +import net.minecraftforge.client.model.ModelFormatException; + +@SideOnly(Side.CLIENT) +public class TechneModelLoader implements IModelCustomLoader { + + @Override + public String getType() + { + return "Techne model"; + } + + private static final String[] types = { "tcn" }; + @Override + public String[] getSuffixes() + { + return types; + } + + @Override + public IModelCustom loadInstance(String resourceName, URL resource) throws ModelFormatException + { + return new TechneModel(resourceName, resource); + } + +} From f3caada13abdc5ea151ce6c63555350665ab4440 Mon Sep 17 00:00:00 2001 From: richardg867 Date: Mon, 22 Apr 2013 13:08:59 -0300 Subject: [PATCH 2/2] Complete it, got the bug figured out --- .../client/model/AdvancedModelLoader.java | 2 +- .../client/model/techne/TechneModel.java | 50 ++++++++----------- .../model/techne/TechneModelLoader.java | 1 - 3 files changed, 22 insertions(+), 31 deletions(-) diff --git a/client/net/minecraftforge/client/model/AdvancedModelLoader.java b/client/net/minecraftforge/client/model/AdvancedModelLoader.java index b29ca1cdc..4f2b2deb0 100644 --- a/client/net/minecraftforge/client/model/AdvancedModelLoader.java +++ b/client/net/minecraftforge/client/model/AdvancedModelLoader.java @@ -53,7 +53,7 @@ public class AdvancedModelLoader { FMLLog.severe("The resource name %s is not valid", resourceName); throw new IllegalArgumentException("The resource name is not valid"); } - String suffix = resourceName.substring(i + 1); + String suffix = resourceName.substring(i); IModelCustomLoader loader = instances.get(suffix); if (loader == null) { diff --git a/client/net/minecraftforge/client/model/techne/TechneModel.java b/client/net/minecraftforge/client/model/techne/TechneModel.java index 4e5565080..09d48410e 100644 --- a/client/net/minecraftforge/client/model/techne/TechneModel.java +++ b/client/net/minecraftforge/client/model/techne/TechneModel.java @@ -39,6 +39,9 @@ import net.minecraft.client.renderer.RenderEngine; 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 cubeTypes = Arrays.asList( @@ -47,7 +50,7 @@ public class TechneModel extends ModelBase implements IModelCustom { ); private String fileName; - private Map zipFile = new HashMap(); + private Map zipContents = new HashMap(); private Map parts = new LinkedHashMap(); private String texture = null; @@ -70,11 +73,16 @@ public class TechneModel extends ModelBase implements IModelCustom { while ((entry = zipInput.getNextEntry()) != null) { byte[] data = new byte[(int) entry.getSize()]; - zipInput.read(data); - zipFile.put(entry.getName(), data); + // 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 = zipFile.get("model.xml"); + byte[] modelXml = zipContents.get("model.xml"); if (modelXml == null) { throw new ModelFormatException("Model " + fileName + " contains no model.xml file"); @@ -129,17 +137,13 @@ public class TechneModel extends ModelBase implements IModelCustom { shapeName = "Shape #" + (i + 1); } + String shapeType = null; Node type = shapeAttributes.getNamedItem("type"); - if (type == null) + if (type != null) { - throw new ModelFormatException("Shape [" + shapeName + "] in " + fileName + " has no type"); + shapeType = type.getNodeValue(); } - String shapeType = type.getNodeValue(); - if (shapeType == null) - { - throw new ModelFormatException("Shape [" + shapeName + "] in " + fileName + " has an invalid type"); - } - if (!cubeTypes.contains(shapeType)) + if (shapeType != null && !cubeTypes.contains(shapeType)) { FMLLog.warning("Model shape [" + shapeName + "] in " + fileName + " is not a cube, ignoring"); continue; @@ -235,15 +239,14 @@ public class TechneModel extends ModelBase implements IModelCustom { { if (!textureNameSet) { - try { - byte[] textureEntry = zipFile.get(texture); + byte[] textureEntry = zipContents.get(texture); if (textureEntry == null) { throw new ModelFormatException("Model " + fileName + " has no such texture " + texture); } - System.out.println(textureEntry.length); + BufferedImage image = ImageIO.read(new ByteArrayInputStream(textureEntry)); textureName = Minecraft.getMinecraft().renderEngine.allocateAndSetupTexture(image); textureNameSet = true; @@ -254,17 +257,6 @@ public class TechneModel extends ModelBase implements IModelCustom { } catch (IOException e) { - try - { - FileOutputStream fileOutput = new FileOutputStream("techne_texture_debug.png"); - fileOutput.write(zipFile.get(texture)); - fileOutput.flush(); - fileOutput.close(); - } - catch (Throwable e1) - { - } - throw new ModelFormatException("Texture for model " + fileName + " could not be read", e); } } @@ -296,12 +288,12 @@ public class TechneModel extends ModelBase implements IModelCustom { @Override public void renderPart(String partName) - { - bindTexture(); - + { ModelRenderer part = parts.get(partName); if (part != null) { + bindTexture(); + part.renderWithRotation(1.0F); } } diff --git a/client/net/minecraftforge/client/model/techne/TechneModelLoader.java b/client/net/minecraftforge/client/model/techne/TechneModelLoader.java index 0c9f182c8..c9dd469b3 100644 --- a/client/net/minecraftforge/client/model/techne/TechneModelLoader.java +++ b/client/net/minecraftforge/client/model/techne/TechneModelLoader.java @@ -9,7 +9,6 @@ import net.minecraftforge.client.model.IModelCustom; import net.minecraftforge.client.model.IModelCustomLoader; import net.minecraftforge.client.model.ModelFormatException; -@SideOnly(Side.CLIENT) public class TechneModelLoader implements IModelCustomLoader { @Override