Beispiel #1
0
        /// <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
            }
        }
Beispiel #2
0
        /// <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
                }
            }
        }
Beispiel #3
0
        /// <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();
        }
Beispiel #4
0
 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);
        }
Beispiel #6
0
        /// <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
            }
        }
Beispiel #7
0
        /// <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();
            }
        }
    }
Beispiel #10
0
        /// <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);
        }
Beispiel #11
0
        /// <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);
        }
Beispiel #12
0
        /// <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");
                }
            }
        }
Beispiel #13
0
        /// <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;
            }
        }
Beispiel #14
0
 static public void ImportObject(Scene scene,
                                 GameObject go,
                                 pxr.UsdPrim usdPrim,
                                 SceneImportOptions options)
 {
 }
Beispiel #15
0
        /// <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);
        }
Beispiel #16
0
        /// <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);
        }