public static HoudiniGeo Create() { HoudiniGeo geo = CreateInstance <HoudiniGeo>(); // Populate it with some default info. geo.fileVersion = "18.5.408"; geo.fileInfo = new HoudiniGeoFileInfo { date = DateTime.Now, software = "Unity " + Application.unityVersion, artist = Environment.UserName, hostname = Environment.MachineName, }; geo.attributes = new HoudiniGeoAttribute[0]; geo.pointRefs = new int[0]; geo.polyPrimitives = new PolyPrimitive[0]; geo.bezierCurvePrimitives = new BezierCurvePrimitive[0]; geo.nurbCurvePrimitives = new NURBCurvePrimitive[0]; geo.primitiveGroups = new PrimitiveGroup[0]; geo.pointGroups = new PointGroup[0]; geo.edgeGroups = new EdgeGroup[0]; return(geo); }
public override void OnInspectorGUI() { if (!isHoudiniGeoFile && !fileCheckPerformed) { string assetPath = AssetDatabase.GetAssetPath(target); isHoudiniGeoFile = assetPath.EndsWith("." + HoudiniGeo.EXTENSION); fileCheckPerformed = true; } else if (!isHoudiniGeoFile) { return; } if (houdiniGeo == null) { string assetPath = AssetDatabase.GetAssetPath(target); string outDir = Path.GetDirectoryName(assetPath); string assetName = Path.GetFileNameWithoutExtension(assetPath); // Parse geo string geoOutputPath = $"{outDir}/{assetName}.asset"; houdiniGeo = AssetDatabase.LoadAllAssetsAtPath(geoOutputPath).FirstOrDefault(a => a is HoudiniGeo) as HoudiniGeo; houdiniGeoInspector = CreateEditor(houdiniGeo); } if (houdiniGeoInspector != null) { GUI.enabled = true; houdiniGeoInspector.DrawDefaultInspector(); } }
public static PointCollection <PointType> GetPoints <PointType>(this HoudiniGeo houdiniGeo) where PointType : PointData { PointCollection <PointType> points = new PointCollection <PointType>(); Type pointType = typeof(PointType); for (int i = 0; i < houdiniGeo.pointCount; i++) { PointType point = (PointType)FormatterServices.GetUninitializedObject(typeof(PointType)); foreach (HoudiniGeoAttribute attribute in houdiniGeo.attributes) { FieldInfo field = pointType.GetField(attribute.name); object value = GetAttributeValue(field.FieldType, attribute, i); if (value != null) { field.SetValue(point, value); } } points.Add(point); } return(points); }
public static void ExportToGeoFile <PointType>( this PointCollection <PointType> pointCollection, string path, bool convertPosition = true) where PointType : PointData { // Check if the filename is valid. if (string.IsNullOrEmpty(path) || string.IsNullOrEmpty(Path.GetFileName(path))) { Debug.LogWarning( $"Tried to export PointCollection<{typeof(PointType).Name}> to invalid path: '{path}'"); return; } // If a relative path is specified, make it an absolute path in the Assets folder. if (string.IsNullOrEmpty(Path.GetDirectoryName(path)) || !Path.IsPathRooted(path)) { path = Path.Combine(Application.dataPath, path); } // Make sure it ends with the Houdini extension. path = Path.ChangeExtension(path, HoudiniGeo.EXTENSION); // Clean up the path a little. path = path.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); HoudiniGeo houdiniGeo = HoudiniGeo.Create(); houdiniGeo.SetPoints(pointCollection, convertPosition); Debug.Log($"Export GEO file '{path}'"); houdiniGeo.Export(path); }
public static bool HasAttribute(this HoudiniGeo geo, string attrName, HoudiniGeoAttributeOwner owner) { if (owner == HoudiniGeoAttributeOwner.Any) { return(geo.attributes.Any(a => a.name == attrName)); } return(geo.attributes.Any(a => a.owner == owner && a.name == attrName)); }
public static bool TryGetAttribute(this HoudiniGeo geo, string attrName, HoudiniGeoAttributeType type, HoudiniGeoAttributeOwner owner, out HoudiniGeoAttribute attr) { if (owner == HoudiniGeoAttributeOwner.Any) { attr = geo.attributes.FirstOrDefault(a => a.type == type && a.name == attrName); } else { attr = geo.attributes.FirstOrDefault(a => a.owner == owner && a.type == type && a.name == attrName); } return(attr != null); }
private static void ParseTopology(HoudiniGeo geo, JToken topologyValueToken) { //"topology",[ // "pointref",[ // "indices",[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18] // ] //], Dictionary <string, JToken> topologyDict = ArrayKeyValueToDictionary(topologyValueToken.Children().ToArray()); Dictionary <string, JToken> pointRefDict = ArrayKeyValueToDictionary(topologyDict["pointref"].Children().ToArray()); geo.pointRefs = pointRefDict["indices"].Values <int>().ToArray(); }
private static HoudiniGeo ParseInternal(string assetPath, HoudiniGeo existingGeo = null) { if (!File.Exists(assetPath)) { throw new FileNotFoundException("File not found: " + assetPath); } // Parse the json JToken mainToken = null; try { mainToken = JToken.Parse(File.ReadAllText(assetPath)); } catch (System.Exception e) { Debug.LogError(string.Format("HoudiniGeoParseError: JSON in file '{0}' could not be parsed", assetPath)); throw e; } // The houdini geo format expects the main element to be an array if (mainToken.Type != JTokenType.Array) { throw new HoudiniGeoParseException("Unexpected type in geo json."); } // The main element is an array that actually functions as a dictionary! Dictionary <string, JToken> geoDataDict = ArrayKeyValueToDictionary(mainToken.Children().ToArray()); HoudiniGeo houdiniGeo = existingGeo; if (houdiniGeo == null) { houdiniGeo = ScriptableObject.CreateInstance <HoudiniGeo>(); } houdiniGeo.sourceAsset = AssetDatabase.LoadMainAssetAtPath(assetPath); houdiniGeo.fileVersion = geoDataDict["fileversion"].ValueSafe <string>(); houdiniGeo.hasIndex = geoDataDict["hasindex"].ValueSafe <bool>(); houdiniGeo.pointCount = geoDataDict["pointcount"].ValueSafe <int>(); houdiniGeo.vertexCount = geoDataDict["vertexcount"].ValueSafe <int>(); houdiniGeo.primCount = geoDataDict["primitivecount"].ValueSafe <int>(); houdiniGeo.fileInfo = ParseFileInfo(geoDataDict["info"] as JObject); ParseTopology(houdiniGeo, geoDataDict["topology"]); ParseAttributes(houdiniGeo, geoDataDict["attributes"]); ParsePrimitives(houdiniGeo, geoDataDict["primitives"]); return(houdiniGeo); }
private void DrawExportSection() { HoudiniGeo houdiniGeo = target as HoudiniGeo; serializedObject.Update(); // Nicely format the export related field and buttons. EditorGUILayout.BeginHorizontal(); GUI.enabled = !string.IsNullOrEmpty(exportPathProperty.stringValue) && exportPathProperty.stringValue.EndsWith("." + HoudiniGeo.EXTENSION); bool pressedExport = GUILayout.Button("Export", GUILayout.Width(75)); GUI.enabled = true; EditorGUILayout.PropertyField(exportPathProperty, GUIContent.none); bool pressedPick = GUILayout.Button("...", GUILayout.Width(25)); EditorGUILayout.EndHorizontal(); if (pressedExport) { HoudiniGeoFileExporter.Export(houdiniGeo); } // Show a nice dialog for picking a directory. if (pressedPick) { string directory, fileName; if (string.IsNullOrEmpty(exportPathProperty.stringValue)) { directory = Application.dataPath; fileName = "Geometry"; } else { directory = Path.GetDirectoryName(exportPathProperty.stringValue); fileName = Path.GetFileName(exportPathProperty.stringValue); } exportPathProperty.stringValue = EditorUtility.SaveFilePanel( "GEO File to Export", directory, fileName, HoudiniGeo.EXTENSION); } serializedObject.ApplyModifiedProperties(); }
private static void ParseAttributes(HoudiniGeo geo, JToken attributesValueToken) { // "attributes",[ // "vertexattributes",[ // [(attribute obj)], // [(attribute obj)], // ... // ], // "pointattributes",[ // [(attribute obj)], // [(attribute obj)], // ... // ], // ... // ], Dictionary <string, JToken> attributeTokensDict = ArrayKeyValueToDictionary(attributesValueToken.Children().ToArray()); // Parse each attribute group var geoAttributes = new List <HoudiniGeoAttribute>(); foreach (var attrKeyVal in ATTRIBUTES_TO_PARSE) { string attrGroupKey = attrKeyVal.Key; HoudiniGeoAttributeOwner attrOwner = attrKeyVal.Value; JToken groupValueToken; if (attributeTokensDict.TryGetValue(attrGroupKey, out groupValueToken)) { // Parse each attribute in group foreach (var attributeToken in groupValueToken.Children()) { var attribute = ParseSingleAttribute(attributeToken, attrOwner); if (attribute != null) { geoAttributes.Add(attribute); } } } } geo.attributes = geoAttributes.ToArray(); }
public override void OnInspectorGUI() { DrawDefaultInspector(); HoudiniGeo houdiniGeo = target as HoudiniGeo; GUILayout.Space(20); GUILayout.BeginHorizontal(); { GUILayout.FlexibleSpace(); if (GUILayout.Button("Reimport Meshes")) { houdiniGeo.ImportAllMeshes(); } } GUILayout.EndHorizontal(); GUILayout.Space(EditorGUIUtility.singleLineHeight); DrawExportSection(); }
internal static void ImportAllMeshes(this HoudiniGeo geo) { string geoAssetPath = AssetDatabase.GetAssetPath(geo); if (!File.Exists(geoAssetPath)) { return; } // Convert to unity mesh and store mesh as sub asset if (geo.polyPrimitives.Length > 0) { var mesh = AssetDatabase.LoadAllAssetsAtPath(geoAssetPath).Where(a => a is Mesh).FirstOrDefault() as Mesh; if (mesh == null) { mesh = new Mesh(); AssetDatabase.AddObjectToAsset(mesh, geoAssetPath); } geo.ToUnityMesh(mesh); EditorUtility.SetDirty(mesh); } }
public static void Export(HoudiniGeo data, string path = null) { if (string.IsNullOrEmpty(path)) { HoudiniGeoFileExporter.path = data.exportPath; } else { HoudiniGeoFileExporter.path = path; } stringWriter = new StringWriter(); stringWriter = new StringWriter(); writer = new JsonTextWriterAdvanced(stringWriter); writer.DateFormatString = DateFormat; writer.Formatting = Formatting.Indented; writer.IndentChar = '\t'; HoudiniGeoFileExporter.data = data; WriteData(); SaveDataToFile(); }
public static void SetPoints <PointType>( this HoudiniGeo houdiniGeo, PointCollection <PointType> pointCollection, bool convertPosition = true) where PointType : PointData { houdiniGeo.pointCount = pointCollection.Count; // First create the attributes. Dictionary <FieldInfo, HoudiniGeoAttribute> attributes = new Dictionary <FieldInfo, HoudiniGeoAttribute>(); Type pointType = typeof(PointType); FieldInfo[] fieldCandidates = pointType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo field in fieldCandidates) { // Ignore private fields that aren't tagged with SerializeField. if (field.IsPrivate && field.GetCustomAttribute <SerializeField>() == null) { continue; } bool wasValidAttributeType = TryCreateAttribute(field, out HoudiniGeoAttribute attribute); if (!wasValidAttributeType) { continue; } attribute.owner = HoudiniGeoAttributeOwner.Point; attributes.Add(field, attribute); } // Then populate the attributes with values. foreach (KeyValuePair <FieldInfo, HoudiniGeoAttribute> kvp in attributes) { List <float> floatValues = new List <float>(); List <int> intValues = new List <int>(); List <string> stringValues = new List <string>(); foreach (PointType point in pointCollection) { object value = kvp.Key.GetValue(point); // If specified, automatically translate the position to Houdini's format. if (convertPosition && kvp.Key.Name == "P") { Vector3 p = Units.ToHoudiniPosition((Vector3)value); value = p; } AddValueAsTuples(value, floatValues, intValues, stringValues); } kvp.Value.floatValues = floatValues.ToArray(); kvp.Value.intValues = intValues.ToArray(); kvp.Value.stringValues = stringValues.ToArray(); } // Then add the attributes to the geometry. houdiniGeo.attributes = new HoudiniGeoAttribute[attributes.Count]; int index = 0; foreach (KeyValuePair <FieldInfo, HoudiniGeoAttribute> kvp in attributes) { houdiniGeo.attributes[index] = kvp.Value; index++; } }
public static void Export(this HoudiniGeo houdiniGeo, string path = null) { HoudiniGeoFileExporter.Export(houdiniGeo, path); }
public static void ToUnityMesh(this HoudiniGeo geo, Mesh mesh) { if (geo.polyPrimitives.Length == 0) { Debug.LogError("Cannot convert HoudiniGeo to Mesh because geo has no PolyPrimitives"); return; } mesh.name = geo.name; int[] indices = geo.polyPrimitives.SelectMany(p => p.indices).ToArray(); int vertexCount = indices.Length; if (vertexCount > 65000) { throw new Exception(string.Format("Vertex count ({0}) exceeds limit of {1}!", geo.vertexCount, 65000)); } // Check if position attribute P exists HoudiniGeoAttribute posAttr = null; if (!geo.TryGetAttribute(HoudiniGeo.POS_ATTR_NAME, HoudiniGeoAttributeType.Float, out posAttr)) { Debug.LogWarning("HoudiniGEO has no Position attribute on points or vertices"); } // Get Vertex/Point positions Vector3[] posAttrValues = null; posAttr.GetValues(out posAttrValues); // Get uv attribute values HoudiniGeoAttribute uvAttr = null; Vector2[] uvAttrValues = null; if (geo.TryGetAttribute(HoudiniGeo.UV_ATTR_NAME, HoudiniGeoAttributeType.Float, out uvAttr)) { uvAttr.GetValues(out uvAttrValues); } // Get uv2 attribute values HoudiniGeoAttribute uv2Attr = null; Vector2[] uv2AttrValues = null; if (geo.TryGetAttribute(HoudiniGeo.UV2_ATTR_NAME, HoudiniGeoAttributeType.Float, out uv2Attr)) { uv2Attr.GetValues(out uv2AttrValues); } // Get normal attribute values HoudiniGeoAttribute normalAttr = null; Vector3[] normalAttrValues = null; if (geo.TryGetAttribute(HoudiniGeo.NORMAL_ATTR_NAME, HoudiniGeoAttributeType.Float, out normalAttr)) { normalAttr.GetValues(out normalAttrValues); } // Get color attribute values HoudiniGeoAttribute colorAttr = null; Color[] colorAttrValues = null; if (geo.TryGetAttribute(HoudiniGeo.COLOR_ATTR_NAME, HoudiniGeoAttributeType.Float, out colorAttr)) { colorAttr.GetValues(out colorAttrValues); // Get alpha color values HoudiniGeoAttribute alphaAttr = null; float[] alphaAttrValues = null; if (geo.TryGetAttribute(HoudiniGeo.ALPHA_ATTR_NAME, HoudiniGeoAttributeType.Float, colorAttr.owner, out alphaAttr)) { alphaAttr.GetValues(out alphaAttrValues); if (colorAttrValues.Length == alphaAttrValues.Length) { for (int i = 0; i < colorAttrValues.Length; i++) { colorAttrValues[i].a = alphaAttrValues[i]; } } } } // Get tangent attribute values HoudiniGeoAttribute tangentAttr = null; Vector3[] tangentAttrValues = null; if (geo.TryGetAttribute(HoudiniGeo.TANGENT_ATTR_NAME, HoudiniGeoAttributeType.Float, out tangentAttr)) { tangentAttr.GetValues(out tangentAttrValues); } // Get material primitive attribute (Multiple materials result in multiple submeshes) HoudiniGeoAttribute materialAttr = null; string[] materialAttributeValues = null; if (geo.TryGetAttribute(HoudiniGeo.MATERIAL_ATTR_NAME, HoudiniGeoAttributeType.String, HoudiniGeoAttributeOwner.Primitive, out materialAttr)) { materialAttr.GetValues(out materialAttributeValues); } // Create our mesh attribute buffers var submeshInfo = new Dictionary <string, List <int> >(); var positions = new Vector3[vertexCount]; var uvs = new Vector2[vertexCount]; // unity doesn't like it when meshes have no uvs var uvs2 = (uv2Attr != null) ? new Vector2[vertexCount] : null; var normals = (normalAttr != null) ? new Vector3[vertexCount] : null; var colors = (colorAttr != null) ? new Color[vertexCount] : null; var tangents = (tangentAttr != null) ? new Vector4[vertexCount] : null; // Fill the mesh buffers int[] vertToPoint = geo.pointRefs; Dictionary <int, int> vertIndexGlobalToLocal = new Dictionary <int, int>(); for (int i = 0; i < vertexCount; ++i) { int vertIndex = indices[i]; int pointIndex = vertToPoint[vertIndex]; vertIndexGlobalToLocal.Add(vertIndex, i); // Position switch (posAttr.owner) { case HoudiniGeoAttributeOwner.Vertex: positions[i] = posAttrValues[vertIndex]; break; case HoudiniGeoAttributeOwner.Point: positions[i] = posAttrValues[pointIndex]; break; } // UV1 if (uvAttr != null) { switch (uvAttr.owner) { case HoudiniGeoAttributeOwner.Vertex: uvs[i] = uvAttrValues[vertIndex]; break; case HoudiniGeoAttributeOwner.Point: uvs[i] = uvAttrValues[pointIndex]; break; } } else { // Unity likes to complain when a mesh doesn't have any UVs so we'll just add a default uvs[i] = Vector2.zero; } // UV2 if (uv2Attr != null) { switch (uv2Attr.owner) { case HoudiniGeoAttributeOwner.Vertex: uvs2[i] = uv2AttrValues[vertIndex]; break; case HoudiniGeoAttributeOwner.Point: uvs2[i] = uv2AttrValues[pointIndex]; break; } } // Normals if (normalAttr != null) { switch (normalAttr.owner) { case HoudiniGeoAttributeOwner.Vertex: normals[i] = normalAttrValues[vertIndex]; break; case HoudiniGeoAttributeOwner.Point: normals[i] = normalAttrValues[pointIndex]; break; } } // Colors if (colorAttr != null) { switch (colorAttr.owner) { case HoudiniGeoAttributeOwner.Vertex: colors[i] = colorAttrValues[vertIndex]; break; case HoudiniGeoAttributeOwner.Point: colors[i] = colorAttrValues[pointIndex]; break; } } // Fill tangents info if (tangentAttr != null) { switch (tangentAttr.owner) { case HoudiniGeoAttributeOwner.Vertex: tangents[i] = tangentAttrValues[vertIndex]; break; case HoudiniGeoAttributeOwner.Point: tangents[i] = tangentAttrValues[pointIndex]; break; } } } // Get primitive attribute values and created submeshes foreach (var polyPrim in geo.polyPrimitives) { // Normals if (normalAttr != null && normalAttr.owner == HoudiniGeoAttributeOwner.Primitive) { foreach (var vertIndex in polyPrim.indices) { int localVertIndex = vertIndexGlobalToLocal[vertIndex]; normals[localVertIndex] = normalAttrValues[polyPrim.id]; } } // Colors if (colorAttr != null && colorAttr.owner == HoudiniGeoAttributeOwner.Primitive) { foreach (var vertIndex in polyPrim.indices) { int localVertIndex = vertIndexGlobalToLocal[vertIndex]; colors[localVertIndex] = colorAttrValues[polyPrim.id]; } } // Add face to submesh based on material attribute var materialName = (materialAttr == null) ? HoudiniGeo.DEFAULT_MATERIAL_NAME : materialAttributeValues[polyPrim.id]; if (!submeshInfo.ContainsKey(materialName)) { submeshInfo.Add(materialName, new List <int>()); } submeshInfo[materialName].AddRange(polyPrim.triangles); } // Assign buffers to mesh mesh.vertices = positions; mesh.subMeshCount = submeshInfo.Count; mesh.uv = uvs; mesh.uv2 = uvs2; mesh.normals = normals; mesh.colors = colors; mesh.tangents = tangents; // Set submesh indexbuffers int submeshIndex = 0; foreach (var item in submeshInfo) { // Skip empty submeshes if (item.Value.Count == 0) { continue; } // Set the indices for the submesh (Reversed by default because axis coordinates Z flipped) IEnumerable <int> submeshIndices = item.Value; if (!geo.importSettings.reverseWinding) { submeshIndices = submeshIndices.Reverse(); } mesh.SetIndices(submeshIndices.ToArray(), MeshTopology.Triangles, submeshIndex); submeshIndex++; } // Calculate any missing buffers mesh.ConvertToUnityCoordinates(); mesh.RecalculateBounds(); if (normalAttr == null) { mesh.RecalculateNormals(); } }
public static bool TryGetAttribute(this HoudiniGeo geo, string attrName, HoudiniGeoAttributeType type, out HoudiniGeoAttribute attr) { attr = geo.attributes.FirstOrDefault(a => a.type == type && a.name == attrName); return(attr != null); }
private static void ParsePrimitives(HoudiniGeo geo, JToken primitivesValueToken) { // Polygon Mesh // Only type: "run", runtiype: "Poly" supported for now. // // "primitives",[ // [ // [Header], // [Body] // ], // [ // [Header], // [Body] // ], // ... // ], int primIdCounter = 0; var polyPrimitives = new List <PolyPrimitive>(); var bezierCurvePrimitives = new List <BezierCurvePrimitive>(); var nurbCurvePrimitives = new List <NURBCurvePrimitive>(); foreach (var primitiveToken in primitivesValueToken.Children()) { // Primitive [[Header], [Body]] //[ // [ // "type","run", // "runtype","Poly", // "varyingfields",["vertex"], // "uniformfields",{ // "closed":true // } // ], // [ // [[0,1,2,3]], // [[4,5,6,7]], // [[8,9,10,11]], // [[12,13,14,15]], // ... // ] //] JToken[] childBlockTokens = primitiveToken.Children().ToArray(); JToken headerToken = childBlockTokens[0]; JToken bodyToken = childBlockTokens[1]; // Parse header Dictionary <string, JToken> headerDict = ArrayKeyValueToDictionary(headerToken.Children().ToArray()); string type = headerDict["type"].Value <string>(); // Parse RunType primitives if (type == "run") { string runType = headerDict["runtype"].Value <string>(); switch (runType) { case "Poly": polyPrimitives.AddRange(ParsePolyPrimitiveGroup(headerDict, bodyToken, primIdCounter)); break; case "BezierCurve": //bezierCurvePrimitives.AddRange(primitives); break; case "NURBCurve": //nurbCurvePrimitives.AddRange(primitives); break; } } } geo.polyPrimitives = polyPrimitives.ToArray(); geo.bezierCurvePrimitives = bezierCurvePrimitives.ToArray(); geo.nurbCurvePrimitives = nurbCurvePrimitives.ToArray(); }
public static void ParseInto(string assetPath, HoudiniGeo existingGeo) { ParseInternal(assetPath, existingGeo); }