Add spray brush, paint interval, and per-channel paint opacity
Spray brush mode: bakes a procedural scattered-dot mask per stamp (random position, size, alpha within the brush disc) and paints it through the existing single-stamp pipeline; dot centers clamped to (0.5 - dotR) so the spray footprint matches the solid brush radius. Paint interval: float on MeshPainter; 0 keeps the continuous interpolated stroke, >0 emits a single timed dab every x.xx seconds. Color picker alpha: horizontal alpha bar on both HSV pickers, runtime-built in Rebind (no prefab re-bake), passed through to brushColor.a / emissionColor.a. Metallic/smoothness + occlusion paint opacity: new opacity fields applied in both CPU and GPU backends (GPU MSO draw split into MS and occlusion sub-stamps for independent opacity), with cloned alpha sliders wired into the HUD. Also includes the prefab layout tweaks and scene adjustments. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
af3aa34d66
commit
b07d9a22d2
9 files changed
+1227
-86
No files matched your search
@@ -691,6 +691,81 @@ MonoBehaviour:
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!1 &2796953647161411337
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1063095855848914906}
|
||||
- component: {fileID: 4779579190783925503}
|
||||
- component: {fileID: 7768445624927669295}
|
||||
m_Layer: 0
|
||||
m_Name: Check
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &1063095855848914906
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2796953647161411337}
|
||||
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: 2436255323372548463}
|
||||
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: -6, y: -6}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &4779579190783925503
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2796953647161411337}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &7768445624927669295
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2796953647161411337}
|
||||
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.4, g: 0.75, 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: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!1 &2975656316574628353
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -845,6 +920,7 @@ RectTransform:
|
||||
- {fileID: 7851905535026198058}
|
||||
- {fileID: 8454988543450548733}
|
||||
- {fileID: 8376856457293460334}
|
||||
- {fileID: 2492869794191913777}
|
||||
m_Father: {fileID: 7387928529902370415}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0}
|
||||
@@ -970,6 +1046,7 @@ RectTransform:
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 3634224269323718290}
|
||||
- {fileID: 4342959377125151652}
|
||||
- {fileID: 4923756974383574983}
|
||||
- {fileID: 3665381071208701621}
|
||||
@@ -3028,6 +3105,250 @@ MonoBehaviour:
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!1 &7726549018629373785
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2942748398039547410}
|
||||
- component: {fileID: 1892836100004737345}
|
||||
- component: {fileID: 7199706241071600495}
|
||||
m_Layer: 0
|
||||
m_Name: Label
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &2942748398039547410
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7726549018629373785}
|
||||
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: 3098102965295711989}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 12, y: 0}
|
||||
m_SizeDelta: {x: -24, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &1892836100004737345
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7726549018629373785}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &7199706241071600495
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7726549018629373785}
|
||||
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: 0
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: Spray
|
||||
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: 14
|
||||
m_fontSizeBase: 14
|
||||
m_fontWeight: 400
|
||||
m_enableAutoSizing: 0
|
||||
m_fontSizeMin: 18
|
||||
m_fontSizeMax: 72
|
||||
m_fontStyle: 0
|
||||
m_HorizontalAlignment: 1
|
||||
m_VerticalAlignment: 8192
|
||||
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 &7935444948555430782
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 3098102965295711989}
|
||||
- component: {fileID: 7542572879761213426}
|
||||
- component: {fileID: 4136943736630015774}
|
||||
m_Layer: 0
|
||||
m_Name: Toggle_Spray
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &3098102965295711989
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7935444948555430782}
|
||||
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: 2436255323372548463}
|
||||
- {fileID: 2942748398039547410}
|
||||
m_Father: {fileID: 3634224269323718290}
|
||||
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 &7542572879761213426
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7935444948555430782}
|
||||
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: 22
|
||||
m_PreferredWidth: -1
|
||||
m_PreferredHeight: 22
|
||||
m_FlexibleWidth: -1
|
||||
m_FlexibleHeight: -1
|
||||
m_LayoutPriority: 1
|
||||
--- !u!114 &4136943736630015774
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7935444948555430782}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 9085046f02f69544eb97fd06b6048fe2, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Toggle
|
||||
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: 1
|
||||
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: 2223657820663403196}
|
||||
toggleTransition: 1
|
||||
graphic: {fileID: 7768445624927669295}
|
||||
m_Group: {fileID: 0}
|
||||
onValueChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_IsOn: 0
|
||||
--- !u!1 &7992086713309035181
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -3315,6 +3636,213 @@ MonoBehaviour:
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!1 &8719893497228303187
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 3634224269323718290}
|
||||
- component: {fileID: 3171558510481401849}
|
||||
- component: {fileID: 2983136256694628734}
|
||||
- component: {fileID: 8174275387821514335}
|
||||
- component: {fileID: 7781015511497060513}
|
||||
- component: {fileID: 2733789635056426743}
|
||||
m_Layer: 0
|
||||
m_Name: Panel_Spray
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &3634224269323718290
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8719893497228303187}
|
||||
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: 3098102965295711989}
|
||||
m_Father: {fileID: 7387928529902370415}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0}
|
||||
m_AnchorMax: {x: 0.5, y: 0}
|
||||
m_AnchoredPosition: {x: -900, y: 20}
|
||||
m_SizeDelta: {x: 280, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0}
|
||||
--- !u!222 &3171558510481401849
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8719893497228303187}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &2983136256694628734
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8719893497228303187}
|
||||
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.09, g: 0.09, b: 0.11, a: 0.9}
|
||||
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: 10907, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_Type: 1
|
||||
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 &8174275387821514335
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8719893497228303187}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.VerticalLayoutGroup
|
||||
m_Padding:
|
||||
m_Left: 12
|
||||
m_Right: 12
|
||||
m_Top: 12
|
||||
m_Bottom: 12
|
||||
m_ChildAlignment: 0
|
||||
m_Spacing: 10
|
||||
m_ChildForceExpandWidth: 1
|
||||
m_ChildForceExpandHeight: 0
|
||||
m_ChildControlWidth: 1
|
||||
m_ChildControlHeight: 1
|
||||
m_ChildScaleWidth: 0
|
||||
m_ChildScaleHeight: 0
|
||||
m_ReverseArrangement: 0
|
||||
--- !u!114 &7781015511497060513
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8719893497228303187}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.ContentSizeFitter
|
||||
m_HorizontalFit: 0
|
||||
m_VerticalFit: 2
|
||||
--- !u!225 &2733789635056426743
|
||||
CanvasGroup:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8719893497228303187}
|
||||
m_Enabled: 1
|
||||
m_Alpha: 0.5
|
||||
m_Interactable: 1
|
||||
m_BlocksRaycasts: 1
|
||||
m_IgnoreParentGroups: 0
|
||||
--- !u!1 &8749262302241821053
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2436255323372548463}
|
||||
- component: {fileID: 3347812339218018783}
|
||||
- component: {fileID: 2223657820663403196}
|
||||
m_Layer: 0
|
||||
m_Name: Box
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &2436255323372548463
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8749262302241821053}
|
||||
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: 1063095855848914906}
|
||||
m_Father: {fileID: 3098102965295711989}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0.5}
|
||||
m_AnchorMax: {x: 0, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 18, y: 18}
|
||||
m_Pivot: {x: 0, y: 0.5}
|
||||
--- !u!222 &3347812339218018783
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8749262302241821053}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &2223657820663403196
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8749262302241821053}
|
||||
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.2, g: 0.2, b: 0.24, 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!1 &8994899293704923971
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -3350,6 +3878,7 @@ RectTransform:
|
||||
m_Children:
|
||||
- {fileID: 4936290884599347572}
|
||||
- {fileID: 265579357309326112}
|
||||
- {fileID: 5867721996605831938}
|
||||
m_Father: {fileID: 7387928529902370415}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0}
|
||||
@@ -3814,6 +4343,140 @@ RectTransform:
|
||||
m_CorrespondingSourceObject: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
m_PrefabInstance: {fileID: 889416535110432801}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!1001 &1796302450561085447
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 8239117552197655738}
|
||||
m_Modifications:
|
||||
- target: {fileID: 3983135770763320344, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_text
|
||||
value: Alpha
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4562821246304388210, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_text
|
||||
value: 1,00
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_Pivot.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_Pivot.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchorMax.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchorMin.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchorMin.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_SizeDelta.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: -0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: -0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: -0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5381822788309442337, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchorMax.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5381822788309442337, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6369511037010461285, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_Value
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6993348405844982934, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchorMax.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6993348405844982934, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6993348405844982934, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchorMin.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7273293964960786644, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: Slider_OcclusionAlpha
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
--- !u!224 &5867721996605831938 stripped
|
||||
RectTransform:
|
||||
m_CorrespondingSourceObject: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
m_PrefabInstance: {fileID: 1796302450561085447}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!1001 &4383637231553397496
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -4188,3 +4851,137 @@ RectTransform:
|
||||
m_CorrespondingSourceObject: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
m_PrefabInstance: {fileID: 5344768466403301413}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!1001 &7717931987048658996
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 4923756974383574983}
|
||||
m_Modifications:
|
||||
- target: {fileID: 3983135770763320344, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_text
|
||||
value: Alpha
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4562821246304388210, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_text
|
||||
value: 1,00
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_Pivot.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_Pivot.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchorMax.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchorMin.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchorMin.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_SizeDelta.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: -0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: -0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: -0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5381822788309442337, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchorMax.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5381822788309442337, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6369511037010461285, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_Value
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6993348405844982934, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchorMax.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6993348405844982934, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6993348405844982934, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_AnchorMin.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7273293964960786644, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: Slider_MetallicSmoothnessAlpha
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
--- !u!224 &2492869794191913777 stripped
|
||||
RectTransform:
|
||||
m_CorrespondingSourceObject: {fileID: 5297334453724826373, guid: ddd3fe86b03da644faf6d52ae79646b6, type: 3}
|
||||
m_PrefabInstance: {fileID: 7717931987048658996}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
@@ -446,6 +446,12 @@ MonoBehaviour:
|
||||
brushShader: {fileID: 0}
|
||||
dilateShader: {fileID: 0}
|
||||
packMsShader: {fileID: 0}
|
||||
spray: 0
|
||||
sprayDots: 24
|
||||
sprayDotSizeMin: 0.05
|
||||
sprayDotSizeMax: 0.18
|
||||
sprayDotAlphaMin: 0.25
|
||||
sprayDotAlphaMax: 1
|
||||
useGpu: 1
|
||||
paintAlbedo: 1
|
||||
paintMetallicSmoothness: 0
|
||||
@@ -465,6 +471,7 @@ MonoBehaviour:
|
||||
brushAlignment: 0
|
||||
brushRotation: 0
|
||||
paintOnMoveOnly: 1
|
||||
paintInterval: 0
|
||||
previewMode: 2
|
||||
previewOpacity: 1
|
||||
textureSize: 1024
|
||||
@@ -1412,6 +1419,12 @@ MonoBehaviour:
|
||||
brushShader: {fileID: 0}
|
||||
dilateShader: {fileID: 0}
|
||||
packMsShader: {fileID: 0}
|
||||
spray: 0
|
||||
sprayDots: 24
|
||||
sprayDotSizeMin: 0.05
|
||||
sprayDotSizeMax: 0.18
|
||||
sprayDotAlphaMin: 0.25
|
||||
sprayDotAlphaMax: 1
|
||||
useGpu: 1
|
||||
paintAlbedo: 1
|
||||
paintMetallicSmoothness: 0
|
||||
@@ -1431,6 +1444,7 @@ MonoBehaviour:
|
||||
brushAlignment: 0
|
||||
brushRotation: 0
|
||||
paintOnMoveOnly: 1
|
||||
paintInterval: 0
|
||||
previewMode: 2
|
||||
previewOpacity: 1
|
||||
textureSize: 1024
|
||||
@@ -1605,9 +1619,9 @@ Transform:
|
||||
m_GameObject: {fileID: 721670969}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 10, y: 10, z: 10}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_LocalPosition: {x: 0, y: 0, z: 11.5}
|
||||
m_LocalScale: {x: 5, y: 5, z: 5}
|
||||
m_ConstrainProportionsScale: 1
|
||||
m_Children: []
|
||||
m_Father: {fileID: 686187287}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
@@ -2756,6 +2770,12 @@ MonoBehaviour:
|
||||
brushShader: {fileID: 0}
|
||||
dilateShader: {fileID: 0}
|
||||
packMsShader: {fileID: 0}
|
||||
spray: 0
|
||||
sprayDots: 24
|
||||
sprayDotSizeMin: 0.05
|
||||
sprayDotSizeMax: 0.18
|
||||
sprayDotAlphaMin: 0.25
|
||||
sprayDotAlphaMax: 1
|
||||
useGpu: 1
|
||||
paintAlbedo: 1
|
||||
paintMetallicSmoothness: 0
|
||||
@@ -2775,6 +2795,7 @@ MonoBehaviour:
|
||||
brushAlignment: 0
|
||||
brushRotation: 0
|
||||
paintOnMoveOnly: 1
|
||||
paintInterval: 0
|
||||
previewMode: 2
|
||||
previewOpacity: 1
|
||||
textureSize: 1024
|
||||
|
||||
@@ -3,27 +3,35 @@ using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Compact runtime HSV colour picker: a saturation/value square, a hue bar and a live swatch.
|
||||
/// The square is a pure-hue base with two static gradient overlays on top (white-out ->
|
||||
/// saturation, black-out -> value), so only the base tint changes per hue - no per-frame
|
||||
/// Compact runtime HSV colour picker: a saturation/value square, a hue bar, an alpha bar and a
|
||||
/// live swatch. The square is a pure-hue base with two static gradient overlays on top (white-out
|
||||
/// -> saturation, black-out -> value), so only the base tint changes per hue - no per-frame
|
||||
/// texture rebuilds. Hue is never lost while dragging the square (the square only edits S/V,
|
||||
/// the bar only edits H).
|
||||
/// the bar only edits H, the alpha bar only edits A).
|
||||
///
|
||||
/// <see cref="Build"/> creates the child structure (stable names, no sprites or hooks); it can
|
||||
/// be baked into a prefab. <see cref="Rebind"/> then (re)assigns the runtime gradient sprites
|
||||
/// (which a prefab cannot store) and (re)hooks the drag relays - it runs for both the code-built
|
||||
/// picker and one instantiated from a prefab, so the two are behaviourally identical.
|
||||
/// picker and one instantiated from a prefab, so the two are behaviourally identical. For a prefab
|
||||
/// baked before the alpha bar existed it also builds that bar on the fly (and grows the picker's
|
||||
/// reserved layout height), so no re-bake is required.
|
||||
/// </summary>
|
||||
public class HsvColorPicker : MonoBehaviour
|
||||
{
|
||||
/// <summary>Raised with the new colour when the user moves the square or hue bar.</summary>
|
||||
/// <summary>Raised with the new colour when the user moves the square, hue bar or alpha bar.</summary>
|
||||
public event Action<Color> Changed;
|
||||
|
||||
private float _h, _s, _v, _a = 1f;
|
||||
|
||||
// Gap between the stacked square / hue / alpha rows (matches the layout in Build).
|
||||
private const float RowGap = 6f;
|
||||
private static readonly Color AlphaBackdrop = new Color(0.5f, 0.5f, 0.5f, 1f);
|
||||
|
||||
private Image _hueBase; // pure-hue background of the SV square
|
||||
private RectTransform _svHandle;
|
||||
private RectTransform _hueHandle;
|
||||
private Image _alphaGrad; // colour-tinted transparency ramp of the alpha bar
|
||||
private RectTransform _alphaHandle;
|
||||
private Image _swatch;
|
||||
|
||||
/// <summary>Current colour (HSV -> RGB, with the preserved alpha).</summary>
|
||||
@@ -46,9 +54,10 @@ public class HsvColorPicker : MonoBehaviour
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the picker structure (square + hue bar + swatch) under this object, top-left aligned.
|
||||
/// Layout only - no gradient sprites and no event hooks, so the result is safe to bake into a
|
||||
/// prefab. Call <see cref="Rebind"/> afterwards (at runtime) to make it functional.
|
||||
/// Builds the picker structure (square + hue bar + alpha bar + swatch) under this object,
|
||||
/// top-left aligned. Layout only - no gradient sprites and no event hooks, so the result is
|
||||
/// safe to bake into a prefab. Call <see cref="Rebind"/> afterwards (at runtime) to make it
|
||||
/// functional.
|
||||
/// </summary>
|
||||
public void Build(float squareSize, float hueHeight)
|
||||
{
|
||||
@@ -68,7 +77,7 @@ public class HsvColorPicker : MonoBehaviour
|
||||
|
||||
// Hue bar below the square.
|
||||
var hue = PaintHud.NewRect("Hue", transform);
|
||||
TopLeft(hue, new Vector2(0f, -(squareSize + 6f)), new Vector2(squareSize, hueHeight));
|
||||
TopLeft(hue, new Vector2(0f, -(squareSize + RowGap)), new Vector2(squareSize, hueHeight));
|
||||
var hueImg = hue.gameObject.AddComponent<Image>();
|
||||
hueImg.type = Image.Type.Simple;
|
||||
|
||||
@@ -78,12 +87,41 @@ public class HsvColorPicker : MonoBehaviour
|
||||
hueHandle.sizeDelta = new Vector2(4f, hueHeight + 4f);
|
||||
PaintHud.AddImage(hueHandle.gameObject, Color.white).raycastTarget = false;
|
||||
|
||||
// Alpha bar below the hue bar.
|
||||
BuildAlphaBar(sv, hue);
|
||||
|
||||
// Swatch, to the right of the square.
|
||||
var sw = PaintHud.NewRect("Swatch", transform);
|
||||
TopLeft(sw, new Vector2(squareSize + 10f, 0f), new Vector2(34f, 34f));
|
||||
PaintHud.AddImage(sw.gameObject, Color.white).raycastTarget = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the alpha bar (grey backdrop + colour-tinted transparency ramp + drag relay + handle)
|
||||
/// directly below the hue bar, sized/positioned from the existing SV square and hue bar so it
|
||||
/// works for both a fresh build and an older baked prefab. Sprites/hooks are applied by
|
||||
/// <see cref="Rebind"/>.
|
||||
/// </summary>
|
||||
private RectTransform BuildAlphaBar(RectTransform sv, RectTransform hue)
|
||||
{
|
||||
float width = sv.sizeDelta.x;
|
||||
float barHeight = hue.sizeDelta.y;
|
||||
float y = hue.anchoredPosition.y - (barHeight + RowGap);
|
||||
|
||||
var alpha = PaintHud.NewRect("Alpha", transform);
|
||||
TopLeft(alpha, new Vector2(0f, y), new Vector2(width, barHeight));
|
||||
PaintHud.AddImage(alpha.gameObject, AlphaBackdrop); // raycast target for the drag relay
|
||||
|
||||
PaintHud.AddOverlay(alpha, "AlphaGrad"); // tinted alpha ramp, sprite assigned in Rebind
|
||||
|
||||
alpha.gameObject.AddComponent<UiPointerSurface>();
|
||||
|
||||
var handle = PaintHud.NewRect("Handle", alpha);
|
||||
handle.sizeDelta = new Vector2(4f, barHeight + 4f);
|
||||
PaintHud.AddImage(handle.gameObject, Color.white).raycastTarget = false;
|
||||
return alpha;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-finds the child widgets, (re)assigns the runtime gradient sprites and (re)hooks the drag
|
||||
/// relays. Idempotent (unsubscribe-then-subscribe), so it is safe whether the structure was just
|
||||
@@ -110,7 +148,7 @@ public class HsvColorPicker : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
var hue = transform.Find("Hue");
|
||||
var hue = transform.Find("Hue") as RectTransform;
|
||||
if (hue)
|
||||
{
|
||||
if (hue.TryGetComponent(out Image hueImg)) hueImg.sprite = PaintHud.HueSprite();
|
||||
@@ -123,12 +161,48 @@ public class HsvColorPicker : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
// Alpha bar: build it if the (older) prefab lacks it, then (re)assign sprite + relay.
|
||||
var alpha = transform.Find("Alpha") as RectTransform;
|
||||
bool builtAlpha = false;
|
||||
if (!alpha && sv is RectTransform svRect && hue)
|
||||
{
|
||||
alpha = BuildAlphaBar(svRect, hue);
|
||||
builtAlpha = true;
|
||||
}
|
||||
if (alpha)
|
||||
{
|
||||
var grad = alpha.Find("AlphaGrad");
|
||||
if (grad && grad.TryGetComponent(out Image gradImg))
|
||||
{
|
||||
gradImg.sprite = PaintHud.AlphaSprite();
|
||||
_alphaGrad = gradImg;
|
||||
}
|
||||
_alphaHandle = alpha.Find("Handle") as RectTransform;
|
||||
|
||||
if (alpha.TryGetComponent(out UiPointerSurface alphaPointer))
|
||||
{
|
||||
alphaPointer.Picked -= OnAlphaPicked;
|
||||
alphaPointer.Picked += OnAlphaPicked;
|
||||
}
|
||||
|
||||
if (builtAlpha) GrowReservedHeight(alpha.sizeDelta.y + RowGap);
|
||||
}
|
||||
|
||||
var swatch = transform.Find("Swatch");
|
||||
if (swatch) _swatch = swatch.GetComponent<Image>();
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
/// <summary>Grows this picker's LayoutElement so a runtime-added alpha bar does not overlap the
|
||||
/// widget below it; the panel's ContentSizeFitter then expands to match. No-op without a LayoutElement.</summary>
|
||||
private void GrowReservedHeight(float extra)
|
||||
{
|
||||
if (!TryGetComponent(out LayoutElement le)) return;
|
||||
if (le.minHeight > 0f) le.minHeight += extra;
|
||||
if (le.preferredHeight > 0f) le.preferredHeight += extra;
|
||||
}
|
||||
|
||||
private void OnSvPicked(Vector2 p)
|
||||
{
|
||||
_s = p.x;
|
||||
@@ -144,6 +218,13 @@ public class HsvColorPicker : MonoBehaviour
|
||||
Changed?.Invoke(Value);
|
||||
}
|
||||
|
||||
private void OnAlphaPicked(Vector2 p)
|
||||
{
|
||||
_a = p.x;
|
||||
Refresh();
|
||||
Changed?.Invoke(Value);
|
||||
}
|
||||
|
||||
private void Refresh()
|
||||
{
|
||||
if (_hueBase) _hueBase.color = Color.HSVToRGB(_h, 1f, 1f);
|
||||
@@ -157,6 +238,12 @@ public class HsvColorPicker : MonoBehaviour
|
||||
_hueHandle.anchorMin = _hueHandle.anchorMax = new Vector2(_h, 0.5f);
|
||||
_hueHandle.anchoredPosition = Vector2.zero;
|
||||
}
|
||||
if (_alphaGrad) _alphaGrad.color = Color.HSVToRGB(_h, _s, _v); // tint the transparency ramp to the current colour
|
||||
if (_alphaHandle)
|
||||
{
|
||||
_alphaHandle.anchorMin = _alphaHandle.anchorMax = new Vector2(_a, 0.5f);
|
||||
_alphaHandle.anchoredPosition = Vector2.zero;
|
||||
}
|
||||
if (_swatch) _swatch.color = Color.HSVToRGB(_h, _s, _v);
|
||||
}
|
||||
|
||||
|
||||
@@ -112,6 +112,8 @@ public partial class MeshPainter
|
||||
float msoR = Mathf.Clamp01(metallic) * 255f; // metallic
|
||||
float msoG = Mathf.Clamp01(occlusion) * 255f; // occlusion
|
||||
float msoA = Mathf.Clamp01(smoothness) * 255f; // smoothness
|
||||
float msOpMul = Mathf.Clamp01(metallicSmoothnessOpacity); // metallic+smoothness paint opacity
|
||||
float aoOpMul = Mathf.Clamp01(occlusionOpacity); // occlusion paint opacity
|
||||
CpuMap mso = _cpu[MAP_MSO];
|
||||
bool aMSO = aMS || aAO;
|
||||
|
||||
@@ -210,14 +212,16 @@ public partial class MeshPainter
|
||||
|
||||
if (aMSO)
|
||||
{
|
||||
// Mask map blend (op = mask, no extra alpha multiplier). Metallic(R) +
|
||||
// smoothness(A) only when painting MS; occlusion(G) only when painting AO.
|
||||
// Untouched channels keep their previous value; B stays 0.
|
||||
// Mask map blend. Metallic(R) + smoothness(A) blend with the MS opacity,
|
||||
// occlusion(G) with its own opacity - each = mask * the channel's opacity
|
||||
// multiplier (the albedo brushColor.a equivalent). Untouched channels keep
|
||||
// their previous value; B stays 0.
|
||||
Color32 dst = mso.pixels[idx];
|
||||
float it = 1f - maskOp;
|
||||
byte mr = aMS ? (byte)(dst.r * it + msoR * maskOp) : dst.r;
|
||||
byte mg = aAO ? (byte)(dst.g * it + msoG * maskOp) : dst.g;
|
||||
byte ma = aMS ? (byte)(dst.a * it + msoA * maskOp) : dst.a;
|
||||
float msOp = maskOp * msOpMul;
|
||||
float aoOp = maskOp * aoOpMul;
|
||||
byte mr = aMS ? (byte)(dst.r * (1f - msOp) + msoR * msOp) : dst.r;
|
||||
byte mg = aAO ? (byte)(dst.g * (1f - aoOp) + msoG * aoOp) : dst.g;
|
||||
byte ma = aMS ? (byte)(dst.a * (1f - msOp) + msoA * msOp) : dst.a;
|
||||
mso.pixels[idx] = new Color32(mr, mg, 0, ma);
|
||||
mso.painted[idx] = true;
|
||||
ExpandDirty(mso, x, y);
|
||||
|
||||
@@ -182,27 +182,59 @@ public partial class MeshPainter
|
||||
Graphics.SetRenderTarget(gm.display);
|
||||
GL.sRGBWrite = gm.sRGB; // sRGB color map vs linear data map (MS)
|
||||
|
||||
SetBrushColorForMap(mi);
|
||||
// Per-channel write mask: lets the shared mask map (R=metallic, G=smoothness work,
|
||||
// B=occlusion) be painted by metallic/smoothness and occlusion INDEPENDENTLY -
|
||||
// untouched channels keep their value while coverage (A) still accumulates.
|
||||
_brushMat.SetFloat(ColorWriteMaskId, BrushColorMaskForMap(mi));
|
||||
_brushMat.SetTexture(MaskTexId, _resolvedMask);
|
||||
// Stamp placement is shared by every draw on this map.
|
||||
_brushMat.SetTexture(MaskTexId, _stampMask);
|
||||
_brushMat.SetVector(BrushCenterId, pLocal);
|
||||
_brushMat.SetFloat(BrushRadiusId, radiusLocal);
|
||||
_brushMat.SetVector(BrushUId, u);
|
||||
_brushMat.SetVector(BrushVId, v);
|
||||
_brushMat.SetVector(BrushNormalId, n);
|
||||
if (!_brushMat.SetPass(0)) continue;
|
||||
|
||||
int sub = _bakedMesh.subMeshCount;
|
||||
for (int i = 0; i < sub; i++)
|
||||
Graphics.DrawMeshNow(_bakedMesh, Matrix4x4.identity, i);
|
||||
if (mi == MAP_MSO)
|
||||
{
|
||||
// Metallic/smoothness and occlusion share one texture but paint with their OWN
|
||||
// opacity, so each is a separate stamp (own _BrushColor.a + per-channel write
|
||||
// mask). Untouched channels keep their value while coverage (A) accumulates.
|
||||
if (paintMetallicSmoothness)
|
||||
StampMso(Mathf.Clamp01(metallicSmoothnessOpacity), 13f); // R|G|A (skip B = occlusion)
|
||||
if (paintOcclusion)
|
||||
StampMso(Mathf.Clamp01(occlusionOpacity), 3f); // B|A (occlusion only)
|
||||
}
|
||||
else
|
||||
{
|
||||
// Albedo / emission: full RGBA, opacity carried by the colour's own alpha.
|
||||
_brushMat.SetColor(BrushColorId, mi == MAP_ALBEDO ? brushColor : emissionColor);
|
||||
_brushMat.SetFloat(ColorWriteMaskId, 15f);
|
||||
DrawStamp();
|
||||
}
|
||||
|
||||
gm.drew = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Issues one mask-map stamp: raw (metallic, smoothness, occlusion) data with the given paint
|
||||
/// opacity in alpha and a per-channel colour write mask, so metallic/smoothness and occlusion
|
||||
/// stay independently paintable with their own opacity. EncodeData pre-compensates the Color
|
||||
/// property's sRGB->linear upload so the values round-trip exactly (CPU parity); alpha is not converted.
|
||||
/// </summary>
|
||||
private void StampMso(float opacity, float colorMask)
|
||||
{
|
||||
_brushMat.SetVector(BrushColorId, new Vector4(
|
||||
EncodeData(metallic), EncodeData(smoothness), EncodeData(occlusion), opacity));
|
||||
_brushMat.SetFloat(ColorWriteMaskId, colorMask);
|
||||
DrawStamp();
|
||||
}
|
||||
|
||||
/// <summary>Draws the baked mesh once per sub-mesh with the current brush pass (UV-space rasterization).</summary>
|
||||
private void DrawStamp()
|
||||
{
|
||||
if (!_brushMat.SetPass(0)) return;
|
||||
int sub = _bakedMesh.subMeshCount;
|
||||
for (int i = 0; i < sub; i++)
|
||||
Graphics.DrawMeshNow(_bakedMesh, Matrix4x4.identity, i);
|
||||
}
|
||||
|
||||
private void EndGpuStroke()
|
||||
{
|
||||
Graphics.SetRenderTarget(null);
|
||||
@@ -230,33 +262,6 @@ public partial class MeshPainter
|
||||
GL.sRGBWrite = _prevSRGB;
|
||||
}
|
||||
|
||||
/// <summary>Sets _BrushColor for the given map. Albedo/emission are colors; the mask map packs (metallic, smoothness, occlusion) as raw data.</summary>
|
||||
private void SetBrushColorForMap(int mi)
|
||||
{
|
||||
switch (mi)
|
||||
{
|
||||
case MAP_ALBEDO:
|
||||
_brushMat.SetColor(BrushColorId, brushColor);
|
||||
break;
|
||||
case MAP_MSO:
|
||||
// Raw data via SetVector. Work layout: R=metallic, G=smoothness, B=occlusion
|
||||
// (ColorMask picks the channels). _BrushColor is a Color property, so Unity
|
||||
// applies sRGB->linear on upload in a linear project - we pre-apply the inverse
|
||||
// so the data round-trips EXACTLY and matches the CPU's raw byte writes.
|
||||
_brushMat.SetVector(BrushColorId, new Vector4(
|
||||
EncodeData(metallic), EncodeData(smoothness), EncodeData(occlusion), 1f));
|
||||
break;
|
||||
default: // emission
|
||||
_brushMat.SetColor(BrushColorId, emissionColor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ColorMask bitmask for the brush draw (ColorWriteMask: R=8, G=4, B=2, A=1). Color maps
|
||||
/// write everything; the mask map writes only the active sub-channels so metallic/smoothness
|
||||
/// and occlusion can be painted independently into one texture. Coverage (A) is always written.
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// Pre-compensates a raw data value (0..1) for the brush's Color property, which Unity
|
||||
/// converts sRGB->linear on upload in a linear project. Applying the inverse here makes
|
||||
@@ -268,13 +273,4 @@ public partial class MeshPainter
|
||||
v = Mathf.Clamp01(v);
|
||||
return QualitySettings.activeColorSpace == ColorSpace.Linear ? Mathf.LinearToGammaSpace(v) : v;
|
||||
}
|
||||
|
||||
private float BrushColorMaskForMap(int mi)
|
||||
{
|
||||
if (mi != MAP_MSO) return 15f; // RGBA
|
||||
bool ms = paintMetallicSmoothness, occ = paintOcclusion;
|
||||
if (ms && occ) return 15f; // R|G|B|A
|
||||
if (ms) return 13f; // R|G|A (skip B = occlusion)
|
||||
return 3f; // B|A (skip R,G = metallic/smoothness) -> occlusion only
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
using UnityEngine;
|
||||
|
||||
public partial class MeshPainter
|
||||
{
|
||||
// --- Spray can brush ---
|
||||
//
|
||||
// Instead of one solid stamp, the spray scatters many small dots (random position,
|
||||
// size and alpha) within the brush footprint. It is implemented by baking those dots
|
||||
// into a fresh procedural MASK each stamp and then painting that mask with ONE normal
|
||||
// brush stamp. This reuses the entire seam-bridging / curvature-following / per-channel
|
||||
// pipeline as-is and costs a single mesh draw per stamp (not one per dot). Because the
|
||||
// per-dot alpha lives in the mask value, it flows into every channel (albedo, mask map,
|
||||
// emission) automatically - on both the GPU and CPU backends.
|
||||
|
||||
[Header("Brush - spray can")]
|
||||
[Tooltip("Spray-can mode: scatter random dots (position, size, alpha) within the brush " +
|
||||
"radius instead of a single solid stamp. The dots are baked into the brush mask, " +
|
||||
"so this works on all channels and both backends.")]
|
||||
public bool spray = false;
|
||||
|
||||
[Tooltip("Number of dots scattered per stamp.")]
|
||||
[Range(1, 128)]
|
||||
public int sprayDots = 24;
|
||||
|
||||
[Tooltip("Dot diameter as a fraction of the brush diameter (randomized per dot between min and max).")]
|
||||
[Range(0.01f, 1f)]
|
||||
public float sprayDotSizeMin = 0.05f;
|
||||
|
||||
[Tooltip("Dot diameter as a fraction of the brush diameter (randomized per dot between min and max).")]
|
||||
[Range(0.01f, 1f)]
|
||||
public float sprayDotSizeMax = 0.18f;
|
||||
|
||||
[Tooltip("Per-dot opacity (randomized per dot between min and max). Multiplied on top of the channel color's alpha.")]
|
||||
[Range(0f, 1f)]
|
||||
public float sprayDotAlphaMin = 0.25f;
|
||||
|
||||
[Tooltip("Per-dot opacity (randomized per dot between min and max). Multiplied on top of the channel color's alpha.")]
|
||||
[Range(0f, 1f)]
|
||||
public float sprayDotAlphaMax = 1f;
|
||||
|
||||
// Resolution of the per-stamp spray mask (square). Small + reused so regenerating it
|
||||
// every stamp stays cheap.
|
||||
private const int SprayRes = 128;
|
||||
private const float SprayDotSoftEdge = 0.4f; // fraction of the dot radius that fades to 0
|
||||
|
||||
private Texture2D _sprayTex; // the baked spray mask uploaded to the brush
|
||||
private Color32[] _sprayPixels; // reused upload buffer
|
||||
private float[] _sprayOpacity; // reused per-texel opacity (CPU sampling)
|
||||
|
||||
// --- Active stamp mask (indirection shared by both backends) ---
|
||||
// The standard mask cache (_resolvedMask / _maskOpacity / _maskW / _maskH) stays the
|
||||
// canonical resolved mask used by the preview. _stamp* is what the CURRENT stamp paints
|
||||
// with - the resolved mask for normal painting, or a fresh spray mask in spray mode.
|
||||
private Texture2D _stampMask;
|
||||
private float[] _stampOpacity;
|
||||
private int _stampW, _stampH;
|
||||
|
||||
/// <summary>Points the active stamp mask at the resolved standard mask (normal painting).</summary>
|
||||
private void UseResolvedStampMask()
|
||||
{
|
||||
_stampMask = _resolvedMask;
|
||||
_stampOpacity = _maskOpacity;
|
||||
_stampW = _maskW;
|
||||
_stampH = _maskH;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bakes a fresh random spray pattern into the spray mask and makes it the active stamp
|
||||
/// mask. Each dot gets a random center inside the brush disc, a random diameter and a
|
||||
/// random opacity; dots composite by max so overlaps never exceed full opacity.
|
||||
/// </summary>
|
||||
private void BuildSprayStampMask()
|
||||
{
|
||||
EnsureSprayBuffers();
|
||||
|
||||
System.Array.Clear(_sprayOpacity, 0, _sprayOpacity.Length);
|
||||
|
||||
int dots = Mathf.Max(1, sprayDots);
|
||||
float sizeLo = Mathf.Clamp(Mathf.Min(sprayDotSizeMin, sprayDotSizeMax), 0.01f, 1f);
|
||||
float sizeHi = Mathf.Clamp(Mathf.Max(sprayDotSizeMin, sprayDotSizeMax), 0.01f, 1f);
|
||||
float alphaLo = Mathf.Clamp01(Mathf.Min(sprayDotAlphaMin, sprayDotAlphaMax));
|
||||
float alphaHi = Mathf.Clamp01(Mathf.Max(sprayDotAlphaMin, sprayDotAlphaMax));
|
||||
|
||||
for (int k = 0; k < dots; k++)
|
||||
{
|
||||
// Diameter is a fraction of the brush diameter -> uv radius = fraction * 0.5.
|
||||
float dotR = Mathf.Max(0.5f / SprayRes, Random.Range(sizeLo, sizeHi) * 0.5f);
|
||||
float alpha = Random.Range(alphaLo, alphaHi);
|
||||
// Keep the WHOLE dot inside the brush disc (uv radius 0.5 = the brush radius) so the
|
||||
// spray footprint matches the normal brush instead of bleeding past it by dotR.
|
||||
float centerR = Mathf.Max(0f, 0.5f - dotR);
|
||||
Vector2 c = Random.insideUnitCircle * centerR + new Vector2(0.5f, 0.5f);
|
||||
|
||||
SplatDot(c.x, c.y, dotR, alpha);
|
||||
}
|
||||
|
||||
for (int i = 0; i < _sprayOpacity.Length; i++)
|
||||
{
|
||||
byte g = (byte)(Mathf.Clamp01(_sprayOpacity[i]) * 255f + 0.5f);
|
||||
_sprayPixels[i] = new Color32(g, g, g, 255);
|
||||
}
|
||||
_sprayTex.SetPixels32(_sprayPixels);
|
||||
_sprayTex.Apply(false);
|
||||
|
||||
_stampMask = _sprayTex;
|
||||
_stampOpacity = _sprayOpacity;
|
||||
_stampW = SprayRes;
|
||||
_stampH = SprayRes;
|
||||
}
|
||||
|
||||
/// <summary>Rasterizes one soft dot (center/radius in uv, value = alpha) into the opacity buffer, compositing by max.</summary>
|
||||
private void SplatDot(float cx, float cy, float r, float alpha)
|
||||
{
|
||||
if (alpha <= 0f) return;
|
||||
|
||||
float inner = 1f - SprayDotSoftEdge; // solid core up to this normalized distance, then fade
|
||||
int minX = Mathf.Max(0, Mathf.FloorToInt((cx - r) * SprayRes));
|
||||
int maxX = Mathf.Min(SprayRes - 1, Mathf.CeilToInt((cx + r) * SprayRes));
|
||||
int minY = Mathf.Max(0, Mathf.FloorToInt((cy - r) * SprayRes));
|
||||
int maxY = Mathf.Min(SprayRes - 1, Mathf.CeilToInt((cy + r) * SprayRes));
|
||||
float invR = 1f / r;
|
||||
|
||||
for (int y = minY; y <= maxY; y++)
|
||||
{
|
||||
float vY = (y + 0.5f) / SprayRes;
|
||||
int row = y * SprayRes;
|
||||
for (int x = minX; x <= maxX; x++)
|
||||
{
|
||||
float vX = (x + 0.5f) / SprayRes;
|
||||
float dx = vX - cx, dy = vY - cy;
|
||||
float dist = Mathf.Sqrt(dx * dx + dy * dy) * invR; // 0 = center, 1 = edge
|
||||
if (dist >= 1f) continue;
|
||||
|
||||
float falloff = dist <= inner ? 1f : 1f - Mathf.SmoothStep(inner, 1f, dist);
|
||||
float val = alpha * falloff;
|
||||
int idx = row + x;
|
||||
if (val > _sprayOpacity[idx]) _sprayOpacity[idx] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureSprayBuffers()
|
||||
{
|
||||
if (_sprayTex) return;
|
||||
|
||||
_sprayTex = new Texture2D(SprayRes, SprayRes, TextureFormat.RGBA32, false, true) // linear: raw mask values
|
||||
{
|
||||
name = "MeshPainter_SprayMask",
|
||||
wrapMode = TextureWrapMode.Clamp,
|
||||
filterMode = FilterMode.Bilinear,
|
||||
hideFlags = HideFlags.HideAndDontSave
|
||||
};
|
||||
_sprayPixels = new Color32[SprayRes * SprayRes];
|
||||
_sprayOpacity = new float[SprayRes * SprayRes];
|
||||
}
|
||||
|
||||
private void TeardownSpray()
|
||||
{
|
||||
if (_sprayTex) { Destroy(_sprayTex); _sprayTex = null; }
|
||||
_sprayPixels = null;
|
||||
_sprayOpacity = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b607cbaf7556b3489d0ccfbd4f926dc
|
||||
@@ -85,12 +85,24 @@ public partial class MeshPainter : MonoBehaviour
|
||||
[Range(0f, 1f)]
|
||||
public float smoothness = 0.5f;
|
||||
|
||||
[Tooltip("Paint opacity for metallic + smoothness on top of the mask (1 = mask opacity " +
|
||||
"as-is, lower = blends the metallic/smoothness target in more faintly, 0 = no-op). " +
|
||||
"The albedo equivalent of brushColor.a, but for the metallic/smoothness channels.")]
|
||||
[Range(0f, 1f)]
|
||||
public float metallicSmoothnessOpacity = 1f;
|
||||
|
||||
[Tooltip("Ambient occlusion painted into the mask map (1 = fully lit / no darkening, " +
|
||||
"0 = fully occluded / dark). NOTE: URP only darkens INDIRECT light (ambient + " +
|
||||
"reflections), so the effect is subtle under strong direct lighting. 1 = no-op.")]
|
||||
[Range(0f, 1f)]
|
||||
public float occlusion = 0.5f;
|
||||
|
||||
[Tooltip("Paint opacity for occlusion on top of the mask (1 = mask opacity as-is, lower = " +
|
||||
"blends the occlusion target in more faintly, 0 = no-op). The albedo equivalent of " +
|
||||
"brushColor.a, but for the occlusion channel.")]
|
||||
[Range(0f, 1f)]
|
||||
public float occlusionOpacity = 1f;
|
||||
|
||||
[Header("Brush - emission")]
|
||||
[Tooltip("Emission color painted into the map. Alpha = global opacity multiplier on top of the mask.")]
|
||||
public Color emissionColor = Color.black;
|
||||
@@ -123,6 +135,13 @@ public partial class MeshPainter : MonoBehaviour
|
||||
"stationary button no longer keeps re-stamping the same spot). OFF = continuous painting while held.")]
|
||||
public bool paintOnMoveOnly = false;
|
||||
|
||||
[Tooltip("Seconds between paint stamps while the button is held. 0 = paint every frame as " +
|
||||
"an interpolated, continuous stroke (as before). Above 0 = deposit one stamp at the " +
|
||||
"cursor every this many seconds (timed dabs, no movement interpolation - pairs well " +
|
||||
"with the spray can).")]
|
||||
[Min(0f)]
|
||||
public float paintInterval = 0f;
|
||||
|
||||
[Header("Preview")]
|
||||
[Tooltip("Brush preview at the hover point (shows size + mask without painting). " +
|
||||
"Flat Decal = a flat quad in the tangent plane (cheap). On Surface = a real " +
|
||||
@@ -188,6 +207,7 @@ public partial class MeshPainter : MonoBehaviour
|
||||
|
||||
private bool _hasLastScreen;
|
||||
private Vector2 _lastScreen;
|
||||
private float _intervalTimer; // accumulates while held; gates timed-dab stamping (paintInterval > 0)
|
||||
private bool _gpuActive;
|
||||
private bool _setupValid;
|
||||
|
||||
@@ -385,24 +405,24 @@ public partial class MeshPainter : MonoBehaviour
|
||||
return px;
|
||||
}
|
||||
|
||||
/// <summary>Bilinear sample of the resolved mask opacity at uv in [0,1]. Returns 1 (full) if no mask.</summary>
|
||||
/// <summary>Bilinear sample of the active stamp mask opacity at uv in [0,1]. Returns 1 (full) if no mask.</summary>
|
||||
private float SampleMaskOpacity(float u, float v)
|
||||
{
|
||||
if (_maskOpacity == null || _maskW <= 0 || _maskH <= 0) return 1f;
|
||||
if (_stampOpacity == null || _stampW <= 0 || _stampH <= 0) return 1f;
|
||||
|
||||
float fx = Mathf.Clamp01(u) * (_maskW - 1);
|
||||
float fy = Mathf.Clamp01(v) * (_maskH - 1);
|
||||
float fx = Mathf.Clamp01(u) * (_stampW - 1);
|
||||
float fy = Mathf.Clamp01(v) * (_stampH - 1);
|
||||
int x0 = Mathf.FloorToInt(fx);
|
||||
int y0 = Mathf.FloorToInt(fy);
|
||||
int x1 = Mathf.Min(x0 + 1, _maskW - 1);
|
||||
int y1 = Mathf.Min(y0 + 1, _maskH - 1);
|
||||
int x1 = Mathf.Min(x0 + 1, _stampW - 1);
|
||||
int y1 = Mathf.Min(y0 + 1, _stampH - 1);
|
||||
float tx = fx - x0;
|
||||
float ty = fy - y0;
|
||||
|
||||
float l00 = _maskOpacity[y0 * _maskW + x0];
|
||||
float l10 = _maskOpacity[y0 * _maskW + x1];
|
||||
float l01 = _maskOpacity[y1 * _maskW + x0];
|
||||
float l11 = _maskOpacity[y1 * _maskW + x1];
|
||||
float l00 = _stampOpacity[y0 * _stampW + x0];
|
||||
float l10 = _stampOpacity[y0 * _stampW + x1];
|
||||
float l01 = _stampOpacity[y1 * _stampW + x0];
|
||||
float l11 = _stampOpacity[y1 * _stampW + x1];
|
||||
|
||||
float a = Mathf.Lerp(l00, l10, tx);
|
||||
float b = Mathf.Lerp(l01, l11, tx);
|
||||
@@ -509,7 +529,9 @@ public partial class MeshPainter : MonoBehaviour
|
||||
return;
|
||||
|
||||
_previewMat.SetColor(BrushColorId, brushColor);
|
||||
_previewMat.SetTexture(MaskTexId, _resolvedMask);
|
||||
// Spray paints scattered dots, but the preview shows the round footprint (the area
|
||||
// the dots land in), so the hover ring stays a stable, readable size indicator.
|
||||
_previewMat.SetTexture(MaskTexId, spray ? _proceduralMask : _resolvedMask);
|
||||
_previewMat.SetFloat(PreviewOpacityId, Mathf.Max(0f, previewOpacity) * Mathf.Clamp01(brushColor.a));
|
||||
|
||||
int layer = gameObject.layer;
|
||||
@@ -598,18 +620,39 @@ public partial class MeshPainter : MonoBehaviour
|
||||
if (!paintCamera) return;
|
||||
|
||||
bool anyChannel = AnyChannelActive(); // includes occlusion (matches IsChannelActive / PaintStamp3D)
|
||||
float moveDist = _hasLastScreen ? Vector2.Distance(_lastScreen, screenPos) : 0f;
|
||||
bool firstPress = !_hasLastScreen;
|
||||
float moveDist = firstPress ? 0f : Vector2.Distance(_lastScreen, screenPos);
|
||||
|
||||
// Move-only mode: stamp once on mouse-down, then only when the mouse actually
|
||||
// moves - a held but stationary button no longer re-stamps the same spot.
|
||||
bool stamp = anyChannel && (!_hasLastScreen || !paintOnMoveOnly || moveDist > MoveOnlyMinPixels);
|
||||
bool stamp;
|
||||
if (paintInterval > 0f)
|
||||
{
|
||||
// Timed-dab mode: one stamp on press, then one at the cursor every paintInterval
|
||||
// seconds while held (discrete dabs, no movement interpolation).
|
||||
if (firstPress)
|
||||
{
|
||||
_intervalTimer = 0f;
|
||||
stamp = anyChannel;
|
||||
}
|
||||
else
|
||||
{
|
||||
_intervalTimer += Time.deltaTime;
|
||||
stamp = anyChannel && _intervalTimer >= paintInterval;
|
||||
if (stamp) _intervalTimer = 0f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Continuous mode: stamp every frame as an interpolated stroke. Move-only optionally
|
||||
// gates it - a held but stationary button no longer re-stamps the same spot.
|
||||
stamp = anyChannel && (firstPress || !paintOnMoveOnly || moveDist > MoveOnlyMinPixels);
|
||||
}
|
||||
|
||||
if (stamp)
|
||||
{
|
||||
if (_gpuActive) BeginGpuStroke();
|
||||
else ResetCpuDirty();
|
||||
|
||||
if (_hasLastScreen)
|
||||
if (!firstPress && paintInterval <= 0f)
|
||||
{
|
||||
// Interpolate along the mouse movement and re-raycast at each step so the
|
||||
// stroke follows the mesh surface.
|
||||
@@ -619,6 +662,7 @@ public partial class MeshPainter : MonoBehaviour
|
||||
}
|
||||
else
|
||||
{
|
||||
// First stamp of a stroke, or a timed dab: a single discrete stamp at the cursor.
|
||||
PaintAtScreen(screenPos);
|
||||
}
|
||||
|
||||
@@ -637,6 +681,11 @@ public partial class MeshPainter : MonoBehaviour
|
||||
out Vector3 u, out Vector3 v, out Vector3 nLocal))
|
||||
return;
|
||||
|
||||
// Pick what this stamp paints with: a fresh random spray pattern, or the resolved
|
||||
// standard mask. Both backends read it through the _stamp* fields.
|
||||
if (spray) BuildSprayStampMask();
|
||||
else UseResolvedStampMask();
|
||||
|
||||
if (_gpuActive) DrawBrushGpu(pLocal, radiusLocal, u, v, nLocal);
|
||||
else PaintStamp3D(pLocal, radiusLocal, u, v, nLocal);
|
||||
}
|
||||
@@ -761,6 +810,7 @@ public partial class MeshPainter : MonoBehaviour
|
||||
if (_proceduralMask) Destroy(_proceduralMask);
|
||||
if (_previewMat) Destroy(_previewMat);
|
||||
if (_previewQuad) Destroy(_previewQuad);
|
||||
TeardownSpray();
|
||||
TeardownCpu();
|
||||
TeardownGpu();
|
||||
}
|
||||
|
||||
@@ -51,7 +51,9 @@ public class PaintHud : MonoBehaviour
|
||||
private const string PickerEmission = "Picker_Emission";
|
||||
private const string SliderMetallic = "Slider_Metallic";
|
||||
private const string SliderSmoothness = "Slider_Smoothness";
|
||||
private const string SliderMetallicSmoothnessAlpha = "Slider_MetallicSmoothnessAlpha";
|
||||
private const string SliderOcclusion = "Slider_Occlusion";
|
||||
private const string SliderOcclusionAlpha = "Slider_OcclusionAlpha";
|
||||
private const string SliderEmissionIntensity = "Slider_EmissionIntensity";
|
||||
private const string SliderBrushSize = "Slider_BrushSize";
|
||||
|
||||
@@ -168,14 +170,16 @@ public class PaintHud : MonoBehaviour
|
||||
WirePanelFade(panel, PanelEmission, ToggleEmission);
|
||||
|
||||
WirePicker(panel, PickerAlbedo, painter ? painter.brushColor : Color.red,
|
||||
c => { if (painter) painter.brushColor = new Color(c.r, c.g, c.b, painter.brushColor.a); });
|
||||
c => { if (painter) painter.brushColor = c; });
|
||||
|
||||
WireSlider(panel, SliderMetallic, painter ? painter.metallic : 0f, v => { if (painter) painter.metallic = v; });
|
||||
WireSlider(panel, SliderSmoothness, painter ? painter.smoothness : 0.5f, v => { if (painter) painter.smoothness = v; });
|
||||
WireSlider(panel, SliderMetallicSmoothnessAlpha, painter ? painter.metallicSmoothnessOpacity : 1f, v => { if (painter) painter.metallicSmoothnessOpacity = v; });
|
||||
WireSlider(panel, SliderOcclusion, painter ? painter.occlusion : 0.5f, v => { if (painter) painter.occlusion = v; });
|
||||
WireSlider(panel, SliderOcclusionAlpha, painter ? painter.occlusionOpacity : 1f, v => { if (painter) painter.occlusionOpacity = v; });
|
||||
|
||||
WirePicker(panel, PickerEmission, painter ? painter.emissionColor : Color.black,
|
||||
c => { if (painter) painter.emissionColor = new Color(c.r, c.g, c.b, painter.emissionColor.a); });
|
||||
c => { if (painter) painter.emissionColor = c; });
|
||||
WireSlider(panel, SliderEmissionIntensity, painter ? painter.emissionIntensity : 1f, v => { if (painter) painter.emissionIntensity = v; });
|
||||
|
||||
WireSlider(panel, SliderBrushSize, painter ? painter.brushSize : 16f, v => { if (painter) painter.brushSize = Mathf.RoundToInt(v); });
|
||||
@@ -310,7 +314,7 @@ public class PaintHud : MonoBehaviour
|
||||
}
|
||||
|
||||
// Cached gradient sprites for the colour picker.
|
||||
private static Sprite _satSprite, _valSprite, _hueSprite;
|
||||
private static Sprite _satSprite, _valSprite, _hueSprite, _alphaSprite;
|
||||
|
||||
/// <summary>Horizontal white-out: opaque white (left) -> transparent (right) = saturation.</summary>
|
||||
public static Sprite SaturationSprite()
|
||||
@@ -344,6 +348,23 @@ public class PaintHud : MonoBehaviour
|
||||
return _valSprite;
|
||||
}
|
||||
|
||||
/// <summary>Horizontal white transparency ramp: transparent (left) -> opaque (right) = alpha.
|
||||
/// Tinted to the current colour by the picker so it reads as a colour-over-backdrop fade.</summary>
|
||||
public static Sprite AlphaSprite()
|
||||
{
|
||||
if (_alphaSprite) return _alphaSprite;
|
||||
const int n = 64;
|
||||
var tex = NewGradientTex(n, 1);
|
||||
for (int x = 0; x < n; x++)
|
||||
{
|
||||
byte a = (byte)Mathf.RoundToInt(255f * (x / (float)(n - 1)));
|
||||
tex.SetPixel(x, 0, new Color32(255, 255, 255, a));
|
||||
}
|
||||
tex.Apply(false);
|
||||
_alphaSprite = ToSprite(tex);
|
||||
return _alphaSprite;
|
||||
}
|
||||
|
||||
/// <summary>Horizontal full-saturation hue ramp 0..1.</summary>
|
||||
public static Sprite HueSprite()
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user