/// <summary>
        /// Called form the 'Update' function whenever the input device is moved. This function
        /// will snap the target object to the surface hovered by the mouse cursor.
        /// </summary>
        private void OnInputDeviceMoved(IInputDevice inputDevice)
        {
            // We will need to perform a raycast to check what lies underneath the mouse cursor.
            // In order to do this, we will first create a raycast filter instance to specify
            // additional information for the raycast...
            SceneRaycastFilter raycastFilter = new SceneRaycastFilter();

            raycastFilter.LayerMask = _objectSurfaceLayers;                                 // The ray can hit only objects belonging to these layers
            raycastFilter.AllowedObjectTypes.Add(GameObjectType.Mesh);                      // Allow the ray to hit mesh objects
            raycastFilter.AllowedObjectTypes.Add(GameObjectType.Terrain);                   // Allow the ray to hit terrain objects
            raycastFilter.IgnoreObjects.AddRange(_targetHierarchy.GetAllChildrenAndSelf()); // The ray can not hit the target object (i.e. the object we are snapping)

            // Perform the raycast. If nothing is hit, just return.
            SceneRaycastHit raycastHit = RTScene.Get.Raycast(inputDevice.GetRay(RTFocusCamera.Get.TargetCamera), SceneRaycastPrecision.BestFit, raycastFilter);

            if (!raycastHit.WasAnythingHit)
            {
                return;
            }

            // If we reach this point we know that something was hit. We just need to find out what and based
            // on what was hit, we need to fill the necessary information in the snap config instance. Basically,
            // we need to specify surface inf such as the surface normal, hit point, the object that acts as the
            // snap surface etc. This info is extracted from the raycast hit instance differently depending on
            // whether we hit a game object or the scene grid.
            if (raycastHit.WasAnObjectHit)
            {
                // We hit an object. Get its type and return if it's not a mesh or terrain.
                GameObjectType objectType = raycastHit.ObjectHit.HitObject.GetGameObjectType();
                if (objectType != GameObjectType.Mesh && objectType != GameObjectType.Terrain)
                {
                    return;
                }

                // We are dealing with a mesh or terrain object. Extract the surface info from the 'ObjectHit'
                // field of the raycast hit instance.
                _snapConfig.SurfaceHitNormal = raycastHit.ObjectHit.HitNormal;
                _snapConfig.SurfaceHitPlane  = raycastHit.ObjectHit.HitPlane;
                _snapConfig.SurfaceHitPoint  = raycastHit.ObjectHit.HitPoint;
                _snapConfig.SurfaceObject    = raycastHit.ObjectHit.HitObject;
                _snapConfig.SurfaceType      = objectType == GameObjectType.Mesh ? ObjectSurfaceSnap.Type.Mesh : ObjectSurfaceSnap.Type.UnityTerrain;
            }
            else
            {
                // The scene grid was hit. Extract the surface info from the 'GridHit'
                // field of the raycast hit instance.
                _snapConfig.SurfaceHitNormal = raycastHit.GridHit.HitNormal;
                _snapConfig.SurfaceHitPlane  = raycastHit.GridHit.HitPlane;
                _snapConfig.SurfaceHitPoint  = raycastHit.GridHit.HitPoint;
                _snapConfig.SurfaceType      = ObjectSurfaceSnap.Type.SceneGrid;
            }

            // Call 'ObjectSurfaceSnap.SnapHierarchy' on the target hierarchy and pass the snap config data along.
            // Note: The function requires that we first set the position of the hierarchy to the surface hit point.
            _targetHierarchy.transform.position = _snapConfig.SurfaceHitPoint;
            ObjectSurfaceSnap.SnapHierarchy(_targetHierarchy, _snapConfig);
        }
        public static GameObject SpawnInFrontOfCamera(GameObject sourceObject, Camera camera, Config config)
        {
            float halfSize = config.ObjectSize * 0.5f;

            var boundsQConfig = new ObjectBounds.QueryConfig();

            boundsQConfig.ObjectTypes  = GameObjectTypeHelper.AllCombined;
            boundsQConfig.NoVolumeSize = Vector3Ex.FromValue(1.0f);

            Transform cameraTransform = camera.transform;
            AABB      aabb            = ObjectBounds.CalcHierarchyWorldAABB(sourceObject, boundsQConfig);

            if (!aabb.IsValid)
            {
                return(null);
            }

            Sphere  sphere          = new Sphere(aabb);
            Vector3 fromCenterToPos = sourceObject.transform.position - sphere.Center;
            float   zOffset         = Mathf.Max(camera.nearClipPlane + sphere.Radius, sphere.Radius / halfSize);
            Vector3 spherePos       = cameraTransform.position + cameraTransform.forward * zOffset;

            GameObject spawned = GameObject.Instantiate(sourceObject, spherePos + fromCenterToPos, sourceObject.transform.rotation) as GameObject;

            spawned.SetActive(true);

            //VRCTS
            spawned.name = sourceObject.name;

            OBB spawnedOBB    = ObjectBounds.CalcHierarchyWorldOBB(spawned, boundsQConfig);
            Ray ray           = new Ray(camera.transform.position, (spawnedOBB.Center - camera.transform.position).normalized);
            var raycastFilter = new SceneRaycastFilter();

            raycastFilter.AllowedObjectTypes.Add(GameObjectType.Mesh);
            raycastFilter.AllowedObjectTypes.Add(GameObjectType.Terrain);
            raycastFilter.AllowedObjectTypes.Add(GameObjectType.Sprite);

            var rayHit = RTScene.Get.Raycast(ray, SceneRaycastPrecision.BestFit, raycastFilter);

            if (rayHit.WasAnObjectHit)
            {
                Vector3 oldCenter = spawnedOBB.Center;
                spawnedOBB.Center = rayHit.ObjectHit.HitPoint;
                Vector3 offsetVector = spawnedOBB.Center - oldCenter;
                offsetVector += ObjectSurfaceSnap.CalculateSitOnSurfaceOffset(spawnedOBB, rayHit.ObjectHit.HitPlane, 0.0f);

                spawned.transform.position += offsetVector;
            }

            return(spawned);
        }
