diff --git a/Assets/Brushes.meta b/Assets/Brushes.meta new file mode 100644 index 0000000..911aa51 --- /dev/null +++ b/Assets/Brushes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: be326f78a1100464994d2e0c795843e0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Brushes/Brushes.psb b/Assets/Brushes/Brushes.psb new file mode 100644 index 0000000..fc17a9d Binary files /dev/null and b/Assets/Brushes/Brushes.psb differ diff --git a/Assets/Brushes/Brushes.psb.meta b/Assets/Brushes/Brushes.psb.meta new file mode 100644 index 0000000..dcad72a --- /dev/null +++ b/Assets/Brushes/Brushes.psb.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0d435b29fdbd9d94596872e9a687c72f +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Brushes/BrushesHard.png b/Assets/Brushes/BrushesHard.png new file mode 100644 index 0000000..dc6e6d6 Binary files /dev/null and b/Assets/Brushes/BrushesHard.png differ diff --git a/Assets/Brushes/BrushesHard.png.meta b/Assets/Brushes/BrushesHard.png.meta new file mode 100644 index 0000000..5cc034e --- /dev/null +++ b/Assets/Brushes/BrushesHard.png.meta @@ -0,0 +1,117 @@ +fileFormatVersion: 2 +guid: d07c01c40cc7be54da0ff3ae52484cd2 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 0 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Brushes/BrushesSoft.png b/Assets/Brushes/BrushesSoft.png new file mode 100644 index 0000000..a77e6c2 Binary files /dev/null and b/Assets/Brushes/BrushesSoft.png differ diff --git a/Assets/Brushes/BrushesSoft.png.meta b/Assets/Brushes/BrushesSoft.png.meta new file mode 100644 index 0000000..307e6be --- /dev/null +++ b/Assets/Brushes/BrushesSoft.png.meta @@ -0,0 +1,117 @@ +fileFormatVersion: 2 +guid: ccc205272b051834eb39593f647c7bf1 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 0 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Prefabs/PaintHudPanel.prefab b/Assets/Prefabs/PaintHudPanel.prefab index d6bca44..12463b3 100644 --- a/Assets/Prefabs/PaintHudPanel.prefab +++ b/Assets/Prefabs/PaintHudPanel.prefab @@ -1,5 +1,80 @@ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: +--- !u!1 &326942557749094753 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6025592237405964704} + - component: {fileID: 5722523044830971982} + - component: {fileID: 8122138234825868302} + m_Layer: 0 + m_Name: Preview + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6025592237405964704 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 326942557749094753} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2694655898154754188} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -8, y: -8} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &5722523044830971982 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 326942557749094753} + m_CullTransparentMesh: 1 +--- !u!114 &8122138234825868302 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 326942557749094753} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 1 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!1 &358948636961454364 GameObject: m_ObjectHideFlags: 0 @@ -148,6 +223,143 @@ MonoBehaviour: m_FillOrigin: 0 m_UseSpriteMesh: 0 m_PixelsPerUnitMultiplier: 1 +--- !u!1 &645850171180752197 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4701518254420950362} + - component: {fileID: 8009184166825254532} + - component: {fileID: 929418628186076474} + m_Layer: 0 + m_Name: Label + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4701518254420950362 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 645850171180752197} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 5857890906983273014} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 16} + m_Pivot: {x: 0.5, y: 1} +--- !u!222 &8009184166825254532 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 645850171180752197} + m_CullTransparentMesh: 1 +--- !u!114 &929418628186076474 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 645850171180752197} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: Mask + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: 964ae5cd392ba08499b88de012ff8213, type: 2} + m_sharedMaterial: {fileID: 8234806113954670000, guid: 964ae5cd392ba08499b88de012ff8213, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 12 + m_fontSizeBase: 12 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_HorizontalAlignment: 1 + m_VerticalAlignment: 512 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_characterHorizontalScale: 1 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_TextWrappingMode: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 0 + m_ActiveFontFeatures: 6e72656b + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_EmojiFallbackSupport: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} --- !u!1 &692511385771061426 GameObject: m_ObjectHideFlags: 0 @@ -223,6 +435,65 @@ MonoBehaviour: m_FillOrigin: 0 m_UseSpriteMesh: 0 m_PixelsPerUnitMultiplier: 1 +--- !u!1 &778858144144351492 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5857890906983273014} + - component: {fileID: 2449389532931556151} + m_Layer: 0 + m_Name: MaskSelector + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &5857890906983273014 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 778858144144351492} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 4701518254420950362} + - {fileID: 5507289864410394337} + - {fileID: 2694655898154754188} + m_Father: {fileID: 3665381071208701621} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2449389532931556151 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 778858144144351492} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3} + m_Name: + m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.LayoutElement + m_IgnoreLayout: 0 + m_MinWidth: -1 + m_MinHeight: 94 + m_PreferredWidth: -1 + m_PreferredHeight: 94 + m_FlexibleWidth: -1 + m_FlexibleHeight: -1 + m_LayoutPriority: 1 --- !u!1 &1094829826781531081 GameObject: m_ObjectHideFlags: 0 @@ -873,8 +1144,9 @@ RectTransform: m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: - - {fileID: 1189839221186869986} + - {fileID: 5857890906983273014} - {fileID: 5031715123348730660} + - {fileID: 1189839221186869986} m_Father: {fileID: 7387928529902370415} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0} @@ -1094,6 +1366,127 @@ CanvasGroup: m_Interactable: 1 m_BlocksRaycasts: 1 m_IgnoreParentGroups: 0 +--- !u!1 &3559599530917887295 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5507289864410394337} + - component: {fileID: 7767449407280346710} + - component: {fileID: 2998209428586400035} + - component: {fileID: 2761373132084477281} + m_Layer: 0 + m_Name: MaskSwatch_0 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &5507289864410394337 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3559599530917887295} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1208878907889542010} + m_Father: {fileID: 5857890906983273014} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 0, y: -22} + m_SizeDelta: {x: 72, y: 72} + m_Pivot: {x: 0, y: 1} +--- !u!222 &7767449407280346710 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3559599530917887295} + m_CullTransparentMesh: 1 +--- !u!114 &2998209428586400035 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3559599530917887295} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image + m_Material: {fileID: 0} + m_Color: {r: 0.18, g: 0.18, b: 0.21, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &2761373132084477281 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3559599530917887295} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Button + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 0 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 2998209428586400035} + m_OnClick: + m_PersistentCalls: + m_Calls: [] --- !u!1 &3563111358790320208 GameObject: m_ObjectHideFlags: 0 @@ -1682,6 +2075,127 @@ MonoBehaviour: m_PersistentCalls: m_Calls: [] m_IsOn: 0 +--- !u!1 &4421683085817810643 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2694655898154754188} + - component: {fileID: 7755262049701548687} + - component: {fileID: 3735688695217994597} + - component: {fileID: 1148017337161198583} + m_Layer: 0 + m_Name: MaskSwatch_1 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2694655898154754188 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4421683085817810643} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 6025592237405964704} + m_Father: {fileID: 5857890906983273014} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 80, y: -22} + m_SizeDelta: {x: 72, y: 72} + m_Pivot: {x: 0, y: 1} +--- !u!222 &7755262049701548687 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4421683085817810643} + m_CullTransparentMesh: 1 +--- !u!114 &3735688695217994597 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4421683085817810643} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image + m_Material: {fileID: 0} + m_Color: {r: 0.18, g: 0.18, b: 0.21, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &1148017337161198583 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4421683085817810643} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Button + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 0 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 3735688695217994597} + m_OnClick: + m_PersistentCalls: + m_Calls: [] --- !u!1 &4533464575680292808 GameObject: m_ObjectHideFlags: 0 @@ -1908,6 +2422,81 @@ MonoBehaviour: m_FillOrigin: 0 m_UseSpriteMesh: 0 m_PixelsPerUnitMultiplier: 1 +--- !u!1 &4747694847183416769 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1208878907889542010} + - component: {fileID: 7032043542872723534} + - component: {fileID: 7819872403142465117} + m_Layer: 0 + m_Name: Preview + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1208878907889542010 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4747694847183416769} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 5507289864410394337} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -8, y: -8} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &7032043542872723534 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4747694847183416769} + m_CullTransparentMesh: 1 +--- !u!114 &7819872403142465117 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4747694847183416769} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 1 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 --- !u!1 &4864789021642692155 GameObject: m_ObjectHideFlags: 0 @@ -4996,11 +5585,11 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 6369511037010461285, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3} propertyPath: m_MaxValue - value: 300 + value: 200 objectReference: {fileID: 0} - target: {fileID: 6369511037010461285, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3} propertyPath: m_MinValue - value: 2 + value: 1 objectReference: {fileID: 0} - target: {fileID: 6369511037010461285, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3} propertyPath: m_WholeNumbers diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity index 42ab0bb..ba30491 100644 --- a/Assets/Scenes/SampleScene.unity +++ b/Assets/Scenes/SampleScene.unity @@ -2036,8 +2036,8 @@ MonoBehaviour: emissionIntensity: 20 brushSize: 50 brushMasks: - - {fileID: 10912, guid: 0000000000000000f000000000000000, type: 0} - - {fileID: 10300, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 2800000, guid: d07c01c40cc7be54da0ff3ae52484cd2, type: 3} + - {fileID: 2800000, guid: ccc205272b051834eb39593f647c7bf1, type: 3} activeBrushIndex: 0 brushAlignment: 0 brushRotation: 0 diff --git a/Assets/Scripts/MeshPainter.Cpu.cs b/Assets/Scripts/MeshPainter.Cpu.cs index 6e910c7..e86514d 100644 --- a/Assets/Scripts/MeshPainter.Cpu.cs +++ b/Assets/Scripts/MeshPainter.Cpu.cs @@ -18,7 +18,10 @@ public partial class MeshPainter filterMode = FilterMode.Bilinear }, pixels = SeedCpuPixels(mi, canonical), - painted = new bool[textureSize * textureSize], + // Seed the dilation-source marker with the UV islands so seeded (loaded / + // switched-in) content is protected from edge padding right away; the gutter + // stays paddable. Clone: painted[] is mutated per stroke. + painted = (bool[])GetIslandMask().Clone(), fillStamp = new int[textureSize * textureSize], dilateStamp = 0 }; diff --git a/Assets/Scripts/MeshPainter.Gpu.cs b/Assets/Scripts/MeshPainter.Gpu.cs index 842be15..de465a5 100644 --- a/Assets/Scripts/MeshPainter.Gpu.cs +++ b/Assets/Scripts/MeshPainter.Gpu.cs @@ -68,7 +68,7 @@ public partial class MeshPainter return true; } - /// Seeds a GPU map's display RT from canonical data (alpha = 0 = unpainted), then packs MS to final. + /// Seeds a GPU map's display RT from canonical data (coverage alpha = UV islands), then packs MS to final. private void SeedGpuMap(int mi, Color32[][] canonical) { var src = (canonical != null && canonical[mi] != null && canonical[mi].Length == textureSize * textureSize) @@ -78,15 +78,15 @@ public partial class MeshPainter if (mi == MAP_MSO) { // Canonical (R=metallic, G=occlusion, A=smoothness) -> work layout - // (R=metallic, G=smoothness, B=occlusion); alpha = coverage stays 0 (unpainted). + // (R=metallic, G=smoothness, B=occlusion); UploadColorsToRT fills A = island coverage. var work = new Color32[src.Length]; for (int i = 0; i < src.Length; i++) work[i] = new Color32(src[i].r, src[i].a, src[i].g, 0); - UploadColorsToRT(work, gm.display, paintedAlpha: false, linear: true); + UploadColorsToRT(work, gm.display, linear: true); PackMs(gm); // build the final mask RT once so the material shows the seeded values } else { - UploadColorsToRT((Color32[])src.Clone(), gm.display, paintedAlpha: false, linear: gm.sRGB == false); + UploadColorsToRT((Color32[])src.Clone(), gm.display, linear: gm.sRGB == false); } } @@ -125,12 +125,15 @@ public partial class MeshPainter } /// Writes Color32 data into a RenderTexture via a temporary texture. - private void UploadColorsToRT(Color32[] colors, RenderTexture rt, bool paintedAlpha, bool linear) + private void UploadColorsToRT(Color32[] colors, RenderTexture rt, bool linear) { var tmp = new Texture2D(textureSize, textureSize, TextureFormat.RGBA32, false, linear); - // Alpha marks painted (255) vs unpainted (0) for the dilation pass. - byte a = paintedAlpha ? (byte)255 : (byte)0; - for (int i = 0; i < colors.Length; i++) colors[i].a = a; + // Alpha is the dilation coverage marker: island texels (real content) = 255 so the + // dilate pass keeps them; the gutter = 0 so it stays paddable. Driving this from the UV + // islands (not a flat "unpainted") protects seeded content from the first stroke's bleed. + var island = GetIslandMask(); + bool useMask = island.Length == colors.Length; + for (int i = 0; i < colors.Length; i++) colors[i].a = (useMask && island[i]) ? (byte)255 : (byte)0; tmp.SetPixels32(colors); tmp.Apply(false); diff --git a/Assets/Scripts/MeshPainter.Setup.cs b/Assets/Scripts/MeshPainter.Setup.cs index 498a13b..726493b 100644 --- a/Assets/Scripts/MeshPainter.Setup.cs +++ b/Assets/Scripts/MeshPainter.Setup.cs @@ -72,6 +72,67 @@ public partial class MeshPainter if (sumAreaUv > 1e-9) _avgUvToLocal = (float)System.Math.Sqrt(sumArea3D / sumAreaUv); } + /// + /// Rasterizes the mesh UV islands into a per-texel coverage marker (true = the texel lies + /// inside a UV triangle). This is the dilation SOURCE / overwrite guard: island texels are + /// real content the edge-padding pass must never overwrite, while the gutter (false) stays + /// paddable. Deriving the marker from the STATIC UV layout - instead of from transient + /// per-stroke coverage - makes every seed (fresh start, loaded maps, backend switch) mark + /// the same real content as painted, so the first stroke after a load/switch no longer + /// bleeds over the existing paint. Built once and cached (UV layout + textureSize are fixed). + /// + private bool[] GetIslandMask() + { + int n = textureSize * textureSize; + if (_islandMask != null && _islandMask.Length == n) return _islandMask; + + var mask = new bool[n]; + if (_tris == null || _uvs == null) { _islandMask = mask; return mask; } + + float ts = textureSize; + const float eps = -1e-4f; // matches PaintStamp3D: tolerate 1px cracks between triangles + int triCount = _tris.Length / 3; + + for (int t = 0; t < triCount; t++) + { + int i0 = _tris[t * 3], i1 = _tris[t * 3 + 1], i2 = _tris[t * 3 + 2]; + Vector2 uv0 = _uvs[i0], uv1 = _uvs[i1], uv2 = _uvs[i2]; + + float p0x = uv0.x * ts, p0y = uv0.y * ts; + float p1x = uv1.x * ts, p1y = uv1.y * ts; + float p2x = uv2.x * ts, p2y = uv2.y * ts; + + int minX = Mathf.Clamp(Mathf.FloorToInt(Mathf.Min(p0x, Mathf.Min(p1x, p2x))), 0, textureSize - 1); + int maxX = Mathf.Clamp(Mathf.CeilToInt(Mathf.Max(p0x, Mathf.Max(p1x, p2x))), 0, textureSize - 1); + int minY = Mathf.Clamp(Mathf.FloorToInt(Mathf.Min(p0y, Mathf.Min(p1y, p2y))), 0, textureSize - 1); + int maxY = Mathf.Clamp(Mathf.CeilToInt(Mathf.Max(p0y, Mathf.Max(p1y, p2y))), 0, textureSize - 1); + if (maxX < minX || maxY < minY) continue; + + float d = (p1y - p2y) * (p0x - p2x) + (p2x - p1x) * (p0y - p2y); + if (Mathf.Abs(d) < 1e-7f) continue; + float invD = 1f / d; + + for (int y = minY; y <= maxY; y++) + { + float py = y + 0.5f; + int row = y * textureSize; + for (int x = minX; x <= maxX; x++) + { + float px = x + 0.5f; + float a = ((p1y - p2y) * (px - p2x) + (p2x - p1x) * (py - p2y)) * invD; + if (a < eps) continue; + float b = ((p2y - p0y) * (px - p2x) + (p0x - p2x) * (py - p2y)) * invD; + if (b < eps) continue; + if (1f - a - b < eps) continue; + mask[row + x] = true; + } + } + } + + _islandMask = mask; + return mask; + } + /// Builds the per-map seed colors (in canonical/material layout) from the existing material. private void BuildSeedColors() { diff --git a/Assets/Scripts/MeshPainter.cs b/Assets/Scripts/MeshPainter.cs index 00c898a..18609fe 100644 --- a/Assets/Scripts/MeshPainter.cs +++ b/Assets/Scripts/MeshPainter.cs @@ -204,6 +204,7 @@ public partial class MeshPainter : MonoBehaviour private Vector3[] _triCenter; // centroid per triangle (CPU culling) private float[] _triRadius; // circumscribed radius per triangle (CPU culling) private float _avgUvToLocal = 1f; // average UV unit -> local unit + private bool[] _islandMask; // texel lies inside a UV island: dilation source / overwrite guard (built once) private bool _hasLastScreen; private Vector2 _lastScreen; diff --git a/Assets/Scripts/PaintHud.Persistence.cs b/Assets/Scripts/PaintHud.Persistence.cs index 129f5bc..6d300b7 100644 --- a/Assets/Scripts/PaintHud.Persistence.cs +++ b/Assets/Scripts/PaintHud.Persistence.cs @@ -68,6 +68,7 @@ public partial class PaintHud public Color emissionColor; public float emissionIntensity; public int brushSize; + public int activeBrushIndex; public int sprayDots; public float sprayDotSizeMin, sprayDotSizeMax, sprayDotAlphaMin, sprayDotAlphaMax; } @@ -116,6 +117,7 @@ public partial class PaintHud v.emissionColor = painter.emissionColor; v.emissionIntensity = painter.emissionIntensity; v.brushSize = painter.brushSize; + v.activeBrushIndex = painter.activeBrushIndex; v.sprayDots = painter.sprayDots; v.sprayDotSizeMin = painter.sprayDotSizeMin; v.sprayDotSizeMax = painter.sprayDotSizeMax; @@ -161,6 +163,7 @@ public partial class PaintHud painter.emissionColor = v.emissionColor; painter.emissionIntensity = v.emissionIntensity; painter.brushSize = v.brushSize; + painter.activeBrushIndex = v.activeBrushIndex; painter.sprayDots = v.sprayDots; painter.sprayDotSizeMin = v.sprayDotSizeMin; painter.sprayDotSizeMax = v.sprayDotSizeMax; diff --git a/Assets/Scripts/PaintHud.cs b/Assets/Scripts/PaintHud.cs index 2f4eeb3..1ba8889 100644 --- a/Assets/Scripts/PaintHud.cs +++ b/Assets/Scripts/PaintHud.cs @@ -63,6 +63,8 @@ public partial class PaintHud : MonoBehaviour private const string SliderSprayDotSizeMax = "Slider_SprayDotSizeMax"; private const string SliderSprayDotAlphaMin = "Slider_SprayDotAlphaMin"; private const string SliderSprayDotAlphaMax = "Slider_SprayDotAlphaMax"; + private const string MaskSelector = "MaskSelector"; + private const string MaskSwatchPrefix = "MaskSwatch_"; // Panels made draggable at runtime - grabbable from any non-widget surface (both prefab and code-gen paths). private static readonly string[] DraggablePanels = @@ -206,6 +208,8 @@ public partial class PaintHud : MonoBehaviour WireSlider(panel, SliderBrushSize, painter ? painter.brushSize : 16f, v => { if (painter) painter.brushSize = Mathf.RoundToInt(v); }); + WireMaskSelector(panel); + WireSlider(panel, SliderSprayDots, painter ? painter.sprayDots : 24f, v => { if (painter) painter.sprayDots = Mathf.RoundToInt(v); }); WireSlider(panel, SliderSprayDotSizeMin, painter ? painter.sprayDotSizeMin : .05f, v => { if (painter) painter.sprayDotSizeMin = v; }); WireSlider(panel, SliderSprayDotSizeMax, painter ? painter.sprayDotSizeMax : .18f, v => { if (painter) painter.sprayDotSizeMax = v; }); @@ -250,6 +254,8 @@ public partial class PaintHud : MonoBehaviour SetSlider(panel, SliderSprayDotSizeMax, painter.sprayDotSizeMax); SetSlider(panel, SliderSprayDotAlphaMin, painter.sprayDotAlphaMin); SetSlider(panel, SliderSprayDotAlphaMax, painter.sprayDotAlphaMax); + + RefreshMaskHighlights(); } private static void SetToggle(Transform panel, string name, bool value) @@ -271,6 +277,82 @@ public partial class PaintHud : MonoBehaviour if (picker) picker.SetColor(color); } + // Active = accent frame, inactive = subtle frame. Selection lives on the painter (activeBrushIndex); + // the swatch click only sets it - the painter resolves the new mask on its next Update. + private static readonly Color MaskFrameSelected = new Color(0.30f, 0.62f, 1f, 1f); + private static readonly Color MaskFrameUnselected = new Color(0.18f, 0.18f, 0.21f, 1f); + + /// + /// Binds the baked mask swatches (one per MaskSwatch_i) to the painter's brush masks: + /// each swatch previews painter.brushMasks[i] and selects it on click. A swatch with no + /// matching mask is hidden, so the prefab can carry more swatches than the painter has masks. + /// + private void WireMaskSelector(Transform panel) + { + var selector = FindDeep(panel, MaskSelector); + if (!selector) return; + var masks = painter ? painter.brushMasks : null; + + for (int i = 0; ; i++) + { + var swatch = FindDeep(selector, MaskSwatchPrefix + i); + if (!swatch) break; + + bool has = masks != null && i < masks.Count && masks[i]; + swatch.gameObject.SetActive(has); + if (!has) continue; + + var preview = swatch.Find("Preview"); + var previewImg = preview ? preview.GetComponent() : null; + if (previewImg) previewImg.sprite = MaskSprite(masks[i]); + + var btn = swatch.GetComponent