/// <summary> /// Get a binding from a USD type /// </summary> /// <remarks> /// Used the C# type needs to be derived from the USD type. /// Example: UVs can be of type float2, float3, texcoord2f, texcoord2f /// </remarks> public bool GetReverseBinding(pxr.SdfValueTypeName key, out UsdTypeBinding binding) { // Check if the given sdf type name is an alias // https://graphics.pixar.com/usd/docs/api/_usd__page__datatypes.html#Usd_Roles string name; bool match = true; if (!typeAliases.TryGetValue(key.GetAsToken(), out name)) { name = key.GetAsToken(); match = false; } // TODO: we could keep a reverse mapping, but waiting for deeper performance analysis first. foreach (var kvp in bindings) { if (kvp.Value.sdfTypeName.GetAsToken() == name) { binding = kvp.Value; return(true); } } binding = new UsdTypeBinding(); return(false); }
public UsdTypeBinding(ToVtConverter toVtConverter, ToCsConverter toCsConverter, pxr.SdfValueTypeName sdfName) { toVtValue = toVtConverter; toCsObject = toCsConverter; sdfTypeName = sdfName; }
public void BindNativeType(Type csType, pxr.SdfValueTypeName sdfName) { string name; if (!typeNameMapping.TryGetValue(csType.Name, out name)) { name = csType.Name; } var converter = typeof(pxr.UsdCs).GetMethod("VtValueTo" + name, new Type[] { typeof(pxr.VtValue) }); if (converter == null) { throw new ArgumentException(string.Format("No VtValueTo... converter found for type {0}, VtValueTo{1}", csType.ToString(), name)); } bindings[csType] = new UsdTypeBinding(DefaultConversions.ToVtValue, (x) => converter.Invoke(null, new object[] { x }), sdfName); }
/// <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 { // use the sparse attribute value writer, to skip redundant time samples m_sparseValueWriter.SetAttribute(attr, vtValue, time); } } if (!isCustomData && srcObject != null) { lock (m_stageLock) { attr.SetCustomDataByKey(sm_tokenCache["sourceMember"], srcObject); } } return(true); }
/// <summary> /// Binds the specified C# type to the given USD array and scene description (Sdf) types, /// looking for ConverterT.ToVtArray(csType) and ConverterT.FromVtArray(vtArrayType). /// </summary> /// /// <typeparam name="ConverterT"> /// The C# class type providing type conversion rules ToVtArray and FromVtArray. /// </typeparam> /// /// <param name="csType">The C# type to be mapped to USD</param> /// <param name="vtArrayType">The USD C++ value type (Vt)</param> /// <param name="sdfName">The USD scene description (Sdf) type</param> /// /// TODO: The C++ type can be inferred from the Sdf type, so vtArrayType is not needed. /// public void BindArrayType <ConverterT>(Type csType, Type vtArrayType, pxr.SdfValueTypeName sdfName, string methodNamePrefix = "") { // ConverterT and the function being found will be something like: // class IntrinsicTypeConverter { // static public VtTokenArray ToVtArray(string[] input); // var csToVtArray = typeof(ConverterT) .GetMethod(methodNamePrefix + "ToVtArray", new Type[] { csType }); if (csToVtArray == null) { throw new ArgumentException(string.Format("No ToVtArray overload found for type {0}", csType.ToString())); } // ConverterT and the function being found will be something like: // class IntrinsicTypeConverter { // static public string[] FromVtArray(VtTokenArray input); // var vtToCsArray = typeof(ConverterT) .GetMethod(methodNamePrefix + "FromVtArray", new Type[] { vtArrayType }); if (vtToCsArray == null) { throw new ArgumentException(string.Format("No FromVtArray overload found for type {0}", vtArrayType.ToString())); } // The specific UsdCs method being located here will be somthing like: // class UsdCs { // public static void VtValueToVtTokenArray(VtValue value, VtTokenArray output); // var valToVtArray = typeof(pxr.UsdCs).GetMethod("VtValueTo" + vtArrayType.Name, new Type[] { typeof(pxr.VtValue), vtArrayType }); if (valToVtArray == null) { throw new ArgumentException(string.Format("No VtValueTo{...} converter found for type {0}", vtArrayType.ToString())); } // The following code constructs functions to: // // 1) Convert the VtValue (type-erased container) to a specific VtArray<T> type and then // convert the VtArray<T> to a native C# type. // // 2) Convert a strongly typed C# array to a strongly typed VtArray<T> and then // convert the VtArray<T> to a type-erased VtValue. // // For example, to will convert: // // 1) VtValue -> VtArray<TfToken> -> string[] // 2) string[] -> VtArray<TfToken> -> VtValue // ToCsConverter toCs = null; ToVtConverter toVt = null; if (IsCodeGenEnabled()) { // EmitToCs and EmitToVt are not defined when not using NET_4_6 #if NET_4_6 var copyConverter = (ToCsCopyConverter)CodeGen.EmitToCs <ToCsCopyConverter>(valToVtArray, vtToCsArray); toCs = (vtValue) => ToCsConvertHelper(vtValue, vtArrayType, copyConverter); toVt = (ToVtConverter)CodeGen.EmitToVt <ToVtConverter>(csToVtArray, csType, vtArrayType); #endif } else { // In .NET2 or when IL2CPP is enabled , we cannot dynamically emit code. // Instead, we use late binding, which is slower, but also doesn't crash. // In the future, we should generate code to do these conversions, rather than using late binding. toCs = (vtValue) => ToCsDynamicConvertHelper(vtValue, vtArrayType, valToVtArray, vtToCsArray); toVt = CsArrayToVtValue(csToVtArray, csType, vtArrayType); } bindings[csType] = new UsdTypeBinding(toVt, toCs, sdfName); }