/// <summary> /// Updates the "inner" half edges clockwise so the next pointers are correct. /// Is called after a face is inserted. /// </summary> /// <param name="edgeList">A list of edges that belong to a specific face</param> private void UpdateCWHedges(List<HandleEdge> edgeList) { // Proceed the loop for every edge and connect "hedge1" to the next hedge. for (int i = 0; i < edgeList.Count; i++) { // Test if the hedge1 or hedge2 is used. Decide by looking at their face pointers. int indexhedge1 = _LedgePtrCont[edgeList[i]._DataIndex]._he1; int indexhedge2 = _LedgePtrCont[edgeList[i]._DataIndex]._he2; HEdgePtrCont hedgePtrCont1 = _LhedgePtrCont[indexhedge1]; HEdgePtrCont hedgePtrCont2 = _LhedgePtrCont[indexhedge2]; if (hedgePtrCont1._f == _LfacePtrCont.Count - 1) { #region UseHEdge1 // The face the edge1 points to is the active face - hurray! use this edge and move on. if (i + 1 < edgeList.Count) { // Just use the next hedge HEdgePtrCont nextHedgePtrCont = _LhedgePtrCont[_LedgePtrCont[edgeList[i + 1]._DataIndex]._he1]; if (nextHedgePtrCont._f == _LfacePtrCont.Count - 1) { // use first hedgePtrCont1._nhe._DataIndex = hedgePtrCont1._nhe._DataIndex == -1 ? nextHedgePtrCont._he._DataIndex - 1 : hedgePtrCont1._nhe._DataIndex; } else { // use second hedgePtrCont1._nhe._DataIndex = hedgePtrCont1._nhe._DataIndex == -1 ? nextHedgePtrCont._he._DataIndex : hedgePtrCont1._nhe._DataIndex; } _LhedgePtrCont[indexhedge1] = new HEdgePtrCont() { _f = hedgePtrCont1._f, _he = hedgePtrCont1._he, _nhe = hedgePtrCont1._nhe, _v = hedgePtrCont1._v, _vn = hedgePtrCont1._vn, _vuv = hedgePtrCont1._vuv }; } else { // Connect to the first hedge in the list because the current is the last one in the face HEdgePtrCont nextHedgePtrCont = _LhedgePtrCont[_LedgePtrCont[edgeList[0]._DataIndex]._he1]; if (nextHedgePtrCont._f == _LfacePtrCont.Count - 1) { // use first hedgePtrCont1._nhe._DataIndex = hedgePtrCont1._nhe._DataIndex == -1 ? nextHedgePtrCont._he._DataIndex - 1 : hedgePtrCont1._nhe._DataIndex; } else { // use second hedgePtrCont1._nhe._DataIndex = hedgePtrCont1._nhe._DataIndex == -1 ? nextHedgePtrCont._he._DataIndex : hedgePtrCont1._nhe._DataIndex; } _LhedgePtrCont[indexhedge1] = new HEdgePtrCont() { _f = hedgePtrCont1._f, _he = hedgePtrCont1._he, _nhe = hedgePtrCont1._nhe, _v = hedgePtrCont1._v, _vn = hedgePtrCont1._vn, _vuv = hedgePtrCont1._vuv }; } #endregion UseHEdge1 } else if (hedgePtrCont2._f == _LfacePtrCont.Count - 1 || hedgePtrCont2._f == -1) { #region UseHEdge2 hedgePtrCont2._f._DataIndex = _LfacePtrCont.Count - 1; // The face the edge2 points to is the current face - let's use the second one then if (i + 1 < edgeList.Count) { // Just use the next hedge HEdgePtrCont nextHedgePtrCont = _LhedgePtrCont[_LedgePtrCont[edgeList[i + 1]._DataIndex]._he1]; if (nextHedgePtrCont._f == _LfacePtrCont.Count - 1) { // use first hedgePtrCont2._nhe._DataIndex = hedgePtrCont2._nhe._DataIndex == -1 ? nextHedgePtrCont._he._DataIndex - 1 : hedgePtrCont2._nhe._DataIndex; } else { // use second hedgePtrCont2._nhe._DataIndex = hedgePtrCont2._nhe._DataIndex == -1 ? nextHedgePtrCont._he._DataIndex : hedgePtrCont2._nhe._DataIndex; } _LhedgePtrCont[indexhedge2] = new HEdgePtrCont() { _f = hedgePtrCont2._f, _he = hedgePtrCont2._he, _nhe = hedgePtrCont2._nhe, _v = hedgePtrCont2._v, _vn = hedgePtrCont2._vn, _vuv = hedgePtrCont2._vuv }; } else { // Connect to the first hedge in the list because the current is the last one in the face HEdgePtrCont nextHedgePtrCont = _LhedgePtrCont[_LedgePtrCont[edgeList[0]._DataIndex]._he1]; if (nextHedgePtrCont._f == _LfacePtrCont.Count - 1) { // use first hedgePtrCont2._nhe._DataIndex = hedgePtrCont2._nhe._DataIndex == -1 ? nextHedgePtrCont._he._DataIndex - 1 : hedgePtrCont2._nhe._DataIndex; } else { // use second hedgePtrCont2._nhe._DataIndex = hedgePtrCont2._nhe._DataIndex == -1 ? nextHedgePtrCont._he._DataIndex : hedgePtrCont2._nhe._DataIndex; } _LhedgePtrCont[indexhedge2] = new HEdgePtrCont() { _f = hedgePtrCont2._f, _he = hedgePtrCont2._he, _nhe = hedgePtrCont2._nhe, _v = hedgePtrCont2._v, _vn = hedgePtrCont2._vn, _vuv = hedgePtrCont2._vuv }; } #endregion UseHEdge2 } else { /* It's an edge shared by more than two faces. So we need to do something. * TODO: * * Insert new edge between the two vertices. * Update the pointers of the edge */ Debug.WriteLine("Both edges are already in use and don't point to the current face. Do something different."); HEdgePtrCont hedge1 = hedgePtrCont1; HEdgePtrCont hedge2 = hedgePtrCont2; hedge1._f._DataIndex = _LfacePtrCont.Count - 1; hedge1._he._DataIndex = _LhedgePtrCont.Count + 1; hedge2._he._DataIndex = _LhedgePtrCont.Count; if (i + 1 < edgeList.Count) { int nextIndex = _LedgePtrCont[edgeList[i + 1]._DataIndex]._he1._DataIndex; hedge1._nhe._DataIndex = nextIndex; } else { int firstHEdgeIndex = _LedgePtrCont[edgeList[0]._DataIndex]._he1._DataIndex; hedge1._nhe._DataIndex = firstHEdgeIndex; } // Add the hedges to the global list _LhedgePtrCont.Add(hedge1); _LhedgePtrCont.Add(hedge2); // Add an edge to the global list _LedgePtrCont.Add( new EdgePtrCont() { _he1 = new HandleHalfEdge() { _DataIndex = _LhedgePtrCont.Count - 2 }, _he2 = new HandleHalfEdge() { _DataIndex = _LhedgePtrCont.Count - 1 } } ); _LedgeHndl.Add(new HandleEdge() { _DataIndex = _LedgePtrCont.Count - 1 } ); // Change the current edgelist edgeList.RemoveAt(i); edgeList.Insert(i, _LedgeHndl[_LedgeHndl.Count - 1]); // Change the next pointer for the edge before the current one int indexPrevhedge1 = i == 0 ? _LedgePtrCont[edgeList[i]._DataIndex]._he1 : _LedgePtrCont[edgeList[i - 1]._DataIndex]._he1; int indexPrevhedge2 = i == 0 ? _LedgePtrCont[edgeList[i]._DataIndex]._he2 : _LedgePtrCont[edgeList[i - 1]._DataIndex]._he2; HEdgePtrCont prevhedge1 = _LhedgePtrCont[indexhedge1]; HEdgePtrCont prevhedge2 = _LhedgePtrCont[indexhedge2]; if (prevhedge1._f._DataIndex == hedge1._f._DataIndex) { // use the first hedge prevhedge1._nhe._DataIndex = _LhedgePtrCont.Count - 2; _LhedgePtrCont[indexPrevhedge1] = new HEdgePtrCont() { _f = prevhedge1._f, _he = prevhedge1._he, _nhe = prevhedge1._nhe, _v = prevhedge1._v, _vn = prevhedge1._vn, _vuv = prevhedge1._vuv }; } else { // use the second hedge prevhedge2._nhe._DataIndex = _LhedgePtrCont.Count - 2; _LhedgePtrCont[indexPrevhedge2] = new HEdgePtrCont() { _f = prevhedge2._f, _he = prevhedge2._he, _nhe = prevhedge2._nhe, _v = prevhedge2._v, _vn = prevhedge2._vn, _vuv = prevhedge2._vuv }; } } } }
/// <summary> /// This method converts a quad based 'Geometry' object to a triangle based one. /// </summary> private void TriangulateGeometry() { List<HandleFace> LtmpFaces = new List<HandleFace>(); foreach (HandleFace currentFace in _LfaceHndl) { // Pruefe zuerst ob man das face triangulaten sollte oder nicht. if (EnFaceAdjacentHalfEdges(currentFace).Count() == 3) continue; // Hole aktuelles face und merke den index. FacePtrCont currentFaceCont = _LfacePtrCont[currentFace]; // Merke erste hedge h0. HandleHalfEdge h0H = currentFaceCont._h; HEdgePtrCont h0Cont = _LhedgePtrCont[h0H]; // Merke ersten vert v0. HandleVertex v0H = _LhedgePtrCont[h0Cont._he]._v; // Merke die letzte hedge im face hl. //HandleHalfEdge hlH = RetLastHalfEdgeInFaceCw(currentFace); HandleHalfEdge hlH = EnFaceAdjacentHalfEdges(currentFace).Last(); HEdgePtrCont hlCont = _LhedgePtrCont[hlH]; // Lege zwei neue hedges an und fülle sie korrekt. int hedgeCount = _LhedgePtrCont.Count; HandleHalfEdge hedge0H = new HandleHalfEdge() { _DataIndex = hedgeCount }; HandleHalfEdge hedge1H = new HandleHalfEdge() { _DataIndex = hedgeCount + 1 }; HandleEdge edgeHNew = new HandleEdge() { _DataIndex = _LedgeHndl.Count }; EdgePtrCont edgeContNew = new EdgePtrCont() { _he1 = hedge0H, _he2 = hedge1H }; HEdgePtrCont newhedge0 = new HEdgePtrCont() { _nhe = h0H, _v = v0H, _he = hedge1H, _f = currentFace, _vn = hlCont._vn, _vuv = hlCont._vuv }; // Hole h1 und h2 zum Merken. HandleHalfEdge h1H = h0Cont._nhe; HEdgePtrCont h1Cont = _LhedgePtrCont[h1H]; HandleHalfEdge h2H = h1Cont._nhe; HEdgePtrCont h2Cont = _LhedgePtrCont[h2H]; HEdgePtrCont newhedge1 = new HEdgePtrCont() { _nhe = h1Cont._nhe, _v = h1Cont._v, _he = hedge0H, _f = new HandleFace(-1), _vn = h1Cont._vn, _vuv = h1Cont._vuv, }; // Update die jeweiligen next pointer der angrenzenden hedges. h1Cont._nhe = hedge0H; hlCont._nhe = hedge1H; // Lege ein neues face an für das triangle 2. HandleFace f1H = new HandleFace() { _DataIndex = (_LfaceHndl.Count - 1) + LtmpFaces.Count + 1 }; FacePtrCont f1Cont = new FacePtrCont() { _fn = currentFaceCont._fn, _h = hlH }; // Update das neue triangle bezüglich des neuen faces. Dazu erstmal h2 holen noch. newhedge1._f = f1H; h2Cont._f = f1H; hlCont._f = f1H; // Sichere die Änderungen in den listen. _LedgeHndl.Add(edgeHNew); _LedgePtrCont.Add(edgeContNew); _LhedgePtrCont.Add(newhedge0); _LhedgePtrCont.Add(newhedge1); // Speichere das face handle erstmal in tmp faces wegen der iteration. LtmpFaces.Add(f1H); _LfacePtrCont.Add(f1Cont); _LhedgePtrCont[h1H] = new HEdgePtrCont() { _f = h1Cont._f, _he = h1Cont._he, _nhe = h1Cont._nhe, _v = h1Cont._v, _vn = h1Cont._vn, _vuv = h1Cont._vuv }; _LhedgePtrCont[h2H] = new HEdgePtrCont() { _f = h2Cont._f, _he = h2Cont._he, _nhe = h2Cont._nhe, _v = h2Cont._v, _vn = h2Cont._vn, _vuv = h2Cont._vuv }; _LhedgePtrCont[hlH] = new HEdgePtrCont() { _f = hlCont._f, _he = hlCont._he, _nhe = hlCont._nhe, _v = hlCont._v, _vn = hlCont._vn, _vuv = hlCont._vuv }; } foreach (HandleFace handleFace in LtmpFaces) { _LfaceHndl.Add(handleFace); } LtmpFaces.Clear(); }
/// <summary> /// For testing only now. /// </summary> /// <param name="existingEdge"></param> /// <param name="fromVert"></param> /// <param name="toVert"></param> /// <returns></returns> private HandleHalfEdge ReuseExistingConnection(HandleEdge existingEdge, HandleVertex fromVert, HandleVertex toVert) { // Check half-edge 1 and 2 if one points to the actual face. This is the one we use for our face then. If no one we build a new connection. HEdgePtrCont hedge1 = _LhedgePtrCont[_LedgePtrCont[existingEdge]._he1]; HEdgePtrCont hedge2 = _LhedgePtrCont[_LedgePtrCont[existingEdge]._he2]; HandleHalfEdge hedgeToUse = new HandleHalfEdge(-1); if (hedge2._f == -1) { // It is hedge 2 that is free. We should use it. hedgeToUse = _LedgePtrCont[existingEdge]._he2; } else if (hedge1._f == -1) { // It is hedge 1 that is free. We should use it. Should never happen. TODO: Exception throw? hedgeToUse = _LedgePtrCont[existingEdge]._he1; } else { // Neither one of the faces of the existing half-edges was free so we build a new edge. return CreateAllNewConnection(fromVert, toVert); } // Updating the face pointer. HEdgePtrCont hedge = _LhedgePtrCont[hedgeToUse]; hedge._f = new HandleFace(_LfacePtrCont.Count - 1); _LhedgePtrCont[hedgeToUse] = new HEdgePtrCont() { _f = hedge._f, _he = hedge._he, _nhe = hedge._nhe, _v = hedge._v, _vn = hedge._vn, _vuv = hedge._vuv }; return hedgeToUse; }
/// <summary> /// For testing now only. /// </summary> /// <returns></returns> private HandleHalfEdge CreateAllNewConnection(HandleVertex fromVert, HandleVertex toVert) { HEdgePtrCont hedge1 = new HEdgePtrCont() { _f = new HandleFace(_LfacePtrCont.Count - 1), _he = new HandleHalfEdge(_LedgePtrCont.Count == 0 ? 1 : _LhedgePtrCont.Count + 1), _v = new HandleVertex(toVert), _vn = new HandleVertexNormal(-1), _vuv = new HandleVertexUV(-1), _nhe = new HandleHalfEdge(-1) }; HEdgePtrCont hedge2 = new HEdgePtrCont() { _f = new HandleFace(-1), _he = new HandleHalfEdge(_LedgePtrCont.Count == 0 ? 0 : _LhedgePtrCont.Count), _v = new HandleVertex(fromVert), _vn = new HandleVertexNormal(-1), _vuv = new HandleVertexUV(-1), _nhe = new HandleHalfEdge(-1) }; _LhedgePtrCont.Add(hedge1); _LhedgePtrCont.Add(hedge2); _LedgePtrCont.Add( new EdgePtrCont() { _he1 = new HandleHalfEdge(_LhedgePtrCont.Count - 2), _he2 = new HandleHalfEdge(_LhedgePtrCont.Count - 1) } ); _LedgeHndl.Add( new HandleEdge() { _DataIndex = _LedgePtrCont.Count - 1 } ); // Update the vertices. VertexPtrCont vertFrom = _LvertexPtrCont[fromVert._DataIndex]; VertexPtrCont vertTo = _LvertexPtrCont[toVert._DataIndex]; if (!vertFrom._h.isValid) { vertFrom._h = _LedgePtrCont[_LedgePtrCont.Count - 1]._he1; _LvertexPtrCont[fromVert] = new VertexPtrCont() { _h = vertFrom._h }; } if (!vertTo._h.isValid) { vertTo._h = _LedgePtrCont[_LedgePtrCont.Count - 1]._he2; _LvertexPtrCont[toVert] = new VertexPtrCont() { _h = vertTo._h }; } return _LedgePtrCont.Last()._he1; }
/// <summary> /// Adds a face from the importer to the geometry container /// </summary> /// <param name="gf">GeoFace object from the importer</param> private void AddFace(GeoFace gf) { // Add a face container. _LfacePtrCont.Add( new FacePtrCont() { _h = new HandleHalfEdge() { _DataIndex = -1 } } ); // Add a face handle. _LfaceHndl.Add( new HandleFace() { _DataIndex = _LfacePtrCont.Count - 1 } ); // Insert all the vertices for the face. List<HandleVertex> LHandleVertsForFace = new List<HandleVertex>(); foreach (float3 vVal in gf._LFVertices) { LHandleVertsForFace.Add( AddVertex(vVal) ); } // Insert all the uv coordinates for the face. List<HandleVertexUV> LHandleUVsForFace = new List<HandleVertexUV>(); foreach (float2 uvVal in gf._UV) { _LuvCoordinates.Add(uvVal); LHandleUVsForFace.Add(new HandleVertexUV() { _DataIndex = _LuvCoordinates.Count - 1 }); } // Build up the half-edge connections for the face List<HandleHalfEdge> LHandleHEForFace = new List<HandleHalfEdge>(); for (int i = 0; i < LHandleVertsForFace.Count; i++) { HandleVertex fromVert = LHandleVertsForFace[i]; HandleVertex toVert = i + 1 < LHandleVertsForFace.Count ? LHandleVertsForFace[i + 1] : LHandleVertsForFace[0]; LHandleHEForFace.Add( CreateConnection(fromVert, toVert) ); } // Loop over all the half-edges for the face and concat them and set the correct uv coordinates. for (int i = 0; i < LHandleHEForFace.Count; i++) { HandleHalfEdge currentHedge = LHandleHEForFace[i]; HEdgePtrCont hedge = _LhedgePtrCont[currentHedge]; HandleHalfEdge nextHedge = i + 1 < LHandleHEForFace.Count ? LHandleHEForFace[i + 1] : LHandleHEForFace[0]; hedge._nhe = nextHedge; if (LHandleUVsForFace.Count > 0) { HandleVertexUV currentUV = i + 1 < LHandleUVsForFace.Count ? LHandleUVsForFace[i + 1] : LHandleUVsForFace[0]; hedge._vuv = currentUV; } //_LhedgePtrCont.RemoveAt(currentHedge); //_LhedgePtrCont.Insert(currentHedge, hedge); _LhedgePtrCont[currentHedge] = new HEdgePtrCont() { _f = hedge._f, _he = hedge._he, _nhe = hedge._nhe, _v = hedge._v, _vn = hedge._vn, _vuv = hedge._vuv }; } // Set the half-edge the face points to. FacePtrCont face = _LfacePtrCont.Last(); face._h = new HandleHalfEdge(LHandleHEForFace.First()); _LfacePtrCont.RemoveAt(_LfacePtrCont.Count - 1); _LfacePtrCont.Add(face); }
/// <summary> /// This method calculates vertex normals for a specific vertex in the geometry and inserts them at the corresponding half-edges on the correct faces. /// This method uses an angle based algorithm to determine whether to calculate with another faces normal or not. /// </summary> /// <param name="vertexHandle">A handle for the vertex to calc the normals for.</param> public void CalcVertexNormal(HandleVertex vertexHandle) { List<HandleHalfEdge> EincomingHEdges = EnVertexIncomingHalfEdge(vertexHandle).ToList(); // Loop over every incoming half-edge. foreach (HandleHalfEdge handleHedge in EincomingHEdges) { int hedgeIndex = handleHedge; // Check if the half-edge is pointing to a face. int faceIndex = _LhedgePtrCont[hedgeIndex]._f; if (faceIndex == -1) return; float3 currentFaceNormal = _LfaceNormals[_LfacePtrCont[faceIndex]._fn]; float3 normalAggregate = new float3(); // Loop over every incoming half-edge again, so we can compare the angles between the current one and all the others. // We do this to decide which normal should be added to the sum and which not. foreach (int eincomingHEdge in EincomingHEdges) { // Add the current normal if the index is on it and do not compare any angles etc. if (eincomingHEdge == hedgeIndex) { normalAggregate += currentFaceNormal; continue; } // Stop when the current half-edge is not pointing to a face. int faceIndex2 = _LhedgePtrCont[eincomingHEdge]._f; if (faceIndex2 == -1) continue; float3 normalToCompare = _LfaceNormals[_LfacePtrCont[faceIndex2]._fn]; float dot = float3.Dot(currentFaceNormal, normalToCompare); if (System.Math.Acos(dot) * _constPiFactor < _SmoothingAngle) normalAggregate += float3.Add(normalAggregate, normalToCompare); } _LVertexNormals.Add(float3.NormalizeFast(normalAggregate)); HEdgePtrCont currentHedge = _LhedgePtrCont[hedgeIndex]; _LhedgePtrCont[hedgeIndex] = new HEdgePtrCont() { _f = currentHedge._f, _he = currentHedge._he, _nhe = currentHedge._nhe, _v = currentHedge._v, _vn = new HandleVertexNormal(_LVertexNormals.Count - 1), _vuv = currentHedge._vuv }; } }