// indices arrays are optional - if null is passed the index will be 0, 1, 2... up to values array length. // this is done to avoid allocating a separate array just to pass linear indices internal static UVTransform CalculateDelta(IList <Vector2> src, IList <int> srcIndices, IList <Vector2> dst, IList <int> dstIndices) { // rotate to match target points by comparing the angle between old UV and new auto projection Vector2 dstAngle = dst[GetIndex(dstIndices, 1)] - dst[GetIndex(dstIndices, 0)]; Vector2 srcAngle = src[GetIndex(srcIndices, 1)] - src[GetIndex(srcIndices, 0)]; float rotation = Vector2.Angle(dstAngle, srcAngle); if (Vector2.Dot(Vector2.Perpendicular(dstAngle), srcAngle) < 0) { rotation = 360f - rotation; } Vector2 dstCenter = dstIndices == null?Bounds2D.Center(dst) : Bounds2D.Center(dst, dstIndices); // inverse the rotation to get an axis-aligned scale Vector2 dstSize = GetRotatedSize(dst, dstIndices, dstCenter, -rotation); var srcBounds = srcIndices == null ? new Bounds2D(src) : new Bounds2D(src, srcIndices); return(new UVTransform() { translation = dstCenter - srcBounds.center, rotation = rotation, scale = dstSize.DivideBy(srcBounds.size) }); }
public MeshAndTextures(ProBuilderMesh mesh, PivotPoint pivot, HandleOrientation orientation) : base(mesh, pivot, orientation, k_CollectCoincidentVertices) { m_Textures = new List <Vector4>(); mesh.GetUVs(k_TextureChannel, m_Textures); m_Origins = new List <Vector4>(m_Textures); preApplyMatrix = Matrix4x4.Translate(-Bounds2D.Center(m_Origins, mesh.selectedIndexesInternal)); }
/// <summary> /// Reset the AutoUnwrapParameters of a set of faces to best match their current UV coordinates. /// </summary> /// <remarks> /// Auto UVs do not support distortion, so this conversion process cannot be loss-less. However as long as there /// is minimal skewing the results are usually very close. /// </remarks> internal static void SetAutoAndAlignUnwrapParamsToUVs(ProBuilderMesh mesh, IEnumerable <Face> facesToConvert) { var origins = mesh.textures.ToArray(); var faces = facesToConvert as Face[] ?? facesToConvert.ToArray(); foreach (var face in faces) { face.uv = AutoUnwrapSettings.defaultAutoUnwrapSettings; face.elementGroup = -1; face.textureGroup = -1; face.manualUV = false; } mesh.RefreshUV(faces); var textures = mesh.texturesInternal; foreach (var face in faces) { var indices = face.indexesInternal; var distinct = face.distinctIndexesInternal; // rotate to match target points by comparing the angle between old UV and new auto projection Vector2 dstAngle = origins[indices[1]] - origins[indices[0]]; Vector2 srcAngle = textures[indices[1]] - textures[indices[0]]; float rotation = Vector2.Angle(dstAngle, srcAngle); if (Vector2.Dot(Vector2.Perpendicular(dstAngle), srcAngle) < 0) { rotation = 360f - rotation; } // inverse the rotation to get an axis-aligned scale Vector2 dstCenter = Bounds2D.Center(origins, indices); for (int i = 0, c = distinct.Length; i < c; i++) { origins[distinct[i]] = origins[distinct[i]].RotateAroundPoint(dstCenter, -rotation); } var dstBounds = new Bounds2D(origins, indices); var srcBounds = new Bounds2D(textures, indices); Vector2 scale = dstBounds.size.DivideBy(srcBounds.size); Vector2 translation = dstCenter - srcBounds.center; var uv = face.uv; uv.offset = -translation; uv.rotation = rotation; uv.scale = scale; face.uv = uv; } mesh.RefreshUV(faces); }
public void SetManualFaceToAuto_MatchesOriginalUVs( [ValueSource("AutoUVOffsetParameters")] Vector2 offset, [ValueSource("AutoUVRotationParameters")] float rotation, [ValueSource("AutoUVScaleParameters")] Vector2 scale) { var unwrap = face.uv; unwrap.offset = offset; unwrap.rotation = rotation; unwrap.scale = scale; face.uv = unwrap; // Verify that UV settings have actually been applied Assume.That(face.uv.offset, Is.EqualTo(offset)); Assume.That(face.uv.rotation, Is.EqualTo(rotation)); Assume.That(face.uv.scale, Is.EqualTo(scale)); Assume.That(face.manualUV, Is.EqualTo(false)); mesh.Refresh(RefreshMask.UV); // Verify that the UVs are in the correct place Assume.That(GetEdgeRotation(mesh, verticalEdge), Is.EqualTo(rotation).Within(.1f)); Assume.That(GetEdgeScale(mesh, verticalEdge), Is.EqualTo(scale.y).Within(.1f)); Assume.That(GetEdgeScale(mesh, horizontalEdge), Is.EqualTo(scale.x).Within(.1f)); // Offset is flipped in code for legacy reasons var center = Bounds2D.Center(mesh.texturesInternal, face.distinctIndexesInternal); Assume.That(center.x, Is.EqualTo(-offset.x).Within(.1f)); Assume.That(center.y, Is.EqualTo(-offset.y).Within(.1f)); face.uv = AutoUnwrapSettings.defaultAutoUnwrapSettings; face.manualUV = true; // Verify that UV settings have been reset Assume.That(face.uv.offset, Is.EqualTo(new Vector2(0f, 0f))); Assume.That(face.uv.rotation, Is.EqualTo(0f)); Assume.That(face.uv.scale, Is.EqualTo(new Vector2(1f, 1f))); Assume.That(face.manualUV, Is.EqualTo(true)); // This sets the manualFlag to false, sets the AutoUnwrap settings, and rebuilds UVs UvUnwrapping.SetAutoAndAlignUnwrapParamsToUVs(mesh, new [] { face }); Assert.That(face.uv.offset.x, Is.EqualTo(offset.x).Within(.1f)); Assert.That(face.uv.offset.y, Is.EqualTo(offset.y).Within(.1f)); Assert.That(face.uv.rotation, Is.EqualTo(rotation).Within(.1f)); Assert.That(face.uv.scale.x, Is.EqualTo(scale.x).Within(.1f)); Assert.That(face.uv.scale.y, Is.EqualTo(scale.y).Within(.1f)); Assert.That(face.manualUV, Is.EqualTo(false)); }
/** * 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 Transform2D MatchCoordinates(Vector2[] points, Vector2[] target) { int length = points.Length < target.Length ? points.Length : target.Length; Bounds2D t_bounds = new Bounds2D(target, length); // only match the bounds of known matching points // move points to the center of target Vector2 translation = t_bounds.center - 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(Vector2.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 Bounds2D p_bounds = new 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 Transform2D(translation, angle, scale)); }