Пример #1
0
        // -------------------------------------------------------------------------------------------- //
        // Lights
        // -------------------------------------------------------------------------------------------- //

        // Returns lights in "srgb-ish" color space.
        // Note that light values may be > 1, which is not really kosher in sRGB
        static void BuildLights(SceneStatePayload payload)
        {
            var lightPayload = payload.lights;

            lightPayload.ambientColor = RenderSettings.ambientLight;

            for (int i = 0; i < App.Scene.GetNumLights(); ++i)
            {
                var   transform  = App.Scene.GetLight(i).transform;
                Light unityLight = transform.GetComponent <Light>();
                Debug.Assert(unityLight != null);

                Color lightColor = unityLight.color * unityLight.intensity;
                lightColor.a = 1.0f; // No use for alpha with light color.

                // because transform.name might not be unique
                int uniquifyingId = payload.idGenerator.GetIdFromInstanceId(transform);
                // Match the old gltf export logic for easier diffing
                var transformNameNoSpaces = transform.name.Replace(" ", "_");
                lightPayload.lights.Add(new ExportUtils.LightPayload
                {
                    legacyUniqueName = $"{transformNameNoSpaces}_i{uniquifyingId}",
                    name             = transformNameNoSpaces,
                    type             = unityLight.type,
                    lightColor       = lightColor,
                    xform            = ExportUtils.ChangeBasis(transform, payload)
                });
            }
        }
Пример #2
0
        // widget.ReferenceImage must be != null
        // Never returns null
        // id is an instance id
        static ImageQuadPayload BuildImageQuadPayload(
            SceneStatePayload payload, ImageWidget widget, DynamicExportableMaterial material, int id)
        {
            string nodeName = $"image_{widget.GetExportName()}_{id}";

            // The child has no T or R but has some non-uniform S that we need to preserve.
            // I'm going to do this by baking it into the GeometryPool

            GameObject widgetChild = widget.m_ImageQuad.gameObject;
            Matrix4x4  xform       = ExportUtils.ChangeBasis(widget.transform, payload);
            Vector3    localScale  = widgetChild.transform.localScale;

            // localScale is a muddle of aspect ratio and overall size. We don't know which of x or y
            // was modified to set the aspect ratio, so get the size from z.
            if (localScale.z > 0)
            {
                float overallSize = localScale.z;
                localScale /= overallSize;
                xform       = xform * Matrix4x4.Scale(Vector3.one * overallSize);
            }

            // Create pool and bake in the leftover non-uniform scale
            GeometryPool pool = new GeometryPool();

            pool.Layout = material.VertexLayout;
            pool.Append(widgetChild.GetComponent <MeshFilter>().sharedMesh, fallbackColor: Color.white);
            pool.ApplyTransform(
                Matrix4x4.Scale(localScale), Matrix4x4.identity, 1f,
                0, pool.NumVerts);

            // Important: This transform should only be applied once, since the pools are shared, and
            // cannot include any mesh-local transformations.
            ExportUtils.ConvertUnitsAndChangeBasis(pool, payload);
            if (payload.reverseWinding)
            {
                // Note: this triangle flip intentionally un-does the Unity FBX import flip.
                ExportUtils.ReverseTriangleWinding(pool, 1, 2);
            }

            return(new ExportUtils.ImageQuadPayload(payload.groupIdMapping.GetId(widget.Group))
            {
                // because Count always increments, this is guaranteed unique even if two different images
                // have the same export name
                legacyUniqueName = $"refimage_i{payload.imageQuads.Count}",
                // We could (but don't) share the same aspect-ratio'd-quad for all instances of a refimage.
                // Since the mesh is unique to the node, it can have the same name as the node.
                geometryName = nodeName,
                nodeName = nodeName,
                xform = xform,
                geometry = pool,
                exportableMaterial = material,
            });
        }
Пример #3
0
        // -------------------------------------------------------------------------------------------- //
        // Xform only, no mesh data
        // -------------------------------------------------------------------------------------------- //

        static void BuildEmptyXforms(SceneStatePayload payload, IEnumerable <MediaWidget> widgets)
        {
            foreach (var mediaWidget in widgets)
            {
                payload.referenceThings.Add(new ExportUtils.XformPayload(
                                                payload.groupIdMapping.GetId(mediaWidget.Group))
                {
                    name  = mediaWidget.GetExportName(),
                    xform = ExportUtils.ChangeBasis(mediaWidget.transform, payload)
                });
            }
        }
Пример #4
0
        public void Serialize()
        {
            // Get transform in canvas space, and write to scene.
            TrTransform xf_CS    = Coords.AsCanvas[transform];
            var         basisMat = Matrix4x4.identity;

            if (m_UnitsInMeters)
            {
                basisMat[0, 0] *= App.UNITS_TO_METERS;
                basisMat[1, 1] *= App.UNITS_TO_METERS;
                basisMat[2, 2] *= -1 * App.UNITS_TO_METERS;
            }
            m_UsdCamera.transform = ExportUtils.ChangeBasis(xf_CS.ToMatrix4x4(), basisMat, basisMat.inverse);
            m_UsdCamera.fov       = m_RecordingCamera.fieldOfView;
            m_Scene.Write(m_xformName, m_UsdCamera);
        }
