Files
Dino_Love_Simulation/Assets/02_Scripts/Interaction/DrawablePaper.cs

136 lines
5.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using UnityEngine.Events;
// 펜(PenTip)으로 그릴 수 있는 종이.
// 시작 시 RenderTexture를 만들어 원본 텍스처(계약서 인쇄물)를 복사해 넣고,
// 렌더러의 텍스처를 그 RT로 교체한다. 이후 펜이 UV 좌표로 스탬프를 찍는다.
//
// 요구 사항:
// - 이 오브젝트(또는 콜라이더)가 MeshCollider여야 한다 — RaycastHit.textureCoord는 MeshCollider에서만 나온다
// - 머티리얼은 DinoLove/Paper 셰이더 기준 (_FrontTex). 다른 셰이더면 프로퍼티 이름만 맞춰주면 됨
public class DrawablePaper : MonoBehaviour
{
[Header("Canvas")]
[SerializeField] private Renderer _renderer; // 비우면 자기 자신의 Renderer
[SerializeField] private string _texturePropertyName = "_FrontTex";
[Tooltip("그리기 전 원본(계약서 인쇄물). 비우면 머티리얼의 현재 텍스처, 그것도 없으면 흰 종이")]
[SerializeField] private Texture _sourceTexture;
[SerializeField] private int _textureSize = 1024;
[Header("서명란 판정")]
[Tooltip("서명으로 인정할 UV 영역 (좌하단 0,0 ~ 우상단 1,1)")]
[SerializeField] private Rect _signatureZone = new Rect(0.55f, 0.05f, 0.4f, 0.2f);
[Tooltip("서명란 안에 스탬프가 이만큼 찍히면 서명 완료로 판정")]
[SerializeField] private int _requiredStamps = 80;
[Tooltip("서명이 완료되는 순간 1회 호출 (계약 진행 이벤트 연결용)")]
public UnityEvent OnSigned;
public bool IsSigned { get; private set; }
private RenderTexture _rt;
private int _zoneStamps;
private static Texture2D s_softBrush; // 절차 생성 소프트 원 브러시 (전 종이 공유)
private void Awake()
{
if (_renderer == null) _renderer = GetComponent<Renderer>();
_rt = new RenderTexture(_textureSize, _textureSize, 0, RenderTextureFormat.ARGB32);
_rt.Create();
// 원본 텍스처로 초기화 (없으면 흰 종이)
var src = _sourceTexture != null ? _sourceTexture : _renderer.material.GetTexture(_texturePropertyName);
if (src != null)
{
Graphics.Blit(src, _rt);
}
else
{
var prev = RenderTexture.active;
RenderTexture.active = _rt;
GL.Clear(false, true, Color.white);
RenderTexture.active = prev;
}
_renderer.material.SetTexture(_texturePropertyName, _rt);
}
private void OnDestroy()
{
if (_rt != null)
{
_rt.Release();
Destroy(_rt);
}
}
// 두 UV 사이를 브러시 간격으로 보간해서 연속된 선으로 찍는다 (프레임 사이 점선 방지)
public void DrawStroke(Vector2 fromUv, Vector2 toUv, Color ink, float brushSize)
{
float dist = Vector2.Distance(fromUv, toUv);
int steps = Mathf.Max(1, Mathf.CeilToInt(dist / Mathf.Max(brushSize * 0.25f, 0.0005f)));
for (int i = 1; i <= steps; i++)
StampAt(Vector2.Lerp(fromUv, toUv, (float)i / steps), ink, brushSize);
}
// UV 위치에 브러시 한 번 찍기. brushSize는 종이 가로 대비 지름 비율 (0.02 = 2%)
public void StampAt(Vector2 uv, Color ink, float brushSize)
{
if (_rt == null) return;
var prev = RenderTexture.active;
RenderTexture.active = _rt;
GL.PushMatrix();
GL.LoadPixelMatrix(0, _rt.width, _rt.height, 0); // 좌상단 원점 픽셀 좌표계
float d = brushSize * _rt.width; // 지름(픽셀)
float px = uv.x * _rt.width - d * 0.5f;
float py = (1f - uv.y) * _rt.height - d * 0.5f; // DrawTexture는 y가 위→아래
// DrawTexture의 색 변조는 0.5가 중립(×2 곱)이라 절반으로 넘긴다
Graphics.DrawTexture(new Rect(px, py, d, d), GetBrush(), new Rect(0, 0, 1, 1), 0, 0, 0, 0, ink * 0.5f);
GL.PopMatrix();
RenderTexture.active = prev;
// 서명란 판정
if (!IsSigned && _signatureZone.Contains(uv))
{
_zoneStamps++;
if (_zoneStamps >= _requiredStamps)
{
IsSigned = true;
OnSigned?.Invoke();
}
}
}
// 가장자리로 갈수록 투명해지는 원형 브러시를 한 번만 생성
private static Texture2D GetBrush()
{
if (s_softBrush != null) return s_softBrush;
const int size = 64;
s_softBrush = new Texture2D(size, size, TextureFormat.RGBA32, false);
s_softBrush.hideFlags = HideFlags.HideAndDontSave;
var pixels = new Color32[size * size];
float half = size * 0.5f;
for (int y = 0; y < size; y++)
{
for (int x = 0; x < size; x++)
{
float r = Vector2.Distance(new Vector2(x + 0.5f, y + 0.5f), new Vector2(half, half)) / half;
// 중심은 진하게, 70% 지점부터 부드럽게 빠지는 잉크 느낌
float a = Mathf.Clamp01(1f - Mathf.InverseLerp(0.7f, 1f, r));
pixels[y * size + x] = new Color32(255, 255, 255, (byte)(a * 255f));
}
}
s_softBrush.SetPixels32(pixels);
s_softBrush.Apply();
return s_softBrush;
}
}