Replicate brush strokes as self-contained commands instead of texture
pixels: the owner emits each committed stamp (local placement plus brush
state, byte-quantized) and remotes replay it through the same draw path,
reproducing identical maps without streaming megabytes.
- MeshPainter.Net.cs: PaintStampNet serializable stamp, OnStampCommitted
owner-capture event (no-op without listener), ApplyNetworkStamp remote
replay, seedable spray for deterministic speckle.
- MeshPainter.cs: emit stamp after drawing, seed spray only when synced.
- PlayerPaintSync.cs: NetworkBehaviour batching owner stamps reliably
(ServerRpc -> ClientRpc) with echo guard; remote disables local painting.
- Player/Hider/Hunter prefabs: PlayerPaintSync plus NGO GlobalObjectIdHash.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- ConcealmentMeter: standalone component measuring the percentage of the
character volume embedded inside environment geometry (bake -> voxelize via
column ray-stabbing parity -> classify interior voxels against box/sphere/
capsule/convex colliders and terrain). Always-on, throttled, gizmo debug.
- SkinnedColliderBaker: sole per-frame collider bake owner for skinned rigs.
MeshPainter now delegates baking and only borrows BakedMesh for the GPU
raster and cache refresh, so the collider stays fresh outside paint mode.
- Move pose assets to Assets/Poses, retire unused Insect scripts to
Assets/Unused, restore Paintman.controller; mode scripts and scene updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add mask selector with previews to the brush card (data-driven from
painter.brushMasks); persist activeBrushIndex with HUD layout.
- Fix alpha/coverage artifacts when overpainting after load or backend
switch: derive the dilation source marker from a deterministic UV island
mask instead of resetting it to "unpainted" on every seed.
- Import the two final brush mask textures (Brushes/).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add MeshPainter.Persistence.cs: auto-save the three canonical paint maps
(albedo, MSO, emission) as PNGs under persistentDataPath on quit/play-stop
and auto-load them as the backend seed on start, so painting survives
sessions. Reuses the existing ReadCanonical/SetupBackend seed round-trip;
no new color-space code.
Add PaintHud.Persistence.cs: auto-save draggable panel positions and the
widget-bound painter values to a small JSON layout file, restored on start
via the painter -> SyncFromPainter -> widgets path. Complete SyncFromPainter
to also cover the spray sliders.
The pipette now raycasts the nearest collider of any object, not just the
paint mesh. On a foreign hit it reads all channels texel-accurately from the
object's material maps (BaseMap/MetallicGloss/Occlusion/Emission) combined
with the material scalars per URP/Lit conventions, falling back to scalar
properties where a map is missing or the collider yields no UV. Includes
scene test props and materials for verifying foreign-object picking.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pipette samples all painted channel values (albedo, metallic, smoothness,
occlusion, emission) at the clicked texel and adopts them into the brush
with every opacity forced to 1, instead of painting. The screen raycast is
extracted into a shared TryRaycastPaintMesh helper used by painting, preview
and the pipette. A pipette toggle is added to the HUD panel prefab and wired
in PaintHud.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Add a code-driven HUD that appears in paint mode and binds live to the
active MeshPainter: four channel toggles, HSV pickers for albedo and
emission, sliders for metallic/smoothness/occlusion, emission intensity
and brush size. The HUD self-assembles its own overlay canvas and event
system at runtime.
Add a ModeChanged event to GameModeManager so the HUD can follow the
active mode, and guard MeshPainter against painting through the UI.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Split MeshPainter into partial classes (Setup/Backend/Gpu/Cpu) for maintainability
- Add ambient occlusion painting, packed with metallic/smoothness into one
MSO mask map (R=metallic, G=occlusion, A=smoothness) bound to both
_MetallicGlossMap and _OcclusionMap, kept independently paintable via
per-channel ColorMask
- Gate painting from a single IsChannelActive source so AO-only strokes work
- Blend albedo/emission in linear space on CPU to match the GPU framebuffer
- Pre-compensate data-channel writes for sRGB->linear so GPU matches CPU
- Harden GPU shader fallback to reflect real backend state
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extend the painter from a single albedo map to three paint maps:
albedo (_BaseMap), packed metallic+smoothness (_MetallicGlossMap,
R=metallic A=smoothness), and emission (_EmissionMap/_EmissionColor).
- Per-channel toggles (paint albedo / metallic+smoothness / emission)
- Metallic/Smoothness sliders and emission color + intensity brush params
- One brush shader drives all maps; metallic-smoothness uses a work
layout (R=metallic G=smoothness A=coverage) and a pack pass
(Hidden/MeshPaintPackMS) to the URP layout after each stroke
- GPU and CPU backends mirror all three maps with the same mask and
seam-dilation logic
- Material keywords (_METALLICSPECGLOSSMAP, _EMISSION) enabled and the
new maps seeded from the original material values
- Add hover preview (MeshPaintPreview.shader) with a Preview Mode enum
(Off / Flat Decal / On Surface) sharing the paint placement path so
size, mask, alignment and rotation match the actual stroke.
- Preview Opacity now ranges up to 4.0 so faint/grey masks can be shown
more strongly (saturates to full opacity before the hardware blend).
- Add Paint On Move Only: stamp once on mouse-down, then only while the
cursor moves (GPU and CPU paths alike).
- Remove brushOpacity; brushColor.a is now the global opacity multiplier
over the mask in both backends (the alpha channel was previously unused
by the brush shader).
Replace the 3D sphere brush with a square stamp in the surface tangent
plane whose shape and opacity come from a grayscale/alpha mask. Masks
are assignable via an inspector list with a live-switchable active index;
an empty list falls back to a procedurally generated circle mask that
reproduces the previous round brush.
- Mask drives opacity (luma * alpha * brushOpacity) via source-over blend
instead of a hard threshold cut; gray 0.5 = 50%.
- GPU and CPU paths stay identical; non-readable textures are supported on
the CPU via GPU blit-readback.
- Dilate pass treats any alpha > 0 as painted so soft edges survive seam
padding.
- Brush alignment enum (Camera/World) plus manual rotation; tangent frame
handedness fixed so stamps read upright and unmirrored in both modes.
Reduce comments to non-obvious "why" notes and convert UnityEngine.Object
null checks to implicit bool (if (obj) / if (!obj)). Input System devices
(Mouse.current, Keyboard.current) and plain arrays keep explicit != null.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce an extensible game mode system toggled with Tab:
- Player Control: WASD movement with always-on mouse camera orbit
- Paint: player frozen, left-click paints, right-click holds to orbit
Add GameMode base class, GameModeManager, and the two concrete modes.
Refactor controlled scripts to expose clean state flags instead of
handling input themselves: ThirdPersonOrbitInput.LookMode,
PlayerCharacterController.ControlEnabled, MeshPainter.PaintingEnabled.
Also fix camera zoom: remove the obsolete /120 wheel normalization so
one scroll notch maps to one zoomSpeed step under the new Input System.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Install Cinemachine 3.1.7 and set up an orbital third-person camera rig
(CinemachineBrain, OrbitalFollow, RotationComposer, Deoccluder).
- Add ThirdPersonOrbitInput: mouse-driven orbit and wheel zoom with a Tab
toggle that unlocks the cursor and pauses rotation.
- Fix MeshPainter so the paint ray skips the player's CharacterController
capsule (and any other collider) and always targets the mesh collider,
so painting works from every camera angle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Unity 6000.3 (URP) project with live mesh painting in play mode.
- MeshPainter.cs: paint a SkinnedMesh with the mouse, seamless across
UV islands via 3D-space painting, GPU and CPU backends (switchable),
edge-padding/dilation against bilinear seam bleed
- FrameRateLimiter.cs: simple frame rate limiting component
- MeshPaintBrush / MeshPaintDilate shaders for the GPU paint path
- Unity-specific .gitignore and .gitattributes
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>