Kaynağa Gözat

Support for loading custom properties added.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7675 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
Kae..pl 14 yıl önce
ebeveyn
işleme
fd2184784c

+ 10 - 0
engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/CurvesHelper.java

@@ -30,6 +30,7 @@ import com.jme3.scene.plugins.blender.utils.BlenderInputStream;
 import com.jme3.scene.plugins.blender.utils.DataRepository;
 import com.jme3.scene.plugins.blender.utils.DynamicArray;
 import com.jme3.scene.plugins.blender.utils.Pointer;
+import com.jme3.scene.plugins.blender.utils.Properties;
 import com.jme3.scene.shape.Curve;
 import com.jme3.scene.shape.Surface;
 import com.jme3.util.BufferUtils;
@@ -203,6 +204,15 @@ public class CurvesHelper extends AbstractBlenderHelper {
                 }
             }
         }
+        
+        //reading custom properties
+		Properties properties = this.loadProperties(curveStructure, dataRepository);
+		if(properties != null && properties.getValue() != null) {
+			for(Geometry geom : result) {
+				geom.setUserData("properties", properties);
+			}
+		}
+        
         return result;
     }
 

+ 7 - 0
engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/MeshHelper.java

@@ -58,6 +58,7 @@ import com.jme3.scene.plugins.blender.utils.DataRepository;
 import com.jme3.scene.plugins.blender.utils.DataRepository.LoadedFeatureDataType;
 import com.jme3.scene.plugins.blender.utils.DynamicArray;
 import com.jme3.scene.plugins.blender.utils.Pointer;
+import com.jme3.scene.plugins.blender.utils.Properties;
 import com.jme3.texture.Texture;
 import com.jme3.util.BufferUtils;
 
@@ -292,6 +293,9 @@ public class MeshHelper extends AbstractBlenderHelper {
 					BufferUtils.createFloatBuffer(uvCoordinates.toArray(new Vector2f[uvCoordinates.size()])));
 		}
 
+		//reading custom properties
+		Properties properties = this.loadProperties(structure, dataRepository);
+		
 		// generating meshes
 		FloatBuffer verticesColorsBuffer = this.createFloatBuffer(verticesColors);
 		for (Entry<Integer, List<Integer>> meshEntry : meshesMap.entrySet()) {
@@ -364,6 +368,9 @@ public class MeshHelper extends AbstractBlenderHelper {
 			} else {
 				geometry.setMaterial(dataRepository.getDefaultMaterial());
 			}
+			if(properties != null && properties.getValue() != null) {
+				geometry.setUserData("properties", properties);
+			}
 			geometries.add(geometry);
 		}
 		dataRepository.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, geometries);

+ 8 - 0
engine/src/blender/com/jme3/scene/plugins/blender/helpers/v249/ObjectHelper.java

@@ -62,6 +62,7 @@ import com.jme3.scene.plugins.blender.utils.DataRepository;
 import com.jme3.scene.plugins.blender.utils.DataRepository.LoadedFeatureDataType;
 import com.jme3.scene.plugins.blender.utils.DynamicArray;
 import com.jme3.scene.plugins.blender.utils.Pointer;
