public static bool IsSolid(Mesh mesh) { #if CORK_WRAPPER_CLOCK_DEBUG var stopwatch = new Stopwatch(); stopwatch.Start(); #endif var result = ImportIsSolid(mesh.vertices, mesh.vertices.Length, mesh.triangles, mesh.triangles.Length); #if CORK_WRAPPER_CLOCK_DEBUG Debug.Log("Mesh IsSolid finished in " + stopwatch.ElapsedMilliseconds + " ms for meshes:\n" + MeshUtils.ShortMeshInfo(mesh)); #endif return(result); }
public bool TryGetValue(int key, out T value) { MeshUtils.Assert(key < Size, "Key index out of range! " + key + " maxSize: " + Size); var item = dictionary[key]; if (item.valid) { value = item.data; return(true); } value = default(T); return(false); }
public T this[int key] { get { MeshUtils.Assert(key < Size, "Key index out of range! " + key + " maxSize: " + Size); MeshUtils.Assert(dictionary[key].valid == true, "Key does not exists!"); return(dictionary[key].data); } set { MeshUtils.Assert(dictionary[key].valid == true, "Key does not exists!"); dictionary[key].data = value; } }
/// <summary> /// Creates a new edge such that eNew == eOrg.Lnext and eNew.Dst is a newly created vertex. /// eOrg and eNew will have the same left face. /// </summary> public MeshUtils.Edge AddEdgeVertex(MeshUtils.Edge eOrg) { var eNew = MeshUtils.MakeEdge(eOrg); var eNewSym = eNew._Sym; // Connect the new edge appropriately MeshUtils.Splice(eNew, eOrg._Lnext); // Set vertex and face information eNew._Org = eOrg._Dst; MeshUtils.MakeVertex(eNewSym, eNew._Org); eNew._Lface = eNewSym._Lface = eOrg._Lface; return(eNew); }
void Start() { _meshFilter = this.GetComponent <MeshFilter>(); _meshRenderer = this.GetComponent <MeshRenderer>(); _mesh = MeshUtils.CreatePlaneMesh(Vector3.zero, _potentialControlManager.FieldWidth, _potentialControlManager.FieldHeight, _fieldSplitNumW, _fieldSplitNumH); _meshFilter.sharedMesh = _mesh; _meshRenderer.material = _potentialFieldMat; _potentialTex = new Texture2D(_fieldSplitNumW, _fieldSplitNumH, TextureFormat.RGFloat, false); _potentialTex.wrapMode = TextureWrapMode.Clamp; _potentialFieldMat.SetTexture("_PotentialFieldTex", _potentialTex); }
public T GetFirstValue() { for (int i = 0; i < Size; i++) { var item = dictionary[i]; if (item.valid) { return(item.data); } } MeshUtils.Assert(false, "No valid key!"); return(default(T)); }
protected virtual void UpdateTexts() { //float yJump = chart.maxYValue / chart.yDivisions; int fontsize = chart.axisText.GetComponent <TextMesh>().fontSize - chart.yDivisions; float xMargin = 0; for (int i = 0; i < YPos.Length; i++) { String name = MeshUtils.NameGenerator(objectString, i); Transform yt = YAxis.transform.Find(name); yt.localPosition = new Vector3(-AxisWidth * 1.1f, YPos[i]); TextMesh mesh = yt.gameObject.GetComponent <TextMesh>(); mesh.text = chart.YValues[i]; //mesh.text = (chart.maxYValue - i * yJump).ToString(chart.specifier); mesh.color = axisColor; mesh.fontSize = fontsize; if (xMargin < mesh.GetComponent <Renderer>().bounds.extents.x) { xMargin = mesh.GetComponent <Renderer>().bounds.extents.x; } } //print(xMargin); chart.margins.x = (xMargin * 2 + AxisWidth + lineWidth) * 1.1f; //float xJump = chart.maxXValue / chart.xDivisions; //fontsize = chart.axisText.GetComponent<TextMesh>().fontSize - chart.xDivisions; float yMargin = 0; for (int i = 0; i < XPos.Length; i++) { String name = MeshUtils.NameGenerator(objectString, i); Transform xt = XAxis.transform.Find(name); xt.localPosition = new Vector3(XPos[i], -AxisWidth * 1.1f); TextMesh mesh = xt.gameObject.GetComponent <TextMesh>(); mesh.text = chart.XValues[i]; //mesh.text = (chart.maxXValue - i * xJump).ToString(chart.specifier); mesh.color = axisColor; mesh.fontSize = fontsize; if (yMargin < mesh.GetComponent <Renderer>().bounds.extents.y) { yMargin = mesh.GetComponent <Renderer>().bounds.extents.y; } } //print(yMargin); chart.margins.y = (yMargin * 2 + AxisWidth + lineWidth) * 1.1f; }
void Awake() { grid = MeshUtils.CreatePlane(50f, 50f, 1, 1, new Vector3(0f, -0.001f, 0f)); grid.layer = LayerMask.NameToLayer("Selection"); grid.AddComponent <BoxCollider>().center = new Vector3(0f, 0.001f, 0f); grid.name = "SelectionBase"; Destroy(grid.GetComponent <MeshRenderer>()); selectionData = new float[4] { 0, 0, 0, 0 }; nineSlice = GetComponent <NineSlice>(); nineSlice.SetSize(unitSize, unitSize); nineSlice.transform.rotation = Quaternion.Euler(90f, 0f, 0f); }
void CreatePlane(string objectName, Vector3 origin, Vector3 horizontalAxis, int resolution) { var mesh = MeshUtils.GeneratePlane(Vector3.zero, horizontalAxis, new Vector3(0, Size.y, 0), resolution, 2, Vector2.zero, Vector2.one); var go = new GameObject(objectName); go.AddComponent <MeshFilter>().sharedMesh = mesh; go.AddComponent <MeshRenderer>().material = Material; go.transform.SetParent(transform); go.transform.localPosition = origin; }
public static List <Vector2> PredictOutCirCleUVs(IList <Vector2> vectors, float length) { List <Vector2> ret = new List <Vector2>(); for (int i = 0; i < vectors.Count; i++) { Vector3 a = vectors[(i - 1 + vectors.Count) % vectors.Count]; Vector3 b = vectors[i]; Vector3 c = vectors[(i + 1) % vectors.Count]; Vector3 predict = MeshUtils.PredictNormal2D(a, b, c) * length + new Vector3(vectors[i].x, vectors[i].y, 0); ret.Add(predict); } return(ret); }
/// <summary> /// Given three vertices u,v,w such that VertLeq(u,v) .and. VertLeq(v,w), /// evaluates the t-coord of the edge uw at the s-coord of the vertex v. /// Returns v->t - (uw)(v->s), ie. the signed distance from uw to v. /// If uw is vertical (and thus passes thru v), the result is zero. /// /// The calculation is extremely accurate and stable, even when v /// is very close to u or w. In particular if we set v->t = 0 and /// let r be the negated result (this evaluates (uw)(v->s)), then /// r is guaranteed to satisfy MIN(u->t,w->t) less than or equal r less than or equal MAX(u->t,w->t). /// </summary> public static float EdgeEval(MeshUtils.Vertex u, MeshUtils.Vertex v, MeshUtils.Vertex w) { Debug.Assert(VertLeq(u, v) && VertLeq(v, w)); float gapL = v._s - u._s; float gapR = w._s - v._s; if (gapL + gapR > 0.0f) { if (gapL < gapR) return (v._t - u._t) + (u._t - w._t)*(gapL/(gapL + gapR)); return (v._t - w._t) + (w._t - u._t) * (gapR / (gapL + gapR)); } /* vertical line */ return 0.0f; }
public static List <int> CreateTrianglesClock(IList <int> indices) { Debug.Assert(indices.Count % 2 == 0); List <int> rets = new List <int>(); var n = indices.Count; for (var i = 0; i < n / 2; i++) { var id1 = indices[i + 0]; var id2 = indices[i + 1]; var id3 = indices[n - 1 - (i + 1)]; var id4 = indices[n - 1 - (i + 0)]; MeshUtils.AddQuad(rets, id1, id2, id3, id4); } return(rets); }
protected override void OnCreate() { base.OnCreate(); var query = new EntityQueryDesc { All = new ComponentType[] { typeof(SpriteRenderComponent), typeof(LocalToWorld) } }; _spriteGroup = GetEntityQuery(query); _mesh = MeshUtils.CreateQuad(); }
/* TODO */ // Game logic // Update is called once per frame void Update() { if (Input.GetMouseButtonUp(1) && Input.GetMouseButtonUp(0)) { } else if (Input.GetMouseButtonUp(0)) { Vector3 mousePosition = MeshUtils.GetMouseWorldPosition(); tileArray.SetTileArrayType(mousePosition, TileArrayObject.TileArrayType.Mine); } else if (Input.GetMouseButtonUp(1)) { Vector3 mousePosition = MeshUtils.GetMouseWorldPosition(); tileArray.SetFlagStatus(mousePosition); } }
private void Start() { Vector3 meshCenter = MeshUtils.Center(filter.sharedMesh); RaycastHit hit; if (Physics.Raycast(transform.position + meshCenter, -Vector3.up, out hit)) { transform.position = hit.point - Vector3.up * MeshUtils.MinHeight(filter.sharedMesh); transform.RotateAround(transform.position + Vector3.up * MeshUtils.MinHeight(filter.sharedMesh), Vector3.forward, hit.transform.eulerAngles.z - transform.eulerAngles.z); transform.RotateAround(transform.position + Vector3.up * MeshUtils.MinHeight(filter.sharedMesh), Vector3.right, hit.transform.eulerAngles.x - transform.eulerAngles.x); } }
static Mesh <VertexPositionColor> CreateArrow(Matrix44 matrix, Color4 color) { const float tipHeight = 0.25f; const float tipRadius = 0.06f; const float poleRadius = 0.01f; var tip = MeshUtils.CreateCone(tipHeight, tipRadius, 20, color); TransformMesh(tip, Matrix44.CreateTranslation(0, 1 - tipHeight, 0)); var pole = MeshUtils.CreateCylinder(1 - tipHeight - 0.1f, poleRadius, 6, color); TransformMesh(pole, Matrix44.CreateTranslation(0, 0.1f, 0)); var arrow = MeshUtils.Combine(tip, pole); TransformMesh(arrow, matrix); return(arrow); }
public CustomMaterial(string fileName) { FileName = fileName; if (fileName != "DefaultMaterials") { try { AssetBundle = AssetBundle.LoadFromFile(Path.Combine(Plugin.PluginAssetPath, fileName)); GameObject = AssetBundle.LoadAsset <GameObject>("Assets/_CustomMaterial.prefab"); Descriptor = GameObject.GetComponent <MaterialDescriptor>(); MaterialRenderer = MaterialUtils.GetGameObjectRenderer(GameObject, "pixie"); MaterialMeshFilter = MeshUtils.GetGameObjectMeshFilter(GameObject, "pixie"); } catch (Exception ex) { Logger.log.Warn($"Something went wrong getting the AssetBundle for '{fileName}'!"); Logger.log.Warn(ex); Descriptor = new MaterialDescriptor() { MaterialName = "Invalid Wall (Delete it!)", AuthorName = fileName, Icon = Utils.GetErrorIcon() }; ErrorMessage = $"File: '{fileName}'" + "\n\nThis file failed to load." + "\n\nThis may have been caused by having duplicated files," + " another wall with the same name already exists or that the custom wall is simply just broken." + "\n\nThe best thing is probably just to delete it!"; FileName = "DefaultMaterials"; } } else { Descriptor = new MaterialDescriptor { MaterialName = "Default", AuthorName = "Beat Saber", Description = "This is the default walls. (No preview available)", Icon = Utils.GetDefaultIcon() }; } }
public override void OnImportAsset(AssetImportContext ctx) { stage = AnimData.LoadFromFile(ctx.assetPath); if (stage == null) { return; } MeshUtils.SimplifyStage(stage, Settings.SimplifyFactor); savedClips = new Dictionary <AudioDataPool.AudioPoolKey, AudioClip>(); totalVertices = 0; totalLines = 0; HasFades = false; m_Materials.Clear(); PreviewTexture = new Texture2D(1, 1); PreviewTexture.LoadImage(stage.previewFrames[0], false); PreviewTexture.Apply(); if (Settings.Shader == null || Settings.Shader == "AnimVR/Standard") { Debug.Log("Resetting shader"); Settings.Shader = "AnimVR/ImportedLine"; } baseMaterial = new Material(Shader.Find(Settings.Shader)); baseMaterial.SetFloat("_Unlit", Settings.UnlitByDefault ? 1 : 0); baseMaterial.SetFloat("_Gamma", PlayerSettings.colorSpace == ColorSpace.Gamma ? 1.0f : 2.2f); baseMaterial.name = Path.GetFileNameWithoutExtension(ctx.assetPath) + "_BaseMaterial"; needsAudioReimport = false; GenerateUnityObject(stage, ctx); //var externalObjects = GetExternalObjectMap(); ctx.AddObjectToAsset(Path.GetFileNameWithoutExtension(ctx.assetPath) + "_BaseMaterial", baseMaterial); m_Materials.Add(new SerializableIdentifier(baseMaterial)); InfoString = "FPS: " + stage.fps + ", " + stage.timelineLength + " frames \n" + totalVertices + " verts, " + totalLines + " lines"; savedClips = null; stage = null; }
protected void RemovePlotter(int index) { string name = MeshUtils.NameGenerator(Identifier, index); GameObject obj = PlotterGroup.transform.Find(name).gameObject; if (obj) { // Gameobject will not be destroyed until after Update() // hence it must detach with parent before Destroy() obj.transform.parent = null; Destroy(obj); } else { Debug.LogError(String.Format("{0} not found?!", name)); } }
/* Standard Laplacian Smooth Filter */ public static Vector3[] laplacianFilter(Vector3[] sv, int[] t, bool[] affectedVertices) { Vector3[] wv = new Vector3[sv.Length]; List<Vector3> adjacentVertices = new List<Vector3>(); float dx = 0.0f; float dy = 0.0f; float dz = 0.0f; for (int vi=0; vi< sv.Length; vi++) { /* Added Condition to only affect needed Vertices */ if ( !affectedVertices[vi] ) { wv [vi] = sv [vi]; continue; } // Find the sv neighboring vertices adjacentVertices = MeshUtils.findAdjacentNeighbors (sv, t, sv[vi]); if (adjacentVertices.Count != 0) { dx = 0.0f; dy = 0.0f; dz = 0.0f; //Debug.Log("Vertex Index Length = "+vertexIndexes.Length); // Add the vertices and divide by the number of vertices for (int j=0; j<adjacentVertices.Count; j++) { dx += adjacentVertices[j].x; dy += adjacentVertices[j].y; dz += adjacentVertices[j].z; } wv[vi].x = dx / adjacentVertices.Count; wv[vi].y = dy / adjacentVertices.Count; wv[vi].z = dz / adjacentVertices.Count; } } return wv; }
// Token: 0x06004179 RID: 16761 RVA: 0x0014AA50 File Offset: 0x00148E50 public void Invert() { foreach (CSGPolygon csgpolygon in this.Polygons) { csgpolygon.Flip(); } this.Plane.Flip(); if (this.FrontChild != null) { this.FrontChild.Invert(); } if (this.BackChild != null) { this.BackChild.Invert(); } MeshUtils.Swap <CSGNode>(ref this.FrontChild, ref this.BackChild); }
// Token: 0x060041CA RID: 16842 RVA: 0x0014E178 File Offset: 0x0014C578 public GameObject Duplicate(bool duplicateMaterials) { GameObject gameObject = UnityEngine.Object.Instantiate <GameObject>(base.gameObject); Mesh sharedMesh = base.GetComponent <MeshFilter>().sharedMesh; Mesh sharedMesh2 = MeshUtils.CopyMesh(sharedMesh); gameObject.GetComponent <MeshFilter>().sharedMesh = sharedMesh2; if (duplicateMaterials) { MeshRenderer component = gameObject.GetComponent <MeshRenderer>(); if (component && component.sharedMaterials.Length > 0) { component.sharedMaterials = MeshUtils.CopyMaterials(component.sharedMaterials); } } return(gameObject); }
public void Start() { Application.RegisterLogCallback(HandleLog); MeshUtils.InitStaticValues(); CubeWorldPlayerPreferences.LoadPreferences(); PreferencesUpdated(); State = state = GameManagerUnityState.MAIN_MENU; mainMenu = new MainMenu(this); sectorManagerUnity = new SectorManagerUnity(this); objectsManagerUnity = new CWObjectsManagerUnity(this); fxManagerUnity = new CWFxManagerUnity(this); worldManagerUnity = new WorldManagerUnity(this); }
// Update is called once per frame void Update() { Vector3 arrowPosBr = flecheBr.localPosition; Vector3 arrowPosTr = flecheTr.localPosition; if (arrowPosBr.x == lastArrowPosBr.x && arrowPosTr.x == lastArrowPosTr.x) { return; } mf.mesh = MeshUtils.CreateCone(2f, flecheBr.GetComponent <MeshRenderer>().bounds.center.x - flecheBr.GetComponent <MeshRenderer>().bounds.extents.x - transform.position.x, transform.position.x - flecheTr.GetComponent <MeshRenderer>().bounds.center.x - flecheTr.GetComponent <MeshRenderer>().bounds.extents.x, 18); mc.sharedMesh = mesh; //mat.mainTexture = mainTex; lastArrowPosTr = arrowPosTr; lastArrowPosBr = arrowPosBr; }
public void UpdateMesh() { // GetRearrangedData make the latest sampling as the last point of the Line chart. // Prepare the input for making the mesh of the each Line. Vector3[] lines = CreateLineFromData(GetRearrangedData()); // Generate Line Mesh in the DataSeries which contain a mesh Renderer. UpdateMesh(MeshUtils.GenerateLineMesh(lines, timeChart.lineWidth)); // TODO: A method that pre-arrange yPos of floating values so that // it won't be overlapping (when there are several Lines) // Update the floating value tracing the last sampling of the each Line. // GetRearrangedData() begins with the lastest sampling. //UpdateFloatingValue(lines[0]); UpdateFloatingValue(lines, timeChart.TimeSliderPosition); }
/// <summary> /// Flip normals to opposite direction /// </summary> public void FlipNormals() { if (generationMode == 1) { return; } flipNormals = !flipNormals; var mesh = GetComponent <MeshFilter>().sharedMesh; // reverse normals MeshUtils.ReverseNormals(mesh); // recalculate tangents MeshUtils.CalculateTangents(mesh); }
private void InitVT() { ResetVUT(); if (_strokeType == StrokeType.Point) { CalCircleMeshData(SemiCircleSegment * 2); } else { _verticesCurve = MeshUtils.GetVertices(_curvePoints, _width * 0.5f); if (!_isClose) { AddSectorMeshData(); } UpdatePercent(1); } }
void UpdateQuad() { float desired_ratio = 1.0f; if (sprite_sheet != null) { desired_ratio = (float)(sprite_sheet.height / sprite_count_y) / (float)(sprite_sheet.width / sprite_count_x); } if (float.IsInfinity(desired_ratio)) { desired_ratio = 1.0f; } MeshFilter mf = GetComponent <MeshFilter>(); DestroyImmediate(mf.sharedMesh); mf.sharedMesh = MeshUtils.MakeQuad(desired_height, desired_ratio); }
/// <summary> /// Splice is the basic operation for changing the /// mesh connectivity and topology. It changes the mesh so that /// eOrg->Onext = OLD( eDst->Onext ) /// eDst->Onext = OLD( eOrg->Onext ) /// where OLD(...) means the value before the meshSplice operation. /// /// This can have two effects on the vertex structure: /// - if eOrg->Org != eDst->Org, the two vertices are merged together /// - if eOrg->Org == eDst->Org, the origin is split into two vertices /// In both cases, eDst->Org is changed and eOrg->Org is untouched. /// /// Similarly (and independently) for the face structure, /// - if eOrg->Lface == eDst->Lface, one loop is split into two /// - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one /// In both cases, eDst->Lface is changed and eOrg->Lface is unaffected. /// /// Some special cases: /// If eDst == eOrg, the operation has no effect. /// If eDst == eOrg->Lnext, the new face will have a single edge. /// If eDst == eOrg->Lprev, the old face will have a single edge. /// If eDst == eOrg->Onext, the new vertex will have a single edge. /// If eDst == eOrg->Oprev, the old vertex will have a single edge. /// </summary> public static void Splice(IPool pool, MeshUtils.Edge eOrg, MeshUtils.Edge eDst) { if (eOrg == eDst) { return; } bool joiningVertices = false; if (eDst._Org != eOrg._Org) { // We are merging two disjoint vertices -- destroy eDst->Org joiningVertices = true; MeshUtils.KillVertex(pool, eDst._Org, eOrg._Org); } bool joiningLoops = false; if (eDst._Lface != eOrg._Lface) { // We are connecting two disjoint loops -- destroy eDst->Lface joiningLoops = true; MeshUtils.KillFace(pool, eDst._Lface, eOrg._Lface); } // Change the edge structure MeshUtils.Splice(eDst, eOrg); switch (joiningVertices) { case false: // We split one vertex into two -- the new vertex is eDst->Org. // Make sure the old vertex points to a valid half-edge. MeshUtils.MakeVertex(pool, eDst, eOrg._Org); eOrg._Org._anEdge = eOrg; break; } switch (joiningLoops) { case false: // We split one loop into two -- the new loop is eDst->Lface. // Make sure the old face points to a valid half-edge. MeshUtils.MakeFace(pool, eDst, eOrg._Lface); eOrg._Lface._anEdge = eOrg; break; } }
private static void Postfix(ref ObstacleController __instance, StretchableObstacle ____stretchableObstacle, ref SimpleColorSO ____color) { try { CustomMaterial customMaterial = MaterialAssetLoader.CustomMaterialObjects[MaterialAssetLoader.SelectedMaterial]; if (customMaterial.FileName != "DefaultMaterials") { Renderer mesh = __instance.gameObject.GetComponentInChildren <Renderer>(); Color color = ____color.color; if (customMaterial.Descriptor.Overlay) { GameObject overlay = MeshUtils.CreateOverlay(mesh, customMaterial.MaterialRenderer, customMaterial.Descriptor.OverlayOffset); MaterialUtils.SetMaterialsColor(overlay?.GetComponent <Renderer>().materials, color); if (customMaterial.Descriptor.ReplaceMesh) { MeshUtils.ReplaceMesh(overlay.GetComponent <MeshFilter>(), customMaterial.MaterialMeshFilter, customMaterial.Descriptor.MeshScaleMultiplier); if (!customMaterial.Descriptor.ReplaceOnlyOverlayMesh) { MeshUtils.ReplaceMesh(__instance.gameObject.GetComponentInChildren <MeshFilter>(), customMaterial.MaterialMeshFilter, customMaterial.Descriptor.MeshScaleMultiplier); } } } else { MaterialUtils.ReplaceRenderer(mesh, customMaterial.MaterialRenderer); MaterialUtils.SetMaterialsColor(mesh?.materials, color); if (customMaterial.Descriptor.ReplaceMesh) { MeshUtils.ReplaceMesh(__instance.gameObject.GetComponentInChildren <MeshFilter>(), customMaterial.MaterialMeshFilter, customMaterial.Descriptor.MeshScaleMultiplier); } } } if (!Configuration.EnableObstacleFrame) { ParametricBoxFrameController frame = ____stretchableObstacle.GetPrivateField <ParametricBoxFrameController>("_obstacleFrame"); frame.enabled = false; } } catch (Exception ex) { Logger.log.Error(ex); } }
/// <summary> /// Splits eOrg into two edges eOrg and eNew such that eNew == eOrg.Lnext. /// The new vertex is eOrg.Dst == eNew.Org. /// eOrg and eNew will have the same left face. /// </summary> public MeshUtils.Edge SplitEdge(IPool pool, MeshUtils.Edge eOrg) { var eTmp = AddEdgeVertex(pool, eOrg); var eNew = eTmp._Sym; // Disconnect eOrg from eOrg->Dst and connect it to eNew->Org MeshUtils.Splice(eOrg._Sym, eOrg._Sym._Oprev); MeshUtils.Splice(eOrg._Sym, eNew); // Set the vertex and face information eOrg._Dst = eNew._Org; eNew._Dst._anEdge = eNew._Sym; // may have pointed to eOrg->Sym eNew._Rface = eOrg._Rface; eNew._winding = eOrg._winding; // copy old winding information eNew._Sym._winding = eOrg._Sym._winding; return(eNew); }
public static bool VertCCW(MeshUtils.Vertex u, MeshUtils.Vertex v, MeshUtils.Vertex w) { return (u._s * (v._t - w._t) + v._s * (w._t - u._t) + w._s * (u._t - v._t)) >= 0.0f; }
/// <summary> /// Add a new active region to the sweep line, *somewhere* below "regAbove" /// (according to where the new edge belongs in the sweep-line dictionary). /// The upper edge of the new region will be "eNewUp". /// Winding number and "inside" flag are not updated. /// </summary> private ActiveRegion AddRegionBelow(ActiveRegion regAbove, MeshUtils.Edge eNewUp) { var regNew = new ActiveRegion(); regNew._eUp = eNewUp; regNew._nodeUp = _dict.InsertBefore(regAbove._nodeUp, regNew); regNew._fixUpperEdge = false; regNew._sentinel = false; regNew._dirty = false; eNewUp._activeRegion = regNew; return regNew; }
public static float VertL1Dist(MeshUtils.Vertex u, MeshUtils.Vertex v) { return Math.Abs(u._s - v._s) + Math.Abs(u._t - v._t); }
/// <summary> /// The event vertex lies exacty on an already-processed edge or vertex. /// Adding the new vertex involves splicing it into the already-processed /// part of the mesh. /// </summary> private void ConnectLeftDegenerate(ActiveRegion regUp, MeshUtils.Vertex vEvent) { var e = regUp._eUp; if (Geom.VertEq(e._Org, vEvent)) { // e.Org is an unprocessed vertex - just combine them, and wait // for e.Org to be pulled from the queue // C# : in the C version, there is a flag but it was never implemented // the vertices are before beginning the tesselation throw new InvalidOperationException("Vertices should have been merged before"); } if (!Geom.VertEq(e._Dst, vEvent)) { // General case -- splice vEvent into edge e which passes through it _mesh.SplitEdge(e._Sym); if (regUp._fixUpperEdge) { // This edge was fixable -- delete unused portion of original edge _mesh.Delete(e._Onext); regUp._fixUpperEdge = false; } _mesh.Splice(vEvent._anEdge, e); SweepEvent(vEvent); // recurse return; } // See above throw new InvalidOperationException("Vertices should have been merged before"); }
/// <summary> /// Find some weights which describe how the intersection vertex is /// a linear combination of "org" and "dest". Each of the two edges /// which generated "isect" is allocated 50% of the weight; each edge /// splits the weight between its org and dst according to the /// relative distance to "isect". /// </summary> private void VertexWeights(MeshUtils.Vertex isect, MeshUtils.Vertex org, MeshUtils.Vertex dst, out float w0, out float w1) { var t1 = Geom.VertL1Dist(org, isect); var t2 = Geom.VertL1Dist(dst, isect); w0 = 0.5f * t2 / (t1 + t2); w1 = 0.5f * t1 / (t1 + t2); isect._coords.X += w0 * org._coords.X + w1 * dst._coords.X; isect._coords.Y += w0 * org._coords.Y + w1 * dst._coords.Y; isect._coords.Z += w0 * org._coords.Z + w1 * dst._coords.Z; }
/// <summary> /// Purpose: connect a "right" vertex vEvent (one where all edges go left) /// to the unprocessed portion of the mesh. Since there are no right-going /// edges, two regions (one above vEvent and one below) are being merged /// into one. "regUp" is the upper of these two regions. /// /// There are two reasons for doing this (adding a right-going edge): /// - if the two regions being merged are "inside", we must add an edge /// to keep them separated (the combined region would not be monotone). /// - in any case, we must leave some record of vEvent in the dictionary, /// so that we can merge vEvent with features that we have not seen yet. /// For example, maybe there is a vertical edge which passes just to /// the right of vEvent; we would like to splice vEvent into this edge. /// /// However, we don't want to connect vEvent to just any vertex. We don''t /// want the new edge to cross any other edges; otherwise we will create /// intersection vertices even when the input data had no self-intersections. /// (This is a bad thing; if the user's input data has no intersections, /// we don't want to generate any false intersections ourselves.) /// /// Our eventual goal is to connect vEvent to the leftmost unprocessed /// vertex of the combined region (the union of regUp and regLo). /// But because of unseen vertices with all right-going edges, and also /// new vertices which may be created by edge intersections, we don''t /// know where that leftmost unprocessed vertex is. In the meantime, we /// connect vEvent to the closest vertex of either chain, and mark the region /// as "fixUpperEdge". This flag says to delete and reconnect this edge /// to the next processed vertex on the boundary of the combined region. /// Quite possibly the vertex we connected to will turn out to be the /// closest one, in which case we won''t need to make any changes. /// </summary> private void ConnectRightVertex(ActiveRegion regUp, MeshUtils.Edge eBottomLeft) { var eTopLeft = eBottomLeft._Onext; var regLo = RegionBelow(regUp); var eUp = regUp._eUp; var eLo = regLo._eUp; bool degenerate = false; if (eUp._Dst != eLo._Dst) { CheckForIntersect(regUp); } // Possible new degeneracies: upper or lower edge of regUp may pass // through vEvent, or may coincide with new intersection vertex if (Geom.VertEq(eUp._Org, _event)) { _mesh.Splice(eTopLeft._Oprev, eUp); regUp = TopLeftRegion(regUp); eTopLeft = RegionBelow(regUp)._eUp; FinishLeftRegions(RegionBelow(regUp), regLo); degenerate = true; } if (Geom.VertEq(eLo._Org, _event)) { _mesh.Splice(eBottomLeft, eLo._Oprev); eBottomLeft = FinishLeftRegions(regLo, null); degenerate = true; } if (degenerate) { AddRightEdges(regUp, eBottomLeft._Onext, eTopLeft, eTopLeft, true); return; } // Non-degenerate situation -- need to add a temporary, fixable edge. // Connect to the closer of eLo.Org, eUp.Org. MeshUtils.Edge eNew; if (Geom.VertLeq(eLo._Org, eUp._Org)) { eNew = eLo._Oprev; } else { eNew = eUp; } eNew = _mesh.Connect(eBottomLeft._Lprev, eNew); // Prevent cleanup, otherwise eNew might disappear before we've even // had a chance to mark it as a temporary edge. AddRightEdges(regUp, eNew, eNew._Onext, eNew._Onext, false); eNew._Sym._activeRegion._fixUpperEdge = true; WalkDirtyRegions(regUp); }
/// <summary> /// Given three vertices u,v,w such that VertLeq(u,v) && VertLeq(v,w), /// evaluates the t-coord of the edge uw at the s-coord of the vertex v. /// Returns v->t - (uw)(v->s), ie. the signed distance from uw to v. /// If uw is vertical (and thus passes thru v), the result is zero. /// /// The calculation is extremely accurate and stable, even when v /// is very close to u or w. In particular if we set v->t = 0 and /// let r be the negated result (this evaluates (uw)(v->s)), then /// r is guaranteed to satisfy MIN(u->t,w->t) <= r <= MAX(u->t,w->t). /// </summary> public static Real EdgeEval(MeshUtils.Vertex u, MeshUtils.Vertex v, MeshUtils.Vertex w) { Debug.Assert(VertLeq(u, v) && VertLeq(v, w)); var gapL = v._s - u._s; var gapR = w._s - v._s; if (gapL + gapR > 0.0f) { if (gapL < gapR) { return (v._t - u._t) + (u._t - w._t) * (gapL / (gapL + gapR)); } else { return (v._t - w._t) + (w._t - u._t) * (gapR / (gapL + gapR)); } } /* vertical line */ return 0; }
public static Real TransSign(MeshUtils.Vertex u, MeshUtils.Vertex v, MeshUtils.Vertex w) { Debug.Assert(TransLeq(u, v) && TransLeq(v, w)); var gapL = v._t - u._t; var gapR = w._t - v._t; if (gapL + gapR > 0.0f) { return (v._s - w._s) * gapL + (v._s - u._s) * gapR; } /* vertical line */ return 0; }
public static bool TransLeq(MeshUtils.Vertex lhs, MeshUtils.Vertex rhs) { return (lhs._t < rhs._t) || (lhs._t == rhs._t && lhs._s <= rhs._s); }
/// <summary> /// Returns a number whose sign matches EdgeEval(u,v,w) but which /// is cheaper to evaluate. Returns > 0, == 0 , or < 0 /// as v is above, on, or below the edge uw. /// </summary> public static Real EdgeSign(MeshUtils.Vertex u, MeshUtils.Vertex v, MeshUtils.Vertex w) { Debug.Assert(VertLeq(u, v) && VertLeq(v, w)); var gapL = v._s - u._s; var gapR = w._s - v._s; if (gapL + gapR > 0.0f) { return (v._t - w._t) * gapL + (v._t - u._t) * gapR; } /* vertical line */ return 0; }
// Use this for initialization void Start() { GameObject editorQuad = GameObject.Find ("EditorQuad"); meshUtils = new MeshUtils (); mesh = editorQuad.GetComponent<MeshFilter>().mesh; selectionQuad = GameObject.Find("SelectionQuad"); // Initalize the size of the render texture RenderTexture rt = editorQuad.GetComponent<Renderer> ().material.mainTexture as RenderTexture; int resolution = (int)Mathf.Max (Screen.width, Screen.height) * 2; rt.width = resolution; rt.height = resolution; Init (); }
/// <summary> /// Find some weights which describe how the intersection vertex is /// a linear combination of "org" and "dest". Each of the two edges /// which generated "isect" is allocated 50% of the weight; each edge /// splits the weight between its org and dst according to the /// relative distance to "isect". /// </summary> private void VertexWeights(MeshUtils.Vertex isect, MeshUtils.Vertex org, MeshUtils.Vertex dst, out Real w0, out Real w1) { var t1 = Geom.VertL1dist(org, isect); var t2 = Geom.VertL1dist(dst, isect); w0 = (t2 / (t1 + t2)) / 2.0f; w1 = (t1 / (t1 + t2)) / 2.0f; isect._coords.X += w0 * org._coords.X + w1 * dst._coords.X; isect._coords.Y += w0 * org._coords.Y + w1 * dst._coords.Y; isect._coords.Z += w0 * org._coords.Z + w1 * dst._coords.Z; }
/// <summary> /// Two vertices with idential coordinates are combined into one. /// e1.Org is kept, while e2.Org is discarded. /// </summary> private void SpliceMergeVertices(MeshUtils.Edge e1, MeshUtils.Edge e2) { _mesh.Splice(e1, e2); }
/// <summary> /// Replace an upper edge which needs fixing (see ConnectRightVertex). /// </summary> private void FixUpperEdge(ActiveRegion reg, MeshUtils.Edge newEdge) { Debug.Assert(reg._fixUpperEdge); _mesh.Delete(reg._eUp); reg._fixUpperEdge = false; reg._eUp = newEdge; newEdge._activeRegion = reg; }
public static bool VertEq(MeshUtils.Vertex lhs, MeshUtils.Vertex rhs) { return lhs._s == rhs._s && lhs._t == rhs._t; }
public static bool VertLeq(MeshUtils.Vertex lhs, MeshUtils.Vertex rhs) { return (lhs._s < rhs._s) || (lhs._s == rhs._s && lhs._t <= rhs._t); }
public static bool EdgeGoesLeft(MeshUtils.Edge e) { return VertLeq(e._Dst, e._Org); }
/// <summary> /// TessellateMonoRegion( face ) tessellates a monotone region /// (what else would it do??) The region must consist of a single /// loop of half-edges (see mesh.h) oriented CCW. "Monotone" in this /// case means that any vertical line intersects the interior of the /// region in a single interval. /// /// Tessellation consists of adding interior edges (actually pairs of /// half-edges), to split the region into non-overlapping triangles. /// /// The basic idea is explained in Preparata and Shamos (which I don't /// have handy right now), although their implementation is more /// complicated than this one. The are two edge chains, an upper chain /// and a lower chain. We process all vertices from both chains in order, /// from right to left. /// /// The algorithm ensures that the following invariant holds after each /// vertex is processed: the untessellated region consists of two /// chains, where one chain (say the upper) is a single edge, and /// the other chain is concave. The left vertex of the single edge /// is always to the left of all vertices in the concave chain. /// /// Each step consists of adding the rightmost unprocessed vertex to one /// of the two chains, and forming a fan of triangles from the rightmost /// of two chain endpoints. Determining whether we can add each triangle /// to the fan is a simple orientation test. By making the fan as large /// as possible, we restore the invariant (check it yourself). /// </summary> private void TessellateMonoRegion(MeshUtils.Face face) { // All edges are oriented CCW around the boundary of the region. // First, find the half-edge whose origin vertex is rightmost. // Since the sweep goes from left to right, face->anEdge should // be close to the edge we want. var up = face._anEdge; Debug.Assert(up._Lnext != up && up._Lnext._Lnext != up); for (; Geom.VertLeq(up.Dst, up._Org); up = up.Lprev) { } for (; Geom.VertLeq(up._Org, up.Dst); up = up._Lnext) { } var lo = up.Lprev; while (up._Lnext != lo) { if (Geom.VertLeq(up.Dst, lo._Org)) { // up.Dst is on the left. It is safe to form triangles from lo.Org. // The EdgeGoesLeft test guarantees progress even when some triangles // are CW, given that the upper and lower chains are truly monotone. while (lo._Lnext != up && (Geom.EdgeGoesLeft(lo._Lnext) || Geom.EdgeSign(lo._Org, lo.Dst, lo._Lnext.Dst) <= 0.0f)) { lo = _mesh.Connect(lo._Lnext, lo)._Sym; } lo = lo.Lprev; } else { // lo.Org is on the left. We can make CCW triangles from up.Dst. while (lo._Lnext != up && (Geom.EdgeGoesRight(up.Lprev) || Geom.EdgeSign(up.Dst, up._Org, up.Lprev._Org) >= 0.0f)) { up = _mesh.Connect(up, up.Lprev)._Sym; } up = up._Lnext; } } // Now lo.Org == up.Dst == the leftmost vertex. The remaining region // can be tessellated in a fan from this leftmost vertex. Debug.Assert(lo._Lnext != up); while (lo._Lnext._Lnext != up) { lo = _mesh.Connect(lo._Lnext, lo)._Sym; } }
/// <summary> /// We've computed a new intersection point, now we need a "data" pointer /// from the user so that we can refer to this new vertex in the /// rendering callbacks. /// </summary> private void GetIntersectData(MeshUtils.Vertex isect, MeshUtils.Vertex orgUp, MeshUtils.Vertex dstUp, MeshUtils.Vertex orgLo, MeshUtils.Vertex dstLo) { isect._coords = Vec3.Zero; Real w0, w1, w2, w3; VertexWeights(isect, orgUp, dstUp, out w0, out w1); VertexWeights(isect, orgLo, dstLo, out w2, out w3); if (_combineCallback != null) { isect._data = _combineCallback( isect._coords, new object[] { orgUp._data, dstUp._data, orgLo._data, dstLo._data }, new Real[] { w0, w1, w2, w3 } ); } }
private int GetNeighbourFace(MeshUtils.Edge edge) { if (edge.Rface == null) return MeshUtils.Undef; if (!edge.Rface._inside) return MeshUtils.Undef; return edge.Rface._n; }
/// <summary> /// Does everything necessary when the sweep line crosses a vertex. /// Updates the mesh and the edge dictionary. /// </summary> private void SweepEvent(MeshUtils.Vertex vEvent) { _event = vEvent; // Check if this vertex is the right endpoint of an edge that is // already in the dictionary. In this case we don't need to waste // time searching for the location to insert new edges. var e = vEvent._anEdge; while (e._activeRegion == null) { e = e._Onext; if (e == vEvent._anEdge) { // All edges go right -- not incident to any processed edges ConnectLeftVertex(vEvent); return; } } // Processing consists of two phases: first we "finish" all the // active regions where both the upper and lower edges terminate // at vEvent (ie. vEvent is closing off these regions). // We mark these faces "inside" or "outside" the polygon according // to their winding number, and delete the edges from the dictionary. // This takes care of all the left-going edges from vEvent. var regUp = TopLeftRegion(e._activeRegion); var reg = RegionBelow(regUp); var eTopLeft = reg._eUp; var eBottomLeft = FinishLeftRegions(reg, null); // Next we process all the right-going edges from vEvent. This // involves adding the edges to the dictionary, and creating the // associated "active regions" which record information about the // regions between adjacent dictionary edges. if (eBottomLeft._Onext == eTopLeft) { // No right-going edges -- add a temporary "fixable" edge ConnectRightVertex(regUp, eBottomLeft); } else { AddRightEdges(regUp, eBottomLeft._Onext, eTopLeft, eTopLeft, true); } }
public static bool VertLeq(MeshUtils.Vertex lhs, MeshUtils.Vertex rhs) { // ReSharper disable CompareOfFloatsByEqualityOperator return (lhs._s < rhs._s) || (lhs._s == rhs._s && lhs._t <= rhs._t); // ReSharper restore CompareOfFloatsByEqualityOperator }
public static void AddWinding(MeshUtils.Edge eDst, MeshUtils.Edge eSrc) { eDst._winding += eSrc._winding; eDst._Sym._winding += eSrc._Sym._winding; }
static void Swap(ref MeshUtils.Vertex a, ref MeshUtils.Vertex b) { var tmp = a; a = b; b = tmp; }
/// <summary> /// Given edges (o1,d1) and (o2,d2), compute their point of intersection. /// The computed point is guaranteed to lie in the intersection of the /// bounding rectangles defined by each edge. /// </summary> public static void EdgeIntersect(MeshUtils.Vertex o1, MeshUtils.Vertex d1, MeshUtils.Vertex o2, MeshUtils.Vertex d2, MeshUtils.Vertex v) { // This is certainly not the most efficient way to find the intersection // of two line segments, but it is very numerically stable. // // Strategy: find the two middle vertices in the VertLeq ordering, // and interpolate the intersection s-value from these. Then repeat // using the TransLeq ordering to find the intersection t-value. if (!VertLeq(o1, d1)) { Swap(ref o1, ref d1); } if (!VertLeq(o2, d2)) { Swap(ref o2, ref d2); } if (!VertLeq(o1, o2)) { Swap(ref o1, ref o2); Swap(ref d1, ref d2); } if (!VertLeq(o2, d1)) { // Technically, no intersection -- do our best v._s = (o2._s + d1._s) / 2.0f; } else if (VertLeq(d1, d2)) { // Interpolate between o2 and d1 var z1 = EdgeEval(o1, o2, d1); var z2 = EdgeEval(o2, d1, d2); if (z1 + z2 < 0.0f) { z1 = -z1; z2 = -z2; } v._s = Interpolate(z1, o2._s, z2, d1._s); } else { // Interpolate between o2 and d2 var z1 = EdgeSign(o1, o2, d1); var z2 = -EdgeSign(o1, d2, d1); if (z1 + z2 < 0.0f) { z1 = -z1; z2 = -z2; } v._s = Interpolate(z1, o2._s, z2, d2._s); } // Now repeat the process for t if (!TransLeq(o1, d1)) { Swap(ref o1, ref d1); } if (!TransLeq(o2, d2)) { Swap(ref o2, ref d2); } if (!TransLeq(o1, o2)) { Swap(ref o1, ref o2); Swap(ref d1, ref d2); } if (!TransLeq(o2, d1)) { // Technically, no intersection -- do our best v._t = (o2._t + d1._t) / 2.0f; } else if (TransLeq(d1, d2)) { // Interpolate between o2 and d1 var z1 = TransEval(o1, o2, d1); var z2 = TransEval(o2, d1, d2); if (z1 + z2 < 0.0f) { z1 = -z1; z2 = -z2; } v._t = Interpolate(z1, o2._t, z2, d1._t); } else { // Interpolate between o2 and d2 var z1 = TransSign(o1, o2, d1); var z2 = -TransSign(o1, d2, d1); if (z1 + z2 < 0.0f) { z1 = -z1; z2 = -z2; } v._t = Interpolate(z1, o2._t, z2, d2._t); } }
public static bool EdgeGoesRight(MeshUtils.Edge e) { return VertLeq(e._Org, e._Dst); }
/// <summary> /// Purpose: insert right-going edges into the edge dictionary, and update /// winding numbers and mesh connectivity appropriately. All right-going /// edges share a common origin vOrg. Edges are inserted CCW starting at /// eFirst; the last edge inserted is eLast.Oprev. If vOrg has any /// left-going edges already processed, then eTopLeft must be the edge /// such that an imaginary upward vertical segment from vOrg would be /// contained between eTopLeft.Oprev and eTopLeft; otherwise eTopLeft /// should be null. /// </summary> private void AddRightEdges(ActiveRegion regUp, MeshUtils.Edge eFirst, MeshUtils.Edge eLast, MeshUtils.Edge eTopLeft, bool cleanUp) { bool firstTime = true; var e = eFirst; do { Debug.Assert(Geom.VertLeq(e._Org, e._Dst)); AddRegionBelow(regUp, e._Sym); e = e._Onext; } while (e != eLast); // Walk *all* right-going edges from e.Org, in the dictionary order, // updating the winding numbers of each region, and re-linking the mesh // edges to match the dictionary ordering (if necessary). if (eTopLeft == null) { eTopLeft = RegionBelow(regUp)._eUp._Rprev; } ActiveRegion regPrev = regUp, reg; var ePrev = eTopLeft; while (true) { reg = RegionBelow(regPrev); e = reg._eUp._Sym; if (e._Org != ePrev._Org) break; if (e._Onext != ePrev) { // Unlink e from its current position, and relink below ePrev _mesh.Splice(e._Oprev, e); _mesh.Splice(ePrev._Oprev, e); } // Compute the winding number and "inside" flag for the new regions reg._windingNumber = regPrev._windingNumber - e._winding; reg._inside = Geom.IsWindingInside(_windingRule, reg._windingNumber); // Check for two outgoing edges with same slope -- process these // before any intersection tests (see example in tessComputeInterior). regPrev._dirty = true; if (!firstTime && CheckForRightSplice(regPrev)) { Geom.AddWinding(e, ePrev); DeleteRegion(regPrev); _mesh.Delete(ePrev); } firstTime = false; regPrev = reg; ePrev = e; } regPrev._dirty = true; Debug.Assert(regPrev._windingNumber - e._winding == reg._windingNumber); if (cleanUp) { // Check for intersections between newly adjacent edges. WalkDirtyRegions(regPrev); } }
public static Real VertL1dist(MeshUtils.Vertex u, MeshUtils.Vertex v) { return Math.Abs(u._s - v._s) + Math.Abs(u._t - v._t); }
/// <summary> /// Purpose: connect a "left" vertex (one where both edges go right) /// to the processed portion of the mesh. Let R be the active region /// containing vEvent, and let U and L be the upper and lower edge /// chains of R. There are two possibilities: /// /// - the normal case: split R into two regions, by connecting vEvent to /// the rightmost vertex of U or L lying to the left of the sweep line /// /// - the degenerate case: if vEvent is close enough to U or L, we /// merge vEvent into that edge chain. The subcases are: /// - merging with the rightmost vertex of U or L /// - merging with the active edge of U or L /// - merging with an already-processed portion of U or L /// </summary> private void ConnectLeftVertex(MeshUtils.Vertex vEvent) { var tmp = new ActiveRegion(); // Get a pointer to the active region containing vEvent tmp._eUp = vEvent._anEdge._Sym; var regUp = _dict.Find(tmp).Key; var regLo = RegionBelow(regUp); if (regLo == null) { // This may happen if the input polygon is coplanar. return; } var eUp = regUp._eUp; var eLo = regLo._eUp; // Try merging with U or L first if (Geom.EdgeSign(eUp._Dst, vEvent, eUp._Org) == 0.0f) { ConnectLeftDegenerate(regUp, vEvent); return; } // Connect vEvent to rightmost processed vertex of either chain. // e._Dst is the vertex that we will connect to vEvent. var reg = Geom.VertLeq(eLo._Dst, eUp._Dst) ? regUp : regLo; if (regUp._inside || reg._fixUpperEdge) { MeshUtils.Edge eNew; if (reg == regUp) { eNew = _mesh.Connect(vEvent._anEdge._Sym, eUp._Lnext); } else { eNew = _mesh.Connect(eLo._Dnext, vEvent._anEdge)._Sym; } if (reg._fixUpperEdge) { FixUpperEdge(reg, eNew); } else { ComputeWinding(AddRegionBelow(regUp, eNew)); } SweepEvent(vEvent); } else { // The new vertex is in a region which does not belong to the polygon. // We don't need to connect this vertex to the rest of the mesh. AddRightEdges(regUp, vEvent._anEdge, vEvent._anEdge, null, true); } }