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