Example #3
0
        private void SnapTargets()
        {
            if (!CalculateTargetAABB())
            {
                return;
            }

            Vector3 oldCenter = _targetAABB.Center;

            _targetAABB.Center = _sitSurface.SitPoint;

            Plane sitSurfacePlane = _sitSurface.SitPlane;

            if (_sitBelowSurface)
            {
                sitSurfacePlane = sitSurfacePlane.InvertNormal();
            }

            Vector3 sitOnPlaneOffset = ObjectSurfaceSnap.CalculateSitOnSurfaceOffset(_targetAABB, sitSurfacePlane, 0.0f);

            _targetAABB.Center += sitOnPlaneOffset;

            Vector3 parentMove = _targetAABB.Center - oldCenter;

            foreach (var parent in _targetParents)
            {
                parent.transform.position += parentMove;
            }

            if (!SharedHotkeys.EnableFlexiSnap.IsActive())
            {
                var snapConfig = new Object2ObjectSnap.Config();
                snapConfig.Prefs             = SharedHotkeys.EnableMoreControl.IsActive() ? Object2ObjectSnap.Prefs.None : Object2ObjectSnap.Prefs.TryMatchArea;
                snapConfig.AreaMatchEps      = 1e-2f;
                snapConfig.SnapRadius        = SharedSettings.SnapRadius;
                snapConfig.DestinationLayers = SharedSettings.SnapDestinationLayers;
                snapConfig.IgnoreDestObjects = new List <GameObject>();
                snapConfig.IgnoreDestObjects.AddRange(_targetParents);
                foreach (var parent in _targetParents)
                {
                    snapConfig.IgnoreDestObjects.AddRange(parent.GetAllChildren());
                }

                Object2ObjectSnap.Snap(_targetParents, snapConfig);
            }
        }
        private void SnapTargetsToSurface()
        {
            if (_grabSurfaceInfo.SurfaceType == GrabSurfaceType.Invalid)
            {
                return;
            }

            ObjectSurfaceSnap.SnapConfig snapConfig = new ObjectSurfaceSnap.SnapConfig();
            snapConfig.SurfaceHitPoint  = _grabSurfaceInfo.AnchorPoint;
            snapConfig.SurfaceHitNormal = _grabSurfaceInfo.AnchorNormal;
            snapConfig.SurfaceHitPlane  = _grabSurfaceInfo.AnchorPlane;
            snapConfig.SurfaceObject    = _grabSurfaceInfo.SceneRaycastHit.WasAnObjectHit ? _grabSurfaceInfo.SceneRaycastHit.ObjectHit.HitObject : null;

            snapConfig.SurfaceType = ObjectSurfaceSnap.Type.UnityTerrain;
            if (_grabSurfaceInfo.SurfaceType == GrabSurfaceType.Mesh)
            {
                snapConfig.SurfaceType = ObjectSurfaceSnap.Type.Mesh;
            }
            else if (_grabSurfaceInfo.SurfaceType == GrabSurfaceType.Grid)
            {
                snapConfig.SurfaceType = ObjectSurfaceSnap.Type.SceneGrid;
            }
            else if (_grabSurfaceInfo.SurfaceType == GrabSurfaceType.SphericalMesh)
            {
                snapConfig.SurfaceType = ObjectSurfaceSnap.Type.SphericalMesh;
            }
            else if (_grabSurfaceInfo.SurfaceType == GrabSurfaceType.TerrainMesh)
            {
                snapConfig.SurfaceType = ObjectSurfaceSnap.Type.TerrainMesh;
            }

            foreach (GrabTarget grabTarget in _grabTargets)
            {
                if (grabTarget.GameObject == null)
                {
                    continue;
                }
                grabTarget.Transform.position = _grabSurfaceInfo.AnchorPoint + grabTarget.AnchorVector;

                ObjectLayerGrabSettings layerGrabSettings = SharedSettings.GetLayerGrabSettings(grabTarget.GameObject.layer);
                if (layerGrabSettings.IsActive)
                {
                    snapConfig.AlignAxis         = layerGrabSettings.AlignAxis;
                    snapConfig.AlignmentAxis     = layerGrabSettings.AlignmentAxis;
                    snapConfig.OffsetFromSurface = layerGrabSettings.DefaultOffsetFromSurface + grabTarget.OffsetFromSurface;
                }
                else
                {
                    snapConfig.AlignAxis         = SharedSettings.AlignAxis;
                    snapConfig.AlignmentAxis     = SharedSettings.AlignmentAxis;
                    snapConfig.OffsetFromSurface = SharedSettings.DefaultOffsetFromSurface + grabTarget.OffsetFromSurface;
                }

                ObjectSurfaceSnap.SnapResult snapResult = ObjectSurfaceSnap.SnapHierarchy(grabTarget.GameObject, snapConfig);
                if (snapResult.Success)
                {
                    grabTarget.SittingPlane = snapResult.SittingPlane;
                    grabTarget.SittingPoint = snapResult.SittingPoint;
                }
            }
        }
