/// <summary> /// Create visual game object and adds it as child to shape game object. /// </summary> /// <param name="shape">Shape instance.</param> /// <param name="isRenderData">If true we wont try to load predefined mesh from resources.</param> /// <returns>Visual game object if successful - otherwise null.</returns> protected static GameObject CreateGameObject(Collide.Shape shape, bool isRenderData) { GameObject go = null; try { go = isRenderData || shape is Collide.Mesh || shape is Collide.HollowCylinder || shape is Collide.Cone || shape is Collide.HollowCone ? new GameObject("") : PrefabLoader.Instantiate <GameObject>(@"Debug/" + shape.GetType().Name + "Renderer"); if (go == null) { Debug.LogWarning("Unable to find shape visual resource: " + @"Debug/" + shape.GetType().Name + "Renderer", shape); return(null); } go.name = shape.name + "_Visual"; go.transform.hideFlags = HideFlags.NotEditable; shape.gameObject.AddChild(go); go.transform.localPosition = Vector3.zero; go.transform.localRotation = Quaternion.identity; } catch (System.Exception e) { Debug.LogException(e); if (go != null) { GameObject.DestroyImmediate(go); } } return(go); }
/// <summary> /// Creates instance with one or more render meshes. Each mesh will /// be child to parent ShapeVisualRenderData object if number of /// meshes > 1. If meshes.Length == 1 the mesh renderer and filter /// will be added directly in the returned game object. /// </summary> /// <param name="shape">Shape to add render data for.</param> /// <param name="meshes">Array of meshes.</param> /// <param name="material">Material.</param> /// <param name="isRenderData">True if render data, i.e., decoupled from Collide.Mesh source objects.</param> /// <returns>Visual game object as child to shape game object if successful - otherwise null.</returns> protected static GameObject CreateInstance(Collide.Shape shape, Mesh[] meshes, Material material, bool isRenderData) { if (shape == null || meshes.Length == 0) { return(null); } var parent = CreateGameObject(shape, isRenderData); if (parent == null) { return(null); } CreateOnSelectionProxy(parent, shape); var visual = AddVisualComponent(parent, shape, isRenderData); if (visual == null) { return(null); } for (int i = 0; i < meshes.Length; ++i) { AddChildMesh(shape, parent, meshes[i], parent.name + "_" + (i + 1).ToString(), material, i > 0); } return(parent); }
/// <summary> /// Adds child game object to visual parent (the one with ShapeVisual component). /// </summary> /// <param name="shape">Parent shape this visual belongs to.</param> /// <param name="shapeVisualParent">Parent game object (the one with ShapeVisual component).</param> /// <param name="mesh">Visual mesh.</param> /// <param name="name">Name of the child.</param> /// <param name="material">Material.</param> /// <returns>Created child game object.</returns> protected static GameObject AddChildMesh(Collide.Shape shape, GameObject shapeVisualParent, Mesh mesh, string name, Material material, bool createNewGameObject) { GameObject child = shapeVisualParent; if (createNewGameObject) { child = new GameObject(""); child.name = name; shapeVisualParent.AddChild(child); child.transform.localPosition = Vector3.zero; child.transform.localRotation = Quaternion.identity; child.transform.hideFlags = HideFlags.NotEditable; child.AddComponent <MeshFilter>(); child.AddComponent <MeshRenderer>(); } var filter = child.GetComponent <MeshFilter>(); var renderer = child.GetComponent <MeshRenderer>(); filter.sharedMesh = mesh; SetMaterial(filter, renderer, material); CreateOnSelectionProxy(child, shape); return(child); }
/// <summary> /// Creates game object and ShapeVisual component given shape and if this is /// pure render data or not. /// </summary> /// <param name="shape">Shape to create ShapeVisual for.</param> /// <returns>Game object with ShapeVisual component if successful, otherwise null.</returns> protected static GameObject CreateInstance(Collide.Shape shape) { if (shape == null) { return(null); } GameObject go = CreateGameObject(shape, false); if (go == null) { return(null); } var visual = AddVisualComponent(go, shape, false); if (visual == null) { Debug.LogWarning("Unsupported shape type for visual: " + shape.GetType().FullName); return(null); } CreateOnSelectionProxy(go, shape); visual.SetMaterial(DefaultMaterial); visual.OnSizeUpdated(); return(go); }
/// <summary> /// Generates custom shape mesh /// </summary> public static Mesh GenerateMesh(Collide.Shape shape) { Collide.HollowCylinder hollowCylinder = shape as Collide.HollowCylinder; Mesh mesh = new Mesh(); float outerRadius = Mathf.Max(0, hollowCylinder.Radius); float innerRadius = Mathf.Max(0, hollowCylinder.Radius - hollowCylinder.Thickness); float halfHeight = Mathf.Max(0, hollowCylinder.Height) / 2f; int triangleCount = m_resolution * 8; int vertexCount = m_resolution * 8; //Vector2[] uvs = new Vector2[vertexCount]; Vector3[] vertices = new Vector3[vertexCount]; Vector3[] normals = new Vector3[vertexCount]; int[] triangles = new int[triangleCount * 3]; // Loop over each "pie slice" of the hollow cylinder. Vertices two per point of the slice, triangles also arranged looping around the slice for (int i = 0; i < m_resolution; i++) { int currentVertex = i * 8; int nextCurrentVertex = ((i + 1) % m_resolution) * 8; float angle = i * Mathf.PI * 2 / m_resolution; float sinI = Mathf.Sin(angle); float cosI = Mathf.Cos(angle); vertices[currentVertex] = vertices[currentVertex + 7] = new Vector3(sinI * innerRadius, halfHeight, cosI * innerRadius); vertices[currentVertex + 1] = vertices[currentVertex + 2] = new Vector3(sinI * outerRadius, halfHeight, cosI * outerRadius); vertices[currentVertex + 3] = vertices[currentVertex + 4] = new Vector3(sinI * outerRadius, -halfHeight, cosI * outerRadius); vertices[currentVertex + 5] = vertices[currentVertex + 6] = new Vector3(sinI * innerRadius, -halfHeight, cosI * innerRadius); normals[currentVertex] = normals[currentVertex + 1] = new Vector3(0, 1, 0); normals[currentVertex + 2] = normals[currentVertex + 3] = new Vector3(sinI, 0, cosI); normals[currentVertex + 4] = normals[currentVertex + 5] = new Vector3(0, -1, 0); normals[currentVertex + 6] = normals[currentVertex + 7] = new Vector3(-sinI, 0, -cosI); for (int j = 0; j < 8; j += 2) { int currentTriangle = i * 4 * 6 + j * 3; int nextJ = (j + 1) % 8; triangles[currentTriangle] = currentVertex + j; triangles[currentTriangle + 1] = currentVertex + nextJ; triangles[currentTriangle + 2] = nextCurrentVertex + nextJ; triangles[currentTriangle + 3] = currentVertex + j; triangles[currentTriangle + 4] = nextCurrentVertex + nextJ; triangles[currentTriangle + 5] = nextCurrentVertex + j; } } mesh.vertices = vertices; //m_mesh.uv = uvs; // TODO, possibly mesh.triangles = triangles; mesh.normals = normals; return(mesh); }
/// <summary> /// Adds OnSelectionProxy to <paramref name="visualParent"/> and all its children. /// </summary> /// <param name="visualParent">Visual parent game object.</param> /// <param name="shape">Shape reference.</param> private static void CreateOnSelectionProxy(GameObject visualParent, Collide.Shape shape) { visualParent.GetOrCreateComponent <OnSelectionProxy>().Component = shape; foreach (Transform child in visualParent.transform) { child.gameObject.GetOrCreateComponent <OnSelectionProxy>().Component = shape; } }
private void SynchronizeScaleIfNodeExist(Collide.Shape shape) { var data = shape.gameObject.GetComponent <ShapeDebugRenderData>(); if (data != null) { data.SynchronizeScale(shape); } }
/// <summary> /// Callback from Collide.Shape when the size of a shape has been changed. /// </summary> /// <param name="shape"></param> public static void SynchronizeScale(Collide.Shape shape) { if (!IsActiveForSynchronize) { return; } Instance.SynchronizeScaleIfNodeExist(shape); }
/// <summary> /// Called after Simulation.StepForward from shapes without rigid bodies. /// </summary> public static void OnPostSynchronizeTransforms(Collide.Shape shape) { if (!IsActiveForSynchronize) { return; } Instance.SynchronizeShape(shape); }
private static Collide.Shape[] CollectShapes(GameObject parent, RigidBody rb, Collide.Shape shape, bool searchChildren) { return(shape != null && !searchChildren?parent.GetComponents <Collide.Shape>() : shape != null || rb != null?parent.GetComponentsInChildren <Collide.Shape>() : // Both shape and rb == null and PropagateToChildren == true. parent.GetComponentsInChildren <Collide.Shape>()); }
/// <summary> /// Adds shape visual type given shape type and <paramref name="isRenderData"/>. /// </summary> /// <param name="go">Game object to add ShapeVisual component to.</param> /// <param name="shape">Shape ShapeVisual is referring.</param> /// <param name="isRenderData">True if the component should be ShapeVisualRenderData regardless of shape type.</param> /// <returns></returns> private static ShapeVisual AddVisualComponent(GameObject go, Collide.Shape shape, bool isRenderData) { ShapeVisual instance = null; if (isRenderData) { instance = go.AddComponent <ShapeVisualRenderData>(); } else if (shape is Collide.Box) { instance = go.AddComponent <ShapeVisualBox>(); } else if (shape is Collide.Sphere) { instance = go.AddComponent <ShapeVisualSphere>(); } else if (shape is Collide.Cylinder) { instance = go.AddComponent <ShapeVisualCylinder>(); } else if (shape is Collide.HollowCylinder) { instance = go.AddComponent <ShapeVisualHollowCylinder>(); } else if (shape is Collide.Cone) { instance = go.AddComponent <ShapeVisualCone>(); } else if (shape is Collide.HollowCone) { instance = go.AddComponent <ShapeVisualHollowCone>(); } else if (shape is Collide.Capsule) { instance = go.AddComponent <ShapeVisualCapsule>(); } else if (shape is Collide.Plane) { instance = go.AddComponent <ShapeVisualPlane>(); } else if (shape is Collide.Mesh) { instance = go.AddComponent <ShapeVisualMesh>(); } if (instance != null) { instance.hideFlags = HideFlags.NotEditable; instance.Shape = shape; instance.OnConstruct(); } return(instance); }
/// <summary> /// Create given shape of supported type (SupportsShapeVisual == true). /// </summary> /// <param name="shape">Shape to create visual for.</param> /// <returns>Game object with ShapeVisual component if successful, otherwise null.</returns> public static GameObject Create(Collide.Shape shape) { if (!SupportsShapeVisual(shape)) { return(null); } return(shape is Collide.Mesh ? CreateInstance(shape, (shape as Collide.Mesh).SourceObjects, DefaultMaterial, false) : CreateInstance(shape)); }
/// <summary> /// True if the given shape supports native creation of visual data, otherwise false. /// </summary> /// <param name="shape">Shape.</param> /// <returns>True if the given shape supports native creation of visual data.</returns> public static bool SupportsShapeVisual(Collide.Shape shape) { return(shape != null && ( shape is Collide.Box || shape is Collide.Sphere || shape is Collide.Cylinder || shape is Collide.Capsule || shape is Collide.Plane || shape is Collide.Mesh )); }
/// <summary> /// Callback from Shape.OnDisable to catch and find disabled shapes, /// disabling debug render node. /// </summary> public static void OnShapeDisable(Collide.Shape shape) { if (!IsActiveForSynchronize) { return; } var data = shape.GetComponent <ShapeDebugRenderData>(); if (data.Node != null) { data.Node.SetActive(false); } }
private MeshUtils.Edge[] FindPrincipalEdges(Collide.Shape shape, float principalEdgeExtension) { if (shape != null && shape.GetUtils() != null) { return(shape.GetUtils().GetPrincipalEdgesWorld(principalEdgeExtension)); } Mesh mesh = shape is Collide.Mesh ? (shape as Collide.Mesh).SourceObjects.FirstOrDefault() : Target.GetComponent <MeshFilter>() != null? Target.GetComponent <MeshFilter>().sharedMesh: null; Vector3 halfExtents = 0.5f * Vector3.one; if (mesh != null) { halfExtents = mesh.bounds.extents; } MeshUtils.Edge[] edges = ShapeUtils.ExtendAndTransformEdgesToWorld(Target.transform, new MeshUtils.Edge[] { new MeshUtils.Edge(BoxShapeUtils.GetLocalFace(halfExtents, ShapeUtils.Direction.Negative_X), BoxShapeUtils.GetLocalFace(halfExtents, ShapeUtils.Direction.Positive_X), ShapeUtils.GetLocalFaceDirection(ShapeUtils.Direction.Positive_Y), MeshUtils.Edge.EdgeType.Principal), new MeshUtils.Edge(BoxShapeUtils.GetLocalFace(halfExtents, ShapeUtils.Direction.Negative_Y), BoxShapeUtils.GetLocalFace(halfExtents, ShapeUtils.Direction.Positive_Y), ShapeUtils.GetLocalFaceDirection(ShapeUtils.Direction.Positive_Z), MeshUtils.Edge.EdgeType.Principal), new MeshUtils.Edge(BoxShapeUtils.GetLocalFace(halfExtents, ShapeUtils.Direction.Negative_Z), BoxShapeUtils.GetLocalFace(halfExtents, ShapeUtils.Direction.Positive_Z), ShapeUtils.GetLocalFaceDirection(ShapeUtils.Direction.Positive_X), MeshUtils.Edge.EdgeType.Principal) }, principalEdgeExtension); return(edges); }
private void SynchronizeShape(Collide.Shape shape) { var data = shape.gameObject.GetOrCreateComponent <ShapeDebugRenderData>(); bool shapeEnabled = shape.IsEnabledInHierarchy; if (data.hideFlags != HideFlags.HideInInspector) { data.hideFlags = HideFlags.HideInInspector; } // Do not create debug render data if the shape is inactive. if (!shapeEnabled && data.Node == null) { return; } data.Synchronize(this); if (data.Node != null && (RenderShapes && shapeEnabled) != data.Node.activeSelf) { data.Node.SetActive(RenderShapes && shapeEnabled); } }
/// <summary> /// Find ShapeVisual instance given shape. /// </summary> /// <param name="shape">Shape.</param> /// <returns>ShapeVisual instance if present, otherwise null.</returns> public static ShapeVisual Find(Collide.Shape shape) { return(shape != null? shape.GetComponentsInChildren <ShapeVisual>().FirstOrDefault(instance => instance.Shape == shape) : null); }
/// <summary> /// Create render data for given shape. Render data is when you have /// your own representation of the mesh and material. /// /// The component added will be ShapeVisualRenderData regardless of the /// type of the shape. /// /// If meshes.Length == 1 the MeshFilter and MeshRenderer will be added /// to the returned game object. If meshes.Length > 1 the filters and /// renderer will be added as children. /// </summary> /// <param name="shape">Shape to create render data for.</param> /// <param name="meshes">Render meshes for the shape.</param> /// <param name="material">Material.</param> /// <returns>Game object with ShapeVisual component if successful, otherwise null.</returns> public static GameObject CreateRenderData(Collide.Shape shape, Mesh[] meshes, Material material) { return(CreateInstance(shape, meshes, material, true)); }
public Hit Test(Ray ray, float rayLength = 500.0f) { LastHit = Hit.Invalid; if (Target == null) { return(Hit.Invalid); } Hit hit = new Hit(); Collide.Shape shape = Target.GetComponent <Collide.Shape>(); if (shape != null) { if (shape is Collide.Mesh) { hit.Triangle = MeshUtils.FindClosestTriangle((shape as Collide.Mesh).SourceObjects, shape.gameObject, ray, rayLength); } else if (shape is Collide.HeightField) { hit.Triangle = TriangleHit.Invalid; } else { GameObject tmp = PrefabLoader.Instantiate <GameObject>(Rendering.DebugRenderData.GetPrefabName(shape.GetType().Name)); if (tmp != null) { tmp.hideFlags = HideFlags.HideAndDontSave; tmp.transform.position = shape.transform.position; tmp.transform.rotation = shape.transform.rotation; tmp.transform.localScale = shape.GetScale(); hit.Triangle = MeshUtils.FindClosestTriangle(tmp, ray, rayLength); hit.Triangle.Target = shape.gameObject; GameObject.DestroyImmediate(tmp); } } } else { MeshFilter filter = Target.GetComponent <MeshFilter>(); hit.Triangle = filter != null?MeshUtils.FindClosestTriangle(filter.sharedMesh, Target, ray, rayLength) : TriangleHit.Invalid; } if (hit.Triangle.Valid) { hit.Triangle.ClosestEdge = ShapeUtils.FindClosestEdgeToSegment(ray.GetPoint(0), ray.GetPoint(rayLength), hit.Triangle.Edges).Edge; } List <MeshUtils.Edge> allEdges = FindPrincipalEdges(shape, 10.0f).ToList(); if (hit.Triangle.Valid) { allEdges.Add(hit.Triangle.ClosestEdge); } var closestEdgeToSegmentResult = ShapeUtils.FindClosestEdgeToSegment(ray.GetPoint(0), ray.GetPoint(rayLength), allEdges.ToArray()); hit.ClosestEdge.Target = Target; hit.ClosestEdge.Edge = closestEdgeToSegmentResult.Edge; hit.ClosestEdge.Distance = closestEdgeToSegmentResult.Distance; return(LastHit = hit); }
/// <summary> /// True if the given shape has a visual instance. /// </summary> /// <param name="shape">Shape.</param> /// <returns>True if the given shape has a visual instance.</returns> public static bool HasShapeVisual(Collide.Shape shape) { return(Find(shape) != null); }
/// <summary> /// Generates custom shape mesh /// </summary> public static Mesh GenerateMesh(Collide.Shape shape) { Collide.Cone cone = shape as Collide.Cone; Mesh mesh = new Mesh(); mesh = new Mesh(); float topRadius = Mathf.Max(0, cone.TopRadius); float bottomRadius = Mathf.Max(0, cone.BottomRadius); float height = Mathf.Max(0, cone.Height); int triangleCount = m_resolution * 4; int vertexCount = m_resolution * 6; //Vector2[] uvs = new Vector2[vertexCount]; Vector3[] vertices = new Vector3[vertexCount]; Vector3[] normals = new Vector3[vertexCount]; int[] triangles = new int[triangleCount * 3]; float externalNormalY = (bottomRadius - topRadius) / height; // Loop over each "pie slice" of the cone. Create vertices on one side, six per slice with two on each point where side meets top/bottom, triangles arranged looping around the slice for (int i = 0; i < m_resolution; i++) { int currentVertex = i * 6; int nextCurrentVertex = ((i + 1) % m_resolution) * 6; float angle = i * Mathf.PI * 2 / m_resolution; float sinI = Mathf.Sin(angle); float cosI = Mathf.Cos(angle); vertices[currentVertex] = new Vector3(0, height, 0); vertices[currentVertex + 1] = vertices[currentVertex + 2] = new Vector3(sinI * topRadius, height, cosI * topRadius); vertices[currentVertex + 3] = vertices[currentVertex + 4] = new Vector3(sinI * bottomRadius, 0, cosI * bottomRadius); vertices[currentVertex + 5] = new Vector3(0, 0, 0); normals[currentVertex] = normals[currentVertex + 1] = new Vector3(0, 1, 0); normals[currentVertex + 2] = normals[currentVertex + 3] = new Vector3(sinI, externalNormalY, cosI).normalized; normals[currentVertex + 4] = normals[currentVertex + 5] = new Vector3(0, -1, 0); ; int currentTriangle = i * 4 * 3; triangles[currentTriangle] = currentVertex; triangles[currentTriangle + 1] = currentVertex + 1; triangles[currentTriangle + 2] = nextCurrentVertex + 1; triangles[currentTriangle + 3] = currentVertex + 2; triangles[currentTriangle + 4] = currentVertex + 3; triangles[currentTriangle + 5] = nextCurrentVertex + 3; triangles[currentTriangle + 6] = currentVertex + 2; triangles[currentTriangle + 7] = nextCurrentVertex + 3; triangles[currentTriangle + 8] = nextCurrentVertex + 2; triangles[currentTriangle + 9] = currentVertex + 4; triangles[currentTriangle + 11] = nextCurrentVertex + 4; triangles[currentTriangle + 10] = currentVertex + 5; } mesh.vertices = vertices; //m_mesh.uv = uvs; // TODO, possibly mesh.triangles = triangles; mesh.normals = normals; return mesh; }
/// <summary> /// Generates custom shape mesh /// </summary> public static Mesh GenerateMesh(Collide.Shape shape) { Collide.HollowCone hollowCone = shape as Collide.HollowCone; Mesh mesh = new Mesh(); float outerRadiusTop = Mathf.Max(0, hollowCone.TopRadius); float outerRadiusBottom = Mathf.Max(0, hollowCone.BottomRadius); float innerRadiusTop = Mathf.Max(0, hollowCone.TopRadius - hollowCone.Thickness); float innerRadiusBottom = Mathf.Max(0, hollowCone.BottomRadius - hollowCone.Thickness); float height = Mathf.Max(0, hollowCone.Height); float innerTopPosition = (innerRadiusTop > 0) ? height : hollowCone.Height * ((hollowCone.BottomRadius - hollowCone.Thickness) / (hollowCone.BottomRadius - hollowCone.TopRadius) - 0.5f); int triangleCount = m_resolution * 8; int vertexCount = m_resolution * 8; //Vector2[] uvs = new Vector2[vertexCount]; Vector3[] vertices = new Vector3[vertexCount]; Vector3[] normals = new Vector3[vertexCount]; int[] triangles = new int[triangleCount * 3]; float externalNormalY = (outerRadiusBottom - outerRadiusTop) / (height * 2); // Loop over each "pie slice" of the hollow cone. Vertices two per point of the slice, triangles also arranged looping around the slice for (int i = 0; i < m_resolution; i++) { int currentVertex = i * 8; int nextCurrentVertex = ((i + 1) % m_resolution) * 8; float angle = i * Mathf.PI * 2 / m_resolution; float sinI = Mathf.Sin(angle); float cosI = Mathf.Cos(angle); vertices[currentVertex] = new Vector3(sinI * innerRadiusTop, height, cosI * innerRadiusTop); vertices[currentVertex + 7] = new Vector3(sinI * innerRadiusTop, innerTopPosition, cosI * innerRadiusTop); vertices[currentVertex + 1] = vertices[currentVertex + 2] = new Vector3(sinI * outerRadiusTop, height, cosI * outerRadiusTop); vertices[currentVertex + 3] = vertices[currentVertex + 4] = new Vector3(sinI * outerRadiusBottom, 0, cosI * outerRadiusBottom); vertices[currentVertex + 5] = vertices[currentVertex + 6] = new Vector3(sinI * innerRadiusBottom, 0, cosI * innerRadiusBottom); normals[currentVertex] = normals[currentVertex + 1] = new Vector3(0, 1, 0); normals[currentVertex + 2] = normals[currentVertex + 3] = new Vector3(sinI, externalNormalY, cosI).normalized; normals[currentVertex + 4] = normals[currentVertex + 5] = new Vector3(0, -1, 0); normals[currentVertex + 6] = normals[currentVertex + 7] = new Vector3(sinI, -externalNormalY, cosI).normalized; for (int j = 0; j < 8; j += 2) { int currentTriangle = i * 4 * 6 + j * 3; int nextJ = (j + 1) % 8; triangles[currentTriangle] = currentVertex + j; triangles[currentTriangle + 1] = currentVertex + nextJ; triangles[currentTriangle + 2] = nextCurrentVertex + nextJ; triangles[currentTriangle + 3] = currentVertex + j; triangles[currentTriangle + 4] = nextCurrentVertex + nextJ; triangles[currentTriangle + 5] = nextCurrentVertex + j; } } mesh.vertices = vertices; //m_mesh.uv = uvs; // TODO, possibly mesh.triangles = triangles; mesh.normals = normals; return(mesh); }