private GameObject FindGazeTarget(out RaycastHit hit, float distance, int layerMask) { //TODO: This code assumes that the collider is in a child object. If this is not the case, // the code fails. if (Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out hit, distance, layerMask)) { if (hit.collider.gameObject.CompareTag(Layers.Instance.surfacePlaneTag)) { HoloToolkit.Unity.SpatialMapping.SurfacePlane p = hit.collider.gameObject.GetComponent <HoloToolkit.Unity.SpatialMapping.SurfacePlane>(); if (p.PlaneType == HoloToolkit.Unity.SpatialMapping.PlaneTypes.Wall && Input.GetButtonDown("Fire2")) { Debug.Log("hit point=" + hit.point); ParticleEffectsManager.Instance.CreateBulletHole(hit.point, hit.normal, p); } } /* * GameObject target = hit.collider.transform.parent.gameObject; * m_gazeTarget = target; * if (target == null) * { * Debug.Log("ERROR: CANNOT IDENTIFY RAYCAST OBJECT"); * return null; * } * cursor1.transform.position = hit.point + hit.normal * 0.01f; * cursor1.transform.forward = hit.normal; * return target.activeSelf ? target : null; */ } return(null); }
/// <summary> /// Gets all active planes of the specified type(s). /// </summary> /// <param name="planeTypes">A flag which includes all plane type(s) that should be returned.</param> /// <returns>A collection of planes that match the expected type(s).</returns> public List <GameObject> GetActivePlanes(PlaneTypes planeTypes) { List <GameObject> typePlanes = new List <GameObject>(); foreach (GameObject plane in ActivePlanes) { SurfacePlane surfacePlane = plane.GetComponent <SurfacePlane>(); if (surfacePlane != null) { if ((planeTypes & surfacePlane.PlaneType) == surfacePlane.PlaneType) { if (surfacePlane.PlaneType == PlaneTypes.Table) { plane.tag = "Table"; } if (surfacePlane.PlaneType == PlaneTypes.Ceiling) { plane.tag = "Ceiling"; } if (surfacePlane.PlaneType == PlaneTypes.Wall) { plane.tag = "Wall"; } if (surfacePlane.PlaneType == PlaneTypes.Floor) { plane.tag = "Floor"; } typePlanes.Add(plane); } } } return(typePlanes); }
public Task(HoloToolkit.Unity.SpatialMapping.SurfacePlane pPlane, GameObject pMarginVolume, Vector3 pCenterPointOnFrontPlane, Vector3 pCenterPointOnBackPlane, GameObject pEmbedded) { plane = pPlane; marginVolume = pMarginVolume; centerPointOnFrontPlane = pCenterPointOnFrontPlane; centerPointOnBackPlane = pCenterPointOnBackPlane; embedded = pEmbedded; }
private bool HasSufficientMargin(HoloToolkit.Unity.SpatialMapping.SurfacePlane plane, MeshFilter meshFilter, float requiredMargin) { Tuple <HoloToolkit.Unity.SpatialMapping.SurfacePlane, MeshFilter> key = new Tuple <HoloToolkit.Unity.SpatialMapping.SurfacePlane, MeshFilter>(plane, meshFilter); //Debug.Log("hash key " + plane.GetInstanceID() + "," + meshFilter.GetInstanceID() + " = " + key.GetHashCode()); float currentMargin; if (m_marginByPlaneAndSpatialMesh.TryGetValue(key, out currentMargin)) { return(currentMargin >= requiredMargin); } return(false); }
/// <summary> /// Creates and positions a collection of Placeable space objects on SurfacePlanes in the environment. /// </summary> /// <param name="terminalObjects">Collection of prefab GameObjects that have the Placeable component.</param> /// <param name="surfaces">Collection of SurfacePlane objects in the world.</param> private void CreateTerminalObjects(List <GameObject> terminalObjects, List <GameObject> surfaces) { List <int> UsedPlanes = new List <int>(); // Sort the planes by distance to user. surfaces.Sort((lhs, rhs) => { Vector3 headPosition = Camera.main.transform.position; Collider rightCollider = rhs.GetComponent <Collider>(); Collider leftCollider = lhs.GetComponent <Collider>(); // This plane is big enough, now we will evaluate how far the plane is from the user's head. // Since planes can be quite large, we should find the closest point on the plane's bounds to the // user's head, rather than just taking the plane's center position. Vector3 rightSpot = rightCollider.ClosestPointOnBounds(headPosition); Vector3 leftSpot = leftCollider.ClosestPointOnBounds(headPosition); return(Vector3.Distance(leftSpot, headPosition).CompareTo(Vector3.Distance(rightSpot, headPosition))); }); foreach (GameObject item in terminalObjects) { int index = -1; Collider collider = item.GetComponent <Collider>(); index = FindNearestPlane(surfaces, collider.bounds.size, UsedPlanes, false); // If we can't find a good plane we will put the object floating in space. Vector3 position = Camera.main.transform.position + Camera.main.transform.forward * 2.0f + Camera.main.transform.right * (Random.value - 1.0f) * 2.0f; Quaternion rotation = Quaternion.identity; // If we do find a good plane we can do something smarter. if (index >= 0) { UsedPlanes.Add(index); GameObject surface = surfaces[index]; SurfacePlane plane = surface.GetComponent <SurfacePlane>(); position = surface.transform.position + (plane.PlaneThickness * plane.SurfaceNormal); position = AdjustPositionWithSpatialMap(position, plane.SurfaceNormal); rotation = Camera.main.transform.localRotation; // Horizontal objects should face the user. rotation = Quaternion.LookRotation(Camera.main.transform.position); rotation.x = 0f; rotation.z = 0f; } //Vector3 finalPosition = AdjustPositionWithSpatialMap(position, surfaceType); GameObject terminalObject = Instantiate(item, position, rotation) as GameObject; terminalObject.transform.parent = gameObject.transform; terminalObject.transform.localPosition += new Vector3(0.0f, item.GetComponent <MeshRenderer>().bounds.size.y / 2, 0.0f); } }
private void CreateSurfaceHitFX(GameObject hitObject, Vector3 hitPoint, Vector3 hitNormal) { if (hitObject.CompareTag(Layers.Instance.surfacePlaneTag)) { ParticleEffectsManager.Instance.CreateBulletImpact(hitPoint, hitNormal); HoloToolkit.Unity.SpatialMapping.SurfacePlane plane = hitObject.GetComponent <HoloToolkit.Unity.SpatialMapping.SurfacePlane>(); switch (plane.PlaneType) { case HoloToolkit.Unity.SpatialMapping.PlaneTypes.Wall: ParticleEffectsManager.Instance.CreateBulletImpactDebris(hitPoint, hitNormal, 0.1f, 3, 0); ParticleEffectsManager.Instance.CreateBulletHole(hitPoint, hitNormal, plane); break; case HoloToolkit.Unity.SpatialMapping.PlaneTypes.Floor: ParticleEffectsManager.Instance.CreateLingeringFireball(hitPoint, hitNormal, 0); ParticleEffectsManager.Instance.CreateCrater(hitPoint, hitNormal); break; default: break; } } else { ParticleEffectsManager.Instance.CreateBulletImpact(hitPoint, hitNormal); float cos80 = 0.1736f; if (Mathf.Abs(Vector3.Dot(hitNormal, Vector3.right)) > cos80 && Mathf.Abs(Vector3.Dot(hitNormal, Vector3.up)) > cos80 && Mathf.Abs(Vector3.Dot(hitNormal, Vector3.forward)) > cos80) { PlayRicochetSound(); } } /* * if (Vector3.Angle(hitNormal, Vector3.up) < 10) * { * // Lingering fireball only when hitting the ground * ParticleEffectsManager.Instance.CreateLingeringFireball(hitPoint, hitNormal, 0); * } * else if (Mathf.Abs(90 - Vector3.Angle(hitNormal, Vector3.up)) < 10) * { * // Debris when hitting walls * //TODO: wall detection should actually involve testing against detected wall planes * ParticleEffectsManager.Instance.CreateBulletImpactDebris(hitPoint, hitNormal, 0.1f, 3, 0); * } */ }
/// <summary> /// Gets all active planes of the specified type(s). /// </summary> /// <param name="planeTypes">A flag which includes all plane type(s) that should be returned.</param> /// <returns>A collection of planes that match the expected type(s).</returns> public List <GameObject> GetActivePlanes(PlaneTypes planeTypes) { List <GameObject> typePlanes = new List <GameObject>(); foreach (GameObject plane in ActivePlanes) { SurfacePlane surfacePlane = plane.GetComponent <SurfacePlane>(); if (surfacePlane != null) { if ((planeTypes & surfacePlane.PlaneType) == surfacePlane.PlaneType) { typePlanes.Add(plane); } } } return(typePlanes); }
public void Embed(GameObject embedded, HoloToolkit.Unity.SpatialMapping.SurfacePlane plane) { if (null == plane) { return; } // Temporarily make the embedded object render in front of spatial mesh MakeHighPriorityRenderOrder(embedded); // Create an object describing the margin volume required for embedded // object placement GameObject marginVolume; Vector3 centerPointOnFrontPlane; Vector3 centerPointOnBackPlane; if (CreateMarginVolumeObject(out marginVolume, out centerPointOnFrontPlane, out centerPointOnBackPlane, embedded, plane)) { return; } // Deform the spatial mesh to create a margin volume large enough for the // embedded object m_taskQueue.Enqueue(new Task(plane, marginVolume, centerPointOnFrontPlane, centerPointOnBackPlane, embedded)); if (!m_working) { m_working = true; StartCoroutine(DeformSurfaceCoroutine()); } if (debugMarginVolumes) { Debug.Log("pos=" + marginVolume.transform.position + ", obbpos = " + marginVolume.GetComponent <BoxCollider>().transform.position); GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube); cube.transform.parent = null; cube.transform.localScale = marginVolume.transform.localScale; cube.transform.position = marginVolume.transform.position; cube.transform.transform.rotation = marginVolume.transform.rotation; cube.GetComponent <Renderer>().material = debugMaterial; cube.GetComponent <Renderer>().material.color = Color.green; cube.SetActive(true); } }
//TODO: move this function into Bullet.cs //TODO: anchors should be embedded into the SurfacePlane and then the bullet holes attached to that plane public void CreateBulletHole(Vector3 position, Vector3 normal, HoloToolkit.Unity.SpatialMapping.SurfacePlane plane) { //m_bulletHoleBuffer.Insert(position + normal * .005f, normal); GameObject bulletHole = Instantiate(bulletHolePrefab, position + normal * .005f, Quaternion.LookRotation(normal)) as GameObject; bulletHole.AddComponent <WorldAnchor>(); bulletHole.transform.parent = this.transform; //SpatialMeshDeformationManager.Instance.Embed(bulletHole); SurfacePlaneDeformationManager.Instance.Embed(bulletHole, plane); //TODO: logic for objects to self destruct after not being gazed at for long enough? m_bulletHoles.Enqueue(bulletHole); while (m_bulletHoles.Count > maxBulletHoles) { GameObject oldBulletHole = m_bulletHoles.Dequeue(); if (oldBulletHole) // if hasn't destroyed itself already { Destroy(oldBulletHole); } } }
/// <summary> /// Sets visibility of planes based on their type. /// </summary> /// <param name="surfacePlane"></param> private void SetPlaneVisibility(SurfacePlane surfacePlane) { surfacePlane.IsVisible = ((drawPlanesMask & surfacePlane.PlaneType) == surfacePlane.PlaneType); }
/// <summary> /// Iterator block, analyzes surface meshes to find planes and create new 3D cubes to represent each plane. /// </summary> /// <returns>Yield result.</returns> private IEnumerator MakePlanesRoutine() { // Remove any previously existing planes, as they may no longer be valid. for (int index = 0; index < ActivePlanes.Count; index++) { Destroy(ActivePlanes[index]); } // Pause our work, and continue on the next frame. yield return(null); float start = Time.realtimeSinceStartup; ActivePlanes.Clear(); // Get the latest Mesh data from the Spatial Mapping Manager. List <PlaneFinding.MeshData> meshData = new List <PlaneFinding.MeshData>(); List <MeshFilter> filters = SpatialMappingManager.Instance.GetMeshFilters(); for (int index = 0; index < filters.Count; index++) { MeshFilter filter = filters[index]; if (filter != null && filter.sharedMesh != null) { // fix surface mesh normals so we can get correct plane orientation. filter.mesh.RecalculateNormals(); meshData.Add(new PlaneFinding.MeshData(filter)); } if ((Time.realtimeSinceStartup - start) > FrameTime) { // Pause our work, and continue to make more PlaneFinding objects on the next frame. yield return(null); start = Time.realtimeSinceStartup; } } // Pause our work, and continue on the next frame. yield return(null); #if !UNITY_EDITOR && UNITY_METRO // When not in the unity editor we can use a cool background task to help manage FindPlanes(). Task <BoundedPlane[]> planeTask = Task.Run(() => PlaneFinding.FindPlanes(meshData, snapToGravityThreshold, MinArea)); while (planeTask.IsCompleted == false) { yield return(null); } BoundedPlane[] planes = planeTask.Result; #else // In the unity editor, the task class isn't available, but perf is usually good, so we'll just wait for FindPlanes to complete. BoundedPlane[] planes = PlaneFinding.FindPlanes(meshData, snapToGravityThreshold, MinArea); #endif // Pause our work here, and continue on the next frame. yield return(null); start = Time.realtimeSinceStartup; float maxFloorArea = 0.0f; float maxCeilingArea = 0.0f; FloorYPosition = 0.0f; CeilingYPosition = 0.0f; float upNormalThreshold = 0.9f; if (SurfacePlanePrefab != null && SurfacePlanePrefab.GetComponent <SurfacePlane>() != null) { upNormalThreshold = SurfacePlanePrefab.GetComponent <SurfacePlane>().UpNormalThreshold; } // Find the floor and ceiling. // We classify the floor as the maximum horizontal surface below the user's head. // We classify the ceiling as the maximum horizontal surface above the user's head. for (int i = 0; i < planes.Length; i++) { BoundedPlane boundedPlane = planes[i]; if (boundedPlane.Bounds.Center.y < 0 && boundedPlane.Plane.normal.y >= upNormalThreshold) { maxFloorArea = Mathf.Max(maxFloorArea, boundedPlane.Area); if (maxFloorArea == boundedPlane.Area) { FloorYPosition = boundedPlane.Bounds.Center.y; } } else if (boundedPlane.Bounds.Center.y > 0 && boundedPlane.Plane.normal.y <= -(upNormalThreshold)) { maxCeilingArea = Mathf.Max(maxCeilingArea, boundedPlane.Area); if (maxCeilingArea == boundedPlane.Area) { CeilingYPosition = boundedPlane.Bounds.Center.y; } } } // Create SurfacePlane objects to represent each plane found in the Spatial Mapping mesh. for (int index = 0; index < planes.Length; index++) { GameObject destPlane; BoundedPlane boundedPlane = planes[index]; // Instantiate a SurfacePlane object, which will have the same bounds as our BoundedPlane object. if (SurfacePlanePrefab != null && SurfacePlanePrefab.GetComponent <SurfacePlane>() != null) { destPlane = Instantiate(SurfacePlanePrefab); } else { destPlane = GameObject.CreatePrimitive(PrimitiveType.Cube); destPlane.AddComponent <SurfacePlane>(); destPlane.GetComponent <Renderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; } destPlane.transform.parent = planesParent.transform; SurfacePlane surfacePlane = destPlane.GetComponent <SurfacePlane>(); // Set the Plane property to adjust transform position/scale/rotation and determine plane type. surfacePlane.Plane = boundedPlane; SetPlaneVisibility(surfacePlane); if ((destroyPlanesMask & surfacePlane.PlaneType) == surfacePlane.PlaneType) { DestroyImmediate(destPlane); } else { // Set the plane to use the same layer as the SpatialMapping mesh. destPlane.layer = SpatialMappingManager.Instance.PhysicsLayer; ActivePlanes.Add(destPlane); } // If too much time has passed, we need to return control to the main game loop. if ((Time.realtimeSinceStartup - start) > FrameTime) { // Pause our work here, and continue making additional planes on the next frame. yield return(null); start = Time.realtimeSinceStartup; } } Debug.Log("Finished making planes."); // We are done creating planes, trigger an event. EventHandler handler = MakePlanesComplete; if (handler != null) { handler(this, EventArgs.Empty); } makingPlanes = false; }
/// <summary> /// Creates and positions a collection of Placeable space objects on SurfacePlanes in the environment. /// </summary> /// <param name="spaceObjects">Collection of prefab GameObjects that have the Placeable component.</param> /// <param name="surfaces">Collection of SurfacePlane objects in the world.</param> /// <param name="surfaceType">Type of objects and planes that we are trying to match-up.</param> private void CreateSpaceObjects(List <GameObject> spaceObjects, List <GameObject> surfaces, PlacementSurfaces surfaceType) { List <int> UsedPlanes = new List <int>(); // Sort the planes by distance to user. surfaces.Sort((lhs, rhs) => { Vector3 headPosition = Camera.main.transform.position; Collider rightCollider = rhs.GetComponent <Collider>(); Collider leftCollider = lhs.GetComponent <Collider>(); // This plane is big enough, now we will evaluate how far the plane is from the user's head. // Since planes can be quite large, we should find the closest point on the plane's bounds to the // user's head, rather than just taking the plane's center position. Vector3 rightSpot = rightCollider.ClosestPointOnBounds(headPosition); Vector3 leftSpot = leftCollider.ClosestPointOnBounds(headPosition); return(Vector3.Distance(leftSpot, headPosition).CompareTo(Vector3.Distance(rightSpot, headPosition))); }); bool mustBePlacedOnTable = false; foreach (GameObject item in spaceObjects) { int index = -1; Collider collider = item.GetComponent <Collider>(); if (item.tag == "PlaceOnTable") { mustBePlacedOnTable = true; } if (surfaceType == PlacementSurfaces.Vertical) { index = FindNearestPlane(surfaces, collider.bounds.size, UsedPlanes, true, mustBePlacedOnTable); } else { index = FindNearestPlane(surfaces, collider.bounds.size, UsedPlanes, false, mustBePlacedOnTable); } mustBePlacedOnTable = false; // If we can't find a good plane we will put the object floating in space. Vector3 position = Camera.main.transform.position + Camera.main.transform.forward * 2.0f + Camera.main.transform.right * (Random.value - 1.0f) * 2.0f; Quaternion rotation = Quaternion.identity; // If we do find a good plane we can do something smarter. if (index >= 0) { UsedPlanes.Add(index); GameObject surface = surfaces[index]; SurfacePlane plane = surface.GetComponent <SurfacePlane>(); position = surface.transform.position + (plane.PlaneThickness * plane.SurfaceNormal); position = AdjustPositionWithSpatialMap(position, plane.SurfaceNormal); rotation = Camera.main.transform.localRotation; if (surfaceType == PlacementSurfaces.Vertical) { // Vertical objects should face out from the wall. rotation = Quaternion.LookRotation(surface.transform.forward, Vector3.up); } else { // Horizontal objects should face the user. rotation = Quaternion.LookRotation(Camera.main.transform.position); rotation.x = 0f; rotation.z = 0f; } } //Vector3 finalPosition = AdjustPositionWithSpatialMap(position, surfaceType); GameObject spaceObject = Instantiate(item, position, rotation) as GameObject; spaceObject.transform.parent = gameObject.transform; } if (onlyOneGaz == 0) { GameObject ceiling = GameObject.FindGameObjectWithTag("Ceiling"); GameObject gazInstantiated = Instantiate(gaz, ceiling.transform.position, Quaternion.identity) as GameObject; onlyOneGaz = 1; } }
private bool CreateMarginVolumeObject(out GameObject marginVolume, out Vector3 centerPointOnFrontPlane, out Vector3 centerPointOnBackPlane, GameObject embedded, HoloToolkit.Unity.SpatialMapping.SurfacePlane plane) { BoxCollider embeddedOBB = embedded.GetComponent <BoxCollider>(); if (null == embeddedOBB) { Debug.Log("ERROR: Embedded object " + embedded.name + " lacks a box collider"); marginVolume = null; centerPointOnFrontPlane = Vector3.zero; centerPointOnBackPlane = Vector3.zero; return(true); } HoloToolkit.Unity.SpatialMapping.OrientedBoundingBox surfaceBounds = plane.Plane.Bounds; /* * Compute margin volume front (i.e., the actual surface) and back (onto * which spatial mesh triangles will be projected) planes based on embedded * object thickness. * * Note that the embedded object position is not in the center of the * object but rather at a point flush with the surface it will be embedded * in. */ Vector3 intoSurfaceNormal = -Vector3.Normalize(embeddedOBB.transform.forward); float embeddedThickness = embeddedOBB.size.z * embeddedOBB.transform.lossyScale.z + extraDisplacement; Vector3 embeddedPosOnPlane = plane.transform.InverseTransformPoint(embedded.transform.position); embeddedPosOnPlane.x = 0; embeddedPosOnPlane.y = 0; centerPointOnFrontPlane = plane.transform.TransformPoint(embeddedPosOnPlane); centerPointOnBackPlane = centerPointOnFrontPlane + intoSurfaceNormal * embeddedThickness; Vector3 centerPoint = 0.5f * (centerPointOnFrontPlane + centerPointOnBackPlane); //Debug.Log("*** PlanePos: " + plane.transform.position + ", PlaneFwd: " + plane.transform.forward + ", Embed Point: " + embedded.transform.position.ToString("F2") + ", IntoNorm: " + intoSurfaceNormal.ToString("F2") + ", thick: " + embeddedThickness + ", embeddedPosOnPlane: " + embeddedPosOnPlane.ToString("F2") + ", centerFront: " + centerPointOnFrontPlane.ToString("F2") + ", centerBack: " + centerPointOnBackPlane.ToString("F2")); /* * Create a game object to describe the size, position, and orientation of * the margin volume we want to create. * * The box collider is something of a formality. The surface mesh * deformation procedure and OBB/mesh intersection tests use box colliders * to describe OBBs. Here, the box collider is made the same size as the * game object to which it is parented. The downstream intersection testing * code knows to properly scale the box based on its parent. */ marginVolume = new GameObject("deformation-" + embedded.name); marginVolume.transform.position = centerPoint; marginVolume.transform.localScale = new Vector3(2 * surfaceBounds.Extents.x, 2 * surfaceBounds.Extents.y, embeddedThickness); marginVolume.transform.rotation = surfaceBounds.Rotation; // need to use surface plane rotation because x, y differ from obj's BoxCollider obb = marginVolume.AddComponent <BoxCollider>(); obb.center = Vector3.zero; obb.size = Vector3.one; obb.enabled = false; // we do not actually want collision detection return(false); }
private void CreateSurfaceHitFX(GameObject hitObject, Vector3 hitPoint, Vector3 hitNormal) { if (hitObject.CompareTag(Layers.Instance.surfacePlaneTag)) { ParticleEffectsManager.Instance.CreateBulletImpact(hitPoint, hitNormal); HoloToolkit.Unity.SpatialMapping.SurfacePlane plane = hitObject.GetComponent <HoloToolkit.Unity.SpatialMapping.SurfacePlane>(); switch (plane.PlaneType) { case HoloToolkit.Unity.SpatialMapping.PlaneTypes.Wall: ParticleEffectsManager.Instance.CreateBulletImpactDebris(hitPoint, hitNormal, 0.1f, 3, 0); ParticleEffectsManager.Instance.CreateBulletHole(hitPoint, hitNormal, plane); break; case HoloToolkit.Unity.SpatialMapping.PlaneTypes.Floor: ParticleEffectsManager.Instance.CreateLingeringFireball(hitPoint, hitNormal, 0); //ParticleEffectsManager.Instance.CreateCrater(hitPoint, hitNormal); break; default: break; } } else { ParticleEffectsManager.Instance.CreateBulletImpact(hitPoint, hitNormal); SpatialUnderstandingDll.Imports.RaycastResult.SurfaceTypes surfaceType = PlayspaceManager.Instance.QuerySurfaceType(hitPoint, hitNormal); Debug.Log("hitNormal=" + hitNormal + ", hitPoint=" + hitPoint); float cos80 = 0.1736f; if (Mathf.Abs(Vector3.Dot(hitNormal, Vector3.right)) > cos80 && Mathf.Abs(Vector3.Dot(hitNormal, Vector3.up)) > cos80 && Mathf.Abs(Vector3.Dot(hitNormal, Vector3.forward)) > cos80) { PlayRicochetSound(); } else if (surfaceType == SpatialUnderstandingDll.Imports.RaycastResult.SurfaceTypes.WallExternal) //else if (Mathf.Abs(hitNormal.y) < 0.05f) { // Must have hit a wall //TODO: for spatial understanding, we should raycast and determine whether we actually hit a real wall ParticleEffectsManager.Instance.CreateBulletImpactDebris(hitPoint, hitNormal, 0.1f, 3, 0); ParticleEffectsManager.Instance.CreateBulletHole(hitPoint, hitNormal); } else if (surfaceType == SpatialUnderstandingDll.Imports.RaycastResult.SurfaceTypes.Floor) //else if (hitNormal.y > 0.95f) { // Must have hit the floor //TODO: raycast spatial understanding ParticleEffectsManager.Instance.CreateLingeringFireball(hitPoint, hitNormal, 0); } } /* * if (Vector3.Angle(hitNormal, Vector3.up) < 10) * { * // Lingering fireball only when hitting the ground * ParticleEffectsManager.Instance.CreateLingeringFireball(hitPoint, hitNormal, 0); * } * else if (Mathf.Abs(90 - Vector3.Angle(hitNormal, Vector3.up)) < 10) * { * // Debris when hitting walls * //TODO: wall detection should actually involve testing against detected wall planes * ParticleEffectsManager.Instance.CreateBulletImpactDebris(hitPoint, hitNormal, 0.1f, 3, 0); * } */ }