/** * Returns true if bounds overlap. */ public bool Intersects(pb_Bounds2D bounds) { Vector2 dist = this.center - bounds.center; Vector2 size = this.size + bounds.size; return(Mathf.Abs(dist.x) * 2f < size.x && Mathf.Abs(dist.y) * 2f < size.y); }
private static Vector2[] ApplyUVSettings(Vector2[] uvs, pb_UV uvSettings) { int len = uvs.Length; switch (uvSettings.fill) { case pb_UV.Fill.Tile: break; case pb_UV.Fill.Fit: uvs = NormalizeUVs(uvs); break; case pb_UV.Fill.Stretch: uvs = StretchUVs(uvs); break; } // if(uvSettings.justify != pb_UV.Justify.None) // uvs = JustifyUVs(uvs, uvSettings.justify); // Apply transform last, so that fill and justify don't override it. pb_Bounds2D bounds = new pb_Bounds2D(uvs); if (!uvSettings.useWorldSpace) { for (int i = 0; i < uvs.Length; i++) { uvs[i] -= (bounds.center - bounds.extents); } } bounds = new pb_Bounds2D(uvs); for (int i = 0; i < uvs.Length; i++) { uvs[i] = uvs[i].ScaleAroundPoint(bounds.center, uvSettings.scale); uvs[i] = uvs[i].RotateAroundPoint(bounds.center, uvSettings.rotation); } for (int i = 0; i < len; i++) { float u = uvs[i].x, v = uvs[i].y; if (uvSettings.flipU) { u = -u; } if (uvSettings.flipV) { v = -v; } if (!uvSettings.swapUV) { uvs[i] = new Vector2(u, v); } else { uvs[i] = new Vector2(v, u); } } bounds = new pb_Bounds2D(uvs); uvSettings.localPivot = bounds.center; // uvSettings.useWorldSpace ? bounds.center : bounds.extents; uvSettings.localSize = bounds.size; for (int i = 0; i < uvs.Length; i++) { uvs[i] -= uvSettings.offset; } return(uvs); }
public static bool GeneratePolygonCrosshatch(Vector2[] polygon, float scale, Color color, int lineSpacing, ref Texture2D texture) #endif { #if PB_DEBUG profiler.BeginSample("GeneratePolygonCrosshatch"); #endif pb_Bounds2D bounds = new pb_Bounds2D(polygon); Vector2 offset = bounds.center - bounds.extents; /// shift polygon to origin 0,0 for(int i = 0; i < polygon.Length; i++) { polygon[i] -= offset; polygon[i] *= scale; } bounds.center -= offset; bounds.size *= scale; int width = (int)(bounds.size.x); int height = (int)(bounds.size.y); if(width <= 0 || height <= 0) return false; #if PB_DEBUG profiler.BeginSample("Allocate Texture"); #endif if(texture == null) { texture = new Texture2D(width, height, TextureFormat.ARGB32, false); texture.filterMode = FilterMode.Point; texture.wrapMode = TextureWrapMode.Clamp; } else { if(texture.width != width || texture.height != height) texture.Resize(width, height, TextureFormat.ARGB32, false); } #if PB_DEBUG profiler.EndSample(); profiler.BeginSample("Fill Clear"); #endif Color[] colors = new Color[width*height]; List<int> intersects = new List<int>(); for(int i = 0; i < width*height; i++) colors[i] = Color.clear; #if PB_DEBUG profiler.EndSample(); #endif /** * Horizontal lines */ for(int h = 0; h < height/lineSpacing; h++) { int y = (h*lineSpacing); intersects.Clear(); #if PB_DEBUG profiler.BeginSample("Find Intersections"); #endif Vector2 start = new Vector2(bounds.center.x - bounds.size.x, y); Vector2 end = new Vector2(bounds.center.x + bounds.size.x, y); for(int i = 0; i < polygon.Length; i+=2) { Vector2 intersect = Vector2.zero; if( pb_Math.GetLineSegmentIntersect(polygon[i], polygon[i+1], start, end, ref intersect) ) intersects.Add((int)intersect.x); } intersects = intersects.Distinct().ToList(); intersects.Sort(); #if PB_DEBUG profiler.EndSample(); profiler.BeginSample("Fill Color"); #endif for(int i = 0; i < intersects.Count-1; i++) { // can't just use Dot product because we the winding order isn't consistent if( pb_Math.PointInPolygon(polygon, new Vector2(intersects[i]+2, y)) ) { for(int n = intersects[i]; n < intersects[i+1]; n++) { colors[ ((height-1)-y) * width + n] = color; } } } #if PB_DEBUG profiler.EndSample(); #endif } /** * Vertical lines */ if(lineSpacing > 1) { for(int w = 0; w < width/lineSpacing; w++) { int x = (w*lineSpacing); intersects.Clear(); Vector2 start = new Vector2(x, bounds.center.y - bounds.size.y); Vector2 end = new Vector2(x, bounds.center.y + bounds.size.y); for(int i = 0; i < polygon.Length; i+=2) { Vector2 intersect = Vector2.zero; if( pb_Math.GetLineSegmentIntersect(polygon[i], polygon[i+1], start, end, ref intersect) ) intersects.Add((int)intersect.y); } intersects = intersects.Distinct().ToList(); intersects.Sort(); for(int i = 0; i < intersects.Count-1; i++) { if(pb_Math.PointInPolygon(polygon, new Vector2(x, intersects[i]+2))) { for(int y = intersects[i]; y < intersects[i+1]; y++) { colors[ ((height-1)-y) * width + x] = color; } } } } } #if PB_DEBUG profiler.BeginSample("SetPixels"); #endif texture.SetPixels(colors); texture.Apply(false); #if PB_DEBUG profiler.EndSample(); profiler.EndSample(); #endif return true; }
/** * Attempts to translate, rotate, and scale @points to match @target as closely as possible. * Only points[0, target.Length] coordinates are used in the matching process - points[target.Length, points.Length] * are just along for the ride. */ public static pb_Transform2D MatchCoordinates(Vector2[] points, Vector2[] target) { int length = points.Length < target.Length ? points.Length : target.Length; pb_Bounds2D t_bounds = new pb_Bounds2D(target, length); // only match the bounds of known matching points // move points to the center of target Vector2 translation = t_bounds.center - pb_Bounds2D.Center(points, length); Vector2[] transformed = new Vector2[points.Length]; for(int i = 0; i < points.Length; i++) transformed[i] = points[i] + translation; // rotate to match target points Vector2 target_angle = target[1]-target[0], transform_angle = transformed[1]-transformed[0]; float angle = Vector2.Angle(target_angle, transform_angle); float dot = Vector2.Dot( pb_Math.Perpendicular(target_angle), transform_angle); if(dot < 0) angle = 360f - angle; for(int i = 0; i < points.Length; i++) transformed[i] = transformed[i].RotateAroundPoint(t_bounds.center, angle); // and lastly scale pb_Bounds2D p_bounds = new pb_Bounds2D(transformed, length); Vector2 scale = t_bounds.size.DivideBy(p_bounds.size); // for(int i = 0; i < points.Length; i++) // transformed[i] = transformed[i].ScaleAroundPoint(t_bounds.center, scale); return new pb_Transform2D(translation, angle, scale); }
/** * Internal because pb_Editor needs to call this sometimes. */ internal void OnFinishUVModification() { pb_Lightmapping.PopGIWorkflowMode(); modifyingUVs = false; if((tool == Tool.Rotate || tool == Tool.Scale) && userPivot) { selected_canvas_bounds = CanvasSelectionBounds(); SetHandlePosition(handlePosition_canvas, true); } if(mode == UVMode.Mixed || mode == UVMode.Auto) { pbUndo.RegisterCompleteObjectUndo(selection, (tool == Tool.Move ? "Translate UVs" : tool == Tool.Rotate ? "Rotate UVs" : "Scale UVs") ); foreach(pb_Object pb in selection) { if(pb.SelectedFaceCount > 0) { /** * Sort faces into texture groups for re-projection */ Dictionary<int, List<pb_Face>> textureGroups = new Dictionary<int, List<pb_Face>>(); int n = -2; foreach(pb_Face face in System.Array.FindAll(pb.SelectedFaces, x => !x.manualUV)) { if(textureGroups.ContainsKey(face.textureGroup)) textureGroups[face.textureGroup].Add(face); else textureGroups.Add( face.textureGroup > 0 ? face.textureGroup : n--, new List<pb_Face>() {face} ); } foreach(KeyValuePair<int, List<pb_Face>> kvp in textureGroups) { /** * Rotation - only applies to rotation tool */ if(tool == Tool.Rotate) { foreach(pb_Face face in kvp.Value) { if((face.uv.flipU ^ face.uv.flipV) ^ face.uv.swapUV) uvRotation = -uvRotation; face.uv.rotation += uvRotation; if(face.uv.rotation > 360f) face.uv.rotation = face.uv.rotation % 360f; if(face.uv.rotation < 0f) face.uv.rotation = 360f + (face.uv.rotation % 360f); } } /** * Scale is applied in real-time */ /** * Reproject because uv.localPivot needs to be accurate for this to work properly */ foreach(pb_Face face in kvp.Value) face.uv.offset = Vector2.zero; Vector3 nrm = Vector3.zero; foreach(pb_Face face in kvp.Value) { nrm += pb_Math.Normal( pb.vertices[face.indices[0]], pb.vertices[face.indices[1]], pb.vertices[face.indices[2]] ); } nrm /= (float)kvp.Value.Count; int[] tris = pb_Face.AllTriangles(kvp.Value).ToArray(); if(kvp.Value[0].uv.useWorldSpace) { pb.transform.TransformDirection(nrm); pb_UVUtility.PlanarMap( pb.transform.ToWorldSpace(pb.GetVertices(tris)), kvp.Value[0].uv, nrm ); } else { pb_UVUtility.PlanarMap( pb.GetVertices(tris), kvp.Value[0].uv, nrm ); } foreach(pb_Face face in kvp.Value) face.uv.localPivot = kvp.Value[0].uv.localPivot; /** * Translation - applies for every tool */ Vector2 handle = pb_Handle_Utility.GUIToUVPoint(handlePosition_canvas, uvGridSize); Vector2 cen = pb_Bounds2D.Center(pb.GetUVs(tris)); foreach(pb_Face face in kvp.Value) face.uv.offset = -((handle - face.uv.localPivot) - (handle-cen)); } } else { FlagSelectedFacesAsManual(pb); } } } else if(mode == UVMode.Manual) { foreach(pb_Object pb in selection) { if(pb.SelectedFaceIndices.Length > 0) { foreach(pb_Face face in pb.SelectedFaces) { face.textureGroup = -1; face.manualUV = true; } } else { FlagSelectedFacesAsManual(pb); } } } // Regenerate UV2s foreach(pb_Object pb in selection) { pb.ToMesh(); pb.Refresh(); pb.Optimize(); } }
/** * Refresh only the selected UV coordinates. */ void RefreshSelectedUVCoordinates() { for(int n = 0; n < selection.Length; n++) { Vector2[] uvs = GetUVs(selection[n], channel); foreach(int i in distinct_indices[n]) uvs_canvas_space[n][i] = pb_Handle_Utility.UVToGUIPoint(uvs[i], uvGridSize); } selected_canvas_bounds = CanvasSelectionBounds(); handlePosition_canvas = selected_canvas_bounds.center - handlePosition_offset; }
/** * If dragRect is null, the selected UV array will be derived using the selected ProBuilder faces. * If it ain't null, selected UVs will be set to the UV coordinates contained within the drag rect. */ void RefreshUVCoordinates(Rect? dragRect, bool isClick) { if(editor == null || selection == null) return; #if PB_DEBUG profiler.BeginSample("RefreshUVCoordinates"); #endif // Collect drawables uvs_canvas_space = new Vector2[selection.Length][]; // Convert dragrect from Unity GUI space to uv_gui_space pb_Bounds2D dragBounds = dragRect != null ? new pb_Bounds2D( GUIToCanvasPoint(((Rect)dragRect).center), new Vector2( ((Rect)dragRect).width, ((Rect)dragRect).height) / uvGraphScale ) : new pb_Bounds2D( Vector2.zero, Vector2.zero ); selectedUVCount = editor.selectedVertexCount; selectedFaceCount = editor.selectedFaceCount; // selectedEdgeCount = editor.selectedEdgeCount; for(int i = 0; i < selection.Length; i++) { pb_Object pb = selection[i]; Vector2[] mshUV = GetUVs(pb, channel); int len = mshUV.Length; uvs_canvas_space[i] = new Vector2[len]; for(int j = 0; j < len; j++) uvs_canvas_space[i][j] = pb_Handle_Utility.UVToGUIPoint(mshUV[j], uvGridSize); // this should probably be separate from RefreshUVCoordinates if(dragRect != null) { switch(selectionMode) { case SelectMode.Vertex: List<int> selectedTris = new List<int>(pb.SelectedTriangles); for(int j = 0; j < len; j++) { if( dragBounds.ContainsPoint( uvs_canvas_space[i][j] ) ) { int indx = selectedTris.IndexOf(j); if(indx > -1) selectedTris.RemoveAt(indx); else selectedTris.Add(j); // if this is a click, only do one thing per-click if(isClick) break; } } pb.SetSelectedTriangles(selectedTris.ToArray()); break; case SelectMode.Edge: List<pb_Edge> selectedEdges = new List<pb_Edge>(pb.SelectedEdges); for(int n = 0; n < pb.faces.Length; n++) { for(int p = 0; p < pb.faces[n].edges.Length; p++) { pb_Edge edge = pb.faces[n].edges[p]; if( dragBounds.IntersectsLineSegment( uvs_canvas_space[i][edge.x], uvs_canvas_space[i][edge.y]) ) { if(!selectedEdges.Contains(edge)) selectedEdges.Add( edge ); else selectedEdges.Remove( edge ); } } } pb.SetSelectedEdges(selectedEdges.ToArray()); break; /** * Check if any of the faces intersect with the mousedrag rect. */ case SelectMode.Face: HashSet<int> selectedFaces = new HashSet<int>(selection[i].SelectedFaceIndices); for(int n = 0; n < pb.faces.Length; n++) { // Vector2[] uvs = pbUtil.ValuesWithIndices(, pb.faces[n].distinctIndices); int[] distinctIndices = pb.faces[n].distinctIndices; bool allPointsContained = true; for(int t = 0; t < distinctIndices.Length; t++) { if( ! dragBounds.ContainsPoint(uvs_canvas_space[i][distinctIndices[t]]) ) { allPointsContained = false; break; } } // // if(dragBounds.Intersects(faceBounds)) // for(int t = 0; t < uvs.Length; t++) // { // if(!dragBounds.ContainsPoint(uvs[t])) // { // allPointsContained = false; // break; // } // } if(allPointsContained) { if( selectedFaces.Contains(n) ) selectedFaces.Remove(n); else selectedFaces.Add(n); } } selection[i].SetSelectedFaces(selectedFaces.ToArray()); break; } editor.UpdateSelection(false); SceneView.RepaintAll(); } } // figure out what the mode of selected faces is if(editor.selectedFaceCount > 0) { // @todo write a more effecient method for this List<bool> manual = new List<bool>(); for(int i = 0; i < selection.Length; i++) manual.AddRange( selection[i].SelectedFaces.Select(x => x.manualUV).ToList() ); int c = manual.Distinct().Count(); if(c > 1) mode = UVMode.Mixed; else if (c > 0) mode = manual[0] ? UVMode.Manual : UVMode.Auto; } else { mode = UVMode.Manual; } editor.GetFirstSelectedMaterial(ref preview_material); selected_canvas_bounds = CanvasSelectionBounds(); handlePosition_canvas = selected_canvas_bounds.center - handlePosition_offset; #if PB_DEBUG profiler.EndSample(); #endif }
/** * Returns true if bounds overlap. */ public bool Intersects(pb_Bounds2D bounds) { Vector2 dist = this.center - bounds.center; Vector2 size = this.size + bounds.size; return Mathf.Abs(dist.x) * 2f < size.x && Mathf.Abs(dist.y) * 2f < size.y; }
private static Vector2[] ApplyUVSettings(Vector2[] uvs, pb_UV uvSettings) { int len = uvs.Length; switch(uvSettings.fill) { case pb_UV.Fill.Tile: break; case pb_UV.Fill.Fit: uvs = NormalizeUVs(uvs); break; case pb_UV.Fill.Stretch: uvs = StretchUVs(uvs); break; } // if(uvSettings.justify != pb_UV.Justify.None) // uvs = JustifyUVs(uvs, uvSettings.justify); // Apply transform last, so that fill and justify don't override it. pb_Bounds2D bounds = new pb_Bounds2D(uvs); if(!uvSettings.useWorldSpace) for(int i = 0; i < uvs.Length; i++) uvs[i] -= (bounds.center - bounds.extents); bounds = new pb_Bounds2D(uvs); for(int i = 0; i < uvs.Length; i++) { uvs[i] = uvs[i].ScaleAroundPoint(bounds.center, uvSettings.scale); uvs[i] = uvs[i].RotateAroundPoint(bounds.center, uvSettings.rotation); } for(int i = 0; i < len; i++) { float u = uvs[i].x, v = uvs[i].y; if(uvSettings.flipU) u = -u; if(uvSettings.flipV) v = -v; if(!uvSettings.swapUV) uvs[i] = new Vector2(u, v); else uvs[i] = new Vector2(v, u); } bounds = new pb_Bounds2D(uvs); uvSettings.localPivot = bounds.center;// uvSettings.useWorldSpace ? bounds.center : bounds.extents; uvSettings.localSize = bounds.size; for(int i = 0; i < uvs.Length; i++) uvs[i] -= uvSettings.offset; return uvs; }