/// <summary> /// Calculate collision /// </summary> /// <param name="ray"></param> /// <param name="sphereCollider"></param> /// <param name="result"></param> private static void CalculateCollision(ref Ray ray, ref SphereCollider3D sphereCollider, out RayCastResult result) { Vector3 sphereCenter = sphereCollider.Transform3D.Position; float sphereRadius = sphereCollider.Radius * sphereCollider.Transform3D.Scale.X / 2; Vector3 m = ray.Position - sphereCenter; float b = Vector3.Dot(m, ray.Direction); float c = Vector3.Dot(m, m) - sphereRadius * sphereRadius; // Exit if r’s origin outside s (c > 0) and r pointing away from s (b > 0) if (c > 0.0f && b > 0.0f) { result = new RayCastResult(); return; } float discr = b * b - c; // A negative discriminant corresponds to ray missing sphere if (discr < 0.0f) { result = new RayCastResult(); return; } // Ray now found to intersect sphere, compute smallest t value of intersection float t = -b - (float)Math.Sqrt(discr); // If t is negative, ray started inside sphere so clamp t to zero if (t < 0.0f) { t = 0.0f; } Vector3 q = ray.Position + t * ray.Direction; Vector3 normal = (q - sphereCenter); normal.Normalize(); result = new RayCastResult() { HitBody = sphereCollider.Owner, HitData = new RayHit3D() { Location = q, Normal = normal, T = t, } }; }
private AxisManipulationHelper CreateHandle(AxisManipulationHelperType amhType, AxisType axisType, Prefab prefab, Material idleMaterial, Material grabbedMaterial, Material focusedMaterial, Quaternion orientation, AxisManipulationHelper[] relatedHandlers) { // Entity name suffix var suffix = $"{amhType}_{axisType}"; // Handle root var handle = new Entity($"handle_{suffix}") .AddComponent(new Transform3D() { LocalScale = Vector3.One * this.HandleScale, }); this.rigRootEntity.AddChild(handle); if (prefab != null) { // Instantiate prefab var prefabInstance = prefab.Instantiate(); var prefabTransform = prefabInstance.FindComponent <Transform3D>(); prefabTransform.LocalOrientation = orientation; handle.AddChild(prefabInstance); } else { // Generate default look for the handle Vector3 position = Vector3.Zero; Vector3 size = Vector3.One; Component mesh = null; Component collider = null; switch (amhType) { case AxisManipulationHelperType.Center: var sphereDiameter = 1f; mesh = new SphereMesh() { Diameter = sphereDiameter, }; collider = new SphereCollider3D() { Margin = 0.0001f, Radius = sphereDiameter, }; break; case AxisManipulationHelperType.Axis: var axisLength = 4f; var axisThickness = 0.5f; size = new Vector3(axisLength, axisThickness, axisThickness); mesh = new CubeMesh(); collider = new BoxCollider3D() { Margin = 0.0001f, Size = size + Vector3.One, Offset = Vector3.UnitX, }; position = 0.5f * Vector3.UnitX * (axisLength + 2f); break; case AxisManipulationHelperType.Plane: var planeLength = 2f; var planeThickness = 0.25f; size = new Vector3(planeLength, planeThickness, planeLength); mesh = new CubeMesh(); collider = new BoxCollider3D() { Margin = 0.0001f, Size = size + Vector3.One, Offset = Vector3.UnitX + Vector3.UnitZ, }; position = 0.5f * Vector3.Normalize(Vector3.UnitX + Vector3.UnitZ) * (planeLength + 2f); break; } // Collider entity var handleCollider = new Entity($"collider_{suffix}") .AddComponent(new Transform3D() { LocalPosition = Vector3.Transform(position, orientation), LocalOrientation = orientation, }) .AddComponent(collider) .AddComponent(new StaticBody3D() { CollisionCategories = this.CollisionCategory, IsSensor = true, }) .AddComponent(new NearInteractionGrabbable()); // Visual entity var handleVisual = new Entity($"visuals_{suffix}") .AddComponent(new Transform3D() { LocalScale = size, }) .AddComponent(mesh) .AddComponent(new MeshRenderer()) .AddComponent(new MaterialComponent()); // Build hierarchy handle.AddChild(handleCollider); handleCollider.AddChild(handleVisual); } // Apply material var materialComponents = handle.FindComponentsInChildren <MaterialComponent>().ToArray(); this.ApplyMaterialToAllComponents(materialComponents, idleMaterial); // Register helper object var helperTargetEntity = handle.FindComponentInChildren <NearInteractionGrabbable>()?.Owner; if (helperTargetEntity == null) { throw new Exception($"The handle entity needs to have a {nameof(NearInteractionGrabbable)} component."); } var handleHelper = new AxisManipulationHelper() { Type = amhType, AxisType = axisType, BaseEntity = handle, MaterialComponents = materialComponents, IdleMaterial = idleMaterial, GrabbedMaterial = grabbedMaterial, FocusedMaterial = focusedMaterial, RelatedHandles = relatedHandlers, }; this.helpers.Add(helperTargetEntity, handleHelper); return(handleHelper); }