Cursors: Krea-Grafiken in voller Auflösung, Blank-Cursor beim Malen, normalisierte Hotspots
- Cursor_Normal/Paint/Pipette: generierte Grafiken jetzt in Vollauflösung (766-829 px, Max Size im Importer steuert Anzeigegröße), Paint-Ring-Inneres freigestellt - Neuer Cursor_Blank (transparent): wird während eines Strokes auf dem Mesh gesetzt statt Cursor.visible zu togglen (fixt Flicker-Kampf mit dem Orbit-Input) - CursorController: Hotspots normalisiert (0-1, unabhängig von Import Max Size); Zustandskette Pipette > (Stroke ? Blank : Paint) > Normal; off-mesh während Stroke -> Normal - MeshPainter: StrokeInProgress-Flag (Maustaste auf Paint-Pfad gehalten) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
No files matched your search
@@ -424,6 +424,8 @@ public partial class MeshPainter : MonoBehaviour
|
||||
// --- preview overlay ---
|
||||
/// <summary>True while the brush preview decal is actually drawn this frame (hover hits the paintable surface).</summary>
|
||||
public bool BrushPreviewVisible { get; private set; }
|
||||
/// <summary>True while the paint button is held on the paint path (stroke running). The cursor hides entirely.</summary>
|
||||
public bool StrokeInProgress { get; private set; }
|
||||
public Shader previewShader; // optional; else Shader.Find("Hidden/MeshPaintPreview")
|
||||
private Material _previewMat;
|
||||
private Mesh _previewQuad; // unit quad in the tangent plane (flat decal mode)
|
||||
@@ -855,14 +857,20 @@ public partial class MeshPainter : MonoBehaviour
|
||||
ResolveActiveMask();
|
||||
|
||||
if (!TryGetMouse(out bool pressed, out Vector2 screenPos))
|
||||
{
|
||||
StrokeInProgress = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't paint through the in-game UI (a HUD click must not also stamp the mesh behind it).
|
||||
if (pressed && InputUtil.PointerOverUi())
|
||||
pressed = false;
|
||||
|
||||
// Brush preview at the hover point - independent of painting AND of the pipette, so the
|
||||
// footprint stays visible before pressing, while the pipette holds, and during wheel resize.
|
||||
// While the button is held on the paint path the hardware cursor is hidden entirely
|
||||
// (CursorController); the preview decal itself stays visible during the stroke.
|
||||
StrokeInProgress = pressed && !pipette;
|
||||
|
||||
// Brush preview at the hover point.
|
||||
if (previewMode != BrushPreviewMode.Off && _previewMat && paintCamera)
|
||||
UpdatePreview(screenPos);
|
||||
|
||||
|
||||
@@ -13,14 +13,17 @@ public class CursorController : MonoBehaviour
|
||||
|
||||
[Header("Cursors (import type: Cursor)")]
|
||||
[SerializeField] private Texture2D normalCursor;
|
||||
[Tooltip("Click point inside the texture, in pixels from the top-left.")]
|
||||
[Tooltip("Click point inside the texture, normalized 0-1 from the top-left. Independent of the importer's Max Size.")]
|
||||
[SerializeField] private Vector2 normalHotspot = Vector2.zero;
|
||||
[SerializeField] private Texture2D paintCursor;
|
||||
[SerializeField] private Vector2 paintHotspot = new(16f, 16f);
|
||||
[SerializeField] private Vector2 paintHotspot = new(0.5f, 0.5f);
|
||||
[SerializeField] private Texture2D pipetteCursor;
|
||||
[SerializeField] private Vector2 pipetteHotspot = new(1f, 30f);
|
||||
[SerializeField] private Vector2 pipetteHotspot = new(0f, 1f);
|
||||
[Tooltip("Fully transparent texture shown while a stroke is running - swapping the cursor " +
|
||||
"image avoids fighting the camera rigs over Cursor.visible (which caused flicker).")]
|
||||
[SerializeField] private Texture2D blankCursor;
|
||||
|
||||
private enum State { None, Normal, Paint, Pipette }
|
||||
private enum State { None, Normal, Paint, Pipette, Blank }
|
||||
|
||||
private State _applied = State.None;
|
||||
private CursorMode _appliedMode = CursorMode.Auto;
|
||||
@@ -68,8 +71,11 @@ public class CursorController : MonoBehaviour
|
||||
{
|
||||
if (painter.pipette) want = State.Pipette;
|
||||
// Paint cursor only while painting is actually possible, i.e. the brush preview
|
||||
// decal is drawn at the hover point this frame; hovering off the mesh -> normal.
|
||||
else if (painter.BrushPreviewVisible) want = State.Paint;
|
||||
// decal is drawn at the hover point this frame (hovering off the mesh -> normal,
|
||||
// also mid-stroke). While the stroke touches the mesh the decal alone marks the
|
||||
// brush position -> blank cursor.
|
||||
else if (painter.BrushPreviewVisible)
|
||||
want = painter.StrokeInProgress ? State.Blank : State.Paint;
|
||||
}
|
||||
|
||||
var mode = CursorMode.Auto;
|
||||
@@ -83,12 +89,21 @@ public class CursorController : MonoBehaviour
|
||||
|
||||
switch (want)
|
||||
{
|
||||
case State.Paint: Cursor.SetCursor(paintCursor, paintHotspot, mode); break;
|
||||
case State.Pipette: Cursor.SetCursor(pipetteCursor, pipetteHotspot, mode); break;
|
||||
default: Cursor.SetCursor(normalCursor, normalHotspot, mode); break;
|
||||
case State.Blank: Apply(blankCursor, Vector2.zero, mode); break;
|
||||
case State.Paint: Apply(paintCursor, paintHotspot, mode); break;
|
||||
case State.Pipette: Apply(pipetteCursor, pipetteHotspot, mode); break;
|
||||
default: Apply(normalCursor, normalHotspot, mode); break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void Apply(Texture2D cursor, Vector2 normalizedHotspot, CursorMode mode)
|
||||
{
|
||||
var hotspot = cursor
|
||||
? Vector2.Scale(normalizedHotspot, new Vector2(cursor.width, cursor.height))
|
||||
: Vector2.zero;
|
||||
Cursor.SetCursor(cursor, hotspot, mode);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
Cursor.SetCursor(null, Vector2.zero, CursorMode.Auto); // back to the OS cursor
|
||||
|
||||
|
After Width: | Height: | Size: 83 B |
@@ -0,0 +1,117 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f924c6157db044a4bd2feac9d02cf38f
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 1
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 1
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 7
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 4
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 64
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
customData:
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spriteCustomMetadata:
|
||||
entries: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
Before Width: | Height: | Size: 237 B After Width: | Height: | Size: 286 KiB |
@@ -34,13 +34,13 @@ TextureImporter:
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 0
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 1
|
||||
nPOTScale: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
@@ -69,7 +69,7 @@ TextureImporter:
|
||||
platformSettings:
|
||||
- serializedVersion: 4
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
maxTextureSize: 64
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
|
||||
|
Before Width: | Height: | Size: 245 B After Width: | Height: | Size: 334 KiB |
@@ -34,13 +34,13 @@ TextureImporter:
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 0
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 1
|
||||
nPOTScale: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
@@ -69,7 +69,7 @@ TextureImporter:
|
||||
platformSettings:
|
||||
- serializedVersion: 4
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
maxTextureSize: 64
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
|
||||
|
Before Width: | Height: | Size: 233 B After Width: | Height: | Size: 255 KiB |
@@ -34,13 +34,13 @@ TextureImporter:
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 0
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 1
|
||||
nPOTScale: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
@@ -69,7 +69,7 @@ TextureImporter:
|
||||
platformSettings:
|
||||
- serializedVersion: 4
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
maxTextureSize: 64
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 0
|
||||
|
||||