diff --git a/Assets/Materials.meta b/Assets/Materials.meta
new file mode 100644
index 0000000..789acc1
--- /dev/null
+++ b/Assets/Materials.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 9977e3f116d56d546ac6d24e57d3684a
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Materials/Blue Mirror.mat b/Assets/Materials/Blue Mirror.mat
new file mode 100644
index 0000000..218cbf8
--- /dev/null
+++ b/Assets/Materials/Blue Mirror.mat
@@ -0,0 +1,137 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+ serializedVersion: 8
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_Name: Blue Mirror
+ m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
+ m_Parent: {fileID: 0}
+ m_ModifiedSerializedProperties: 0
+ m_ValidKeywords: []
+ m_InvalidKeywords: []
+ m_LightmapFlags: 4
+ m_EnableInstancingVariants: 0
+ m_DoubleSidedGI: 0
+ m_CustomRenderQueue: -1
+ stringTagMap:
+ RenderType: Opaque
+ disabledShaderPasses:
+ - MOTIONVECTORS
+ m_LockedProperties:
+ m_SavedProperties:
+ serializedVersion: 3
+ m_TexEnvs:
+ - _BaseMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _BumpMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailAlbedoMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailMask:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailNormalMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _EmissionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MainTex:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MetallicGlossMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _OcclusionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _ParallaxMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _SpecGlossMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - unity_Lightmaps:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - unity_LightmapsInd:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - unity_ShadowMasks:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ m_Ints: []
+ m_Floats:
+ - _AddPrecomputedVelocity: 0
+ - _AlphaClip: 0
+ - _AlphaToMask: 0
+ - _Blend: 0
+ - _BlendModePreserveSpecular: 1
+ - _BumpScale: 1
+ - _ClearCoatMask: 0
+ - _ClearCoatSmoothness: 0
+ - _Cull: 2
+ - _Cutoff: 0.5
+ - _DetailAlbedoMapScale: 1
+ - _DetailNormalMapScale: 1
+ - _DstBlend: 0
+ - _DstBlendAlpha: 0
+ - _EnvironmentReflections: 1
+ - _GlossMapScale: 0
+ - _Glossiness: 0
+ - _GlossyReflections: 0
+ - _Metallic: 1
+ - _OcclusionStrength: 1
+ - _Parallax: 0.005
+ - _QueueOffset: 0
+ - _ReceiveShadows: 1
+ - _Smoothness: 1
+ - _SmoothnessTextureChannel: 0
+ - _SpecularHighlights: 1
+ - _SrcBlend: 1
+ - _SrcBlendAlpha: 1
+ - _Surface: 0
+ - _WorkflowMode: 1
+ - _XRMotionVectorsPass: 1
+ - _ZWrite: 1
+ m_Colors:
+ - _BaseColor: {r: 0.2591703, g: 0.22327036, b: 1, a: 1}
+ - _Color: {r: 0.25917026, g: 0.22327033, b: 1, a: 1}
+ - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+ - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
+ m_BuildTextureStacks: []
+ m_AllowLocking: 1
+--- !u!114 &5288681647234694520
+MonoBehaviour:
+ m_ObjectHideFlags: 11
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
+ version: 10
diff --git a/Assets/Materials/Blue Mirror.mat.meta b/Assets/Materials/Blue Mirror.mat.meta
new file mode 100644
index 0000000..547062f
--- /dev/null
+++ b/Assets/Materials/Blue Mirror.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 27e7e012d216e0143b94df13179d62dc
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 2100000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Materials/Red Metal.mat b/Assets/Materials/Red Metal.mat
new file mode 100644
index 0000000..1ade886
--- /dev/null
+++ b/Assets/Materials/Red Metal.mat
@@ -0,0 +1,137 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+ serializedVersion: 8
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_Name: Red Metal
+ m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
+ m_Parent: {fileID: 0}
+ m_ModifiedSerializedProperties: 0
+ m_ValidKeywords: []
+ m_InvalidKeywords: []
+ m_LightmapFlags: 4
+ m_EnableInstancingVariants: 0
+ m_DoubleSidedGI: 0
+ m_CustomRenderQueue: -1
+ stringTagMap:
+ RenderType: Opaque
+ disabledShaderPasses:
+ - MOTIONVECTORS
+ m_LockedProperties:
+ m_SavedProperties:
+ serializedVersion: 3
+ m_TexEnvs:
+ - _BaseMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _BumpMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailAlbedoMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailMask:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailNormalMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _EmissionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MainTex:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MetallicGlossMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _OcclusionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _ParallaxMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _SpecGlossMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - unity_Lightmaps:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - unity_LightmapsInd:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - unity_ShadowMasks:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ m_Ints: []
+ m_Floats:
+ - _AddPrecomputedVelocity: 0
+ - _AlphaClip: 0
+ - _AlphaToMask: 0
+ - _Blend: 0
+ - _BlendModePreserveSpecular: 1
+ - _BumpScale: 1
+ - _ClearCoatMask: 0
+ - _ClearCoatSmoothness: 0
+ - _Cull: 2
+ - _Cutoff: 0.5
+ - _DetailAlbedoMapScale: 1
+ - _DetailNormalMapScale: 1
+ - _DstBlend: 0
+ - _DstBlendAlpha: 0
+ - _EnvironmentReflections: 1
+ - _GlossMapScale: 0
+ - _Glossiness: 0
+ - _GlossyReflections: 0
+ - _Metallic: 1
+ - _OcclusionStrength: 1
+ - _Parallax: 0.005
+ - _QueueOffset: 0
+ - _ReceiveShadows: 1
+ - _Smoothness: 0.75
+ - _SmoothnessTextureChannel: 0
+ - _SpecularHighlights: 1
+ - _SrcBlend: 1
+ - _SrcBlendAlpha: 1
+ - _Surface: 0
+ - _WorkflowMode: 1
+ - _XRMotionVectorsPass: 1
+ - _ZWrite: 1
+ m_Colors:
+ - _BaseColor: {r: 1, g: 0, b: 0, a: 1}
+ - _Color: {r: 1, g: 0, b: 0, a: 1}
+ - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+ - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
+ m_BuildTextureStacks: []
+ m_AllowLocking: 1
+--- !u!114 &5288681647234694520
+MonoBehaviour:
+ m_ObjectHideFlags: 11
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
+ version: 10
diff --git a/Assets/Materials/Red Metal.mat.meta b/Assets/Materials/Red Metal.mat.meta
new file mode 100644
index 0000000..d571e3b
--- /dev/null
+++ b/Assets/Materials/Red Metal.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8d826f81581c9314fb1c2cd886cd78c1
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 2100000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Materials/Yellow Glow.mat b/Assets/Materials/Yellow Glow.mat
new file mode 100644
index 0000000..a27cea7
--- /dev/null
+++ b/Assets/Materials/Yellow Glow.mat
@@ -0,0 +1,138 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+ serializedVersion: 8
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_Name: Yellow Glow
+ m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
+ m_Parent: {fileID: 0}
+ m_ModifiedSerializedProperties: 0
+ m_ValidKeywords:
+ - _EMISSION
+ m_InvalidKeywords: []
+ m_LightmapFlags: 2
+ m_EnableInstancingVariants: 0
+ m_DoubleSidedGI: 0
+ m_CustomRenderQueue: -1
+ stringTagMap:
+ RenderType: Opaque
+ disabledShaderPasses:
+ - MOTIONVECTORS
+ m_LockedProperties: _BaseColor _BaseMap
+ m_SavedProperties:
+ serializedVersion: 3
+ m_TexEnvs:
+ - _BaseMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _BumpMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailAlbedoMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailMask:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailNormalMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _EmissionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MainTex:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MetallicGlossMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _OcclusionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _ParallaxMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _SpecGlossMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - unity_Lightmaps:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - unity_LightmapsInd:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - unity_ShadowMasks:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ m_Ints: []
+ m_Floats:
+ - _AddPrecomputedVelocity: 0
+ - _AlphaClip: 0
+ - _AlphaToMask: 0
+ - _Blend: 0
+ - _BlendModePreserveSpecular: 1
+ - _BumpScale: 1
+ - _ClearCoatMask: 0
+ - _ClearCoatSmoothness: 0
+ - _Cull: 2
+ - _Cutoff: 0.5
+ - _DetailAlbedoMapScale: 1
+ - _DetailNormalMapScale: 1
+ - _DstBlend: 0
+ - _DstBlendAlpha: 0
+ - _EnvironmentReflections: 1
+ - _GlossMapScale: 0
+ - _Glossiness: 0
+ - _GlossyReflections: 0
+ - _Metallic: 0
+ - _OcclusionStrength: 1
+ - _Parallax: 0.005
+ - _QueueOffset: 0
+ - _ReceiveShadows: 1
+ - _Smoothness: 0
+ - _SmoothnessTextureChannel: 0
+ - _SpecularHighlights: 1
+ - _SrcBlend: 1
+ - _SrcBlendAlpha: 1
+ - _Surface: 0
+ - _WorkflowMode: 1
+ - _XRMotionVectorsPass: 1
+ - _ZWrite: 1
+ m_Colors:
+ - _BaseColor: {r: 1, g: 0.85953015, b: 0.4433961, a: 1}
+ - _Color: {r: 1, g: 0.85953015, b: 0.44339606, a: 1}
+ - _EmissionColor: {r: 89.45407, g: 89.45407, b: 0, a: 1}
+ - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
+ m_BuildTextureStacks: []
+ m_AllowLocking: 1
+--- !u!114 &5288681647234694520
+MonoBehaviour:
+ m_ObjectHideFlags: 11
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
+ version: 10
diff --git a/Assets/Materials/Yellow Glow.mat.meta b/Assets/Materials/Yellow Glow.mat.meta
new file mode 100644
index 0000000..99afacc
--- /dev/null
+++ b/Assets/Materials/Yellow Glow.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3bc5b774ad58c8740b5705e96c54ade6
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 2100000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity
index 5bcbbf3..a9a7342 100644
--- a/Assets/Scenes/SampleScene.unity
+++ b/Assets/Scenes/SampleScene.unity
@@ -446,6 +446,7 @@ MonoBehaviour:
brushShader: {fileID: 0}
dilateShader: {fileID: 0}
packMsShader: {fileID: 0}
+ pipette: 0
spray: 0
sprayDots: 24
sprayDotSizeMin: 0.05
@@ -460,7 +461,9 @@ MonoBehaviour:
brushColor: {r: 0.6, g: 0.6, b: 1, a: 1}
metallic: 1
smoothness: 1
+ metallicSmoothnessOpacity: 1
occlusion: 1
+ occlusionOpacity: 1
emissionColor: {r: 1, g: 0.6, b: 0, a: 1}
emissionIntensity: 200
brushSize: 100
@@ -1352,6 +1355,118 @@ Transform:
m_CorrespondingSourceObject: {fileID: 6779641804511181039, guid: 930b7a19d446f4b4c9c810e37e84e625, type: 3}
m_PrefabInstance: {fileID: 788444}
m_PrefabAsset: {fileID: 0}
+--- !u!1 &468175225
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 468175226}
+ - component: {fileID: 468175229}
+ - component: {fileID: 468175228}
+ - component: {fileID: 468175227}
+ m_Layer: 0
+ m_Name: Sphere
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &468175226
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 468175225}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: -1, y: 0.5, z: 1}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 686187287}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!135 &468175227
+SphereCollider:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 468175225}
+ m_Material: {fileID: 0}
+ m_IncludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_ExcludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_LayerOverridePriority: 0
+ m_IsTrigger: 0
+ m_ProvidesContacts: 0
+ m_Enabled: 1
+ serializedVersion: 3
+ m_Radius: 0.5
+ m_Center: {x: 0, y: 0, z: 0}
+--- !u!23 &468175228
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 468175225}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RayTracingAccelStructBuildFlagsOverride: 0
+ m_RayTracingAccelStructBuildFlags: 1
+ m_SmallMeshCulling: 1
+ m_ForceMeshLod: -1
+ m_MeshLodSelectionBias: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: 2100000, guid: 8d826f81581c9314fb1c2cd886cd78c1, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_GlobalIlluminationMeshLod: 0
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_MaskInteraction: 0
+ m_AdditionalVertexStreams: {fileID: 0}
+--- !u!33 &468175229
+MeshFilter:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 468175225}
+ m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
--- !u!4 &523455893 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 4597260953850914690, guid: 930b7a19d446f4b4c9c810e37e84e625, type: 3}
@@ -1419,6 +1534,7 @@ MonoBehaviour:
brushShader: {fileID: 0}
dilateShader: {fileID: 0}
packMsShader: {fileID: 0}
+ pipette: 0
spray: 0
sprayDots: 24
sprayDotSizeMin: 0.05
@@ -1433,7 +1549,9 @@ MonoBehaviour:
brushColor: {r: 0.6, g: 0.6, b: 1, a: 1}
metallic: 1
smoothness: 1
+ metallicSmoothnessOpacity: 1
occlusion: 1
+ occlusionOpacity: 1
emissionColor: {r: 1, g: 0.6, b: 0, a: 1}
emissionIntensity: 20
brushSize: 50
@@ -1492,6 +1610,9 @@ Transform:
- {fileID: 1478475841}
- {fileID: 1746495279}
- {fileID: 200502122}
+ - {fileID: 468175226}
+ - {fileID: 1504466455}
+ - {fileID: 829281441}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!4 &691617530 stripped
@@ -1762,6 +1883,118 @@ PrefabInstance:
insertIndex: -1
addedObject: {fileID: 682237521}
m_SourcePrefab: {fileID: 100100000, guid: 554c0bdf86263754d862d7effd04045b, type: 3}
+--- !u!1 &829281440
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 829281441}
+ - component: {fileID: 829281444}
+ - component: {fileID: 829281443}
+ - component: {fileID: 829281442}
+ m_Layer: 0
+ m_Name: Sphere
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &829281441
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 829281440}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: -2, y: 0.5, z: 1}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 686187287}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!135 &829281442
+SphereCollider:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 829281440}
+ m_Material: {fileID: 0}
+ m_IncludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_ExcludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_LayerOverridePriority: 0
+ m_IsTrigger: 0
+ m_ProvidesContacts: 0
+ m_Enabled: 1
+ serializedVersion: 3
+ m_Radius: 0.5
+ m_Center: {x: 0, y: 0, z: 0}
+--- !u!23 &829281443
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 829281440}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RayTracingAccelStructBuildFlagsOverride: 0
+ m_RayTracingAccelStructBuildFlags: 1
+ m_SmallMeshCulling: 1
+ m_ForceMeshLod: -1
+ m_MeshLodSelectionBias: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: 2100000, guid: 27e7e012d216e0143b94df13179d62dc, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_GlobalIlluminationMeshLod: 0
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_MaskInteraction: 0
+ m_AdditionalVertexStreams: {fileID: 0}
+--- !u!33 &829281444
+MeshFilter:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 829281440}
+ m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
--- !u!1 &832575517
GameObject:
m_ObjectHideFlags: 0
@@ -2635,6 +2868,118 @@ Transform:
m_Children: []
m_Father: {fileID: 686187287}
m_LocalEulerAnglesHint: {x: 60, y: 0, z: 0}
+--- !u!1 &1504466454
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1504466455}
+ - component: {fileID: 1504466458}
+ - component: {fileID: 1504466457}
+ - component: {fileID: 1504466456}
+ m_Layer: 0
+ m_Name: Sphere (1)
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1504466455
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1504466454}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 1.5, y: 0.5, z: 1}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 686187287}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!135 &1504466456
+SphereCollider:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1504466454}
+ m_Material: {fileID: 0}
+ m_IncludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_ExcludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_LayerOverridePriority: 0
+ m_IsTrigger: 0
+ m_ProvidesContacts: 0
+ m_Enabled: 1
+ serializedVersion: 3
+ m_Radius: 0.5
+ m_Center: {x: 0, y: 0, z: 0}
+--- !u!23 &1504466457
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1504466454}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RayTracingAccelStructBuildFlagsOverride: 0
+ m_RayTracingAccelStructBuildFlags: 1
+ m_SmallMeshCulling: 1
+ m_ForceMeshLod: -1
+ m_MeshLodSelectionBias: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: 2100000, guid: 3bc5b774ad58c8740b5705e96c54ade6, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_GlobalIlluminationMeshLod: 0
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_MaskInteraction: 0
+ m_AdditionalVertexStreams: {fileID: 0}
+--- !u!33 &1504466458
+MeshFilter:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1504466454}
+ m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
--- !u!1 &1516578026
GameObject:
m_ObjectHideFlags: 0
@@ -2770,6 +3115,7 @@ MonoBehaviour:
brushShader: {fileID: 0}
dilateShader: {fileID: 0}
packMsShader: {fileID: 0}
+ pipette: 0
spray: 0
sprayDots: 24
sprayDotSizeMin: 0.05
@@ -2784,7 +3130,9 @@ MonoBehaviour:
brushColor: {r: 0.6, g: 0.6, b: 1, a: 1}
metallic: 1
smoothness: 1
+ metallicSmoothnessOpacity: 1
occlusion: 1
+ occlusionOpacity: 1
emissionColor: {r: 1, g: 0.6, b: 0, a: 1}
emissionIntensity: 200
brushSize: 100
@@ -3685,8 +4033,8 @@ SceneRoots:
- {fileID: 172071436}
- {fileID: 832575519}
- {fileID: 410087041}
+ - {fileID: 2025317892}
- {fileID: 1141039639}
- {fileID: 1129539841}
- {fileID: 1006134333}
- {fileID: 686187287}
- - {fileID: 2025317892}
diff --git a/Assets/Scripts/MeshPainter.Pick.cs b/Assets/Scripts/MeshPainter.Pick.cs
index ca54ce9..a0f5499 100644
--- a/Assets/Scripts/MeshPainter.Pick.cs
+++ b/Assets/Scripts/MeshPainter.Pick.cs
@@ -6,16 +6,23 @@ public partial class MeshPainter
// --- Pipette (eyedropper) ---
//
// A sampling MODE rather than a paint channel: while active, a left-click raycasts onto
- // the mesh, reads ALL painted channel values at the hit texel (albedo, metallic,
- // smoothness, occlusion, emission) and adopts them into the brush properties - with every
- // opacity/alpha forced to 1 so re-painting reproduces the picked look at full strength.
- // The values are read through ReadCanonical(), which already converts both backends (GPU
- // RenderTextures / CPU Texture2D mirrors) into the same canonical material layout, so the
- // pick matches what is stored without any new colour-space handling.
+ // ANY surface (not just the paint mesh) and reads every channel value at the hit point
+ // (albedo, metallic, smoothness, occlusion, emission) into the brush properties - with
+ // every opacity/alpha forced to 1 so re-painting reproduces the picked look at full
+ // strength.
+ //
+ // Two read paths, chosen by what was hit:
+ // - Our own paint mesh: read texel-exact from the painted maps via ReadCanonical(),
+ // which converts both backends (GPU RenderTextures / CPU Texture2D mirrors) into the
+ // same canonical layout, so the pick matches what is stored bit-for-bit.
+ // - Any foreign object: read texel-exact from its material maps at the hit UV
+ // (BaseMap/MetallicGloss/Occlusion/Emission) combined with the material scalars, per
+ // the URP/Lit conventions, falling back to scalar properties where a map is absent or
+ // the collider carries no UV (non-MeshCollider).
[Header("Brush - pipette (eyedropper)")]
- [Tooltip("Pipette mode: a click samples ALL channel values at the hit point (albedo, " +
- "metallic, smoothness, occlusion, emission) and adopts them into the brush " +
+ [Tooltip("Pipette mode: a click samples ALL channel values at the hit point on ANY surface " +
+ "(albedo, metallic, smoothness, occlusion, emission) and adopts them into the brush " +
"(all alphas set to 1) instead of painting. Held button picks only once.")]
public bool pipette = false;
@@ -28,18 +35,42 @@ public partial class MeshPainter
private bool _pickHeld; // edge tracking so a held button picks once, not every frame
///
- /// Raycasts at a screen position, finds the hit texel UV and copies every painted channel
- /// value there into the brush (albedo color, metallic, smoothness, occlusion, emission
- /// color), forcing all opacities/alphas to 1. Reads regardless of which channels are
- /// currently toggled for painting.
+ /// Raycasts at a screen position against ANY collider, samples every channel value at the hit
+ /// (texel-exact where possible) and copies it into the brush, forcing all opacities/alphas to
+ /// 1 and enabling every paint channel. Reads regardless of which channels are currently toggled.
///
private void PickAtScreen(Vector2 screenPos)
{
- if (!TryRaycastPaintMesh(screenPos, out RaycastHit hit)) return;
- if (_verts == null || _uvs == null || _tris == null || hit.triangleIndex < 0) return;
+ if (!TryRaycastAny(screenPos, out RaycastHit hit)) return;
+
+ bool ok = (hit.collider == _collider)
+ ? PickFromPaintTextures(hit)
+ : PickFromForeignMaterial(hit);
+ if (!ok) return;
+
+ // "Set all alpha values to 1": full-strength adoption across every opacity control.
+ metallicSmoothnessOpacity = 1f;
+ occlusionOpacity = 1f;
+
+ // Picking a whole material implies repainting all of it, so enable every channel.
+ paintAlbedo = true;
+ paintMetallicSmoothness = true;
+ paintOcclusion = true;
+ paintEmission = true;
+
+ // Let listeners (the in-game HUD) refresh their widgets to the adopted values + flags.
+ Picked?.Invoke();
+ }
+
+ ///
+ /// Picks from our own painted maps at the hit texel (bit-exact, backend-agnostic).
+ ///
+ private bool PickFromPaintTextures(RaycastHit hit)
+ {
+ if (_verts == null || _uvs == null || _tris == null || hit.triangleIndex < 0) return false;
int t = hit.triangleIndex;
- if (t * 3 + 2 >= _tris.Length) return;
+ if (t * 3 + 2 >= _tris.Length) return false;
int i0 = _tris[t * 3], i1 = _tris[t * 3 + 1], i2 = _tris[t * 3 + 2];
// UV at the hit via barycentric interpolation of the cached UVs - same UV space the
@@ -58,7 +89,7 @@ public partial class MeshPainter
Color32[] alb = ReadCanonical(MAP_ALBEDO);
Color32[] mso = ReadCanonical(MAP_MSO);
Color32[] em = ReadCanonical(MAP_EM);
- if (idx < 0 || idx >= alb.Length || idx >= mso.Length || idx >= em.Length) return;
+ if (idx < 0 || idx >= alb.Length || idx >= mso.Length || idx >= em.Length) return false;
Color32 a = alb[idx];
Color32 m = mso[idx];
@@ -70,18 +101,132 @@ public partial class MeshPainter
occlusion = m.g / 255f;
smoothness = m.a / 255f;
emissionColor = new Color(e.r / 255f, e.g / 255f, e.b / 255f, 1f);
+ return true;
+ }
- // "Set all alpha values to 1": full-strength adoption across every opacity control.
- metallicSmoothnessOpacity = 1f;
- occlusionOpacity = 1f;
+ ///
+ /// Picks from a foreign object's material at the hit UV: texel-exact texture samples
+ /// (where the surface has a MeshCollider and the map exists) combined with the material
+ /// scalars per the URP/Lit conventions, with scalar fallbacks everywhere else.
+ ///
+ private bool PickFromForeignMaterial(RaycastHit hit)
+ {
+ Collider col = hit.collider;
+ if (!col) return false;
+ Renderer rend = col.GetComponent();
+ if (!rend) rend = col.GetComponentInParent();
+ if (!rend) return false;
+ Material mat = rend.sharedMaterial;
+ if (!mat) return false;
- // Picking a whole material implies repainting all of it, so enable every channel.
- paintAlbedo = true;
- paintMetallicSmoothness = true;
- paintOcclusion = true;
- paintEmission = true;
+ // textureCoord is only meaningful for a MeshCollider; other collider shapes carry no UV,
+ // so we fall back to the material's uniform scalar values for those.
+ bool hasUv = col is MeshCollider;
+ Vector2 uv = hasUv ? hit.textureCoord : Vector2.zero;
- // Let listeners (the in-game HUD) refresh their widgets to the adopted values + flags.
- Picked?.Invoke();
+ // --- Albedo: BaseMap (sRGB) * BaseColor ---
+ Color baseCol = GetColorProp(mat, "_BaseColor", "_Color", Color.white);
+ Texture baseTex = GetTexProp(mat, "_BaseMap", "_MainTex");
+ Color albedo = baseCol;
+ if (hasUv && baseTex)
+ albedo = SampleTexel(baseTex, ApplyST(mat, "_BaseMap", "_MainTex", uv), true) * baseCol;
+ brushColor = new Color(Mathf.Clamp01(albedo.r), Mathf.Clamp01(albedo.g), Mathf.Clamp01(albedo.b), 1f);
+
+ // --- Metallic / Smoothness: MetallicGloss map (linear) ---
+ // URP/Lit: metallic = map.r ; smoothness = map.a * _Smoothness (or _Metallic/_Glossiness scalars).
+ float metallicVal = GetFloatProp(mat, "_Metallic", null, 0f);
+ float smoothVal = GetFloatProp(mat, "_Smoothness", "_Glossiness", 0.5f);
+ Texture mgTex = GetTexProp(mat, "_MetallicGlossMap", null);
+ if (hasUv && mgTex)
+ {
+ Color mg = SampleTexel(mgTex, ApplyST(mat, "_MetallicGlossMap", null, uv), false);
+ metallicVal = mg.r;
+ smoothVal = mg.a * smoothVal;
+ }
+ metallic = Mathf.Clamp01(metallicVal);
+ smoothness = Mathf.Clamp01(smoothVal);
+
+ // --- Occlusion: OcclusionMap.g (linear), lerp(1, g, strength) ---
+ float occStrength = GetFloatProp(mat, "_OcclusionStrength", null, 1f);
+ float occ = 1f;
+ Texture occTex = GetTexProp(mat, "_OcclusionMap", null);
+ if (hasUv && occTex)
+ occ = Mathf.Lerp(1f, SampleTexel(occTex, ApplyST(mat, "_OcclusionMap", null, uv), false).g, occStrength);
+ occlusion = Mathf.Clamp01(occ);
+
+ // --- Emission: EmissionMap (sRGB) * EmissionColor ---
+ Color emColor = GetColorProp(mat, "_EmissionColor", null, Color.black);
+ Texture emTex = GetTexProp(mat, "_EmissionMap", null);
+ Color emCol = emColor;
+ if (hasUv && emTex)
+ emCol = SampleTexel(emTex, ApplyST(mat, "_EmissionMap", null, uv), true) * emColor;
+ emissionColor = new Color(Mathf.Clamp01(emCol.r), Mathf.Clamp01(emCol.g), Mathf.Clamp01(emCol.b), 1f);
+
+ return true;
+ }
+
+ // --- Material read helpers (name-tolerant: URP and legacy Standard) ---
+
+ private static Color GetColorProp(Material m, string nameA, string nameB, Color fallback)
+ {
+ if (m.HasProperty(nameA)) return m.GetColor(nameA);
+ if (nameB != null && m.HasProperty(nameB)) return m.GetColor(nameB);
+ return fallback;
+ }
+
+ private static float GetFloatProp(Material m, string nameA, string nameB, float fallback)
+ {
+ if (m.HasProperty(nameA)) return m.GetFloat(nameA);
+ if (nameB != null && m.HasProperty(nameB)) return m.GetFloat(nameB);
+ return fallback;
+ }
+
+ private static Texture GetTexProp(Material m, string nameA, string nameB)
+ {
+ if (m.HasProperty(nameA)) { Texture t = m.GetTexture(nameA); if (t) return t; }
+ if (nameB != null && m.HasProperty(nameB)) { Texture t = m.GetTexture(nameB); if (t) return t; }
+ return null;
+ }
+
+ private static Vector2 ApplyST(Material m, string nameA, string nameB, Vector2 uv)
+ {
+ string prop = m.HasProperty(nameA) ? nameA : (nameB != null && m.HasProperty(nameB) ? nameB : null);
+ if (prop == null) return uv;
+ Vector2 s = m.GetTextureScale(prop);
+ Vector2 o = m.GetTextureOffset(prop);
+ return new Vector2(uv.x * s.x + o.x, uv.y * s.y + o.y);
+ }
+
+ ///
+ /// Samples a single texel from a texture at a UV (texel-exact). Uses GetPixelBilinear when the
+ /// texture is CPU-readable; otherwise blits it to a temporary RenderTexture and reads back the
+ /// one texel, so it works regardless of the texture's "Read/Write" import flag. selects the colour space so the returned value matches the readable path
+ /// (gamma for colour maps, linear for data maps).
+ ///
+ private static Color SampleTexel(Texture tex, Vector2 uv, bool srgb)
+ {
+ Texture2D t2d = tex as Texture2D;
+ if (t2d != null && t2d.isReadable)
+ return t2d.GetPixelBilinear(uv.x, uv.y);
+
+ int w = Mathf.Max(1, tex.width), h = Mathf.Max(1, tex.height);
+ var rw = srgb ? RenderTextureReadWrite.sRGB : RenderTextureReadWrite.Linear;
+ RenderTexture rt = RenderTexture.GetTemporary(w, h, 0, RenderTextureFormat.ARGB32, rw);
+ RenderTexture prev = RenderTexture.active;
+ Graphics.Blit(tex, rt);
+ RenderTexture.active = rt;
+
+ var tmp = new Texture2D(1, 1, TextureFormat.RGBA32, false, !srgb);
+ int px = Mathf.Clamp(Mathf.FloorToInt(Mathf.Repeat(uv.x, 1f) * w), 0, w - 1);
+ int py = Mathf.Clamp(Mathf.FloorToInt(Mathf.Repeat(uv.y, 1f) * h), 0, h - 1);
+ tmp.ReadPixels(new Rect(px, py, 1, 1), 0, 0, false);
+ tmp.Apply(false);
+ Color c = tmp.GetPixel(0, 0);
+
+ RenderTexture.active = prev;
+ RenderTexture.ReleaseTemporary(rt);
+ if (Application.isPlaying) Destroy(tmp); else DestroyImmediate(tmp);
+ return c;
}
}
diff --git a/Assets/Scripts/MeshPainter.cs b/Assets/Scripts/MeshPainter.cs
index b640579..2be4cb8 100644
--- a/Assets/Scripts/MeshPainter.cs
+++ b/Assets/Scripts/MeshPainter.cs
@@ -735,6 +735,18 @@ public partial class MeshPainter : MonoBehaviour
return found;
}
+ ///
+ /// Raycasts at a screen position and returns the nearest hit on ANY collider (not just the
+ /// paint mesh). Used by the pipette, which can sample foreign objects' materials too.
+ ///
+ private bool TryRaycastAny(Vector2 screenPos, out RaycastHit hit)
+ {
+ hit = default;
+ if (!paintCamera) return false;
+ Ray ray = paintCamera.ScreenPointToRay(screenPos);
+ return Physics.Raycast(ray, out hit, 1000f, ~0, QueryTriggerInteraction.Ignore);
+ }
+
///
/// Raycasts at a screen position against the paint mesh and computes the brush
/// placement: hit point (local space), local 3D radius (constant visual size via UV