private void CreateRandomPolygon(ref BottomPolygon[] poly) { BottomPolygon[] enlargedArray = new BottomPolygon[poly.Length + 1]; //just a quick and dirty distorted circle... = simple polygon, convex or concave possible int edgeCount = Random.Range(3, 20); float radius = Random.Range(10F, 30F); Vector3 pos = new Vector3(Random.Range(-220F, 220F), 0F, Random.Range(-220F, 220F)); List <Vector3> newPoly = new List <Vector3>(); //create some random angles List <float> angles = new List <float>(); for (int i = 0; i < edgeCount; i++) { float angle = Random.Range(0F, 2F * Mathf.PI); angles.Add(angle); } //sort Array by either des or asc to test FixCCWOrder if (RandBool()) { angles.Sort((p1, p2) => (p1.CompareTo(p2))); //Debug.Log("normal order"); } else { angles.Sort((p2, p1) => (p1.CompareTo(p2))); //Debug.Log("reversed order"); } for (int i = 0; i < edgeCount; i++) { float dis = Random.Range(0.2F, 1F) * radius; //distance to center newPoly.Add(new Vector3(Mathf.Sin(angles[i]) * dis, 0F, Mathf.Cos(angles[i]) * dis) + pos); } //copy old Polys to Polygon Array for (int i = 0; i < enlargedArray.Length - 1; i++) { enlargedArray[i].vertices = poly[i].vertices; } //check Order if (checkCCW) { FixCCWOrder(ref newPoly); } //add new poly at last position enlargedArray[enlargedArray.Length - 1].vertices = newPoly.ToArray(); //save new array poly = enlargedArray; }
private void GeneratePolygonStructArr(ref BottomPolygon[] poly) { poly = new BottomPolygon[walls.Length]; int iPoly = 0; //polygon integrator foreach(GameObject wall in walls){ //Save Transform reference poly[iPoly].transform = wall.transform; poly[iPoly].renderer = wall.GetComponent<Renderer>(); Mesh mesh = wall.GetComponent<MeshFilter>().mesh; Vector3[] vertices = mesh.vertices; int[] triangles = mesh.triangles; Vector3[] normals = mesh.normals; if(worldNormals){ for(int i = 0; i < vertices.Length; i++){ normals[i] = poly[iPoly].transform.TransformDirection(mesh.normals[i]); } } //SIZECHECK, list usage could get rid of this step //check how much valid vertex are present to assign array lengths in the next step int validVertices = 0; for(int i = 0; i < vertices.Length; i++){ //BOTTOM, or which orthogonal direction you need... e.g. sidescroller: if mesh.normals.z-1 if(normals[i].y == -1){ //if the normal of this vertice is pointing down validVertices++; } } //BOTTOM-VERTICES of the walls bottom-plane poly[iPoly].vertices = new Vector3[validVertices]; //init new Vector3 array of the struct with the needed length int[] validIndices = new int[validVertices]; //the original indices of the valid vertices, used to find the right triangles Vector3[] bottomVertices = new Vector3[validVertices]; //int[] newIndices = new int[validVertices]; //new indices of the vertices, used to map newTriangles //save the valid vertices and triangles of the current wall int iv = 0; //array integrator for(int i = 0; i < vertices.Length; i++){ //for ALL vertices of the wall mesh if(normals[i].y == -1){ //if the normal of this vertice is pointing down, e.g. should be only 4 vertices per cube //actual saving of the vertex in WORLD COORDINATES bottomVertices[iv] = wall.transform.TransformPoint(vertices[i]); validIndices[iv] = i; //newIndices[iv] = iv; iv++; } } if(validIndices.Length == 0){break;}//early out //BOTTOM-TRIANGLES of one poly, maybe we dont need them directly later, but here they are needed to delete inner vertices (e.g. center of cylinder vertex) List<int> bottomTrianglesList = new List<int>(); //using the OLD indices int iAs = 0; //iterator for assigned triangles for(int it = 0; it < triangles.Length;){// iterator triangles //check if the next 3 indices of triangles match int match = 0;//check the next 3 indices of this triangles for(int imatch = 0; imatch<3; imatch++){ for(int ivv = 0; ivv < validIndices.Length; ivv++){//check with all vertices if(validIndices[ivv]==triangles[it+imatch]){ match++; } } } //if all 3 indices of a triangle match with the validIndices, it is a bottom triangle if(match == 3){ //create new triangle in list bottomTrianglesList.Add(triangles[it+0]); bottomTrianglesList.Add(triangles[it+1]); bottomTrianglesList.Add(triangles[it+2]); iAs += 3; //assign iterator rdy for next triangle } it+=3;//next triangle } //now we have all triangles that are contained in the bottom plane, but with the original indices int[] bottomTrianglesArr = bottomTrianglesList.ToArray(); int[] bottomTriangles = new int[bottomTrianglesArr.Length]; //using the OLD indices //Update indices to refer to bottomVertices: for(int ib = 0; ib < bottomTrianglesArr.Length; ib++){ for(int ivi = 0; ivi < validIndices.Length; ivi++){ //check for original index, assign corresponding new index, must hit once per loop! if(bottomTrianglesArr[ib] == validIndices[ivi]){ bottomTriangles[ib] = ivi;//currently the same as newTriangles[ib] = newIndices[ivi];, we dont need newIndices[] } } } //AT THIS POINT we have the bottom vertices and triangles of the bottomPlane rdy for any use // bottomVertices & bottomTriangles //Now we have to find the outlining polygon: ExtractPolygon(bottomVertices, bottomTriangles, ref poly[iPoly]); //extracts polygon and saves it directly in passed poly struct //OTHER assignments for future purpose //add and save visibilityFader Reference and set Blackness of the Fader if(!wall.GetComponent<VisibilityFader>()){ poly[iPoly].fader = wall.AddComponent<VisibilityFader>(); poly[iPoly].fader.ManualInit(); }else{ poly[iPoly].fader = wall.GetComponent<VisibilityFader>(); } poly[iPoly].fader.SetBlackness(0.0F); //set lower fadeout boundary poly[iPoly].fader.EnableTransparentFade(true); //transparent or black obstacles on fadeout poly[iPoly].include = true; CalculateCenterAndRadius(ref poly[iPoly]); //OTHER END iPoly++; } }
private void FaderTest(ref BottomPolygon[] poly) { for(int ip = 0; ip < poly.Length; ip++){ //polygon if(poly[ip].fader){ poly[ip].fader.Fade(fadeTest); } } }
private void ExtractPolygon(Vector3[] vertices, int[] triangles, ref BottomPolygon poly) { //definitions used (see http://www.geosensor.net/papers/duckham08.PR.pdf) //→A triangulation ∆ is a combinatorial map which has the property that every edge in a set of edges belongs to either one or two triangle s //→Aboundary edge of ∆ is an edge that belongs to exactly one triangle in ∆. //for simple polygons(edges do not cross themselfes) which we are dealing with, //the outline polygon consists out of the edges that appear only in one triangle: //earlyOutTest //poly.vertices = vertices; return; List<int[]> allEdges = new List<int[]>(); //list of 2 integers each representing the index of a vertex List<int[]> unsortedBE = new List<int[]>(); //unsortedBoundaryEdges List<int[]> boundaryEdges = new List<int[]>(); //sorted outer edges List<Vector3> boundaryVertices = new List<Vector3>(); //the vertices of the polygon in ccw order, this is what we need! //get all edges for(int it = 0; it<triangles.Length;){//for all triangles, add their adges to the list allEdges.Add(new int[2]{triangles[it+0],triangles[it+1]}); //edge1 allEdges.Add(new int[2]{triangles[it+1],triangles[it+2]}); //edge2 allEdges.Add(new int[2]{triangles[it+2],triangles[it+0]}); //edge3 it+=3; }//Debug.Log("Edges:"+allEdges.Count); //DROP all edges that appear in more than one triangle for( int iT = 0; iT < allEdges.Count; iT++){ //for each edge int o = iT%3; //offset to find the edges that belong to the same triangle bool addEdge = true; for(int iC = 0; iC < allEdges.Count; iC++){ //compare loop, check all other edges if( !((iT+0-o) == iC || (iT+1-o) == iC || (iT+2-o) == iC) ){ //except edges of current triangle from check if( (allEdges[iT][0] == allEdges[iC][0] && allEdges[iT][1] == allEdges[iC][1]) || (allEdges[iT][0] == allEdges[iC][1] && allEdges[iT][1] == allEdges[iC][0]) ){ addEdge = false; break; } } } //if this edge has not appeared twice we can add it to our boundary edge List if(addEdge){ unsortedBE.Add(allEdges[iT]); } }//Debug.Log("Edges:"+unsortedBE.Count); //SORT unsortedBE, no edge will be dropped now, indices of each edge may be swapped //→ unsorted List: // edge1 edge2 edge3 edge4 // [4][2] [0][1] [1][4] [0][2] //→ sorted List: // edge1 edge2(4) edge3(2) edge4(3) // [4][2] [2][0] [0][1] [1][4] //add first edge to start: boundaryEdges.Add(unsortedBE[0]); int failsave = 100; //if bottomplane is faulty we cannot create a closed loop for(int iList = 1; iList < unsortedBE.Count;){ //compare loop, one edge has to match each run (closed edge loop) for(int iC = 1; iC < unsortedBE.Count; iC++){ //check all edges but the first (already added) //check if last index matches with another index, then add it to the sorted list //Debug.Log(boundaryEdges[iList-1][1] +"|"+ unsortedBE[iC][0]); if( boundaryEdges[iList-1][1] == unsortedBE[iC][0] ){ //common vertex on compare-edge[0], add! boundaryEdges.Add(new int[2]{unsortedBE[iC][0],unsortedBE[iC][1]}); iList++; }else if( boundaryEdges[iList-1][1] == unsortedBE[iC][1] ){ //common vertex on compare-edge[1], add swapped! boundaryEdges.Add(new int[2]{unsortedBE[iC][1],unsortedBE[iC][0]}); iList++; } } if(failsave<1){ Debug.Log("Aborted Loop, bottomplane of ["+poly.transform.name+"] has gap or is faulty!"); break; } failsave--; } //Finally! generate the vertices of the polygon out of the sorted list foreach(int[] intArr in boundaryEdges){ //just add one side([0] or [1]) of each element in the sorted List and we have the vertices in right order boundaryVertices.Add(vertices[intArr[0]]); } if(checkCCW){ FixCCWOrder(ref boundaryVertices); } //put this in boundaryVertices assignment if always needed if(clampY){ for(int iV = 0; iV<boundaryVertices.Count; iV++){ boundaryVertices[iV] = new Vector3(boundaryVertices[iV].x,0F,boundaryVertices[iV].z); } } poly.vertices = boundaryVertices.ToArray(); }
//RANDOM POLY private void CreateRandomPolygon(ref BottomPolygon[] poly) { BottomPolygon[] enlargedArray = new BottomPolygon[poly.Length+1]; //just a quick and dirty distorted circle... = simple polygon, convex or concave possible int edgeCount = Random.Range(3,20); float radius = Random.Range(10F,30F); Vector3 pos = new Vector3(Random.Range(-220F,220F),0F,Random.Range(-220F,220F)); List<Vector3> newPoly = new List<Vector3>(); //create some random angles List<float> angles = new List<float>(); for(int i = 0; i < edgeCount; i++){ float angle = Random.Range(0F,pi2); angles.Add(angle); } //sort Array by either des or asc to test FixCCWOrder if(RandBool()){ angles.Sort((p1, p2) => (p1.CompareTo(p2)));//Debug.Log("normal order"); }else{ angles.Sort((p2, p1) => (p1.CompareTo(p2)));//Debug.Log("reversed order"); } for(int i = 0; i < edgeCount; i++){ float dis = Random.Range(0.2F,1F)*radius; //distance to center newPoly.Add(new Vector3(Mathf.Sin(angles[i])*dis,0F,Mathf.Cos(angles[i])*dis) +pos); } //copy old Polys to Polygon Array for(int i = 0; i< enlargedArray.Length-1; i++){ enlargedArray[i].vertices = poly[i].vertices; } //check Order if(checkCCW){ FixCCWOrder(ref newPoly); } //add new poly at last position enlargedArray[enlargedArray.Length-1].vertices = newPoly.ToArray(); //save new array poly = enlargedArray; //CreateBottomPlane(false); RefreshRandPolygons(ref randomPolys); }
//draw the polygon CYAN private void DrawPolygons(ref BottomPolygon[] poly) { for(int ip = 0; ip < poly.Length; ip++){ //polygon for(int iv = 0; iv < poly[ip].vertices.Length; iv++){ //vertices of the polygon int nv = (iv+1)%poly[ip].vertices.Length; //next vertex Debug.DrawLine(poly[ip].vertices[iv], poly[ip].vertices[nv], new Color(0F,1F,1F,0.4F), 0F, false); } } }
private void GeneratePolygonStructArr(ref BottomPolygon[] poly) { //Table of polygones poly = new BottomPolygon[walls.Length]; int iPoly = 0; //polygon integrator int cpt = 0; foreach (GameObject wall in walls) { cpt++; //Save Transform reference poly[iPoly].transform = wall.transform; Mesh mesh = wall.GetComponent <MeshFilter>().mesh; //Get vertices, triangles & normals Vector3[] vertices = mesh.vertices; int[] triangles = mesh.triangles; Vector3[] normals = mesh.normals; /* * if(worldNormals) * { * for(int i = 0; i < vertices.Length; i++) * { * normals[i] = poly[iPoly].transform.TransformDirection(mesh.normals[i]); * } * } */ //SIZECHECK, list usage could get rid of this step //check how much valid vertex are present to assign array lengths in the next step //C'est utile ?????? int validVertices = 0; for (int i = 0; i < vertices.Length; i++) { //BOTTOM, or which orthogonal direction you need... e.g. sidescroller: if mesh.normals.z-1 if (normals[i].y == -1) { //if the normal of this vertice is pointing down, should be only 4 vertices per rectangle validVertices++; } } //BOTTOM-VERTICES of the walls bottom-plane poly[iPoly].vertices = new Vector3[validVertices]; //init new Vector3 array of the struct with the needed length int[] validIndices = new int[validVertices]; //the original indices of the valid vertices, used to find the right triangles Vector3[] bottomVertices = new Vector3[validVertices]; //int[] newIndices = new int[validVertices]; //new indices of the vertices, used to map newTriangles //save the valid vertices and triangles of the current wall int iv = 0; //array integrator for (int i = 0; i < vertices.Length; i++) { //for ALL vertices of the wall mesh if (normals[i].y == -1) { //if the normal of this vertice is pointing down, e.g. should be only 4 vertices per rectangle //actual saving of the vertex in WORLD COORDINATES bottomVertices[iv] = wall.transform.TransformPoint(vertices[i]); validIndices[iv] = i; //newIndices[iv] = iv; iv++; } } if (validIndices.Length == 0) { break; } //early out //BOTTOM-TRIANGLES of one poly, maybe we dont need them directly later, but here they are needed to delete inner vertices (e.g. center of cylinder vertex) List <int> bottomTrianglesList = new List <int>(); //using the OLD indices int iAs = 0; //iterator for assigned triangles for (int it = 0; it < triangles.Length;) { // iterator triangles //check if the next 3 indices of triangles match int match = 0; //check the next 3 indices of this triangles for (int imatch = 0; imatch < 3; imatch++) { for (int ivv = 0; ivv < validIndices.Length; ivv++) { //check with all vertices if (validIndices[ivv] == triangles[it + imatch]) { match++; } } } //if all 3 indices of a triangle match with the validIndices, it is a bottom triangle if (match == 3) { //create new triangle in list bottomTrianglesList.Add(triangles[it + 0]); bottomTrianglesList.Add(triangles[it + 1]); bottomTrianglesList.Add(triangles[it + 2]); iAs += 3; //assign iterator rdy for next triangle } it += 3; //next triangle } //now we have all triangles that are contained in the bottom plane, but with the original indices int[] bottomTrianglesArr = bottomTrianglesList.ToArray(); int[] bottomTriangles = new int[bottomTrianglesArr.Length]; //using the OLD indices //Update indices to refer to bottomVertices: for (int ib = 0; ib < bottomTrianglesArr.Length; ib++) { for (int ivi = 0; ivi < validIndices.Length; ivi++) { //check for original index, assign corresponding new index, must hit once per loop! if (bottomTrianglesArr[ib] == validIndices[ivi]) { bottomTriangles[ib] = ivi; //currently the same as newTriangles[ib] = newIndices[ivi];, we dont need newIndices[] } } } //AT THIS POINT we have the bottom vertices and triangles of the bottomPlane rdy for any use // bottomVertices & bottomTriangles //Now we have to find the outlining polygon: ExtractPolygon(bottomVertices, bottomTriangles, ref poly[iPoly]); //extracts polygon and saves it directly in passed poly struct //OTHER assignments for future purpose //add and save visibilityFader Reference and set Blackness of the Fader if (!wall.GetComponent <VisibilityFader>()) { poly[iPoly].fader = wall.AddComponent <VisibilityFader>(); poly[iPoly].fader.ManualInit(); } else { poly[iPoly].fader = wall.GetComponent <VisibilityFader>(); } poly[iPoly].fader.SetBlackness(0.5F); //Not implemented yet //poly[iPoly].position = CalculateCenter(poly[iPoly].vertices); //poly[iPoly].relevantRadius = CalculateRadius(poly[iPoly].vertices); //OTHER END iPoly++; } }
private void CheapGather(ref BottomPolygon[] poly) { Vector3 off = source.position; for(int ip = 0; ip < poly.Length; ip++){ //polygon if(!poly[ip].include){continue;} //writing values into list "segmentLines" int length = poly[ip].vertices.Length; if(!invisibleFaces){ for(int iv = 0; iv < length; iv++){ //vertices of the polygon //int nv = (iv<length-1)? iv+1 : 0; //next vertex int nv = (iv+1)%length; Vector3 vertexDirection = poly[ip].vertices[iv]-poly[ip].vertices[nv]; Vector3 sourceDirection = source.position-poly[ip].vertices[iv]; if( AngleDir(vertexDirection,sourceDirection,Vector3.up)<0F ){ segmentLines.Add(poly[ip].vertices[iv]-off); segmentLines.Add(poly[ip].vertices[nv]-off); } } }else{ for(int iv = 0; iv < length; iv++){ //vertices of the polygon int nv = (iv+1)%length; Vector3 vertexDirection = poly[ip].vertices[iv]-poly[ip].vertices[nv]; Vector3 sourceDirection = source.position-poly[ip].vertices[iv]; if( AngleDir(vertexDirection,sourceDirection,Vector3.up)>0F ){ segmentLines.Add(poly[ip].vertices[iv]-off); segmentLines.Add(poly[ip].vertices[nv]-off); } } } } }
//cheaper 360° Gather for 2nd approach, without the top/bottom distinction and cut //gather relevant Polygons, check the polygon position increased by its radius if it is near our relevant radius // ↘viewport may be rotateable, for convenience we check if in radius not if within a maybe rotated rectangular area private void CheckInclusion(ref BottomPolygon[] polys, Vector3 lightPosition, float radius) { for(int iP =0; iP<polys.Length;iP++){ //Vector3.magnitude or Distance is slower than sqrMag if( ((polys[iP].position-lightPosition).sqrMagnitude - polys[iP].relevantRadius*polys[iP].relevantRadius) < radius*radius){//faster than distance //if( Vector3.Distance(polys[iP].position, lightPosition) < radius){ polys[iP].include = true; }else{ polys[iP].include = false; } } }
private void RefreshRandPolygons(ref BottomPolygon[] polyArr) { //check Order if(checkCCW){ for(int i = 0; i < polyArr.Length; i++){ polyArr[i].vertices = FixCCWOrder(polyArr[i].vertices); } } }
//cheaper 360° Gather for 2nd approach, without the top/bottom distinction and cut private void CheapGather(ref BottomPolygon[] poly) { Vector3 off = source.position; for(int ip = 0; ip < poly.Length; ip++){ //polygon Segment newSeg = new Segment(); newSeg.vert = new List<Vector3>(); int length = poly[ip].vertices.Length; if(!invisibleFaces){ for(int iv = 0; iv < length; iv++){ //vertices of the polygon //int nv = (iv<length-1)? iv+1 : 0; //next vertex int nv = (iv+1)%length; Vector3 vertexDirection = poly[ip].vertices[iv]-poly[ip].vertices[nv]; Vector3 sourceDirection = source.position-poly[ip].vertices[iv]; if( AngleDir(vertexDirection,sourceDirection,Vector3.up)<0F ){ newSeg.vert.Add(poly[ip].vertices[iv]-off); newSeg.vert.Add(poly[ip].vertices[nv]-off); } } }else{ for(int iv = 0; iv < length; iv++){ //vertices of the polygon int nv = (iv+1)%length; Vector3 vertexDirection = poly[ip].vertices[iv]-poly[ip].vertices[nv]; Vector3 sourceDirection = source.position-poly[ip].vertices[iv]; if( AngleDir(vertexDirection,sourceDirection,Vector3.up)>0F ){ newSeg.vert.Add(poly[ip].vertices[iv]-off); newSeg.vert.Add(poly[ip].vertices[nv]-off); } } } segments.Add(newSeg); } }
private void GatherSegments(ref BottomPolygon[] poly, bool top) { //top or bottom half of poly Vector3 s = source.position; //Vector3 s = Vector3.zero; Segment newSeg = new Segment(); newSeg.vert = new List<Vector3>(); newSeg.segmin = 6500F; newSeg.segmax = 0F; for(int ip = 0; ip < poly.Length; ip++){ //polygon //future: check if this poly can be dropped immidiately //if( Vector3.Distance(polys[i].position, source.position) < radius + polys[i].relevantRadius){ //if( ((polys[iP].position-position).sqrMagnitude - polys[iP].relevantRadius*polys[iP].relevantRadius) < radius*radius){ // continue; //next poly //} bool lastVisible = false; //if last checked face was visible int vertexCount = 0; int length = poly[ip].vertices.Length; bool[] visFaces = new bool[length]; //check visible faces for(int iv = 0; iv < length; iv++){ int nv = (iv+1)%length; Vector3 curV = poly[ip].vertices[iv]; Vector3 nexV = poly[ip].vertices[nv]; //visFaces[iv] = (curV.z*nexV.x-curV.x*nexV.z) < 0F; Vector3 vertexDirection = curV-nexV; Vector3 sourceDirection = s-curV; visFaces[iv] = false; //is the face on the right half and between this and next vertex visible? if(top){ if(curV.z-s.z>0F || nexV.z-s.z>0F){//if at least one vertix of the line is in the right half visFaces[iv] = TestFaceVisibility(ref vertexDirection, ref sourceDirection); } }else{ if(curV.z-s.z<0F || nexV.z-s.z<0F){ visFaces[iv] = TestFaceVisibility(ref vertexDirection, ref sourceDirection); } } } //find any invisible vertex to start from, because segments overlapping first vertex would create 2 seperate segments int offset = 0; for(int iv = 0; iv < length; iv++){ if(!visFaces[iv]){offset = iv+1; break;}; } //for testing visFaces... //Debug.DrawLine(poly[ip].vertices[offset], source.position, Color.cyan,0F,false); //for(int iv = offset; iv < (length+offset); iv++){ // if(visFaces[iv%length]) // Debug.DrawLine(poly[ip].vertices[iv%length], poly[ip].vertices[(iv+1)%length], Color.green,0F,false); //} //needed fpr pseudoangles inaccuracys on Cutlines that are all very close //otherwise the drop rate of segments on horizonatal lines would suffer //Intersection with the X-Axis? bool axisInterFromBelow = false; bool axisInterFromAbove = false; for(int iv = offset; iv < (length+offset); iv++){ //from now on we save vector points with the light source as origin Vector3 curV = poly[ip].vertices[ iv %length]-s; Vector3 nexV = poly[ip].vertices[(iv+1) %length]-s; if(visFaces[iv%length]){ //this step adds one line segment and one vertex (or two vertices if its the first) //Cut line with horizon if(curV.z>0.0f != nexV.z>0.0f){ //horizon is cut if the signs of the 2 vectice's z values don't match float h1 = -curV.z; Vector3 hv = nexV-curV; if(top && hv.z<0.0f || !top && hv.z>0.0f){ nexV.x -= hv.x * (hv.z-h1)/hv.z; //x-axis intersection x nexV.z = 0.0f; //x-axis intersection y (zero of course) axisInterFromAbove = true; //line has intersected X-axis from ABOVE the axis //Debug.DrawLine(drawOrigin, nexV+drawOrigin,Color.blue,0F,false); }else{ curV.x += hv.x * h1/hv.z; //x-axis intersection x curV.z = 0.0f; //x-axis intersection y (zero of course) axisInterFromBelow = true; //line has intersected X-axis from BELOW the axis //Debug.DrawLine(drawOrigin, curV+drawOrigin,Color.red,0F,false); } }else{ axisInterFromBelow = false; axisInterFromAbove = false; } float distance = curV.magnitude; //first vertex if(vertexCount == 0){ newSeg.vert.Add(curV); //add first vertex vertexCount++; //angle(radian) to first point //newSeg.start = Mathf.Atan2(curV.z,curV.x);// * Mathf.Rad2Deg; //if(newSeg.start<0){newSeg.start += pi2;} //fix to positive 0-360° representation //newSeg.start = pi2-newSeg.start;//invert angle count direction if(axisInterFromBelow){ newSeg.start = -1.0f; }else{ newSeg.start = newSeg.startnew = PseudoAngle(curV,Lenght(curV)); newSeg.startd = distance; } } newSeg.vert.Add(nexV); //add next vertex vertexCount++; //Vector3 proj = Vector3.Project(-curV, curV-nexV)+curV; //newSeg.segmin = Min(newSeg.segmin, proj.magnitude);//substitute for LineMinDistance //if(ip == 0 && vertexCount == 2){//test MinDistance substitute // Debug.DrawLine(curV+drawOrigin, nexV+drawOrigin,Color.blue,0F,false); // Debug.DrawRay(drawOrigin,proj,Color.white,0F,false); //}//doesnt work, we would have to add a check if the projection hits the line //CheckXAxisIntersection(ref curV, ref nexV, ref newSeg.right); newSeg.segmin = Min(newSeg.segmin, LineMinDistance(curV,nexV,Vector3.zero)); newSeg.segmin = Min(newSeg.segmin, distance);//it cannot happen that distance issmaller than lineMinDistance newSeg.segmax = Max(newSeg.segmax, distance); lastVisible = true; //remember that last face was visible }else{ //this step ends the Segment if((vertexCount > 0) && lastVisible){ //if this is the first vertex after a segment float distance = curV.magnitude; //angle(radian) to last point //newSeg.end = Mathf.Atan2(curV.z,curV.x);// * Mathf.Rad2Deg; //if(newSeg.end<0){newSeg.end += pi2;} //newSeg.end = pi2-newSeg.end;//invert angle count direction if(axisInterFromAbove){ newSeg.end = 1.0f; }else{ newSeg.end = PseudoAngle(curV,Lenght(curV)); newSeg.endd = distance; } newSeg.segmin = Min(newSeg.segmin, distance); newSeg.segmax = Max(newSeg.segmax, distance); newSeg.vertCnt = vertexCount; //there may be multiple seperated segments on a poly, reset vertCnt for possible second segment on this poly vertexCount = 0; //angleFlip //pseudo angles //if(newSeg.start>newSeg.end){newSeg.end = 1.0f;} /*if(newSeg.start>newSeg.end){ float end = newSeg.end; newSeg.end = newSeg.start; newSeg.start = end; }*/ //normal angles //angle offset: //if(newSeg.right){newSeg.end += pi2;}// newSeg.start = pi2-newSeg.start;} //if(newSeg.right && newSeg.end<180F){newSeg.end += pi2;}//works only if segment is above //if(newSeg.right && newSeg.end>180F){newSeg.end -= pi2/2;}// //angleFlip /*if(newSeg.start>newSeg.end){ float end = newSeg.end; newSeg.end = newSeg.start; newSeg.start = end; }*/ newSeg.startnew = newSeg.start; newSeg.endnew = newSeg.end; newSeg.radLenght = newSeg.end-newSeg.start; //used to make the check independent from the angle jump (0to360°) //SAVE SEGMENT segments.Add(newSeg); segmentCount++; //debug if(showSegStats){ int sv = 0;//which index of segmentValues if(segmentCount==2){sv = 1;} segmentValues[sv] = "Segment owner: random poly"; if(poly[ip].transform){ newSeg.transform = poly[ip].transform; segmentValues[sv] = "Segment"+sv+" owner: "+poly[ip].transform.name; if(poly[ip].fader){newSeg.fader = poly[ip].fader;} } segmentValues[sv] += "\noffset\t\t" +offset+ "\nsegmin\t\t" +newSeg.segmin+ "\nsegmax\t\t" +newSeg.segmax+ "\nstart\t\t\t" +newSeg.start +//* Mathf.Rad2Deg+"(blue)"+ "\nend\t\t\t" +newSeg.end +//* Mathf.Rad2Deg+"(yellow)"+ //"\nstartnew\t\t"+newSeg.startnew +//* Mathf.Rad2Deg+ //"\nendnew\t\t" +newSeg.endnew +//* Mathf.Rad2Deg+ "\nstartd\t\t\t"+newSeg.startd+ "\nendd\t\t\t" +newSeg.endd+ "\nactive\t\t" +newSeg.active+ "\n\nlightpos\t"+s+ "\nvertices:"+ "\nvertCnt\t\t" +newSeg.vertCnt + "\nvertices\t"+newSeg.vert[0]; for(int i = 1; i<newSeg.vertCnt; i++){ segmentValues[sv] += ("\n\t\t\t" +newSeg.vert[i]); } //if(sv==0){ // Vector3 startDirection = Quaternion.Euler(new Vector3(0F, newSeg.start*Mathf.Rad2Deg, 0F)) * Vector3.right; // Vector3 endDirection = Quaternion.Euler(new Vector3(0F, newSeg.end*Mathf.Rad2Deg, 0F)) * Vector3.right; // Debug.DrawRay(drawOrigin,startDirection*3F,Color.blue,0F,false); // Debug.DrawRay(drawOrigin,endDirection*3F,Color.yellow,0F,false); //Debug.DrawRay(drawOrigin,startDirection*newSeg.segmin,Color.blue,0F,false); //Debug.DrawRay(drawOrigin,endDirection*newSeg.segmax,Color.yellow,0F,false); //} } //create new Segment newSeg = new Segment(); newSeg.vert = new List<Vector3>(); newSeg.segmin = 6500F; newSeg.segmax = 0F; } lastVisible = false; } } } segments.Sort((segment2, segment1) => (segment1.start.CompareTo(segment2.start)));//sort segments by the angle of the start vertex //string segmentList = ""; //for(int i=0; i < segments.Count; i++){ // segmentList += "start\t"+segments[i].start+"\n"; //} //Debug.Log(segmentList); /*if(showSegStats){ segmentCount = segments.Count; }*/ }
//NOT NEEDED YET, CONSIDER REMOVAL: JUST USE: Renderer.isVisible maybe //↘EDIT: no if 2 Cameras! we need to have area since objects between 2 cameras could get ignored //gather relevant Polygons, check the polygon position increased by its radius if it is near our relevant radius // ↘viewport may be rotateable, for convenience we check if in radius not if within a maybe rotated rectangular area private void GatherPolygons(ref BottomPolygon[] polys, Vector3 position, float radius) { List<BottomPolygon> relevantPolygons = new List<BottomPolygon>(); for(int iP =0; iP<polys.Length;iP++){ //Vector3.magnitude or Distance is slower than sqrMag if( ((polys[iP].position-position).sqrMagnitude - polys[iP].relevantRadius*polys[iP].relevantRadius) < radius*radius){ //if( Vector3.Distance(polys[i].position, position) < radius){ //relevantPolygons.Add(ref polys[iP]); relevantPolygons.Add(polys[iP]); } } }
private void DrawVisibleFaces(ref BottomPolygon[] poly) { for(int ip = 0; ip < poly.Length; ip++){ //polygon for(int iv = 0; iv < poly[ip].vertices.Length; iv++){ //vertices of the polygon int nv = (iv+1)%poly[ip].vertices.Length; //next vertex Vector3 vertexDirection = poly[ip].vertices[iv]-poly[ip].vertices[nv]; Vector3 sourceDirection = source.position-poly[ip].vertices[iv]; if(!invisibleFaces){ if( AngleDir(vertexDirection,sourceDirection,Vector3.up)<0F ){ Debug.DrawLine(poly[ip].vertices[iv], poly[ip].vertices[nv], Color.white,0F,false); } }else{ if( AngleDir(vertexDirection,sourceDirection,Vector3.up)>0F ){ Debug.DrawLine(poly[ip].vertices[iv], poly[ip].vertices[nv], Color.white,0F,false); } } } } }
private void RefreshRandPolygons(ref BottomPolygon[] polyArr) { //check Order if(checkCCW){ for(int i = 0; i < polyArr.Length; i++){ polyArr[i].vertices = FixCCWOrder(polyArr[i].vertices); polyArr[i].include = true; CalculateCenterAndRadius(ref polyArr[i]); } } }
//costum inclusion check for a game that uses splitscreen cameras //if the 2 cameras would be far apart the objects in between the 2 viewport rectangles would not be into the calculation (renderer.isVisible would be false) //so i include only those objects which extents lie inside the rectangle strip between the 2 viewports private void CheckInclusionPerRectangle(ref BottomPolygon[] polys) { //future }
//average of all vertices does not need to be accurate, only not to small private void CalculateCenterAndRadius(ref BottomPolygon polygon) { float minX = 9000F; float maxX = -9000F; float minY = 9000F; float maxY = -9000F; for(int i=0; i< polygon.vertices.Length; i++){ if(polygon.vertices[i].x<minX) { minX = polygon.vertices[i].x; } if(polygon.vertices[i].x>maxX) { maxX = polygon.vertices[i].x; } if(polygon.vertices[i].z<minY) { minY = polygon.vertices[i].z; } if(polygon.vertices[i].z>maxY) { maxY = polygon.vertices[i].z; } } polygon.position = new Vector3((minX+maxX)*0.5F,0F,(minY+maxY)*0.5F); // center of max values polygon.relevantRadius = (Mathf.Sqrt( (maxX-minX)*(maxX-minX)+(maxY-minY)*(maxY-minY) ))/2F;// perimeter Debug.DrawLine(new Vector3(minX,0F,minY), new Vector3(minX,0F,maxY), Color.blue, 1.5F, false); Debug.DrawLine(new Vector3(minX,0F,maxY), new Vector3(maxX,0F,maxY), Color.blue, 1.5F, false); Debug.DrawLine(new Vector3(maxX,0F,maxY), new Vector3(maxX,0F,minY), Color.blue, 1.5F, false); Debug.DrawLine(new Vector3(maxX,0F,minY), new Vector3(minX,0F,minY), Color.blue, 1.5F, false); Debug.DrawLine( new Vector3(polygon.position.x-polygon.relevantRadius, 0F, polygon.position.z), new Vector3(polygon.position.x+polygon.relevantRadius, 0F, polygon.position.z), Color.cyan, 1.5F, false); Debug.DrawLine( new Vector3(polygon.position.x, 0F, polygon.position.z-polygon.relevantRadius), new Vector3(polygon.position.x, 0F, polygon.position.z+polygon.relevantRadius), Color.cyan, 1.5F, false); }
private void CheckInclusionPerRenderer(ref BottomPolygon[] polys) { for(int iP =0; iP<polys.Length;iP++){ if( polys[iP].renderer.isVisible){ polys[iP].include = true; }else{ polys[iP].include = false; } } }
private void ExtractPolygon(Vector3[] vertices, int[] triangles, ref BottomPolygon poly) { //definitions used (see http://www.geosensor.net/papers/duckham08.PR.pdf) //→A triangulation ∆ is a combinatorial map which has the property that every edge in a set of edges belongs to either one or two triangle s //→Aboundary edge of ∆ is an edge that belongs to exactly one triangle in ∆. //for simple polygons(edges do not cross themselfes) which we are dealing with, //the outline polygon consists out of the edges that appear only in one triangle: //earlyOutTest //poly.vertices = vertices; return; List <int[]> allEdges = new List <int[]>(); //list of 2 integers each representing the index of a vertex List <int[]> unsortedBE = new List <int[]>(); //unsortedBoundaryEdges List <int[]> boundaryEdges = new List <int[]>(); //sorted outer edges List <Vector3> boundaryVertices = new List <Vector3>(); //the vertices of the polygon in ccw order, this is what we need! //get all edges for (int it = 0; it < triangles.Length;) //for all triangles, add their adges to the list { allEdges.Add(new int[2] { triangles[it + 0], triangles[it + 1] }); //edge1 allEdges.Add(new int[2] { triangles[it + 1], triangles[it + 2] }); //edge2 allEdges.Add(new int[2] { triangles[it + 2], triangles[it + 0] }); //edge3 it += 3; } //Debug.Log("Edges:"+allEdges.Count); //DROP all edges that appear in more than one triangle for (int iT = 0; iT < allEdges.Count; iT++) //for each edge { int o = iT % 3; //offset to find the edges that belong to the same triangle bool addEdge = true; for (int iC = 0; iC < allEdges.Count; iC++) //compare loop, check all other edges { if (!((iT + 0 - o) == iC || (iT + 1 - o) == iC || (iT + 2 - o) == iC)) //except edges of current triangle from check { if ((allEdges[iT][0] == allEdges[iC][0] && allEdges[iT][1] == allEdges[iC][1]) || (allEdges[iT][0] == allEdges[iC][1] && allEdges[iT][1] == allEdges[iC][0])) { addEdge = false; break; } } } //if this edge has not appeared twice we can add it to our boundary edge List if (addEdge) { unsortedBE.Add(allEdges[iT]); } } //Debug.Log("Edges:"+unsortedBE.Count); //SORT unsortedBE, no edge will be dropped now, indices of each edge may be swapped //→ unsorted List: // edge1 edge2 edge3 edge4 // [4][2] [0][1] [1][4] [0][2] //→ sorted List: // edge1 edge2(4) edge3(2) edge4(3) // [4][2] [2][0] [0][1] [1][4] //add first edge to start: boundaryEdges.Add(unsortedBE[0]); int failsave = 100; //if bottomplane is faulty for (int iList = 1; iList < unsortedBE.Count;) //compare loop, one edge has to match each run (closed edge loop) { for (int iC = 1; iC < unsortedBE.Count; iC++) //check all edges but the first (already added) //check if last index matches with another index, then add it to the sorted list //Debug.Log(boundaryEdges[iList-1][1] +"|"+ unsortedBE[iC][0]); { if (boundaryEdges[iList - 1][1] == unsortedBE[iC][0]) //common vertex on compare-edge[0], add! { boundaryEdges.Add(new int[2] { unsortedBE[iC][0], unsortedBE[iC][1] }); iList++; } else if (boundaryEdges[iList - 1][1] == unsortedBE[iC][1]) //common vertex on compare-edge[1], add swapped! { boundaryEdges.Add(new int[2] { unsortedBE[iC][1], unsortedBE[iC][0] }); iList++; } } if (failsave < 1) { Debug.Log("Aborted Loop, bottomplane faulty!"); break; } failsave--; } //Finally! generate the vertices of the polygon out of the sorted list foreach (int[] intArr in boundaryEdges) { //just add one side([0] or [1]) of each element in the sorted List and we have the vertices in right order boundaryVertices.Add(vertices[intArr[0]]); } if (checkCCW) { FixCCWOrder(ref boundaryVertices); } //put this in boundaryVertices assignment if always needed if (clampY) { for (int iV = 0; iV < boundaryVertices.Count; iV++) { boundaryVertices[iV] = new Vector3(boundaryVertices[iV].x, 0F, boundaryVertices[iV].z); } } poly.vertices = boundaryVertices.ToArray(); }
private void ClearRandomPolygons(ref BottomPolygon[] polyArr) { polyArr = new BottomPolygon[0]; }
//just to see the order of the vertices, different colors to see order private void DrawLineToVertices(ref BottomPolygon[] poly) { for(int ip = 0; ip < poly.Length; ip++){ //polygon for(int iv = 0; iv < poly[ip].vertices.Length; iv++){ //vertices of the polygon Color color = new Color(0F,0F,1F,0.2F); if(iv == 0){color = new Color(0F,0.5F,1F,1F);} if(iv == 1){color = new Color(0F,0.5F,1F,0.5F);} Debug.DrawLine(drawOrigin, poly[ip].vertices[iv], color, 0F, false); } } }