Пример #5
0
        // Unused and untested
#if false
        /// Converts the existing payload to the new color space and vector basis.
        /// Note that scale is never converted and only gamma -> linear is currently supported.
        public static void ConvertPayload(ColorSpace newColorSpace,
                                          Vector3[] newVectorBasis,
                                          SceneStatePayload payload)
        {
            // We don't handle uninitialized color spaces or linear -> srgb conversions.
            // This is just not currently part of the export logic, so it's not implemented here.
            if (newColorSpace == ColorSpace.Uninitialized ||
                payload.colorSpace == ColorSpace.Uninitialized ||
                (payload.colorSpace == ColorSpace.Linear && newColorSpace == ColorSpace.Gamma))
            {
                throw new NotImplementedException();
            }

            if (newColorSpace != payload.colorSpace)
            {
                ExportUtils.ConvertToLinearColorspace(payload.env);
                ExportUtils.ConvertToLinearColorspace(payload.lights);
                foreach (var group in payload.groups)
                {
                    ExportUtils.ConvertToLinearColorspace(group.brushMeshes);
                }
                ExportUtils.ConvertToLinearColorspace(payload.modelMeshes);
            }

            if (newVectorBasis[0] != payload.vectorBasis[0] ||
                newVectorBasis[1] != payload.vectorBasis[1] ||
                newVectorBasis[2] != payload.vectorBasis[2])
            {
                // Never apply a scale change when changing basis.
                Matrix4x4 basis = ExportUtils.GetBasisMatrix(payload);
                foreach (var group in payload.groups)
                {
                    ExportUtils.ChangeBasis(group.brushMeshes, basis);
                }
                ExportUtils.ChangeBasis(payload.modelMeshes, basis);
                ExportUtils.ChangeBasis(payload.referenceImages, basis);
                ExportUtils.ChangeBasis(payload.referenceModels, basis);
            }

            payload.colorSpace  = newColorSpace;
            payload.vectorBasis = newVectorBasis;
        }
Пример #6
0
        /// Plays back a named transform onto the current transform from a usd path.
        /// The transform can optionally be smoothed using exponential smoothing.
        /// Smoothing will be clamped between 0 - 1.
        public void StartPlayback(string sketchName = "/Sketch", string xformName = "/VideoCamera",
                                  float smoothing   = 0)
        {
            m_xformName   = xformName;
            m_Smoothing   = Mathf.Clamp01(smoothing);
            m_IsRecording = false;

            // Older versions of Tilt Brush exported usda camera paths in decimeters. We now
            // export in meters to match USD geometry export. Older versions also didn't export any sketch
            // data so we check here for the presence of sketch data to decide how to treat the camera
            // path units.
            bool hasSketchRoot = m_Scene.Stage.GetPrimAtPath(new pxr.SdfPath(sketchName));

            m_xformName = hasSketchRoot ? sketchName + xformName : xformName;
            float scale = hasSketchRoot ? App.UNITS_TO_METERS : 1;

            m_UnitsInMeters = hasSketchRoot;

            m_Scene.Time            = null;
            m_UsdCameraInfo         = new UsdCameraSample();
            m_UsdCameraInfo.shutter = new USD.NET.Unity.CameraSample.Shutter();
            m_Scene.Read(m_xformName, m_UsdCameraInfo);

            m_UsdCamera  = new UsdCameraXformSample();
            m_Scene.Time = 0;

            m_Scene.Read(m_xformName, m_UsdCamera);
            var basisMat = AxisConvention.GetFromUnity(AxisConvention.kUsd)
                           * Matrix4x4.Scale(Vector3.one * scale);

            m_UsdCamera.transform = ExportUtils.ChangeBasis(m_UsdCamera.transform, basisMat, basisMat.inverse);

            TrTransform xf_WS = UsdXformToWorldSpaceXform(m_UsdCamera);

            xf_WS.ToTransform(transform);

            m_PlaybackCameras = FindObjectsOfType <Camera>();
        }
