Zusätzlich zum Kugel-Test wird die Oberflächen-Normale jedes Texels gegen
die Brush-Normale geprüft (CPU/GPU/Preview). Neues Inspector-Feld
"Brush Max Surface Angle" (90-180°, Default 120°). Bei fehlenden Mesh-Normalen
Degradation zur reinen Kugel.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stamp testet den Brush wieder als 3D-Kugel (du²+dv²+dn² = r²) statt
als Box (|du|,|dv|,|dn| = r) — konsistent über CPU, GPU-Brush und
Preview. Die Box reichte diagonal bis r·√3 und spannte einen 2r-dicken
Slab entlang der Normale, wodurch an Nähten/Kanten/dünner Geometrie auf
die Gegenseite/Nachbarinseln gemalt wurde. Die Kugel reicht in jede
Richtung exakt r und überbrückt echte 3D-Nähte weiterhin, greift aber
nicht mehr über r hinaus.
cornerGuard war nur ein Pflaster gegen genau diesen Box-Übergriff und
ist damit überflüssig: Config-Felder, Suppress-Check und Helper
(DabWrapsAroundCorner, CornerWrapFraction, InBrushBox, SquareReach)
entfernt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Particles kept playing at the last hit position when the cursor left the
mesh mid-stroke. New _stampLandedThisFrame flag: reset before each stamp
attempt, set only where the dab actually paints (after placement and
corner guard); no landed stamp -> StopPaintParticles immediately.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- 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>
- CursorController: polls painter state in LateUpdate; paint cursor only
while the brush preview decal actually draws (MeshPainter.BrushPreviewVisible),
pipette while held/toggled, fallback to normal over UI (InputUtil.PointerOverUi)
- Auto ForceSoftware while Unity Recorder is recording (reflection into
editor-only assembly, 0.5s poll, editor-only) so the cursor is captured;
back to hardware cursor when recording stops
- 3 placeholder cursor PNGs (32x32, cursor import type) with per-cursor hotspots
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sweep over 48 files (Editor/ excluded): remove comments that restate the
code, condense class summaries, fix three stale doc statements
(HsvColorPicker alpha bar, PaintHud layoutPrefab, MasterVolume
FallbackDefault). Inline two single-use wrappers (IsPointerOverUi,
BuildModes), drop an unused using, replace a magic literal with the
existing ParallaxBase constant, and convert ~10 UnityEngine.Object null
checks to the implicit-bool convention. No serialized fields, signatures,
RPC/NetworkVariable names, defaults, or #if blocks touched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Move brightness out of the half-lambert diffuse term to a final exposure
multiply on the whole shaded body color (diffuse + GI + specular), and add
a saturation grade (0 = neutral, -1 = grayscale, >0 = boosted) on the same
final stage. Saturation rides as a 4th component on CamoKnobs (Vector3 ->
Vector4) so it inherits all three persistence paths: HUD JSON, network
replication (_camo NetworkVariable), and painting-slot metadata. Wires the
renamed Slider_Brightness and new Slider_Saturation. Bumps HudFileVersion
and PaintMetaVersion with load-time remaps so existing paintings keep their
look (old 1=neutral saturation remaps to 0=neutral).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The fur is drawn from a CPU-baked unit-scale mesh whose instance matrix
carries the avatar's world scale, so the extrusion scaled fully 1:1 with
the player (hider 0.5 / hunter 1.5). That read as too-short fur on the
small hider and too-long on the big hunter, and the curvature droop was
sized off the raw unity_ObjectToWorld which doesn't carry the per-instance
scale in the RenderMeshInstanced path.
- FurShellRenderer pushes the avatar's lossyScale (_FurObjScale) plus a new
Fur Scale Influence (0..1, default 0.5) that blends the fur SIZE between
fully proportional (1) and a constant world length (0). The shader
pre-divides the object-space displacement so the net world length follows
effScale; curl droop and wind use the same effScale, keeping the curl
SHAPE invariant across avatar sizes and influence settings.
- The body's see-through X-ray reveal (forward half) is mirrored onto the
visible fur so strands glow WITH the reveal instead of hiding it under
opaque fur. FurShellRenderer pulls the live colour/pulse/flash from the
sibling MeshPainter (new getters), in phase with the body. A new X-Ray
Fur Strength (default 0.2) tames the per-shell accumulation, since the
body writes the glow once (opaque) but the fur stacks N transparent
shells that each composite it.
Co-Authored-By: Claude <noreply@anthropic.com>
X-ray hit flash: drive a dedicated flash mode instead of the normal
reveal - pulse off (steady), intensity boost (xrayFlashBoost, default 3x),
snap-on, then restore authored color/pulse exactly. Fixes the weak/dark
flicker where the 0.35s hit window often caught only the pulse's near-off
plateau.
Spawn/respawn freeze: pool GPU RenderTextures across despawn->respawn
(TeardownGpu recycles, CreateRT pops) so a hider->hunter conversion no
longer churns ~70MB of RT alloc/free on every observer; reuse staging
textures to cut per-map GC. Defer the blank base-seed on reveal-gated
remote replicas (DeferInitialSeed) - the real pixels arrive with the
snapshot apply anyway; gate the FurShellRenderer alongside the body so a
deferred replica never shows the pooled RT's stale fur; EnsureBackendSeeded
+ TryCaptureSnapshot guard against capture-before-reseed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Fur-Map trägt nur noch R = Länge (linearer Datenkanal); Farbe kommt per-texel
aus der Live-Albedo-RT (an _BaseMap des Shell-Shaders gebunden)
- Toggle_Fur als echtes Gate (IsChannelActive = fur), Default aus, furLength 0.5
- Weicher Alpha-Übergang (radial/tip/length coverage) statt Hard-Clip; Transparent-
Pass mit Depth-Bias (Offset -1,-1) gegen Body-Durchflimmern
- Root dunkler / Tip heller aus gemalter Farbe abgeleitet (kein Fix-Tint)
- Gravity/Curl in World-Space nach Transform (unabhängig von Mesh-Rotation)
- Pipette aktiviert Fur-Toggle immer; liest R-Kanal als Länge
- First-Person blendet FurShellRenderer mit aus
- Netzwerk (FlagFur), Undo, Persistence (fur.png R=Länge) mitgezogen
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fur wird die vierte kanonische Paint-Map (RGB=Farbe, A=Länge). Integration
über Seed (kahl), GPU/CPU-Paint, Undo, Netzwerk-Sync, Persistence (fur.png,
v6) und Live-RT-Hook im FurShellRenderer. HUD-Binding (Toggle/Picker/Alpha)
aktiviert. Fur-Alpha lerpt jetzt zu einer Ziel-Opacity (Slider_FurOpacity)
statt additiv Richtung 1 zu akkumulieren (zwei Brush-Passes: Fade + Deposit).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The orbit rig read the wheel unfiltered for zoom, so scrolling a paint-mode
scroll list also zoomed the camera. Gate the zoom behind a shared
InputUtil.PointerOverUi() helper (EventSystem raycast); MeshPainter's private
IsPointerOverUi() now delegates to the same helper (DRY).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move scripts under Assets/Scripts/ into Components, GameMode, Helper,
Network, Enum, Struct and ScriptableObject folders. Pure file moves
(plus folder .meta files); no code changes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>