Skip to content

Commit

Permalink
Decal Scaling (#9)
Browse files Browse the repository at this point in the history
* Apply lossy scale of a Decal Projector transform to handles and rendering in addition to the size. Doesn't support non-positive scale yet.

* Support non-positive scale by Decal Projector handles.

* Workaround for rendering of a Decal Projector with flipped scale.

* Apply scale to a Decal Projector position offset to support scaling along the Z axis.

* Fixed Decal Projector Angle Fade for scale with negative Z.

* Cleaned up Decal Projector handles drawing.
  • Loading branch information
AndrewSaraevUnity authored and pastasfuture committed Aug 19, 2021
1 parent cb17dc6 commit a2846e9
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@ void DrawHandles()

if (editMode == k_EditShapePreservingUV || editMode == k_EditShapeWithoutPreservingUV)
{
using (new Handles.DrawingScope(Color.white, Matrix4x4.TRS(decalProjector.transform.position, decalProjector.transform.rotation, Vector3.one)))
Vector3 scale = decalProjector.transform.lossyScale;
using (new Handles.DrawingScope(Color.white, Matrix4x4.TRS(decalProjector.transform.position, decalProjector.transform.rotation, scale)))
{
bool needToRefreshDecalProjector = false;

Expand All @@ -223,8 +224,26 @@ void DrawHandles()
// Adjust decal transform if handle changed.
Undo.RecordObject(decalProjector, "Decal Projector Change");

decalProjector.size = handle.size;
decalProjector.offset = handle.center;
// Preserve serialized state for axes with scale 0.
Vector3 newSize = decalProjector.size;
Vector3 newOffset = decalProjector.offset;
if (scale.x != 0f)
{
newSize.x = handle.size.x;
newOffset.x = handle.center.x;
}
if (scale.y != 0f)
{
newSize.y = handle.size.y;
newOffset.y = handle.center.y;
}
if (scale.z != 0f)
{
newSize.z = handle.size.z;
newOffset.z = handle.center.z;
}
decalProjector.size = newSize;
decalProjector.offset = newOffset;

Vector3 boundsSizeCurrentOS = handle.size;
Vector3 boundsMinCurrentOS = handle.size * -0.5f + handle.center;
Expand All @@ -233,14 +252,19 @@ void DrawHandles()
{
// Treat decal projector bounds as a crop tool, rather than a scale tool.
// Compute a new uv scale and bias terms to pin decal projection pixels in world space, irrespective of projector bounds.
// Preserve serialized state for axes with scale 0.
Vector2 uvScale = decalProjector.uvScale;
uvScale.x *= Mathf.Max(1e-5f, boundsSizeCurrentOS.x) / Mathf.Max(1e-5f, boundsSizePreviousOS.x);
uvScale.y *= Mathf.Max(1e-5f, boundsSizeCurrentOS.y) / Mathf.Max(1e-5f, boundsSizePreviousOS.y);
if (scale.x != 0f)
uvScale.x *= Mathf.Max(1e-5f, boundsSizeCurrentOS.x) / Mathf.Max(1e-5f, boundsSizePreviousOS.x);
if (scale.y != 0f)
uvScale.y *= Mathf.Max(1e-5f, boundsSizeCurrentOS.y) / Mathf.Max(1e-5f, boundsSizePreviousOS.y);
decalProjector.uvScale = uvScale;

Vector2 uvBias = decalProjector.uvBias;
uvBias.x += (boundsMinCurrentOS.x - boundsMinPreviousOS.x) / Mathf.Max(1e-5f, boundsSizeCurrentOS.x) * decalProjector.uvScale.x;
uvBias.y += (boundsMinCurrentOS.y - boundsMinPreviousOS.y) / Mathf.Max(1e-5f, boundsSizeCurrentOS.y) * decalProjector.uvScale.y;
if (scale.x != 0f)
uvBias.x += (boundsMinCurrentOS.x - boundsMinPreviousOS.x) / Mathf.Max(1e-5f, boundsSizeCurrentOS.x) * decalProjector.uvScale.x;
if (scale.y != 0f)
uvBias.y += (boundsMinCurrentOS.y - boundsMinPreviousOS.y) / Mathf.Max(1e-5f, boundsSizeCurrentOS.y) * decalProjector.uvScale.y;
decalProjector.uvBias = uvBias;
}

Expand All @@ -263,7 +287,7 @@ void DrawHandles()
// Re-center the transform to the center of the decal projector bounds,
// while maintaining the world-space coordinates of the decal projector boundings vertices.
// Center of the decal projector is not the same of the HierarchicalBox as we want it to be on the z face as lights
decalProjector.transform.Translate(decalProjector.offset + new Vector3(0f, 0f, handle.size.z * -0.5f), Space.Self);
decalProjector.transform.Translate(Vector3.Scale(decalProjector.offset + new Vector3(0f, 0f, handle.size.z * -0.5f), scale), Space.Self);

decalProjector.offset = new Vector3(0f, 0f, handle.size.z * 0.5f);
if (PrefabUtility.IsPartOfNonAssetPrefabInstance(decalProjector))
Expand All @@ -290,18 +314,22 @@ void DrawHandles()
[DrawGizmo(GizmoType.Selected | GizmoType.Active)]
static void DrawGizmosSelected(DecalProjector decalProjector, GizmoType gizmoType)
{
//draw them scale independent
//draw them with scale applied to size and offset instead of TRS to keep proportions of the arrow and bold lines.
using (new Handles.DrawingScope(Color.white, Matrix4x4.TRS(decalProjector.transform.position, decalProjector.transform.rotation, Vector3.one)))
{
handle.center = decalProjector.offset;
handle.size = decalProjector.size;
Vector3 scale = decalProjector.transform.lossyScale;
Vector3 scaledOffset = Vector3.Scale(decalProjector.offset, scale);
Vector3 scaledSize = Vector3.Scale(decalProjector.size, scale);

handle.center = scaledOffset;
handle.size = scaledSize;
bool inEditMode = editMode == k_EditShapePreservingUV || editMode == k_EditShapeWithoutPreservingUV;
handle.DrawHull(inEditMode);

Quaternion arrowRotation = Quaternion.LookRotation(Vector3.down, Vector3.right);
float arrowSize = decalProjector.size.z * 0.25f;
Vector3 pivot = decalProjector.offset;
Vector3 projectedPivot = pivot + decalProjector.size.z * 0.5f * Vector3.back;
float arrowSize = scaledSize.z * 0.25f;
Vector3 pivot = scaledOffset;
Vector3 projectedPivot = pivot + scaledSize.z * 0.5f * Vector3.back;
Handles.ArrowHandleCap(0, projectedPivot, Quaternion.identity, arrowSize, EventType.Repaint);

//[TODO: add editable pivot. Uncomment this when ready]
Expand All @@ -315,13 +343,13 @@ static void DrawGizmosSelected(DecalProjector decalProjector, GizmoType gizmoTyp
//Handles.DrawLine(projectedPivot, projectedPivot + decalProjector.m_Size.z * 0.5f * Vector3.forward);

//draw UV and bolder edges
using (new Handles.DrawingScope(Matrix4x4.TRS(decalProjector.transform.position - decalProjector.transform.rotation * (decalProjector.size * 0.5f + decalProjector.offset.z * Vector3.back), decalProjector.transform.rotation, Vector3.one)))
using (new Handles.DrawingScope(Matrix4x4.TRS(decalProjector.transform.position - decalProjector.transform.rotation * (scaledSize * 0.5f + scaledOffset.z * Vector3.back), decalProjector.transform.rotation, Vector3.one)))
{
if (inEditMode)
{
Vector2 size = new Vector2(
(decalProjector.uvScale.x > 100000 || decalProjector.uvScale.x < -100000 ? 0f : 1f / decalProjector.uvScale.x) * decalProjector.size.x,
(decalProjector.uvScale.y > 100000 || decalProjector.uvScale.y < -100000 ? 0f : 1f / decalProjector.uvScale.y) * decalProjector.size.y
(decalProjector.uvScale.x > 100000 || decalProjector.uvScale.x < -100000 ? 0f : 1f / decalProjector.uvScale.x) * scaledSize.x,
(decalProjector.uvScale.y > 100000 || decalProjector.uvScale.y < -100000 ? 0f : 1f / decalProjector.uvScale.y) * scaledSize.y
);
Vector2 start = (Vector2)projectedPivot - new Vector2(decalProjector.uvBias.x * size.x, decalProjector.uvBias.y * size.y);
Handles.DrawDottedLines(
Expand All @@ -335,7 +363,7 @@ static void DrawGizmosSelected(DecalProjector decalProjector, GizmoType gizmoTyp
5f);
}

Vector2 halfSize = decalProjector.size * .5f;
Vector2 halfSize = scaledSize * .5f;
Vector2 halfSize2 = new Vector2(halfSize.x, -halfSize.y);
Vector2 center = (Vector2)projectedPivot + halfSize;
Handles.DrawLine(center - halfSize, center - halfSize2, 3f);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace UnityEngine.Rendering.HighDefinition
public partial class DecalProjector : MonoBehaviour
{
internal static readonly Quaternion k_MinusYtoZRotation = Quaternion.Euler(-90, 0, 0);
static readonly Quaternion k_YtoZRotation = Quaternion.Euler(90, 0, 0);

[SerializeField]
private Material m_Material = null;
Expand Down Expand Up @@ -239,17 +240,63 @@ public float fadeFactor
private DecalSystem.DecalHandle m_Handle = null;


/// <summary>current rotation in a way the DecalSystem will be able to use it</summary>
internal Quaternion rotation => transform.rotation * k_MinusYtoZRotation;
/// <summary>current position in a way the DecalSystem will be able to use it</summary>
internal Vector3 position => transform.position;
/// <summary>current size in a way the DecalSystem will be able to use it</summary>
internal Vector3 decalSize => new Vector3(m_Size.x, m_Size.z, m_Size.y);
/// <summary>current size in a way the DecalSystem will be able to use it</summary>
internal Vector3 decalOffset => new Vector3(m_Offset.x, -m_Offset.z, m_Offset.y);
/// <summary>current uv parameters in a way the DecalSystem will be able to use it</summary>
internal Vector4 uvScaleBias => new Vector4(m_UVScale.x, m_UVScale.y, m_UVBias.x, m_UVBias.y);

/// <summary>current rotation in a way the DecalSystem will be able to use it</summary>
internal Quaternion rotation
{
get
{
// If Z-scale is negative we rotate decal differently to have correct forward direction for Angle Fade.
return transform.rotation * (transform.lossyScale.z >= 0f ? k_MinusYtoZRotation : k_YtoZRotation);
}
}

/// <summary>current size in a way the DecalSystem will be able to use it</summary>
internal Vector3 decalSize
{
get
{
Vector3 scale = transform.lossyScale;

// If Z-scale is negative the forward direction for rendering will be fixed by rotation,
// so we need to flip the scale of the affected axes back.
// The final sign of Z will depend on the other two axes, so we actually need to fix only Y here.
if (scale.z < 0f)
scale.y *= -1f;

// Flipped projector (with 1 or 3 negative components of scale) would be invisible.
// In this case we additionally flip Z.
bool flipped = scale.x < 0f ^ scale.y < 0f ^ scale.z < 0f;
if (flipped)
scale.z *= -1f;

return new Vector3(m_Size.x * scale.x, m_Size.z * scale.z, m_Size.y * scale.y);
}
}

/// <summary>current offset in a way the DecalSystem will be able to use it</summary>
internal Vector3 decalOffset
{
get
{
Vector3 scale = transform.lossyScale;

// If Z-scale is negative the forward direction for rendering will be fixed by rotation,
// so we need to flip the scale of the affected axes back.
if (scale.z < 0f)
{
scale.y *= -1f;
scale.z *= -1f;
}

return new Vector3(m_Offset.x * scale.x, -m_Offset.z * scale.z, m_Offset.y * scale.y);
}
}

internal DecalSystem.DecalHandle Handle
{
get
Expand Down

0 comments on commit a2846e9

Please sign in to comment.