void RemoveGameObjects(PolyRaycastHit hit, BrushTarget target, BrushSettings settings) { Vector3 worldHitPosition = target.editableObject.transform.TransformPoint(hit.position); int count = m_PrefabsInstances.Count; for (int i = 0; i < count; i++) { // Skip the object if prefab is not part of the current loadout. if (!prefabLoadoutEditor.ContainsPrefabInstance(m_PrefabsInstances[i])) { continue; } if (m_PrefabsInstances[i] != null && Vector3.Distance(worldHitPosition, m_PrefabsInstances[i].transform.position) < settings.radius) { GameObject go = m_PrefabsInstances[i]; m_PrefabsInstances.RemoveAt(i); count--; Undo.DestroyObjectImmediate(go); } } }
/// <summary> /// Draw gizmos taking into account handling of normal by raiser lower brush mode. /// </summary> /// <param name="target">Current target Object</param> /// <param name="settings">Current brush settings</param> internal override void DrawGizmos(BrushTarget target, BrushSettings settings) { if (!m_EditableObjectsData.ContainsKey(target.editableObject)) { return; } EditableObjectData data = m_EditableObjectsData[target.editableObject]; UpdateBrushGizmosColor(); int rayCount = target.raycastHits.Count; List <Vector3> brushNormalOnBeginApply = BrushNormalsOnBeginApply(target.editableObject); for (int ri = 0; ri < rayCount; ri++) { PolyRaycastHit hit = target.raycastHits[ri]; Vector3 normal = hit.normal; switch (s_RaiseLowerDirection.value) { case PolyDirection.BrushNormal: { if (s_UseFirstNormalVector && brushNormalOnBeginApply.Count > ri) { normal = brushNormalOnBeginApply[ri]; } } break; case PolyDirection.Up: case PolyDirection.Right: case PolyDirection.Forward: { normal = DirectionUtil.ToVector3(s_RaiseLowerDirection); } break; case PolyDirection.VertexNormal: { //For vertex normal mode we take the vertex with the highest weight to compute the normal //if non has enough we take the hit normal. float highestWeight = .0001f; int highestIndex = -1; for (int i = 0; i < data.CommonVertexCount; i++) { int index = data.CommonVertices[i][0]; if (hit.weights[index] < .0001f || (s_IgnoreOpenEdges && ContainsIndexInNonManifoldIndices(target.editableObject, index))) { continue; } if (hit.weights[index] > highestWeight) { highestIndex = index; } } if (highestIndex != -1) { normal = data.NormalLookup[highestIndex]; } } break; } ; normal = settings.isUserHoldingControl ? normal * -1f : normal; PolyHandles.DrawBrush(hit.position, normal, settings, target.localToWorldMatrix, innerColor, outerColor); } }
internal override void OnBrushApply(BrushTarget target, BrushSettings settings) { if (!m_LikelyToSupportVertexSculpt) { return; } if (!m_EditableObjectsData.ContainsKey(target.editableObject)) { return; } int rayCount = target.raycastHits.Count; if (rayCount < 1) { return; } Vector3 n = s_RaiseLowerDirection.value.ToVector3(); float scale = 1f / (Vector3.Scale(target.transform.lossyScale, n).magnitude); float sign = settings.isUserHoldingControl ? -1f : 1f;//Event.current != null ? (Event.current.control ? -1f : 1f) : 1f; float maxMoveDistance = settings.strength * k_StrengthModifier * sign * s_RaiseLowerStrength; int vertexCount = target.editableObject.vertexCount; PolyMesh mesh = target.editableObject.editMesh; EditableObjectData data = m_EditableObjectsData[target.editableObject]; List <Vector3> brushNormalOnBeginApply = BrushNormalsOnBeginApply(target.editableObject); // rayCount could be different from brushNormalOnBeginApply.Count with some shapes (example: sphere). for (int ri = 0; ri < rayCount && ri < brushNormalOnBeginApply.Count; ri++) { PolyRaycastHit hit = target.raycastHits[ri]; if (hit.weights == null || hit.weights.Length < vertexCount) { continue; } if (s_RaiseLowerDirection == PolyDirection.BrushNormal) { if (s_UseFirstNormalVector) { n = brushNormalOnBeginApply[ri]; } else { n = hit.normal; } scale = 1f / (Vector3.Scale(target.transform.lossyScale, n).magnitude); } for (int i = 0; i < data.CommonVertexCount; i++) { int index = data.CommonVertices[i][0]; if (hit.weights[index] < .0001f || (s_IgnoreOpenEdges && ContainsIndexInNonManifoldIndices(target.editableObject, index))) { continue; } if (s_RaiseLowerDirection == PolyDirection.VertexNormal) { n = data.NormalLookup[index]; scale = 1f / (Vector3.Scale(target.transform.lossyScale, n).magnitude); } Vector3 pos = data.Vertices[index] + n * (hit.weights[index] * maxMoveDistance * scale); int[] indices = data.CommonVertices[i]; for (int it = 0; it < indices.Length; it++) { data.Vertices[indices[it]] = pos; } } } mesh.vertices = data.Vertices; target.editableObject.modifiedChannels |= MeshChannel.Position; // different than setting weights on temp component, // which is what BrushModeMesh.OnBrushApply does. if (tempComponent != null) { tempComponent.OnVerticesMoved(mesh); } base.OnBrushApply(target, settings); }
void PlaceGameObject(PolyRaycastHit hit, PrefabAndSettings prefabAndSettings, BrushTarget target, BrushSettings settings) { if (prefabAndSettings == null) { return; } GameObject prefab = prefabAndSettings.gameObject; var worldPosition = target.transform.TransformPoint(hit.position); var worldNormal = target.transform.TransformDirection(hit.normal); Ray ray = RandomRay(worldPosition, worldNormal, settings.radius, settings.falloff, settings.falloffCurve); ray.origin = target.transform.InverseTransformPoint(ray.origin); ray.direction = target.transform.InverseTransformDirection(ray.direction); PolyRaycastHit rand_hit; Vector3[] vertices = target.editableObject.editMesh.vertices; int[] triangles = target.editableObject.editMesh.GetTriangles(); if (PolySceneUtility.MeshRaycast(ray, vertices, triangles, out rand_hit)) { PlacementSettings placementSettings = prefabAndSettings.settings; Vector3 scaleSetting = prefab.transform.localScale; if (placementSettings.uniformBool) { float uniformScale = Random.Range(placementSettings.uniformScale.x, placementSettings.uniformScale.y); scaleSetting *= uniformScale; } else { if (placementSettings.xScaleBool) { scaleSetting.x = Random.Range(placementSettings.scaleRangeMin.x, placementSettings.scaleRangeMax.x); } if (placementSettings.yScaleBool) { scaleSetting.y = Random.Range(placementSettings.scaleRangeMin.y, placementSettings.scaleRangeMax.y); } if (placementSettings.zScaleBool) { scaleSetting.z = Random.Range(placementSettings.scaleRangeMin.z, placementSettings.scaleRangeMax.z); } } Vector3 rotationSetting = Vector3.zero; if (placementSettings.xRotationBool) { rotationSetting.x = Random.Range(placementSettings.rotationRangeMin.x, placementSettings.rotationRangeMax.x); } if (placementSettings.yRotationBool) { rotationSetting.y = Random.Range(placementSettings.rotationRangeMin.y, placementSettings.rotationRangeMax.y); } if (placementSettings.xRotationBool) { rotationSetting.z = Random.Range(placementSettings.rotationRangeMin.z, placementSettings.rotationRangeMax.z); } Quaternion rotation = Quaternion.FromToRotation(Vector3.up, target.transform.TransformDirection(rand_hit.normal)); GameObject inst = (GameObject)PrefabUtility.InstantiatePrefab(prefab); inst.transform.position = target.transform.TransformPoint(rand_hit.position); inst.transform.rotation = rotation; inst.transform.localScale = scaleSetting; float pivotOffset = s_UsePivotForPlacement ? 0f : GetPivotOffset(inst); inst.name = FormatInstanceName(prefab); inst.transform.position = inst.transform.position - (inst.transform.up * pivotOffset); inst.transform.rotation = inst.transform.rotation * Quaternion.Euler(rotationSetting); if (s_AvoidOverlappingGameObjects && TestIntersection(inst)) { Object.DestroyImmediate(inst); return; } if (s_ParentObjectWithSurface) { inst.transform.SetParent(target.transform); } m_PrefabsInstances.Add(inst); Undo.RegisterCreatedObjectUndo(inst, UndoMessage); } }
internal override void OnBrushApply(BrushTarget target, BrushSettings settings) { if (!likelyToSupportVertexSculpt) { return; } int rayCount = target.raycastHits.Count; Vector3[] normals = (s_SmoothDirection == PolyDirection.BrushNormal) ? target.editableObject.editMesh.normals : null; Vector3 v, t, avg, dirVec = s_SmoothDirection.value.ToVector3(); Plane plane = new Plane(Vector3.up, Vector3.zero); PolyMesh mesh = target.editableObject.editMesh; int vertexCount = mesh.vertexCount; // don't use target.GetAllWeights because brush normal needs // to know which ray to use for normal for (int ri = 0; ri < rayCount; ri++) { PolyRaycastHit hit = target.raycastHits[ri]; if (hit.weights == null || hit.weights.Length < vertexCount) { continue; } for (int i = 0; i < commonVertexCount; i++) { int index = commonVertices[i][0]; if (hit.weights[index] < .0001f || (s_IgnoreOpenEdges && nonManifoldIndices.Contains(index))) { continue; } v = vertices[index]; if (s_SmoothDirection == PolyDirection.VertexNormal) { avg = PolyMath.Average(vertices, neighborLookup[index]); } else { avg = PolyMath.WeightedAverage(vertices, neighborLookup[index], hit.weights); if (s_SmoothDirection == PolyDirection.BrushNormal) { if (s_UseFirstNormalVector) { dirVec = brushNormalOnBeginApply[ri]; } else { dirVec = PolyMath.WeightedAverage(normals, neighborLookup[index], hit.weights).normalized; } } plane.SetNormalAndPosition(dirVec, avg); avg = v - dirVec * plane.GetDistanceToPoint(v); } t = Vector3.Lerp(v, avg, hit.weights[index]); List <int> indices = commonVertices[i]; Vector3 pos = v + (t - v) * settings.strength * SMOOTH_STRENGTH_MODIFIER; for (int n = 0; n < indices.Count; n++) { vertices[indices[n]] = pos; } } } mesh.vertices = vertices; if (tempComponent != null) { tempComponent.OnVerticesMoved(mesh); } base.OnBrushApply(target, settings); }
/// <summary> /// Draw gizmos taking into account handling of normal by smooth brush mode. /// </summary> /// <param name="target">Current target Object</param> ///// <param name="settings">Current brush settings</param> internal override void DrawGizmos(BrushTarget target, BrushSettings settings) { UpdateBrushGizmosColor(); Vector3 normal = s_SmoothDirection.value.ToVector3(); int rayCount = target.raycastHits.Count; PolyMesh mesh = target.editableObject.editMesh; int vertexCount = mesh.vertexCount; Vector3[] normals = (s_SmoothDirection == PolyDirection.BrushNormal) ? mesh.normals : null; List <Vector3> brushNormalOnBeginApply = BrushNormalsOnBeginApply(target.editableObject); var data = m_EditableObjectsData[target.editableObject]; // don't use target.GetAllWeights because brush normal needs // to know which ray to use for normal for (int ri = 0; ri < rayCount; ri++) { PolyRaycastHit hit = target.raycastHits[ri]; if (hit.weights == null || hit.weights.Length < vertexCount) { continue; } if (s_SmoothDirection == PolyDirection.BrushNormal) { if (s_UseFirstNormalVector && brushNormalOnBeginApply.Count > ri) { normal = brushNormalOnBeginApply[ri]; } else { // get the highest weighted vertex to use its direction computation float highestWeight = .0001f; int highestIndex = -1; for (int i = 0; i < data.commonVertexCount; i++) { int index = data.commonVertices[i][0]; if (hit.weights[index] < .0001f || (s_IgnoreOpenEdges && ContainsIndexInNonManifoldIndices(target.editableObject, index))) { continue; } if (hit.weights[index] > highestWeight) { highestIndex = index; } } normal = highestIndex != -1 ? Math.WeightedAverage(normals, data.neighborLookup[highestIndex], hit.weights).normalized : hit.normal; } } else if (s_SmoothDirection == PolyDirection.VertexNormal) { normal = hit.normal; } PolyHandles.DrawBrush(hit.position, normal, settings, target.localToWorldMatrix, innerColor, outerColor); } }
/// <summary> /// Calculate the weights for this ray. /// </summary> /// <param name="mouseRay">The ray used to calculate weights</param> /// <param name="target">The object on which to calculate the weights</param> /// <returns>true if mouseRay hits the target, false otherwise</returns> bool DoMeshRaycast(Ray mouseRay, BrushTarget target, MirrorSettings mirrorSettings) { m_SecondaryBrushTargets.Clear(); if (!Util.IsValid(target)) { return(false); } target.ClearRaycasts(); EditableObject editable = target.editableObject; s_Rays.Clear(); s_Rays.Add(mouseRay); if (mirrorSettings.Axes != BrushMirror.None) { for (int i = 0; i < 3; i++) { if (((uint)mirrorSettings.Axes & (1u << i)) < 1) { continue; } int len = s_Rays.Count; for (int n = 0; n < len; n++) { Vector3 flipVec = ((BrushMirror)(1u << i)).ToVector3(); if (mirrorSettings.Space == MirrorCoordinateSpace.World) { Vector3 cen = editable.gameObjectAttached.GetComponent <Renderer>().bounds.center; s_Rays.Add(new Ray(Vector3.Scale(s_Rays[n].origin - cen, flipVec) + cen, Vector3.Scale(s_Rays[n].direction, flipVec))); } else { Transform t = SceneView.lastActiveSceneView.camera.transform; Vector3 o = t.InverseTransformPoint(s_Rays[n].origin); Vector3 d = t.InverseTransformDirection(s_Rays[n].direction); s_Rays.Add(new Ray(t.TransformPoint(Vector3.Scale(o, flipVec)), t.TransformDirection(Vector3.Scale(d, flipVec)))); } } } } bool hitMesh = false; int[] triangles = editable.editMesh.GetTriangles(); foreach (Ray ray in s_Rays) { PolyRaycastHit hit; if (PolySceneUtility.WorldRaycast(ray, editable.transform, editable.visualMesh.vertices, triangles, out hit)) { target.raycastHits.Add(hit); hitMesh = true; } } PolySceneUtility.CalculateWeightedVertices(target, brushSettings, tool, mode); if (hitMesh && !s_LockBrushToFirst) { Transform[] trs = Selection.GetTransforms(SelectionMode.Unfiltered); var hits = target.raycastHits; foreach (var selectedTransform in trs) { bool isValid = false; if (selectedTransform != editable.transform) { BrushTarget secondaryTarget = GetOrCreateBrushTarget(selectedTransform.gameObject); isValid = Util.IsValid(secondaryTarget); if (isValid) { m_SecondaryBrushTargets.Add(secondaryTarget); secondaryTarget.ClearRaycasts(); foreach (var hit in hits) { PolyRaycastHit secondaryHit = new PolyRaycastHit(hit.distance, secondaryTarget.transform.InverseTransformPoint(editable.transform.TransformPoint(hit.position)), hit.normal, -1); secondaryTarget.raycastHits.Add(secondaryHit); } } PolySceneUtility.CalculateWeightedVertices(secondaryTarget, brushSettings, tool, mode); } } } return(hitMesh); }
/// <summary> /// Cast a ray (in model space) against a mesh. /// </summary> internal static bool MeshRaycast(Ray InRay, Vector3[] vertices, int[] triangles, out PolyRaycastHit hit, float distance = Mathf.Infinity, Culling cullingMode = Culling.Front) { Vector3 hitNormal, vert0, vert1, vert2; Vector3 origin = InRay.origin, direction = InRay.direction; hit = new PolyRaycastHit(Mathf.Infinity, Vector3.zero, Vector3.zero, -1); // Iterate faces, testing for nearest hit to ray origin. for (int CurTri = 0; CurTri < triangles.Length; CurTri += 3) { if (CurTri + 2 >= triangles.Length) { continue; } if (triangles[CurTri + 2] >= vertices.Length) { continue; } vert0 = vertices[triangles[CurTri + 0]]; vert1 = vertices[triangles[CurTri + 1]]; vert2 = vertices[triangles[CurTri + 2]]; // Second pass, test intersection with triangle if (Math.RayIntersectsTriangle2(origin, direction, vert0, vert1, vert2, out distance, out hitNormal)) { if (distance < hit.distance) { hit.distance = distance; hit.triangle = CurTri / 3; hit.position = InRay.GetPoint(hit.distance); hit.normal = hitNormal; } } } return(hit.triangle > -1); }
/// <summary> /// Find the nearest triangle intersected by InWorldRay on this mesh. InWorldRay is in world space. /// @hit contains information about the hit point. @distance limits how far from @InWorldRay.origin the hit /// point may be. @cullingMode determines what face orientations are tested (Culling.Front only tests front /// faces, Culling.Back only tests back faces, and Culling.FrontBack tests both). /// Ray origin and position values are in local space. /// </summary> /// <param name="InWorldRay"></param> /// <param name="transform"></param> /// <param name="vertices"></param> /// <param name="triangles"></param> /// <param name="hit"></param> /// <param name="distance"></param> /// <param name="cullingMode"></param> /// <returns></returns> internal static bool WorldRaycast(Ray InWorldRay, Transform transform, Vector3[] vertices, int[] triangles, out PolyRaycastHit hit, float distance = Mathf.Infinity, Culling cullingMode = Culling.Front) { //null checks, must have a transform, vertices and triangles if (transform == null || vertices == null || triangles == null) { hit = null; return(false); } Ray ray = transform.InverseTransformRay(InWorldRay); return(MeshRaycast(ray, vertices, triangles, out hit, distance, cullingMode)); }
internal override void OnBrushApply(BrushTarget target, BrushSettings settings) { if (!likelyToSupportVertexSculpt) { return; } int rayCount = target.raycastHits.Count; if (rayCount < 1) { return; } Vector3 n = s_RaiseLowerDirection.value.ToVector3(); float scale = 1f / (Vector3.Scale(target.transform.lossyScale, n).magnitude); float sign = settings.isUserHoldingControl ? -1f : 1f;//Event.current != null ? (Event.current.control ? -1f : 1f) : 1f; float maxMoveDistance = settings.strength * k_StrengthModifier * sign * s_RaiseLowerStrength; int vertexCount = target.editableObject.vertexCount; PolyMesh mesh = target.editableObject.editMesh; for (int ri = 0; ri < rayCount; ri++) { PolyRaycastHit hit = target.raycastHits[ri]; if (hit.weights == null || hit.weights.Length < vertexCount) { continue; } if (s_RaiseLowerDirection == PolyDirection.BrushNormal) { if (s_UseFirstNormalVector) { n = brushNormalOnBeginApply[ri]; } else { n = target.raycastHits[ri].normal; } scale = 1f / (Vector3.Scale(target.transform.lossyScale, n).magnitude); } for (int i = 0; i < commonVertexCount; i++) { int index = commonVertices[i][0]; if (hit.weights[index] < .0001f || (s_IgnoreOpenEdges && nonManifoldIndices.Contains(index))) { continue; } if (s_RaiseLowerDirection == PolyDirection.VertexNormal) { n = normalLookup[index]; scale = 1f / (Vector3.Scale(target.transform.lossyScale, n).magnitude); } Vector3 pos = vertices[index] + n * (hit.weights[index] * maxMoveDistance * scale); List <int> indices = commonVertices[i]; for (int it = 0; it < indices.Count; it++) { vertices[indices[it]] = pos; } } } mesh.vertices = vertices; // different than setting weights on temp component, // which is what BrushModeMesh.OnBrushApply does. if (tempComponent != null) { tempComponent.OnVerticesMoved(mesh); } base.OnBrushApply(target, settings); }
/// <summary> /// Cast a ray (in model space) against a mesh. /// </summary> /// <param name="InRay"></param> /// <param name="vertices"></param> /// <param name="triangles"></param> /// <param name="hit"></param> /// <param name="distance"></param> /// <param name="cullingMode"></param> /// <returns></returns> internal static bool MeshRaycast(Ray InRay, Vector3[] vertices, int[] triangles, out PolyRaycastHit hit, float distance = Mathf.Infinity, Culling cullingMode = Culling.Front) { float hitDistance = Mathf.Infinity; Vector3 hitNormal = Vector3.zero; // vars used in loop Vector3 vert0, vert1, vert2; int hitFace = -1; Vector3 origin = InRay.origin, direction = InRay.direction; /** * Iterate faces, testing for nearest hit to ray origin. */ for (int CurTri = 0; CurTri < triangles.Length; CurTri += 3) { if (CurTri + 2 >= triangles.Length) { continue; } if (triangles[CurTri + 2] >= vertices.Length) { continue; } vert0 = vertices[triangles[CurTri + 0]]; vert1 = vertices[triangles[CurTri + 1]]; vert2 = vertices[triangles[CurTri + 2]]; if (PolyMath.RayIntersectsTriangle2(origin, direction, vert0, vert1, vert2, ref distance, ref hitNormal)) { hitFace = CurTri / 3; hitDistance = distance; break; } } hit = new PolyRaycastHit(hitDistance, InRay.GetPoint(hitDistance), hitNormal, hitFace); return(hitFace > -1); }