Пример #7
0
        public void Deserialize()
        {
            m_Scene.Read(m_xformName, m_UsdCamera);

            var basisMat = Matrix4x4.identity;

            if (m_UnitsInMeters)
            {
                basisMat[0, 0] *= App.METERS_TO_UNITS;
                basisMat[1, 1] *= App.METERS_TO_UNITS;
                basisMat[2, 2] *= -1 * App.METERS_TO_UNITS;
            }
            m_UsdCamera.transform = ExportUtils.ChangeBasis(m_UsdCamera.transform, basisMat, basisMat.inverse);

            TrTransform xf_WS  = UsdXformToWorldSpaceXform(m_UsdCamera);
            TrTransform old_WS = TrTransform.FromTransform(transform);
            TrTransform new_WS = TrTransform.Lerp(old_WS, xf_WS, 1 - m_Smoothing);

            new_WS.scale = m_UsdCameraInfo.eyeScale != 0 ? m_UsdCameraInfo.eyeScale : 1;
            new_WS.ToTransform(transform);

            // Pre-M23.3, .usd files won't have fov defined, so this value will be negative.
            if (m_UsdCamera.fov > 0)
            {
                // A bit brute force, but we're running through all cameras in the scene to make
                // sure the preview shows the modified fov.
                for (int i = 0; i < m_PlaybackCameras.Length; ++i)
                {
                    if (m_PlaybackCameras[i].gameObject.activeInHierarchy &&
                        m_PlaybackCameras[i].isActiveAndEnabled)
                    {
                        m_PlaybackCameras[i].fieldOfView = m_UsdCamera.fov;
                    }
                }
            }
        }
Пример #8
0
        private static void BuildModelsAsModelMeshes(
            SceneStatePayload payload, IEnumerable <ModelWidget> modelWidgets)
        {
            // This is a quadruple-nested loop that's triply flattened into a single IEnumerable.
            // The structure of the data is is:
            // 1. There are 1+ Models (which I'll call "prefab" because Model is a loaded term here)
            // 2. Each "prefab" is used by 1+ ModelWidgets ("instance")
            // 3. Each ModelWidget, and the "prefab", contains 1+ GameObjects (which has a single Mesh)
            // 4. Each Mesh has 1+ unity submeshes (almost always exactly 1)
            //    These are converted to ModelMeshPayload
            //
            // The resulting ModelMeshPayloads are a mixture of data from all 4 levels, for example
            // - ModelMeshPayload.model comes from level 1
            // - ModelMeshPayload.modelId comes from level 2
            // - ModelMeshPayload.xform comes from level 3
            // - ModelMeshPayload.pool comes from level 4
            //
            // Orthogonal to this nesting, some data comes from the "prefab" and some from the "instance",
            // since we don't want to have to convert a mesh into GeometryPool once per instance.
            // So we iterate level 3 for the Model once, up front; then each ModelWidget level 3 is
            // a parallel iteration with that Model's pre-computed iteration.
            foreach (var group in modelWidgets.GroupBy(widget => widget.Model))
            {
                Model prefab = group.Key;
                // Each element represents a GameObject in the "prefab"
                List <PrefabGeometry[]> prefabObjs = GetPrefabGameObjs(payload, prefab).ToList();

                foreach ((ModelWidget widget, int widgetIndex) in group.WithIndex())
                {
                    Matrix4x4 widgetXform = ExportUtils.ChangeBasis(widget.transform, payload);
                    // Acts like our AsScene[], AsCanvas[] accessors
                    var AsWidget = new TransformExtensions.RelativeAccessor(widget.transform);

                    // Verify that it's okay to parallel-iterate over the prefab and instance gameObjs.
                    // It should be, unless one or the other has mucked with their m_MeshChildren.
                    MeshFilter[] instanceObjs = widget.GetMeshes();
                    if (prefabObjs.Count != instanceObjs.Length)
                    {
                        Debug.LogError($"Bad Model instance: {prefabObjs.Count} {instanceObjs.Length}");
                        // TODO: check order as well, somehow
                        continue;
                    }

                    // Parallel iterate, taking shared things (GeometryPool, material, name) from the
                    // "prefab" and xforms, unique ids, etc from the "instance".
                    for (int objIndex = 0; objIndex < prefabObjs.Count; ++objIndex)
                    {
                        PrefabGeometry[] prefabSubMeshes = prefabObjs[objIndex];
                        GameObject       instanceObj     = instanceObjs[objIndex].gameObject;
                        Matrix4x4        localXform      = ExportUtils.ChangeBasis(AsWidget[instanceObj.transform], payload);

                        int       objId = payload.idGenerator.GetIdFromInstanceId(instanceObj);
                        Matrix4x4 xform = ExportUtils.ChangeBasis(instanceObj.transform, payload);
                        foreach (PrefabGeometry prefabSubMesh in prefabSubMeshes)
                        {
                            payload.modelMeshes.Add(new ExportUtils.ModelMeshPayload(
                                                        payload.groupIdMapping.GetId(widget.Group))
                            {
                                // Copied from "prefab"
                                model              = prefabSubMesh.model,
                                geometry           = prefabSubMesh.pool,
                                exportableMaterial = prefabSubMesh.exportableMaterial,
                                geometryName       = prefabSubMesh.name,
                                // Unique to instance
                                legacyUniqueName = $"{prefabSubMesh.name}_i{objId}",
                                nodeName         = $"{prefabSubMesh.name}_{widgetIndex}",
                                parentXform      = widgetXform,
                                localXform       = localXform,
                                modelId          = widgetIndex,
                                xform            = xform,
                            });
                        }
                    }
                }
            }
        }