/// <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); }
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; } } }
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()); }