/// <summary> /// Deserialize a single property to a single value. /// </summary> /// <param name="propValue">The referenced value of the property to populate.</param> /// <param name="prim">The USD prim from which to read the value.</param> /// <param name="usdTime">The time at which to sample key frames.</param> /// <param name="field">The field to deserialize.</param> /// <param name="accessMap">A list of members to includ when reading.</param> /// <param name="mayVary">If non-null, is populated to indicate if the value varies over time.</param> /// <param name="usdNamespace">The USD namespace, if any.</param> public void Deserialize(ref object propValue, pxr.UsdPrim prim, pxr.UsdTimeCode usdTime, PropertyInfo field, HashSet <MemberInfo> accessMap, ref bool?mayVary, string usdNamespace = null) { if (Reflect.IsNonSerialized(field)) { return; } if (!ReadAttr(field.Name, field.PropertyType, ref propValue, usdTime, prim, field, accessMap, ref mayVary, usdNamespace)) { // TODO: add options to dictate behavior here } }
/// <summary> /// Serializes an arbitrary object descending from SampleBase from C# to USD. /// </summary> /// <typeparam name="T">Any type which inherits from SampleBase</typeparam> /// <param name="t">The object/data to be serialized.</param> /// <param name="prim">The UsdPrim to which the object should be written.</param> /// <param name="usdTime">The tiem at which key frames should be created.</param> /// <param name="usdNamespace">The USD namespace (if any) of the object.</param> public void Serialize <T>(T t, pxr.UsdPrim prim, pxr.UsdTimeCode usdTime, string usdNamespace = null) { PropertyInfo[] properties = Reflect.GetCachedProperties(t.GetType()); FieldInfo[] fields = Reflect.GetCachedFields(t.GetType()); var imgble = new pxr.UsdGeomImageable(prim); for (int i = 0; i < properties.Length; i++) { PropertyInfo csProp = properties[i]; Type csType = csProp.PropertyType; if (csType == typeof(object)) { if (Reflect.IsCustomData(csProp) || Reflect.IsMetadata(csProp)) { throw new ArgumentException("Writing metadata/customdata with type of object is not currently allowed"); } object o = csProp.GetValue(t, index: null); if (o != null) { csType = o.GetType(); } } if (!WriteAttr(csProp.Name, csType, csProp.GetValue(t, index:null), usdTime, prim, imgble, csProp, usdNamespace)) { // TODO: add options to dictate behavior here } } for (int i = 0; i < fields.Length; i++) { FieldInfo csField = fields[i]; Type csType = csField.FieldType; if (csType == typeof(object)) { if (Reflect.IsCustomData(csField) || Reflect.IsMetadata(csField)) { throw new ArgumentException("Writing metadata/customdata with type of object is not currently allowed"); } object o = csField.GetValue(t); if (o != null) { csType = o.GetType(); } } if (!WriteAttr(csField.Name, csType, csField.GetValue(t), usdTime, prim, imgble, csField, usdNamespace)) { // TODO: add options to dictate behavior here } } }
/// <summary> /// Loads the variant sets and selection state from USD into the UsdVariantSet behaviour. /// </summary> public void LoadFromUsd(pxr.UsdPrim prim) { var variantSets = prim.GetVariantSets(); var setNames = variantSets.GetNames(); m_variantSetNames = setNames.ToArray(); m_selected = m_variantSetNames.Select(setName => variantSets.GetVariantSelection(setName)).ToArray(); m_variants = m_variantSetNames.SelectMany(setName => variantSets.GetVariantSet(setName).GetVariantNames()).ToArray(); m_variantCounts = m_variantSetNames.Select(setName => variantSets.GetVariantSet(setName).GetVariantNames().Count).ToArray(); m_primPath = prim.GetPath(); }
static void CreateTextureUris(pxr.UsdPrim shaderPrim, IExportableMaterial material) { if (material.SupportsDetailedMaterialInfo) { foreach (var kvp in material.TextureUris) { var attr = shaderPrim.CreateAttribute(new pxr.TfToken("info:textureUris:" + kvp.Key), SdfValueTypeNames.String); attr.Set(kvp.Value); } } }
/// <summary> /// Exports a single GameObject to USD, does not export components. /// </summary> static void ObjectToUsd(GameObject gameObj, pxr.UsdPrim prim, Scene scene) { var obj = new SerializedObject(gameObj); var sb = new System.Text.StringBuilder(); var path = prim.GetPath().ToString(); sb.AppendLine("Visited: " + path); prim.SetCustomDataByKey(new pxr.TfToken("unity:name"), new pxr.TfToken(gameObj.name)); var itr = obj.GetIterator(); itr.Next(true); PropertyToUsd(path, "", scene, itr, sb); }
/// <summary> /// Deserialize a single field to a single value. /// </summary> /// <param name="fieldValue">The referenced value of the field to populate.</param> /// <param name="prim">The USD prim from which to read the value.</param> /// <param name="usdTime">The time at which to sample key frames.</param> /// <param name="field">The field to deserialize.</param> /// <param name="usdNamespace">The USD namespace, if any.</param> public void Deserialize(ref object fieldValue, pxr.UsdPrim prim, pxr.UsdTimeCode usdTime, FieldInfo field, string usdNamespace = null) { if (Reflect.IsNonSerialized(field)) { return; } if (!ReadAttr(field.Name, field.FieldType, ref fieldValue, usdTime, prim, field, usdNamespace)) { // TODO: add options to dictate behavior here } }
/// <summary> /// Deserializes an arbitrary object descending from SampleBase from USD to C#. /// </summary> /// <typeparam name="T">The type to serialize, descending from SampleBase</typeparam> /// <param name="t">The object to to populate.</param> /// <param name="prim">The USD prim from which to read data.</param> /// <param name="usdTime">The time at which to read key frames.</param> /// <param name="usdNamespace">The object namespace, if any.</param> public void Deserialize <T>(T t, pxr.UsdPrim prim, pxr.UsdTimeCode usdTime, string usdNamespace = null) where T : SampleBase { if (t == null) { return; } PropertyInfo[] properties = Reflect.GetCachedProperties(t.GetType()); FieldInfo[] fields = Reflect.GetCachedFields(t.GetType()); object value = t; for (int i = 0; i < properties.Length; i++) { PropertyInfo csProp = properties[i]; if (Reflect.IsNonSerialized(csProp)) { continue; } object propValue = csProp.GetValue(t, null); Deserialize(ref propValue, prim, usdTime, csProp, usdNamespace); csProp.SetValue(t, propValue, index: null); } for (int i = 0; i < fields.Length; i++) { FieldInfo csField = fields[i]; if (Reflect.IsNonSerialized(csField)) { continue; } object fieldValue = csField.GetValue(t); Deserialize(ref fieldValue, prim, usdTime, csField, usdNamespace); csField.SetValue(t, fieldValue); } t = (T)value; }
// -------------------------------------------------------------------------------------------- // // Deserialize USD to -> Unity // -------------------------------------------------------------------------------------------- // static public void ImportObject(Scene scene, GameObject go, pxr.UsdPrim usdPrim, SceneImportOptions options) { if (!options.importMonoBehaviours) { return; } var comps = usdPrim.GetAuthoredPropertiesInNamespace("unity:component"); foreach (var compProp in comps) { var compAttr = usdPrim.GetAttribute(compProp.GetName()); string assemblyQualifiedName = (string)compAttr.Get(0); var compType = System.Type.GetType(assemblyQualifiedName); // TODO: Handle multiple components of the same type. Component comp = go.GetComponent(compType); if (comp == null) { comp = go.AddComponent(compType); } var so = new SerializedObject(comp); var prop = so.GetIterator(); prop.Next(true); var sb = new System.Text.StringBuilder(); // TODO: Handle multiple components of the same type. PropertyFromUsd(usdPrim, prop, sb, comp.GetType().Name); so.ApplyModifiedProperties(); Debug.Log(sb.ToString()); } }
static void ExportMesh(ObjectContext objContext, ExportContext exportContext, Mesh mesh, Material sharedMaterial, Material[] sharedMaterials, bool exportMeshPose = true) { string path = objContext.path; if (mesh == null) { Debug.LogWarning("Null mesh for: " + path, objContext.gameObject); return; } #if UNITY_EDITOR if (!CanReadMesh(mesh)) { #else if (!mesh.isReadable) { #endif Debug.LogError( "Mesh is not readable: " + objContext.path + ". To fix this, enable read/write in the inspector for the source asset that you are attempting to export.", objContext.gameObject); return; } var scene = exportContext.scene; bool unvarying = scene.Time == null; bool slowAndSafeConversion = exportContext.basisTransform == BasisTransformation.SlowAndSafe; var sample = (MeshSample)objContext.sample; var go = objContext.gameObject; if (mesh.bounds.center == Vector3.zero && mesh.bounds.extents == Vector3.zero) { mesh.RecalculateBounds(); } sample.extent = mesh.bounds; if (slowAndSafeConversion) { // Unity uses a forward vector that matches DirectX, but USD matches OpenGL, so a change of // basis is required. There are shortcuts, but this is fully general. sample.ConvertTransform(); sample.extent.center = UnityTypeConverter.ChangeBasis(sample.extent.center); } // Only export the mesh topology on the first frame. if (unvarying) { // TODO: Technically a mesh could be the root transform, which is not handled correctly here. // It should have the same logic for root prims as in ExportXform. sample.transform = XformExporter.GetLocalTransformMatrix( go.transform, scene.UpAxis == Scene.UpAxes.Z, new pxr.SdfPath(path).IsRootPrimPath(), exportContext.basisTransform); sample.normals = mesh.normals; sample.points = mesh.vertices; sample.tangents = mesh.tangents; sample.colors = mesh.colors; if (sample.colors != null && sample.colors.Length == 0) { sample.colors = null; } if ((sample.colors == null || sample.colors.Length == 0) && (sharedMaterial != null && sharedMaterial.HasProperty("_Color"))) { sample.colors = new Color[1]; sample.colors[0] = sharedMaterial.color.linear; } // Gah. There is no way to inspect a meshes UVs. sample.st = mesh.uv; // Set face vertex counts and indices. var tris = mesh.triangles; if (slowAndSafeConversion) { // Unity uses a forward vector that matches DirectX, but USD matches OpenGL, so a change // of basis is required. There are shortcuts, but this is fully general. for (int i = 0; i < sample.points.Length; i++) { sample.points[i] = UnityTypeConverter.ChangeBasis(sample.points[i]); if (sample.normals != null && sample.normals.Length == sample.points.Length) { sample.normals[i] = UnityTypeConverter.ChangeBasis(sample.normals[i]); } if (sample.tangents != null && sample.tangents.Length == sample.points.Length) { var w = sample.tangents[i].w; var t = UnityTypeConverter.ChangeBasis(sample.tangents[i]); sample.tangents[i] = new Vector4(t.x, t.y, t.z, w); } } for (int i = 0; i < tris.Length; i += 3) { var t = tris[i]; tris[i] = tris[i + 1]; tris[i + 1] = t; } } sample.SetTriangles(tris); UnityEngine.Profiling.Profiler.BeginSample("USD: Mesh Write"); scene.Write(path, sample); UnityEngine.Profiling.Profiler.EndSample(); // TODO: this is a bit of a half-measure, we need real support for primvar interpolation. // Set interpolation based on color count. if (sample.colors != null && sample.colors.Length == 1) { pxr.UsdPrim usdPrim = scene.GetPrimAtPath(path); var colorPrimvar = new pxr.UsdGeomPrimvar(usdPrim.GetAttribute(pxr.UsdGeomTokens.primvarsDisplayColor)); colorPrimvar.SetInterpolation(pxr.UsdGeomTokens.constant); var opacityPrimvar = new pxr.UsdGeomPrimvar(usdPrim.GetAttribute(pxr.UsdGeomTokens.primvarsDisplayOpacity)); opacityPrimvar.SetInterpolation(pxr.UsdGeomTokens.constant); } string usdMaterialPath; if (exportContext.exportMaterials && sharedMaterial != null) { if (!exportContext.matMap.TryGetValue(sharedMaterial, out usdMaterialPath)) { Debug.LogError("Invalid material bound for: " + path); } else { MaterialSample.Bind(scene, path, usdMaterialPath); } } // In USD subMeshes are represented as UsdGeomSubsets. // When there are multiple subMeshes, convert them into UsdGeomSubsets. if (mesh.subMeshCount > 1) { // Build a table of face indices, used to convert the subMesh triangles to face indices. var faceTable = new Dictionary <Vector3, int>(); for (int i = 0; i < tris.Length; i += 3) { if (!slowAndSafeConversion) { faceTable.Add(new Vector3(tris[i], tris[i + 1], tris[i + 2]), i / 3); } else { // Under slow and safe export, index 0 and 1 are swapped. // This swap will not be present in the subMesh indices, so must be undone here. faceTable.Add(new Vector3(tris[i + 1], tris[i], tris[i + 2]), i / 3); } } var usdPrim = scene.GetPrimAtPath(path); var usdGeomMesh = new pxr.UsdGeomMesh(usdPrim); // Process each subMesh and create a UsdGeomSubset of faces this subMesh targets. for (int si = 0; si < mesh.subMeshCount; si++) { int[] indices = mesh.GetTriangles(si); int[] faceIndices = new int[indices.Length / 3]; for (int i = 0; i < indices.Length; i += 3) { faceIndices[i / 3] = faceTable[new Vector3(indices[i], indices[i + 1], indices[i + 2])]; } var vtIndices = UnityTypeConverter.ToVtArray(faceIndices); var subset = pxr.UsdGeomSubset.CreateUniqueGeomSubset( usdGeomMesh, // The object of which this subset belongs. m_subMeshesToken, // An arbitrary name for the subset. pxr.UsdGeomTokens.face, // Indicator that these represent face indices vtIndices, // The actual face indices. m_materialBindToken // familyName = "materialBind" ); if (exportContext.exportMaterials) { if (si >= sharedMaterials.Length || !sharedMaterials[si] || !exportContext.matMap.TryGetValue(sharedMaterials[si], out usdMaterialPath)) { Debug.LogWarning("Invalid material bound for: " + path + "\n" + (si >= sharedMaterials.Length ? "More submeshes than materials assigned." : (!sharedMaterials[si] ? "Submesh " + si + " has null material" : "ExportMap can't map material"))); } else { MaterialSample.Bind(scene, subset.GetPath(), usdMaterialPath); } } } } } else { // Only write the transform when animating. var meshSample = new MeshSampleBase(); meshSample.extent = sample.extent; meshSample.transform = XformExporter.GetLocalTransformMatrix( go.transform, scene.UpAxis == Scene.UpAxes.Z, new pxr.SdfPath(path).IsRootPrimPath(), exportContext.basisTransform); if (exportMeshPose) { meshSample.points = mesh.vertices; // Set face vertex counts and indices. var tris = mesh.triangles; if (slowAndSafeConversion) { // Unity uses a forward vector that matches DirectX, but USD matches OpenGL, so a change // of basis is required. There are shortcuts, but this is fully general. for (int i = 0; i < meshSample.points.Length; i++) { meshSample.points[i] = UnityTypeConverter.ChangeBasis(meshSample.points[i]); } for (int i = 0; i < tris.Length; i += 3) { var t = tris[i]; tris[i] = tris[i + 1]; tris[i + 1] = t; } } sample.SetTriangles(tris); } UnityEngine.Profiling.Profiler.BeginSample("USD: Mesh Write"); scene.Write(path, meshSample); UnityEngine.Profiling.Profiler.EndSample(); } } }
/// <summary> /// Internal helper for reading data from USD. /// </summary> /// <param name="attrName">The USD attribute name.</param> /// <param name="csType">The C# type.</param> /// <param name="csValue">The C# value to populate.</param> /// <param name="usdTime">The time at which to sample key frames.</param> /// <param name="prim">The USD prim from which to read data.</param> /// <param name="memberInfo">The field/property providing serialization metadata.</param> /// <param name="usdNamespace">The optional USD namespace at which values live.</param> /// <param name="accessMap">A map of members to include when reading.</param> /// <param name="mayVary">When not null, is populated with variability.</param> /// <returns>True on success.</returns> /// <remarks> /// Note that "success" in the return value does not indicate data was read, rather it /// indicates that no unexpected states were encountered. E.g. calling ReadAttr on a field /// with no value stored in USD will not return false, since that is not considered a failure /// state. /// </remarks> bool ReadAttr(string attrName, Type csType, ref object csValue, pxr.UsdTimeCode usdTime, pxr.UsdPrim prim, MemberInfo memberInfo, HashSet <MemberInfo> accessMap, ref bool?mayVary, string usdNamespace) { bool isNewPrimvar = csValue != null && csType.IsGenericType && csType.GetGenericTypeDefinition() == typeof(Primvar <>); bool isPrimvar = Reflect.IsPrimvar(memberInfo) || isNewPrimvar; string ns = IntrinsicTypeConverter.JoinNamespace(usdNamespace, Reflect.GetNamespace(memberInfo)); // ----------------------------------------- // // Dictionaries, read, early exit, recurse. // ----------------------------------------- // // If holding a dictionary, immediately recurse and write keys as attributes. if (csValue != null && csType.IsGenericType && csType.GetGenericTypeDefinition() == typeof(Dictionary <,>) && csType.GetGenericArguments()[0] == typeof(string)) { Type genericTypeDef = csType.GetGenericArguments()[1].IsGenericType ? csType.GetGenericArguments()[1].GetGenericTypeDefinition() : null; isNewPrimvar = genericTypeDef == typeof(Primvar <>); bool isRelationship = csType.GetGenericArguments()[1] == typeof(Relationship); bool isConnection = genericTypeDef == typeof(Connectable <>); // String dictionaries are unrolled directly into the object. // So the namespace is either the incoming namespace or empty, meaning each string value in // the dictionary becomes an attribute on the prim. // Ensure there is always a namespace immediately around this member. if (string.IsNullOrEmpty(Reflect.GetNamespace(memberInfo))) { ns = IntrinsicTypeConverter.JoinNamespace(ns, attrName); usdNamespace = IntrinsicTypeConverter.JoinNamespace(usdNamespace, attrName); } // Unfortunately, the primvars prefixing logic must be replicated here so we can discover // the dictionary member from USD. if (isPrimvar || isNewPrimvar) { ns = IntrinsicTypeConverter.JoinNamespace("primvars", ns); } var dict = csValue as System.Collections.IDictionary; ConstructorInfo ctor = (isNewPrimvar || isConnection || isRelationship) ? csType.GetGenericArguments()[1].GetConstructor(new Type[0]) : null; dict.Clear(); foreach (var prop in prim.GetAuthoredPropertiesInNamespace(ns)) { object value = null; if (ctor != null) { value = ctor.Invoke(new object[0]); } // The recursive call will also discover that this is a primvar and any associated namespace. if (ReadAttr(prop.GetBaseName(), csType.GetGenericArguments()[1], ref value, usdTime, prim, memberInfo, accessMap, ref mayVary, usdNamespace)) { if (value != null) { dict.Add(prop.GetBaseName().ToString(), value); } } } return(true); } pxr.TfToken sdfAttrName = sm_tokenCache[ns, attrName]; // ----------------------------------------- // // Relationship, read + early exit. // ----------------------------------------- // if (csType == typeof(Relationship)) { // mayVary is explicitly not set here because it has accumulation semantics: // mayVary = mayVary || false; // Which is equivalent to the no-op: // mayVary = mayVary; pxr.UsdRelationship rel = null; lock (m_stageLock) { rel = prim.GetRelationship(sm_tokenCache[sdfAttrName]); } var relationship = new Relationship(); csValue = relationship; if (rel == null || !rel.IsValid()) { return(true); } pxr.SdfPathVector paths = rel.GetTargets(); string[] result = new string[paths.Count]; for (int i = 0; i < paths.Count; i++) { result[i] = paths[i].ToString(); } relationship.targetPaths = result; return(true); } // ----------------------------------------- // // Connection Setup. // ----------------------------------------- // Connectable conn = null; if (csValue != null && csType.IsGenericType && csType.GetGenericTypeDefinition() == typeof(Connectable <>)) { conn = csValue as Connectable; if (conn != null) { // Since this is a Connectable<T>, the held value T is what's being read from USD, // so replace csValue with the held T value itself. csValue must be restored before // returning. csValue = conn.GetValue(); // Same treatment for the type. csType = conn.GetValueType(); } } // ----------------------------------------- // // Primvar Setup. // ----------------------------------------- // ValueAccessor pvAccessor = null; PrimvarBase pvBase = null; if (isNewPrimvar) { pvAccessor = csValue as ValueAccessor; pvBase = (PrimvarBase)csValue; // Since this is a Primvar<T>, the held value T is what's being read from USD, // so replace csVAlue with the held T value itself. csValue must be restored before // returning. csValue = pvAccessor.GetValue(); // Same treatment for the type. csType = pvAccessor.GetValueType(); } // ----------------------------------------- // // Lookup Type Conversion Delegate. // ----------------------------------------- // UsdTypeBinding binding; if (!sm_bindings.GetBinding(csType, out binding) && !csType.IsEnum && csType != typeof(object)) { if (string.IsNullOrEmpty(ns)) { return(false); } var sample = csValue as SampleBase; if (csValue == null) { // This could attempt to automatically constuct the needed object, then nullable objects // could be used instead to drive deserialization. return(false); } else if (sample == null) { // In this case, csValue is not null, but also cannot be converted to SampleBase. throw new ArgumentException("Type does not inherit from SampleBase: " + attrName); } Deserialize((SampleBase)csValue, prim, usdTime, accessMap, ref mayVary, usdNamespace: ns); return(true); } // ----------------------------------------- // // Prep to Read. // ----------------------------------------- // // Restore C# value to the actual property value. if (conn != null) { csValue = conn; } else if (pvAccessor != null) { csValue = pvAccessor; } // Append "primvars:" namespace to primvars. if (isPrimvar) { var joinedName = IntrinsicTypeConverter.JoinNamespace(ns, attrName); sdfAttrName = sm_tokenCache["primvars", joinedName]; } // Adjust time for variability. pxr.SdfVariability variability = Reflect.GetVariability(memberInfo); pxr.UsdTimeCode time = variability == pxr.SdfVariability.SdfVariabilityUniform ? kDefaultUsdTime : usdTime; // Allocate a temp VtValue. pxr.VtValue vtValue = (pxr.VtValue)ArrayAllocator.MallocHandle(typeof(pxr.VtValue)); try { // ----------------------------------------- // // Read Connected Paths. // ----------------------------------------- // if (conn != null) { // Connection paths cannot be animated, so mayVary is not affected. var sources = new pxr.SdfPathVector(); if (prim.GetAttribute(sdfAttrName).GetConnections(sources)) { if (sources.Count > 0) { conn.SetConnectedPath(sources[0].ToString()); } } } // ----------------------------------------- // // Read Associated Primvar Data. // ----------------------------------------- // // If this is a Primvar<T>, read the associated primvar metadata and indices. if (pvBase != null) { var attr = prim.GetAttribute(sdfAttrName); if (attr) { var pv = new pxr.UsdGeomPrimvar(attr); // ElementSize and Interpolation are not animatable, so they do not affect mayVary. pvBase.elementSize = pv.GetElementSize(); pvBase.SetInterpolationToken(pv.GetInterpolation()); // Indices are a first class attribute and may vary over time. var indices = pv.GetIndicesAttr(); if (indices) { if (accessMap != null) { if (indices.GetVariability() == pxr.SdfVariability.SdfVariabilityVarying || indices.ValueMightBeTimeVarying()) { accessMap.Add(memberInfo); mayVary |= true; } } indices.Get(vtValue, time); if (!vtValue.IsEmpty()) { var vtIntArray = pxr.UsdCs.VtValueToVtIntArray(vtValue); pvBase.indices = IntrinsicTypeConverter.FromVtArray(vtIntArray); } } } } // ----------------------------------------- // // Read the value of csValue. // ----------------------------------------- // if (Reflect.IsMetadata(memberInfo)) { vtValue = prim.GetMetadata(sdfAttrName); // Metadata cannot vary over time. } else if (Reflect.IsCustomData(memberInfo)) { vtValue = prim.GetCustomDataByKey(sdfAttrName); // Custom data is metadata, which cannot vary over time. } else if (Reflect.IsFusedDisplayColor(memberInfo)) { vtValue = pxr.UsdCs.GetFusedDisplayColor(prim, time); if (accessMap != null) { // Display color is actually two attributes, primvars:displayColor and // primvars:displayOpacity. var gprim = new pxr.UsdGeomGprim(prim); if (gprim && gprim.GetDisplayColorAttr().ValueMightBeTimeVarying()) { accessMap.Add(memberInfo); mayVary |= true; } } } else if (Reflect.IsFusedTransform(memberInfo)) { vtValue = pxr.UsdCs.GetFusedTransform(prim, time); if (accessMap != null) { // Transforms are complicated :/ var xformable = new pxr.UsdGeomXformable(prim); if (xformable) { bool dummy; var orderAttr = xformable.GetXformOpOrderAttr(); if (orderAttr) { if (orderAttr.GetVariability() == pxr.SdfVariability.SdfVariabilityVarying && orderAttr.ValueMightBeTimeVarying()) { mayVary |= true; accessMap.Add(memberInfo); } else { foreach (var op in xformable.GetOrderedXformOps(out dummy)) { var opAttr = op.GetAttr(); if (!opAttr) { continue; } if (opAttr.GetVariability() == pxr.SdfVariability.SdfVariabilityVarying && opAttr.ValueMightBeTimeVarying()) { mayVary |= true; accessMap.Add(memberInfo); break; } } // foreach } } // orderAttr } // xformable } // mayVary } else { if (accessMap != null) { var attr = prim.GetAttribute(sdfAttrName); if (attr.GetVariability() == pxr.SdfVariability.SdfVariabilityVarying && attr.ValueMightBeTimeVarying()) { accessMap.Add(memberInfo); mayVary |= true; } } if (!prim.GetAttributeValue(sdfAttrName, vtValue, time)) { // Object has no value, still considered success. return(true); } } if (vtValue.IsEmpty()) { // Object has no value, still considered success. return(true); } // ------------------------------------------ // // Infer C# type from USD when Type == Object // ------------------------------------------ // if (csType == typeof(object)) { // Blind object serialization needs special handling, since we won't know the C# type a priori. // Instead, do a reverse lookup on the SdfTypeName and let USD dictate the C# type. pxr.UsdAttribute attr = prim.GetAttribute(sdfAttrName); if (attr != null && attr.IsValid()) { // TODO: Assuming the reverse lookup is successful for the binding, the caller may be // surprised by the result, since the USD <-> C# types are not 1-to-1. For example, // a List<Vector2> may have been serialized, but Vector2[] may be read. if (!sm_bindings.GetReverseBinding(attr.GetTypeName(), out binding)) { if (string.IsNullOrEmpty(ns)) { return(false); } // TODO: readback nested object declared as object -- maybe just disable this? //Deserialize(ref csValue, prim, usdTime, usdNamespace: ns); //return true; return(false); } } else { // TODO: Allow reading metadata declared as object in C# return(false); } } // ------------------------------------------ // // Convert USD's VtValue -> Strong C# Type. // ------------------------------------------ // csValue = binding.toCsObject(vtValue); // ------------------------------------------ // // Restore csValue. // ------------------------------------------ // if (conn != null && csValue != null) { conn.SetValue(csValue); csValue = conn; } if (pvAccessor != null) { pvAccessor.SetValue(csValue); csValue = pvAccessor; } } finally { // Would prefer RAII handle, but introduces garbage. ArrayAllocator.FreeHandle(vtValue); } return(true); }
/// <summary> /// Internal helper for serializing data to USD. /// </summary> /// <param name="attrName">The USD attribute name.</param> /// <param name="csType">The C# type.</param> /// <param name="csValue">The C# value.</param> /// <param name="usdTime">The time at which to sample key frames.</param> /// <param name="prim">The USD prim at which to write values.</param> /// <param name="imgble">The UsdGeomImagable attrbiute, used when writing PrimVars.</param> /// <param name="memberInfo">The field/property providing serialization metadata.</param> /// <param name="usdNamespace">The optional USD namespace at which values live.</param> /// <param name="srcObject">The source object name, used when remapping names.</param> /// <returns>True on success.</returns> /// <remarks> /// Note that "success" in the return value does not indicate data was written, rather it /// indicates that no unexpected states were encountered. E.g. calling WriteAttr on a field /// marked as [NotSerialized] does not cause this method to return false, since non-serialized /// fields are an expected state this function may encounter. /// </remarks> bool WriteAttr(string attrName, Type csType, object csValue, pxr.UsdTimeCode usdTime, pxr.UsdPrim prim, pxr.UsdGeomImageable imgble, MemberInfo memberInfo, string usdNamespace, string srcObject = null) { if (Reflect.IsNonSerialized(memberInfo)) { Console.WriteLine("Non serialized"); return(true); } // If serializing a Primvar<T>, extract the held value and save it in csValue, allowing the // all downstream logic to act as if it's operating on the held value itself. PrimvarBase pvBase = null; if (csType.IsGenericType && csType.GetGenericTypeDefinition() == typeof(Primvar <>)) { if (csValue == null) { // Object not written, still considered success. return(true); } pvBase = (PrimvarBase)csValue; csValue = (csValue as ValueAccessor).GetValue(); if (csValue == null) { // Object not written, still considered success. return(true); } csType = csValue.GetType(); } bool isCustomData = Reflect.IsCustomData(memberInfo); bool isMetaData = Reflect.IsMetadata(memberInfo); bool isPrimvar = Reflect.IsPrimvar(memberInfo); bool isNewPrimvar = pvBase != null; int primvarElementSize = Reflect.GetPrimvarElementSize(memberInfo); string ns = IntrinsicTypeConverter.JoinNamespace(usdNamespace, Reflect.GetNamespace(memberInfo)); // If holding a dictionary, immediately recurse and write keys as attributes. if (csValue != null && csType.IsGenericType && csType.GetGenericTypeDefinition() == typeof(Dictionary <,>) && csType.GetGenericArguments()[0] == typeof(string)) { isNewPrimvar = csType.GetGenericArguments()[1].IsGenericType && csType.GetGenericArguments()[1].GetGenericTypeDefinition() == typeof(Primvar <>); // Ensure the immediate dictionary member is always namespaced. if (string.IsNullOrEmpty(Reflect.GetNamespace(memberInfo))) { usdNamespace = IntrinsicTypeConverter.JoinNamespace(usdNamespace, attrName); } var dict = csValue as System.Collections.IDictionary; foreach (System.Collections.DictionaryEntry kvp in dict) { object value = kvp.Value; WriteAttr((string)kvp.Key, value.GetType(), value, usdTime, prim, imgble, memberInfo, usdNamespace, srcObject: attrName); } return(true); } pxr.TfToken sdfAttrName = sm_tokenCache[attrName]; if (csType == typeof(Relationship) && csValue != null) { string[] targetStrings = ((Relationship)csValue).targetPaths; if (targetStrings != null) { // // Write Relationship // string[] arr = IntrinsicTypeConverter.JoinNamespace(ns, sdfAttrName).Split(':'); pxr.StdStringVector elts = new pxr.StdStringVector(arr.Length); foreach (var s in arr) { elts.Add(s); } pxr.UsdRelationship rel = null; lock (m_stageLock) { rel = prim.CreateRelationship(elts, custom: false); } if (!rel.IsValid()) { throw new ApplicationException("Failed to create relationship <" + prim.GetPath().AppendProperty( new pxr.TfToken( IntrinsicTypeConverter.JoinNamespace(ns, sdfAttrName))).ToString() + ">"); } var targets = new pxr.SdfPathVector(); foreach (var path in ((Relationship)csValue).targetPaths) { targets.Add(new pxr.SdfPath(path)); } lock (m_stageLock) { rel.SetTargets(targets); } } return(true); } // // Write Attribute // // FUTURE: When writing sparse overrides, if the csValue is null exit here and avoid // defining the target attribute. However, sparse authoring is not yet supported. UsdTypeBinding binding; // Extract the value and type from the connectable. var conn = csValue as Connectable; if (conn != null) { csType = conn.GetValueType(); csValue = conn.GetValue(); } // Get the binding for the value about to be serialized. if (!sm_bindings.GetBinding(csType, out binding) && !csType.IsEnum) { if (csValue == null) { return(true); } if (string.IsNullOrEmpty(ns)) { return(false); } var sample = csValue as SampleBase; if (sample == null && csValue != null) { throw new ArgumentException("Type does not inherit from SampleBase: " + attrName); } Serialize(csValue, prim, usdTime, usdNamespace: ns); return(true); } // Determine metadata for the attribtue, note that in the case of connections and primvars // these will be the attributes on the outter object, e.g. declared on the Connection<T> or // Primvar<T>. pxr.SdfVariability variability = Reflect.GetVariability(memberInfo); pxr.SdfValueTypeName sdfTypeName = binding.sdfTypeName; pxr.UsdTimeCode time = variability == pxr.SdfVariability.SdfVariabilityUniform ? pxr.UsdTimeCode.Default() : usdTime; bool custom = false; pxr.UsdAttribute attr; if (isCustomData || isMetaData) { // no-op attr = null; } else if (!isPrimvar && !isNewPrimvar) { if (string.IsNullOrEmpty(ns)) { // // Create non-namespaced attribute. // lock (m_stageLock) { attr = prim.CreateAttribute(sdfAttrName, csType.IsEnum ? SdfValueTypeNames.Token : sdfTypeName, custom, variability); } } else { // // Create namespaced attribute. // string[] arr = IntrinsicTypeConverter.JoinNamespace(ns, sdfAttrName).Split(':'); pxr.StdStringVector elts = new pxr.StdStringVector(arr.Length); foreach (var s in arr) { elts.Add(s); } lock (m_stageLock) { attr = prim.CreateAttribute(elts, sdfTypeName, custom, variability); } } } else { // // Create Primvar attribute. // lock (m_stageLock) { var fullAttrName = IntrinsicTypeConverter.JoinNamespace(ns, sdfAttrName); var primvar = imgble.CreatePrimvar(new pxr.TfToken(fullAttrName), sdfTypeName, VertexDataAttribute.Interpolation); if (isNewPrimvar) { primvar.SetElementSize(pvBase.elementSize); if (pvBase.indices != null) { var vtIndices = IntrinsicTypeConverter.ToVtArray(pvBase.indices); primvar.SetIndices(vtIndices, time); } primvar.SetInterpolation(pvBase.GetInterpolationToken()); } else { primvar.SetElementSize(primvarElementSize); } attr = primvar.GetAttr(); } } if (attr != null && conn != null && conn.GetConnectedPath() != null) { // TODO: Pool temp vector, possibly add a single item overload for SetConnections. var paths = new pxr.SdfPathVector(); var connPath = conn.GetConnectedPath(); if (connPath != string.Empty) { paths.Add(new pxr.SdfPath(conn.GetConnectedPath())); } attr.SetConnections(paths); } // This may happen when a connection is present, but has a null default value. // Because the connection is applied just before this point, this is the earliest possible // exit point. if (csValue == null) { return(true); } pxr.VtValue vtValue = binding.toVtValue(csValue); lock (m_stageLock) { if (isMetaData) { prim.SetMetadata(sdfAttrName, vtValue); } else if (isCustomData) { prim.SetCustomDataByKey(sdfAttrName, vtValue); } else if (Reflect.IsFusedDisplayColor(memberInfo)) { pxr.UsdCs.SetFusedDisplayColor(prim, vtValue, time); } else { attr.Set(vtValue, time); } } if (!isCustomData && srcObject != null) { lock (m_stageLock) { attr.SetCustomDataByKey(sm_tokenCache["sourceMember"], srcObject); } } return(true); }
/// <summary> /// Deserializes an arbitrary object descending from SampleBase from USD to C#. /// </summary> /// <typeparam name="T">The type to serialize, descending from SampleBase</typeparam> /// <param name="t">The object to to populate.</param> /// <param name="prim">The USD prim from which to read data.</param> /// <param name="usdTime">The time at which to read key frames.</param> /// <param name="accessMap">A list of memebers to include when reading.</param> /// <param name="mayVary">Indicates if this prim had any time-varying members.</param> /// <param name="usdNamespace">The object namespace, if any.</param> public void Deserialize <T>(T t, pxr.UsdPrim prim, pxr.UsdTimeCode usdTime, HashSet <MemberInfo> accessMap, ref bool?mayVary, string usdNamespace = null) where T : SampleBase { if (t == null) { return; } PropertyInfo[] properties = Reflect.GetCachedProperties(t.GetType()); FieldInfo[] fields = Reflect.GetCachedFields(t.GetType()); var localVarMap = accessMap; bool mayVaryWasNull = mayVary == null; if (mayVary == null) { localVarMap = null; } for (int i = 0; i < properties.Length; i++) { PropertyInfo csProp = properties[i]; if (Reflect.IsNonSerialized(csProp)) { continue; } if (accessMap != null && mayVary == null && !accessMap.Contains(csProp)) { continue; } object propValue = csProp.GetValue(t, null); Deserialize(ref propValue, prim, usdTime, csProp, localVarMap, ref mayVary, usdNamespace); csProp.SetValue(t, propValue, index: null); if ((mayVary == null) != mayVaryWasNull) { throw new ApplicationException("Deserialize modified mayVary to be non-null"); } } for (int i = 0; i < fields.Length; i++) { FieldInfo csField = fields[i]; if (Reflect.IsNonSerialized(csField)) { continue; } if (accessMap != null && mayVary == null && !accessMap.Contains(csField)) { continue; } object fieldValue = csField.GetValue(t); Deserialize(ref fieldValue, prim, usdTime, csField, localVarMap, ref mayVary, usdNamespace); csField.SetValue(t, fieldValue); if ((mayVary == null) != mayVaryWasNull) { throw new ApplicationException("Deserialize modified mayVary to be non-null"); } } }
/// <summary> /// Constructs Unity SerializedProperties from USD. /// </summary> static void PropertyFromUsd(pxr.UsdPrim prim, SerializedProperty prop, System.Text.StringBuilder sb, string propPrefix) { if (prim == null) { Debug.LogError("Null prim - " + propPrefix); } if (!prim.IsValid()) { Debug.LogError("Invalid prim: " + prim.GetPath().ToString()); } string prefix = ""; try { var nameStack = new List <string>(); nameStack.Add("unity"); if (!string.IsNullOrEmpty(propPrefix)) { nameStack.Add(propPrefix); } string lastName = ""; int lastDepth = 0; while (prop.Next(prop.propertyType == SerializedPropertyType.Generic && !prop.isArray)) { string tabIn = ""; for (int i = 0; i < prop.depth; i++) { tabIn += " "; } if (prop.depth > lastDepth) { Debug.Assert(lastName != ""); nameStack.Add(lastName); } else if (prop.depth < lastDepth) { nameStack.RemoveRange(nameStack.Count - (lastDepth - prop.depth), lastDepth - prop.depth); } lastDepth = prop.depth; lastName = prop.name; if (nameStack.Count > 0) { prefix = string.Join(":", nameStack.ToArray()); prefix += ":"; } else { prefix = ""; } sb.Append(tabIn + prefix + prop.name + "[" + prop.propertyType.ToString() + "] = "); if (prop.isArray && prop.propertyType != SerializedPropertyType.String) { // TODO. sb.AppendLine("ARRAY"); } else if (prop.propertyType == SerializedPropertyType.Generic) { sb.AppendLine("Generic"); } else if (prop.propertyType == SerializedPropertyType.AnimationCurve || prop.propertyType == SerializedPropertyType.Gradient) { // TODO. sb.AppendLine(NativeSerialization.ValueToString(prop)); } else { sb.AppendLine(NativeSerialization.ValueToString(prop)); var attrName = new pxr.TfToken(prefix + prop.name); var attr = prim.GetAttribute(attrName); if (attr == null) { Debug.LogError("Null attr: " + prim.GetPath().ToString() + "." + attrName.ToString()); } if (!attr.IsValid()) { Debug.LogError("Attribute not found:" + attr.GetPath().ToString()); } NativeSerialization.VtValueToProp(prop, attr.Get(0)); } } } catch { Debug.LogWarning("Failed on: " + prim.GetPath() + "." + prefix + prop.name); throw; } }
static public void ImportObject(Scene scene, GameObject go, pxr.UsdPrim usdPrim, SceneImportOptions options) { }
/// <summary> /// Internal helper for reading data from USD. /// </summary> /// <param name="attrName">The USD attribute name.</param> /// <param name="csType">The C# type.</param> /// <param name="csValue">The C# value to populate.</param> /// <param name="usdTime">The time at which to sample key frames.</param> /// <param name="prim">The USD prim from which to read data.</param> /// <param name="memberInfo">The field/property providing serialization metadata.</param> /// <param name="usdNamespace">The optional USD namespace at which values live.</param> /// <param name="srcObject">The source object name, used when remapping names.</param> /// <returns>True on success.</returns> /// <remarks> /// Note that "success" in the return value does not indicate data was read, rather it /// indicates that no unexpected states were encountered. E.g. calling ReadAttr on a field /// with no value stored in USD will not return false, since that is not considered a failure /// state. /// </remarks> bool ReadAttr(string attrName, Type csType, ref object csValue, pxr.UsdTimeCode usdTime, pxr.UsdPrim prim, MemberInfo memberInfo, string usdNamespace, string srcObject = null) { bool isPrimvar = Reflect.IsPrimvar(memberInfo); string ns = IntrinsicTypeConverter.JoinNamespace(usdNamespace, Reflect.GetNamespace(memberInfo)); // If holding a dictionary, immediately recurse and write keys as attributes. if (csType == typeof(Dictionary <string, object>)) { string sourceMember; if (isPrimvar) { ns = "primvars"; sourceMember = attrName; } else { ns = IntrinsicTypeConverter.JoinNamespace(ns, attrName); sourceMember = null; } var dict = csValue as Dictionary <string, object>; foreach (var prop in prim.GetAuthoredPropertiesInNamespace(ns)) { object value = null; if (!string.IsNullOrEmpty(sourceMember)) { pxr.VtValue valSrcMember = prop.GetCustomDataByKey(sm_tokenCache["sourceMember"]); if (valSrcMember.IsEmpty() || sourceMember != (string)valSrcMember) { continue; } } if (isPrimvar) { // The recursive call will also discover that this is a primvar. ns = ""; } if (ReadAttr(prop.GetBaseName(), typeof(Object), ref value, usdTime, prim, memberInfo, ns, srcObject)) { if (value != null) { dict.Add(prop.GetBaseName(), value); } } } return(true); } pxr.TfToken sdfAttrName = sm_tokenCache[ns, attrName]; if (csType == typeof(Relationship)) { // // Read Relationship // pxr.UsdRelationship rel = null; lock (m_stageLock) { rel = prim.GetRelationship(sm_tokenCache[sdfAttrName]); } var relationship = new Relationship(); csValue = relationship; if (rel == null || !rel.IsValid()) { return(true); } pxr.SdfPathVector paths = rel.GetTargets(); string[] result = new string[paths.Count]; for (int i = 0; i < paths.Count; i++) { result[i] = paths[i].ToString(); } relationship.targetPaths = result; return(true); } UsdTypeBinding binding; Connectable conn = null; if (csValue != null && csType.IsGenericType && csType.GetGenericTypeDefinition() == typeof(Connectable <>)) { conn = csValue as Connectable; if (conn != null) { csValue = conn.GetValue(); csType = conn.GetValueType(); } } if (!sm_bindings.GetBinding(csType, out binding) && !csType.IsEnum && csType != typeof(object)) { if (string.IsNullOrEmpty(ns)) { return(false); } var sample = csValue as SampleBase; if (sample == null) { throw new Exception("Could not deserialize: Prim: " + prim.GetPath() + " namespace: " + ns); } Deserialize((SampleBase)csValue, prim, usdTime, usdNamespace: ns); return(true); } if (conn != null) { csValue = conn; } pxr.SdfVariability variability = Reflect.GetVariability(memberInfo); // Note that namespaced primvars are not supported, so "primvars" will replace the incoming // namespace. This will happen if a nested/namespaced object has a member declared as a // primvar. if (isPrimvar) { System.Diagnostics.Debug.Assert(string.IsNullOrEmpty(ns)); sdfAttrName = sm_tokenCache["primvars", attrName]; } pxr.UsdTimeCode time = variability == pxr.SdfVariability.SdfVariabilityUniform ? kDefaultUsdTime : usdTime; //using (var valWrapper = new PooledHandle<pxr.VtValue>(ArrayAllocator)) { pxr.VtValue vtValue = (pxr.VtValue)ArrayAllocator.MallocHandle(typeof(pxr.VtValue)); try { if (conn != null) { var sources = new pxr.SdfPathVector(); if (prim.GetAttribute(sdfAttrName).GetConnections(sources)) { if (sources.Count > 0) { conn.SetConnectedPath(sources[0].ToString()); } } } if (Reflect.IsCustomData(memberInfo)) { vtValue = prim.GetCustomDataByKey(sdfAttrName); } else if (Reflect.IsFusedDisplayColor(memberInfo)) { vtValue = pxr.UsdCs.GetFusedDisplayColor(prim, time); } else if (Reflect.IsFusedTransform(memberInfo)) { vtValue = pxr.UsdCs.GetFusedTransform(prim, time); } else { if (!prim.GetAttributeValue(sdfAttrName, vtValue, time)) { // Object has no value, still considered success. return(true); } } if (vtValue.IsEmpty()) { // Object has no value, still considered success. return(true); } if (csType == typeof(object)) { // Blind object serialization needs special handling, since we won't know the C# type a priori. // Instead, do a reverse lookup on the SdfTypeName and let USD dictate the C# type. pxr.UsdAttribute attr = prim.GetAttribute(sdfAttrName); if (attr != null && attr.IsValid()) { // TODO: Assuming the reverse lookup is successful for the binding, the caller may be // surprised by the result, since the USD <-> C# types are not 1-to-1. For example, // a List<Vector2> may have been serialized, but Vector2[] may be read. if (!sm_bindings.GetReverseBinding(attr.GetTypeName(), out binding)) { if (string.IsNullOrEmpty(ns)) { return(false); } // TODO: readback nested object declared as object -- maybe just disable this? //Deserialize(ref csValue, prim, usdTime, usdNamespace: ns); //return true; return(false); } } else { // TODO: Allow reading metadata declared as object in C# return(false); } } csValue = binding.toCsObject(vtValue); if (conn != null && csValue != null) { conn.SetValue(csValue); csValue = conn; } } finally { // Would prefer RAII handle, but introduces garbage. ArrayAllocator.FreeHandle(vtValue); } // Need to deal with this //if (srcObject != null) { // attr.SetCustomDataByKey(m_tokenCache["sourceMember"], srcObject); //} return(true); }
/// <summary> /// Internal helper for serializing data to USD. /// </summary> /// <param name="attrName">The USD attribute name.</param> /// <param name="csType">The C# type.</param> /// <param name="csValue">The C# value.</param> /// <param name="usdTime">The time at which to sample key frames.</param> /// <param name="prim">The USD prim at which to write values.</param> /// <param name="imgble">The UsdGeomImagable attrbiute, used when writing PrimVars.</param> /// <param name="memberInfo">The field/property providing serialization metadata.</param> /// <param name="usdNamespace">The optional USD namespace at which values live.</param> /// <param name="srcObject">The source object name, used when remapping names.</param> /// <returns>True on success.</returns> /// <remarks> /// Note that "success" in the return value does not indicate data was written, rather it /// indicates that no unexpected states were encountered. E.g. calling WriteAttr on a field /// marked as [NotSerialized] does not cause this method to return false, since non-serialized /// fields are an expected state this function may encounter. /// </remarks> bool WriteAttr(string attrName, Type csType, object csValue, pxr.UsdTimeCode usdTime, pxr.UsdPrim prim, pxr.UsdGeomImageable imgble, MemberInfo memberInfo, string usdNamespace, string srcObject = null) { if (Reflect.IsNonSerialized(memberInfo)) { return(true); } // If holding a dictionary, immediately recurse and write keys as attributes. if (csType == typeof(Dictionary <string, object>)) { Dictionary <string, object> dict = csValue as Dictionary <string, object>; foreach (var kvp in dict) { object value = kvp.Value; WriteAttr(kvp.Key, value.GetType(), value, usdTime, prim, imgble, memberInfo, usdNamespace, srcObject: attrName); } return(true); } string ns = IntrinsicTypeConverter.JoinNamespace(usdNamespace, Reflect.GetNamespace(memberInfo)); pxr.TfToken sdfAttrName = sm_tokenCache[attrName]; if (csType == typeof(Relationship) && csValue != null) { string[] targetStrings = ((Relationship)csValue).targetPaths; if (targetStrings != null) { // // Write Relationship // string[] arr = IntrinsicTypeConverter.JoinNamespace(ns, sdfAttrName).Split(':'); pxr.StdStringVector elts = new pxr.StdStringVector(arr.Length); foreach (var s in arr) { elts.Add(s); } pxr.UsdRelationship rel = null; lock (m_stageLock) { rel = prim.CreateRelationship(elts, custom: false); } if (!rel.IsValid()) { throw new ApplicationException("Failed to create relationship <" + prim.GetPath().AppendProperty( new pxr.TfToken( IntrinsicTypeConverter.JoinNamespace(ns, sdfAttrName))).ToString() + ">"); } var targets = new pxr.SdfPathVector(); foreach (var path in ((Relationship)csValue).targetPaths) { targets.Add(new pxr.SdfPath(path)); } lock (m_stageLock) { rel.SetTargets(targets); } } return(true); } // // Write Attribute // // Object not written, still considered success. if (csValue == null) { return(true); } bool isCustomData = Reflect.IsCustomData(memberInfo); bool isPrimvar = Reflect.IsPrimvar(memberInfo); UsdTypeBinding binding; var conn = csValue as Connectable; if (conn != null) { csType = conn.GetValue().GetType(); csValue = conn.GetValue(); } if (!sm_bindings.GetBinding(csType, out binding) && !csType.IsEnum) { if (string.IsNullOrEmpty(ns)) { return(false); } Serialize(csValue, prim, usdTime, usdNamespace: ns); return(true); } pxr.SdfVariability variability = Reflect.GetVariability(memberInfo); pxr.SdfValueTypeName sdfTypeName = binding.sdfTypeName; pxr.UsdTimeCode time = variability == pxr.SdfVariability.SdfVariabilityUniform ? pxr.UsdTimeCode.Default() : usdTime; bool custom = false; pxr.UsdAttribute attr; if (isCustomData) { // no-op attr = null; } else if (!isPrimvar) { if (string.IsNullOrEmpty(ns)) { lock (m_stageLock) { attr = prim.CreateAttribute(sdfAttrName, csType.IsEnum ? SdfValueTypeNames.Token : sdfTypeName, custom, variability); } } else { string[] arr = IntrinsicTypeConverter.JoinNamespace(ns, sdfAttrName).Split(':'); pxr.StdStringVector elts = new pxr.StdStringVector(arr.Length); foreach (var s in arr) { elts.Add(s); } lock (m_stageLock) { attr = prim.CreateAttribute(elts, sdfTypeName, custom, variability); } } } else { // Primvars do not support additional namespaces. lock (m_stageLock) { attr = imgble.CreatePrimvar(sdfAttrName, sdfTypeName, VertexDataAttribute.Interpolation).GetAttr(); } } if (attr != null && conn != null && conn.GetConnectedPath() != null) { // TODO: Pool temp vector, possibly add a single item overload for SetConnections. var paths = new pxr.SdfPathVector(); var connPath = conn.GetConnectedPath(); if (connPath != string.Empty) { paths.Add(new pxr.SdfPath(conn.GetConnectedPath())); } attr.SetConnections(paths); } pxr.VtValue vtValue = binding.toVtValue(csValue); lock (m_stageLock) { if (isCustomData) { prim.SetCustomDataByKey(sdfAttrName, vtValue); } else if (Reflect.IsFusedDisplayColor(memberInfo)) { pxr.UsdCs.SetFusedDisplayColor(prim, vtValue, time); } else { attr.Set(vtValue, time); } } if (!isCustomData && srcObject != null) { lock (m_stageLock) { attr.SetCustomDataByKey(sm_tokenCache["sourceMember"], srcObject); } } return(true); }