Example #5
0
        public static SnapResult SnapHierarchy(GameObject root, SnapConfig snapConfig)
        {
            const float collectEps      = 1e-2f;
            const float collectBoxScale = 1e-3f;

            bool hierarchyHasMeshes  = root.HierarchyHasMesh();
            bool hierarchyHasSprites = root.HierarchyHasSprite();

            if (!hierarchyHasMeshes && !hierarchyHasSprites)
            {
                Transform rootTransform = root.transform;
                rootTransform.position = snapConfig.SurfaceHitPlane.ProjectPoint(rootTransform.position) + snapConfig.OffsetFromSurface * snapConfig.SurfaceHitNormal;
                return(new SnapResult(snapConfig.SurfaceHitPlane, rootTransform.position));
            }

            ObjectBounds.QueryConfig boundsQConfig = new ObjectBounds.QueryConfig();
            boundsQConfig.ObjectTypes = GameObjectType.Sprite | GameObjectType.Mesh;

            bool isSurfaceSpherical    = snapConfig.SurfaceType == Type.SphericalMesh;
            bool isSurfaceTerrain      = snapConfig.SurfaceType == Type.UnityTerrain || snapConfig.SurfaceType == Type.TerrainMesh;
            bool isSurfaceUnityTerrain = snapConfig.SurfaceType == Type.UnityTerrain;

            SurfaceRaycaster raycaster = CreateSurfaceRaycaster(snapConfig.SurfaceType, snapConfig.SurfaceObject, true);

            if (snapConfig.SurfaceType != Type.SceneGrid)
            {
                Transform rootTransform = root.transform;
                if (snapConfig.AlignAxis)
                {
                    if (isSurfaceTerrain)
                    {
                        rootTransform.Align(Vector3.up, snapConfig.AlignmentAxis);

                        OBB hierarchyOBB = ObjectBounds.CalcHierarchyWorldOBB(root, boundsQConfig);
                        if (!hierarchyOBB.IsValid)
                        {
                            return(new SnapResult());
                        }

                        BoxFace        pivotFace      = BoxMath.GetMostAlignedFace(hierarchyOBB.Center, hierarchyOBB.Size, hierarchyOBB.Rotation, -Vector3.up);
                        List <Vector3> collectedVerts = ObjectVertexCollect.CollectHierarchyVerts(root, pivotFace, collectBoxScale, collectEps);

                        if (collectedVerts.Count != 0)
                        {
                            Vector3          vertsCenter = Vector3Ex.GetPointCloudCenter(collectedVerts);
                            Ray              ray         = new Ray(vertsCenter + Vector3.up * 1e-3f, -Vector3.up);
                            GameObjectRayHit surfaceHit  = raycaster.Raycast(ray);

                            if (surfaceHit != null)
                            {
                                Vector3 alignmentAxis = surfaceHit.HitNormal;
                                if (isSurfaceUnityTerrain)
                                {
                                    Terrain terrain = snapConfig.SurfaceObject.GetComponent <Terrain>();
                                    alignmentAxis = terrain.GetInterpolatedNormal(surfaceHit.HitPoint);
                                }
                                Quaternion appliedRotation = rootTransform.Align(alignmentAxis, snapConfig.AlignmentAxis);

                                hierarchyOBB = ObjectBounds.CalcHierarchyWorldOBB(root, boundsQConfig);
                                appliedRotation.RotatePoints(collectedVerts, rootTransform.position);

                                Vector3 sitOnPlaneOffset = ObjectSurfaceSnap.CalculateSitOnSurfaceOffset(hierarchyOBB, new Plane(Vector3.up, surfaceHit.HitPoint), 0.1f);
                                rootTransform.position += sitOnPlaneOffset;
                                hierarchyOBB.Center    += sitOnPlaneOffset;
                                Vector3Ex.OffsetPoints(collectedVerts, sitOnPlaneOffset);

                                Vector3 embedVector = ObjectSurfaceSnap.CalculateEmbedVector(collectedVerts, snapConfig.SurfaceObject, -Vector3.up, snapConfig.SurfaceType);
                                rootTransform.position += (embedVector + alignmentAxis * snapConfig.OffsetFromSurface);
                                return(new SnapResult(new Plane(alignmentAxis, surfaceHit.HitPoint), surfaceHit.HitPoint));
                            }
                        }
                    }
                    else
                    {
                        if (!isSurfaceSpherical)
                        {
                            rootTransform.Align(snapConfig.SurfaceHitNormal, snapConfig.AlignmentAxis);
                            OBB hierarchyOBB = ObjectBounds.CalcHierarchyWorldOBB(root, boundsQConfig);
                            if (!hierarchyOBB.IsValid)
                            {
                                return(new SnapResult());
                            }

                            BoxFace        pivotFace      = BoxMath.GetMostAlignedFace(hierarchyOBB.Center, hierarchyOBB.Size, hierarchyOBB.Rotation, -snapConfig.SurfaceHitNormal);
                            List <Vector3> collectedVerts = ObjectVertexCollect.CollectHierarchyVerts(root, pivotFace, collectBoxScale, collectEps);

                            if (collectedVerts.Count != 0)
                            {
                                Vector3 vertsCenter = Vector3Ex.GetPointCloudCenter(collectedVerts);

                                // Note: Cast the ray from far away enough so that we don't cast from the interior of the mesh.
                                //       This can happen when the object is embedded inside the mesh surface.
                                AABB    surfaceAABB  = ObjectBounds.CalcMeshWorldAABB(snapConfig.SurfaceObject);
                                float   sphereRadius = surfaceAABB.Extents.magnitude;
                                Vector3 rayOrigin    = vertsCenter + snapConfig.SurfaceHitNormal * sphereRadius;

                                Ray ray = new Ray(rayOrigin, -snapConfig.SurfaceHitNormal);
                                GameObjectRayHit surfaceHit = raycaster.Raycast(ray);

                                if (surfaceHit != null)
                                {
                                    Vector3 alignmentAxis = surfaceHit.HitNormal;
                                    rootTransform.Align(alignmentAxis, snapConfig.AlignmentAxis);
                                    hierarchyOBB = ObjectBounds.CalcHierarchyWorldOBB(root, boundsQConfig);

                                    Vector3 sitOnPlaneOffset = ObjectSurfaceSnap.CalculateSitOnSurfaceOffset(hierarchyOBB, surfaceHit.HitPlane, 0.0f);
                                    rootTransform.position += sitOnPlaneOffset;
                                    rootTransform.position += alignmentAxis * snapConfig.OffsetFromSurface;
                                    return(new SnapResult(new Plane(alignmentAxis, surfaceHit.HitPoint), surfaceHit.HitPoint));
                                }
                                else
                                {
                                    Vector3 alignmentAxis = snapConfig.SurfaceHitNormal;
                                    rootTransform.Align(alignmentAxis, snapConfig.AlignmentAxis);
                                    hierarchyOBB = ObjectBounds.CalcHierarchyWorldOBB(root, boundsQConfig);

                                    Vector3 sitOnPlaneOffset = ObjectSurfaceSnap.CalculateSitOnSurfaceOffset(hierarchyOBB, snapConfig.SurfaceHitPlane, 0.0f);
                                    rootTransform.position += sitOnPlaneOffset;
                                    rootTransform.position += alignmentAxis * snapConfig.OffsetFromSurface;
                                    return(new SnapResult(snapConfig.SurfaceHitPlane, snapConfig.SurfaceHitPlane.ProjectPoint(vertsCenter)));
                                }
                            }
                        }
                        else
                        {
                            Transform surfaceObjectTransform = snapConfig.SurfaceObject.transform;
                            Vector3   sphereCenter           = surfaceObjectTransform.position;
                            Vector3   radiusDir    = (rootTransform.position - sphereCenter).normalized;
                            float     sphereRadius = surfaceObjectTransform.lossyScale.GetMaxAbsComp() * 0.5f;

                            rootTransform.Align(radiusDir, snapConfig.AlignmentAxis);
                            OBB hierarchyOBB = ObjectBounds.CalcHierarchyWorldOBB(root, boundsQConfig);
                            if (!hierarchyOBB.IsValid)
                            {
                                return(new SnapResult());
                            }

                            BoxFace        pivotFace      = BoxMath.GetMostAlignedFace(hierarchyOBB.Center, hierarchyOBB.Size, hierarchyOBB.Rotation, -radiusDir);
                            List <Vector3> collectedVerts = ObjectVertexCollect.CollectHierarchyVerts(root, pivotFace, collectBoxScale, collectEps);

                            Vector3 sitPoint         = sphereCenter + radiusDir * sphereRadius;
                            Plane   sitPlane         = new Plane(radiusDir, sitPoint);
                            Vector3 sitOnPlaneOffset = ObjectSurfaceSnap.CalculateSitOnSurfaceOffset(hierarchyOBB, sitPlane, 0.0f);

                            rootTransform.position += sitOnPlaneOffset;
                            hierarchyOBB.Center    += sitOnPlaneOffset;
                            Vector3Ex.OffsetPoints(collectedVerts, sitOnPlaneOffset);

                            rootTransform.position += radiusDir * snapConfig.OffsetFromSurface;
                            return(new SnapResult(sitPlane, sitPoint));
                        }
                    }
                }
                else
                {
                    OBB hierarchyOBB = ObjectBounds.CalcHierarchyWorldOBB(root, boundsQConfig);
                    if (!hierarchyOBB.IsValid)
                    {
                        return(new SnapResult());
                    }

                    if (isSurfaceTerrain || (!isSurfaceSpherical && snapConfig.SurfaceType == Type.Mesh))
                    {
                        Ray ray = new Ray(hierarchyOBB.Center, isSurfaceTerrain ? -Vector3.up : -snapConfig.SurfaceHitNormal);
                        GameObjectRayHit surfaceHit = raycaster.Raycast(ray);
                        if (surfaceHit != null)
                        {
                            Vector3 sitOnPlaneOffset = ObjectSurfaceSnap.CalculateSitOnSurfaceOffset(hierarchyOBB, surfaceHit.HitPlane, 0.0f);
                            rootTransform.position += sitOnPlaneOffset;

                            if (isSurfaceTerrain)
                            {
                                hierarchyOBB.Center += sitOnPlaneOffset;
                                BoxFace        pivotFace      = BoxMath.GetMostAlignedFace(hierarchyOBB.Center, hierarchyOBB.Size, hierarchyOBB.Rotation, -surfaceHit.HitNormal);
                                List <Vector3> collectedVerts = ObjectVertexCollect.CollectHierarchyVerts(root, pivotFace, collectBoxScale, collectEps);

                                Vector3 embedVector = ObjectSurfaceSnap.CalculateEmbedVector(collectedVerts, snapConfig.SurfaceObject, -Vector3.up, snapConfig.SurfaceType);
                                rootTransform.position += embedVector;
                            }

                            rootTransform.position += surfaceHit.HitNormal * snapConfig.OffsetFromSurface;
                            return(new SnapResult(surfaceHit.HitPlane, surfaceHit.HitPoint));
                        }
                        else
                        if (!isSurfaceSpherical && snapConfig.SurfaceType == Type.Mesh)
                        {
                            Vector3 sitOnPlaneOffset = ObjectSurfaceSnap.CalculateSitOnSurfaceOffset(hierarchyOBB, snapConfig.SurfaceHitPlane, 0.0f);
                            rootTransform.position += sitOnPlaneOffset;
                            rootTransform.position += snapConfig.SurfaceHitNormal * snapConfig.OffsetFromSurface;
                            return(new SnapResult(snapConfig.SurfaceHitPlane, snapConfig.SurfaceHitPlane.ProjectPoint(hierarchyOBB.Center)));
                        }
                    }
                    else
                    if (isSurfaceSpherical)
                    {
                        Transform surfaceObjectTransform = snapConfig.SurfaceObject.transform;
                        Vector3   sphereCenter           = surfaceObjectTransform.position;
                        Vector3   radiusDir    = (rootTransform.position - sphereCenter).normalized;
                        float     sphereRadius = surfaceObjectTransform.lossyScale.GetMaxAbsComp() * 0.5f;

                        BoxFace        pivotFace      = BoxMath.GetMostAlignedFace(hierarchyOBB.Center, hierarchyOBB.Size, hierarchyOBB.Rotation, -radiusDir);
                        List <Vector3> collectedVerts = ObjectVertexCollect.CollectHierarchyVerts(root, pivotFace, collectBoxScale, collectEps);

                        Vector3 sitPoint         = sphereCenter + radiusDir * sphereRadius;
                        Plane   sitPlane         = new Plane(radiusDir, sitPoint);
                        Vector3 sitOnPlaneOffset = ObjectSurfaceSnap.CalculateSitOnSurfaceOffset(hierarchyOBB, sitPlane, 0.0f);

                        rootTransform.position += sitOnPlaneOffset;
                        hierarchyOBB.Center    += sitOnPlaneOffset;
                        Vector3Ex.OffsetPoints(collectedVerts, sitOnPlaneOffset);

                        rootTransform.position += radiusDir * snapConfig.OffsetFromSurface;
                        return(new SnapResult(sitPlane, sitPoint));
                    }
                }
            }
            if (snapConfig.SurfaceType == Type.SceneGrid)
            {
                OBB hierarchyOBB = ObjectBounds.CalcHierarchyWorldOBB(root, boundsQConfig);
                if (!hierarchyOBB.IsValid)
                {
                    return(new SnapResult());
                }

                Transform rootTransform = root.transform;
                if (snapConfig.AlignAxis)
                {
                    rootTransform.Align(snapConfig.SurfaceHitNormal, snapConfig.AlignmentAxis);
                    hierarchyOBB = ObjectBounds.CalcHierarchyWorldOBB(root, boundsQConfig);
                }

                rootTransform.position += ObjectSurfaceSnap.CalculateSitOnSurfaceOffset(hierarchyOBB, snapConfig.SurfaceHitPlane, snapConfig.OffsetFromSurface);
                return(new SnapResult(snapConfig.SurfaceHitPlane, snapConfig.SurfaceHitPlane.ProjectPoint(hierarchyOBB.Center)));
            }

            return(new SnapResult());
        }