+import com.jme3.scene.plugins.blender.utils.Properties;
 import com.jme3.scene.plugins.ogre.AnimData;
 
 /**
@@ -259,6 +260,13 @@ public class ObjectHelper extends AbstractBlenderHelper {
 		} finally {
 			dataRepository.popParent();
 		}
+		
+		//reading custom properties
+		Properties properties = this.loadProperties(objectStructure, dataRepository);
+		if(properties != null && properties.getValue() != null) {
+			((Node)result).setUserData("properties", properties);
+		}
+		
 		if(result != null) {
 			dataRepository.addLoadedFeatures(objectStructure.getOldMemoryAddress(), name, objectStructure, result);
 		}

+ 79 - 54
engine/src/blender/com/jme3/scene/plugins/blender/utils/AbstractBlenderHelper.java

@@ -34,6 +34,8 @@ package com.jme3.scene.plugins.blender.utils;
 import java.nio.FloatBuffer;
 import java.util.List;
 
+import com.jme3.scene.plugins.blender.data.Structure;
+import com.jme3.scene.plugins.blender.exception.BlenderFileException;
 import com.jme3.util.BufferUtils;
 
 /**
@@ -43,62 +45,85 @@ import com.jme3.util.BufferUtils;
  */
 public abstract class AbstractBlenderHelper {
 
-    /** The version of the blend file. */
-    protected final int blenderVersion;
+	/** The version of the blend file. */
+	protected final int	blenderVersion;
 
-    /**
-     * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
-     * versions.
-     * @param blenderVersion
-     *            the version read from the blend file
-     */
-    public AbstractBlenderHelper(String blenderVersion) {
-        this.blenderVersion = Integer.parseInt(blenderVersion);
-    }
+	/**
+	 * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
+	 * versions.
+	 * @param blenderVersion
+	 *        the version read from the blend file
+	 */
+	public AbstractBlenderHelper(String blenderVersion) {
+		this.blenderVersion = Integer.parseInt(blenderVersion);
+	}
 
-    /**
-     * This method clears the state of the helper so that it can be used for different calculations of another feature.
-     */
-    public void clearState() {
-    }
+	/**
+	 * This method clears the state of the helper so that it can be used for different calculations of another feature.
+	 */
+	public void clearState() {}
 
-    /**
-     * This method should be used to check if the text is blank. Avoid using text.trim().length()==0. This causes that more strings are
-     * being created and stored in the memory. It can be unwise especially inside loops.
-     * @param text
-     *            the text to be checked
-     * @return <b>true</b> if the text is blank and <b>false</b> otherwise
-     */
-    protected boolean isBlank(String text) {
-        if (text != null) {
-            for (int i = 0; i < text.length(); ++i) {
-                if (!Character.isWhitespace(text.charAt(i))) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
+	/**
+	 * This method should be used to check if the text is blank. Avoid using text.trim().length()==0. This causes that more strings are
+	 * being created and stored in the memory. It can be unwise especially inside loops.
+	 * @param text
+	 *        the text to be checked
+	 * @return <b>true</b> if the text is blank and <b>false</b> otherwise
+	 */
+	protected boolean isBlank(String text) {
+		if (text != null) {
+			for (int i = 0; i < text.length(); ++i) {
+				if (!Character.isWhitespace(text.charAt(i))) {
+					return false;
+				}
+			}
+		}
+		return true;
+	}
 
-    /**
-     * Generate a new FloatBuffer using the given array of float[4] objects. The FloatBuffer will be 4 * data.length
-     * long and contain the vector data as data[0][0], data[0][1], data[0][2], data[0][3], data[1][0]... etc.
-     * @param data
-     *        list of float[4] objects to place into a new FloatBuffer
-     */
-    protected FloatBuffer createFloatBuffer(List<float[]> data) {
-        if (data == null) {
-            return null;
-        }
-        FloatBuffer buff = BufferUtils.createFloatBuffer(4 * data.size());
-        for (float[] v : data) {
-            if (v != null) {
-                buff.put(v[0]).put(v[1]).put(v[2]).put(v[3]);
-            } else {
-                buff.put(0).put(0).put(0).put(0);
-            }
-        }
-        buff.flip();
-        return buff;
-    }
+	/**
+	 * Generate a new FloatBuffer using the given array of float[4] objects. The FloatBuffer will be 4 * data.length
+	 * long and contain the vector data as data[0][0], data[0][1], data[0][2], data[0][3], data[1][0]... etc.
+	 * @param data
+	 *        list of float[4] objects to place into a new FloatBuffer
+	 */
+	protected FloatBuffer createFloatBuffer(List<float[]> data) {
+		if (data == null) {
+			return null;
+		}
+		FloatBuffer buff = BufferUtils.createFloatBuffer(4 * data.size());
+		for (float[] v : data) {
+			if (v != null) {
+				buff.put(v[0]).put(v[1]).put(v[2]).put(v[3]);
+			} else {
+				buff.put(0).put(0).put(0).put(0);
+			}
+		}
+		buff.flip();
+		return buff;
+	}
+
+	/**
+	 * This method loads the properties if they are available and defined for the structure.
+	 * @param structure
+	 *        the structure we read the properties from
+	 * @param dataRepository
+	 *        the data repository
+	 * @return loaded properties or null if they are not available
+	 * @throws BlenderFileException
+	 *         an exception is thrown when the blend file is somehow corrupted
+	 */
+	protected Properties loadProperties(Structure structure, DataRepository dataRepository) throws BlenderFileException {
+		Properties properties = null;
+		Structure id = (Structure) structure.getFieldValue("ID");
+		if (id != null) {
+			Pointer pProperties = (Pointer) id.getFieldValue("properties");
+			if (!pProperties.isNull()) {
+				Structure propertiesStructure = pProperties.fetchData(dataRepository.getInputStream()).get(0);
+				properties = new Properties();
+				properties.load(propertiesStructure, dataRepository);
+			}
+		}
+		return properties;
+	}
 }

+ 441 - 0
engine/src/blender/com/jme3/scene/plugins/blender/utils/Properties.java

@@ -0,0 +1,441 @@
+package com.jme3.scene.plugins.blender.utils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
+import com.jme3.scene.plugins.blender.data.FileBlockHeader;
+import com.jme3.scene.plugins.blender.data.Structure;
+import com.jme3.scene.plugins.blender.exception.BlenderFileException;
+
+/**
+ * The blender object's custom properties.
+ * This class is valid for all versions of blender.
+ * @author Marcin Roguski (Kaelthas)
+ */
+public class Properties implements Cloneable, Savable {
+	private static final Logger		LOGGER				= Logger.getLogger(Properties.class.getName());
+
+	// property type
+	public static final int			IDP_STRING			= 0;
+	public static final int			IDP_INT				= 1;
+	public static final int			IDP_FLOAT			= 2;
+	public static final int			IDP_ARRAY			= 5;
+	public static final int			IDP_GROUP			= 6;
+	// public static final int IDP_ID = 7;//this is not implemented in blender (yet)
+	public static final int			IDP_DOUBLE			= 8;
+	// the following are valid for blender 2.5x+
+	public static final int			IDP_IDPARRAY		= 9;
+	public static final int			IDP_NUMTYPES		= 10;
+
+	protected static final String	RNA_PROPERTY_NAME	= "_RNA_UI";
+	/** Default name of the property (used if the name is not specified in blender file). */
+	protected static final String	DEFAULT_NAME		= "Unnamed property";
+
+	/** The name of the property. */
+	private String					name;
+	/** The type of the property. */
+	private int						type;
+	/** The subtype of the property. Defines the type of array's elements. */
+	private int						subType;
+	/** The value of the property. */
+	private Object					value;
+	/** The description of the property. */
+	private String					description;
+
+	/**
+	 * This method loads the property from the belnder file.
+	 * @param idPropertyStructure
+	 *        the ID structure constining the property
+	 * @param dataRepository
+	 *        the data repository
+	 * @throws BlenderFileException
+	 *         an exception is thrown when the belnder file is somehow invalid
+	 */
+	public void load(Structure idPropertyStructure, DataRepository dataRepository) throws BlenderFileException {
+		name = idPropertyStructure.getFieldValue("name").toString();
+		if (name == null || name.length() == 0) {
+			name = DEFAULT_NAME;
+		}
+		subType = ((Number) idPropertyStructure.getFieldValue("subtype")).intValue();
+		type = ((Number) idPropertyStructure.getFieldValue("type")).intValue();
+
+		// reading the data
+		Structure data = (Structure) idPropertyStructure.getFieldValue("data");
+		int len = ((Number) idPropertyStructure.getFieldValue("len")).intValue();
+		switch (type) {
+			case IDP_STRING: {
+				Pointer pointer = (Pointer) data.getFieldValue("pointer");
+				BlenderInputStream bis = dataRepository.getInputStream();
+				FileBlockHeader dataFileBlock = dataRepository.getFileBlock(pointer.getOldMemoryAddress());
+				bis.setPosition(dataFileBlock.getBlockPosition());
+				value = bis.readString();
+				break;
+			}
+			case IDP_INT:
+				int intValue = ((Number) data.getFieldValue("val")).intValue();
+				value = Integer.valueOf(intValue);
+				break;
+			case IDP_FLOAT:
+				int floatValue = ((Number) data.getFieldValue("val")).intValue();
+				value = Float.valueOf(Float.intBitsToFloat(floatValue));
+				break;
+			case IDP_ARRAY: {
+				Pointer pointer = (Pointer) data.getFieldValue("pointer");
+				BlenderInputStream bis = dataRepository.getInputStream();
+				FileBlockHeader dataFileBlock = dataRepository.getFileBlock(pointer.getOldMemoryAddress());
+				bis.setPosition(dataFileBlock.getBlockPosition());
+				int elementAmount = dataFileBlock.getSize();
+				switch (subType) {
+					case IDP_INT:
+						elementAmount /= 4;
+						int[] intList = new int[elementAmount];
+						for (int i = 0; i < elementAmount; ++i) {
+							intList[i] = bis.readInt();
+						}
+						value = intList;
+						break;
+					case IDP_FLOAT:
+						elementAmount /= 4;
+						float[] floatList = new float[elementAmount];
+						for (int i = 0; i < elementAmount; ++i) {
+							floatList[i] = bis.readFloat();
+						}
+						value = floatList;
+						break;
+					case IDP_DOUBLE:
+						elementAmount /= 8;
+						double[] doubleList = new double[elementAmount];
+						for (int i = 0; i < elementAmount; ++i) {
+							doubleList[i] = bis.readDouble();
+						}
+						value = doubleList;
+						break;
+					default:
+						throw new IllegalStateException("Invalid array subtype: " + subType);
+				}
+			}
+			case IDP_GROUP:
+				Structure group = (Structure) data.getFieldValue("group");
+				List<Structure> dataList = group.evaluateListBase(dataRepository);
+				List<Properties> subProperties = new ArrayList<Properties>(len);
+				for (Structure d : dataList) {
+					Properties properties = new Properties();
+					properties.load(d, dataRepository);
+					subProperties.add(properties);
+				}
+				value = subProperties;
+				break;
+			case IDP_DOUBLE:
+				int doublePart1 = ((Number) data.getFieldValue("val")).intValue();
+				int doublePart2 = ((Number) data.getFieldValue("val2")).intValue();
+				long doubleVal = (long) doublePart2 << 32 | doublePart1;
+				value = Double.valueOf(Double.longBitsToDouble(doubleVal));
+				break;
+			case IDP_IDPARRAY: {
+				Pointer pointer = (Pointer) data.getFieldValue("pointer");
+				List<Structure> arrays = pointer.fetchData(dataRepository.getInputStream());
+				List<Object> result = new ArrayList<Object>(arrays.size());
+				Properties temp = new Properties();
+				for (Structure array : arrays) {
+					temp.load(array, dataRepository);
+					result.add(temp.value);
+				}
+				this.value = result;
+				break;
+			}
+			case IDP_NUMTYPES:
+				throw new NotImplementedException();
+				// case IDP_ID://not yet implemented in blender
+				// return null;
+			default:
+				throw new IllegalStateException("Unknown custom property type: " + type);
+		}
+		this.completeLoading();
+	}
+
+	/**
+	 * This method returns the name of the property.
+	 * @return the name of the property
+	 */
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * This method returns the description of the property.
+	 * @return the description of the property
+	 */
+	public String getDescription() {
+		return description;
+	}
+
+	/**
+	 * This method returns the type of the property.
+	 * @return the type of the property
+	 */
+	public int getType() {
+		return type;
+	}
+
+	/**
+	 * This method returns the value of the property.
+	 * The type of the value depends on the type of the property.
+	 * @return the value of the property
+	 */
+	public Object getValue() {
+		return value;
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		this.append(sb, new StringBuilder());
+		return sb.toString();
+	}
+
+	/**
+	 * This method appends the data of the property to the given string buffer.
+	 * @param sb
+	 *        string buffer
+	 * @param indent
+	 *        indent buffer
+	 */
+	@SuppressWarnings("unchecked")
+	private void append(StringBuilder sb, StringBuilder indent) {
+		sb.append(indent).append("name: ").append(name).append("\n\r");
+		sb.append(indent).append("type: ").append(type).append("\n\r");
+		sb.append(indent).append("subType: ").append(subType).append("\n\r");
+		sb.append(indent).append("description: ").append(description).append("\n\r");
+		indent.append('\t');
+		sb.append(indent).append("value: ");
+		if (value instanceof Properties) {
+			((Properties) value).append(sb, indent);
+		} else if (value instanceof List) {
+			for (Object v : (List<Object>) value) {
+				if (v instanceof Properties) {
+					sb.append(indent).append("{\n\r");
+					indent.append('\t');
+					((Properties) v).append(sb, indent);
+					indent.deleteCharAt(indent.length() - 1);
+					sb.append(indent).append("}\n\r");
+				} else {
+					sb.append(v);
+				}
+			}
+		} else {
+			sb.append(value);
+		}
+		sb.append("\n\r");
+		indent.deleteCharAt(indent.length() - 1);
+	}
+
+	/**
+	 * This method should be called after the properties loading.
+	 * It loads the properties from the _RNA_UI property and removes this property from the
+	 * result list.
+	 */
+	@SuppressWarnings("unchecked")
+	protected void completeLoading() {
+		if (this.type == IDP_GROUP) {
+			List<Properties> groupProperties = (List<Properties>) this.value;
+			Properties rnaUI = null;
+			for (Properties properties : groupProperties) {
+				if (properties.name.equals(RNA_PROPERTY_NAME) && properties.type == IDP_GROUP) {
+					rnaUI = properties;
+					break;
+				}
+			}
+			if (rnaUI != null) {
+				// removing the RNA from the result list
+				groupProperties.remove(rnaUI);
+
+				// loading the descriptions
+				Map<String, String> descriptions = new HashMap<String, String>(groupProperties.size());
+				List<Properties> propertiesRNA = (List<Properties>) rnaUI.value;
+				for (Properties properties : propertiesRNA) {
+					String name = properties.name;
+					String description = null;
+					List<Properties> rnaData = (List<Properties>) properties.value;
+					for (Properties rna : rnaData) {
+						if ("description".equalsIgnoreCase(rna.name)) {
+							description = (String) rna.value;
+							break;
+						}
+					}
+					descriptions.put(name, description);
+				}
+
+				// applying the descriptions
+				for (Properties properties : groupProperties) {
+					properties.description = descriptions.get(properties.name);
+				}
+			}
+		}
+	}
+
+	@Override
+	@SuppressWarnings("rawtypes")
+	public void write(JmeExporter ex) throws IOException {
+		OutputCapsule oc = ex.getCapsule(this);
+		oc.write(name, "name", DEFAULT_NAME);
+		oc.write(type, "type", 0);
+		oc.write(subType, "subtype", 0);
+		oc.write(description, "description", null);
+		switch (type) {
+			case IDP_STRING:
+				oc.write((String) value, "value", null);
+				break;
+			case IDP_INT:
+				oc.write((Integer) value, "value", 0);
+				break;
+			case IDP_FLOAT:
+				oc.write((Float) value, "value", 0);
+				break;
+			case IDP_ARRAY:
+				switch (subType) {
+					case IDP_INT:
+						oc.write((int[]) value, "value", null);
+						break;
+					case IDP_FLOAT:
+						oc.write((float[]) value, "value", null);
+						break;
+					case IDP_DOUBLE:
+						oc.write((double[]) value, "value", null);
+						break;
+					default:
+						LOGGER.warning("Cannot save the property's value! Invalid array subtype! Property: name: " + name + "; subtype: " + subType);
+				}
+			case IDP_GROUP:
+				oc.write((Properties) value, "value", null);
+				break;
+			case IDP_DOUBLE:
+				oc.write((Double) value, "value", 0);
+				break;
+			case IDP_IDPARRAY:
+				oc.writeSavableArrayList((ArrayList) value, "value", null);
+				break;
+			case IDP_NUMTYPES:
+				LOGGER.warning("Numtypes value not supported! Cannot write it!");
+				break;
+			// case IDP_ID://not yet implemented in blender
+			// break;
+			default:
+				LOGGER.warning("Cannot save the property's value! Invalid type! Property: name: " + name + "; type: " + type);
+		}
+	}
+
+	@Override
+	public void read(JmeImporter im) throws IOException {
+		InputCapsule ic = im.getCapsule(this);
+		name = ic.readString("name", DEFAULT_NAME);
+		type = ic.readInt("type", 0);
+		subType = ic.readInt("subtype", 0);
+		description = ic.readString("description", null);
+		switch (type) {
+			case IDP_STRING:
+				value = ic.readString("value", null);
+				break;
+			case IDP_INT:
+				value = Integer.valueOf(ic.readInt("value", 0));
+				break;
+			case IDP_FLOAT:
+				value = Float.valueOf(ic.readFloat("value", 0.0f));
+				break;
+			case IDP_ARRAY:
+				switch (subType) {
+					case IDP_INT:
+						value = ic.readIntArray("value", null);
+						break;
+					case IDP_FLOAT:
+						value = ic.readFloatArray("value", null);
+						break;
+					case IDP_DOUBLE:
+						value = ic.readDoubleArray("value", null);
+						break;
+					default:
+						LOGGER.warning("Cannot read the property's value! Invalid array subtype! Property: name: " + name + "; subtype: " + subType);
+				}
+			case IDP_GROUP:
+				value = ic.readSavable("value", null);
+				break;
+			case IDP_DOUBLE:
+				value = Double.valueOf(ic.readDouble("value", 0.0));
+				break;
+			case IDP_IDPARRAY:
+				value = ic.readSavableArrayList("value", null);
+				break;
+			case IDP_NUMTYPES:
+				LOGGER.warning("Numtypes value not supported! Cannot read it!");
+				break;
+			// case IDP_ID://not yet implemented in blender
+			// break;
+			default:
+				LOGGER.warning("Cannot read the property's value! Invalid type! Property: name: " + name + "; type: " + type);
+		}
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + (description == null ? 0 : description.hashCode());
+		result = prime * result + (name == null ? 0 : name.hashCode());
+		result = prime * result + subType;
+		result = prime * result + type;
+		result = prime * result + (value == null ? 0 : value.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj) {
+			return true;
+		}
+		if (obj == null) {
+			return false;
+		}
+		if (this.getClass() != obj.getClass()) {
+			return false;
+		}
+		Properties other = (Properties) obj;
+		if (description == null) {
+			if (other.description != null) {
+				return false;
+			}
+		} else if (!description.equals(other.description)) {
+			return false;
+		}
+		if (name == null) {
+			if (other.name != null) {
+				return false;
+			}
+		} else if (!name.equals(other.name)) {
+			return false;
+		}
+		if (subType != other.subType) {
+			return false;
+		}
+		if (type != other.type) {
+			return false;
+		}
+		if (value == null) {
+			if (other.value != null) {
+				return false;
+			}
+		} else if (!value.equals(other.value)) {
+			return false;
+		}
+		return true;
+	}
+}