Refactor Assets/Scripts: trim restating comments, minor structure cleanups
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>
This commit is contained in:
1 parent
eeee5fc2f0
commit
6c03e5d3f6
48 files changed
+289
-481
No files matched your search
@@ -7,26 +7,21 @@ using UnityEditor.SceneManagement;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Editor tool that decomposes an environment mesh (typically an open-shell / non-watertight
|
||||
/// furniture mesh) into a small set of CONVEX MeshColliders via surface clustering, so
|
||||
/// <see cref="ConcealmentMeter"/> can treat the furniture as "embeddable" through its
|
||||
/// convex-collider ClosestPoint path WITHOUT a single convex hull filling the open compartments.
|
||||
/// Editor tool that decomposes an environment mesh (typically open-shell furniture) into a small set
|
||||
/// of CONVEX MeshColliders via surface clustering, so <see cref="ConcealmentMeter"/> can treat the
|
||||
/// furniture as "embeddable" through its convex-collider ClosestPoint path WITHOUT a single convex
|
||||
/// hull filling the open compartments.
|
||||
///
|
||||
/// Approach (no voxels, no native plugin — "approximate convex decomposition" on the surface):
|
||||
/// 1. Weld coincident vertices so connectivity is detected across split-normal/UV seams.
|
||||
/// 2. Grow connected triangle regions into SLABS: a region keeps absorbing edge-adjacent
|
||||
/// triangles while it stays thinner than <see cref="maxSlabThickness"/> along its flattest
|
||||
/// axis (PCA minor axis). This spans both faces of one board (they are within a board's
|
||||
/// thickness) but refuses to jump the gap to the next board (that would thicken the region).
|
||||
/// 3. Convex-hull each slab into its own convex collider. Boards become thin convex slabs;
|
||||
/// the empty space between boards is covered by no collider, so it stays hollow.
|
||||
/// Approach (approximate convex decomposition on the surface — no voxels, no native plugin): weld
|
||||
/// coincident vertices so connectivity survives split-normal/UV seams, grow edge-connected triangle
|
||||
/// regions into SLABS kept thinner than <see cref="maxSlabThickness"/> along their PCA minor axis
|
||||
/// (spans both faces of one board, refuses to jump the gap to the next), then convex-hull each slab.
|
||||
/// The empty space between boards is covered by no collider, so it stays hollow.
|
||||
///
|
||||
/// The colliders are real child GameObjects referencing SAVED hull-mesh assets, so they survive
|
||||
/// domain reload / play-mode / scene save (unlike an in-memory-only bake).
|
||||
///
|
||||
/// Runtime-inert: the component exists only so <see cref="ConcealmentMeter"/> can detect it in a
|
||||
/// collider's parent and skip the furniture's own concave MeshCollider (exactly like a baked
|
||||
/// <c>ConcealmentVolume</c> does) — the generated convex slabs are the authoritative embedding shape.
|
||||
/// domain reload / play mode / scene save. Runtime-inert: the component exists only so
|
||||
/// <see cref="ConcealmentMeter"/> can detect it in a collider's parent and skip the furniture's own
|
||||
/// concave MeshCollider — the generated convex slabs are the authoritative embedding shape.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public class ConcealmentColliderBaker : MonoBehaviour
|
||||
@@ -240,7 +235,7 @@ public class ConcealmentColliderBaker : MonoBehaviour
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
EditorUtility.SetDirty(this);
|
||||
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(gameObject.scene);
|
||||
EditorSceneManager.MarkSceneDirty(gameObject.scene);
|
||||
|
||||
Debug.Log($"[ConcealmentColliderBaker] '{name}': baked {made} convex collider(s)" +
|
||||
(dropped > 0 ? $" (+{dropped} smaller slab(s) dropped by Max Colliders={maxColliders})" : "") +
|
||||
@@ -262,7 +257,7 @@ public class ConcealmentColliderBaker : MonoBehaviour
|
||||
|
||||
EditorUtility.SetDirty(this);
|
||||
if (gameObject.scene.IsValid())
|
||||
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(gameObject.scene);
|
||||
EditorSceneManager.MarkSceneDirty(gameObject.scene);
|
||||
}
|
||||
|
||||
private static void EnsureFolder()
|
||||
|
||||
@@ -2,27 +2,19 @@ using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Standalone volumetric concealment meter for the painted character.
|
||||
///
|
||||
/// Each evaluation bakes the character's current (deformed) skinned mesh, voxelizes its
|
||||
/// SOLID volume, and reports what fraction of that volume sits INSIDE environment colliders
|
||||
/// (= embedded / hidden) versus exposed. <see cref="EmbeddedPercent"/> is the headline value,
|
||||
/// Standalone volumetric concealment meter for the painted character. Each evaluation bakes the
|
||||
/// current (deformed) skinned mesh, voxelizes its SOLID volume, and reports the fraction embedded
|
||||
/// inside environment colliders: <see cref="EmbeddedPercent"/> is the headline value,
|
||||
/// <see cref="visiblePercent"/> its complement.
|
||||
///
|
||||
/// Pipeline:
|
||||
/// 1. <see cref="SkinnedMeshRenderer.BakeMesh(Mesh)"/> -> mesh in the renderer's LOCAL space, then the
|
||||
/// player's world scale is divided back out (BakeMesh bakes that scale IN; see PlayerScaler) so the
|
||||
/// convention world = transform.localToWorldMatrix holds at ANY size — the same fixup SkinnedColliderBaker
|
||||
/// applies, and the unit-scale mesh MeshPainter borrows from it.
|
||||
/// 2. Column ray-stabbing in local space: each (x,z) grid column collects the Y where the mesh
|
||||
/// surface crosses it; parity (odd # of crossings below a voxel center) marks interior voxels.
|
||||
/// This is the same XZ triangle rasterization MeshPainter.GetIslandMask uses for UV islands.
|
||||
/// 3. Each interior voxel center -> world; tested against broadphase-filtered environment
|
||||
/// colliders: convex primitives / convex MeshCollider via ClosestPoint, Terrain via
|
||||
/// SampleHeight, optional concave MeshCollider via raycast parity.
|
||||
/// Pipeline: BakeMesh with the player's world scale divided back out (see Evaluate), then column
|
||||
/// ray-stabbing in local space — odd # of surface crossings below a voxel center marks it interior
|
||||
/// (same XZ triangle rasterization MeshPainter.GetIslandMask uses) — then each interior voxel center
|
||||
/// is tested in world space against broadphase-filtered environment colliders: convex via
|
||||
/// ClosestPoint, Terrain via SampleHeight, optional concave MeshCollider via raycast parity.
|
||||
///
|
||||
/// Surface-watertightness caveat: parity assumes a closed mesh. After the Blender merge-by-distance
|
||||
/// the paintman rig is effectively watertight; small leaks only perturb the percentage slightly.
|
||||
/// Watertightness caveat: parity assumes a closed mesh. After the Blender merge-by-distance the
|
||||
/// paintman rig is effectively watertight; small leaks only perturb the percentage slightly.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public class ConcealmentMeter : MonoBehaviour
|
||||
@@ -134,12 +126,11 @@ public class ConcealmentMeter : MonoBehaviour
|
||||
|
||||
targetRenderer.BakeMesh(_baked);
|
||||
|
||||
// BakeMesh freezes the bone deformation — which already carries the player's world scale (PlayerScaler)
|
||||
// — into the mesh. Everything below treats the bake as unit-scale LOCAL geometry and raises it to world
|
||||
// via targetRenderer.localToWorldMatrix, which re-applies that same scale. Divide it back out first,
|
||||
// else a scaled player is measured at scale² (voxel world positions, broadphase box AND gizmos), which
|
||||
// also corrupts EmbeddedPercent. No-op at scale 1; the uniform divide is rotation-independent, which the
|
||||
// player's uniform scale guarantees. Mirrors SkinnedColliderBaker's identical fixup.
|
||||
// BakeMesh freezes the bone deformation — which already carries the player's world scale
|
||||
// (PlayerScaler) — into the mesh, and everything below re-applies that scale by raising the
|
||||
// bake to world via localToWorldMatrix, so a scaled player would be measured at scale²,
|
||||
// corrupting EmbeddedPercent. Divide it back out first. No-op at scale 1; the player's
|
||||
// uniform scale makes the divide rotation-independent. Mirrors SkinnedColliderBaker's fixup.
|
||||
GeometryUtil.RescaleToUnitScale(_baked, targetRenderer.transform.lossyScale, _vbuf);
|
||||
|
||||
// mesh.vertices/.triangles return fresh arrays per call - at 10 evals/s that is steady GC
|
||||
@@ -361,7 +352,7 @@ public class ConcealmentMeter : MonoBehaviour
|
||||
TerrainData td = terr.terrainData;
|
||||
float lx = p.x - tp.x, lz = p.z - tp.z;
|
||||
if (lx < 0f || lz < 0f || lx > td.size.x || lz > td.size.z) continue;
|
||||
if (p.y <= tp.y + terr.SampleHeight(p)) return true; // below the surface
|
||||
if (p.y <= tp.y + terr.SampleHeight(p)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,19 +4,16 @@ using UnityEngine;
|
||||
/// <summary>
|
||||
/// Editor-baked solid/enclosed voxel volume for a single (typically non-convex, non-watertight)
|
||||
/// environment mesh. Runtime lookup is an O(1) grid test, so <see cref="ConcealmentMeter"/> can
|
||||
/// treat furniture as "embeddable" without per-voxel raycast parity (which is unreliable on the
|
||||
/// open-shell furniture meshes of asset kits).
|
||||
/// treat furniture as "embeddable" without per-voxel raycast parity (unreliable on the open-shell
|
||||
/// furniture meshes of asset kits).
|
||||
///
|
||||
/// Bake (editor only): voxelizes the source mesh in LOCAL space and marks a voxel as INSIDE when
|
||||
/// either classifier fires:
|
||||
/// * Generalized Winding Number > 0.5 -> robustly captures SOLID material even for open shells.
|
||||
/// * Exterior flood-fill cannot reach it -> captures fully ENCLOSED air cavities.
|
||||
/// The union is conservative in the safe direction: an unreachable solid OR an enclosed pocket both
|
||||
/// count as concealment; open regions (seat surface, under a thin table) correctly read as exposed.
|
||||
/// The bake marks a voxel INSIDE when either classifier fires: generalized winding number > 0.5
|
||||
/// (captures SOLID material, robust for open shells) OR unreachable by exterior flood-fill
|
||||
/// (captures fully ENCLOSED air cavities). The union errs in the safe direction — solid or enclosed
|
||||
/// both count as concealment; open regions (seat surface, under a thin table) read as exposed.
|
||||
///
|
||||
/// Query is done via <see cref="ContainsWorld"/>; bake and query share the SAME mesh-local space
|
||||
/// (verts at bake time, transform.InverseTransformPoint at query time), so position/rotation/scale
|
||||
/// are handled uniformly.
|
||||
/// Bake and query (<see cref="ContainsWorld"/>) share the SAME mesh-local space, so
|
||||
/// position/rotation/scale are handled uniformly.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public class ConcealmentVolume : MonoBehaviour
|
||||
|
||||
@@ -3,16 +3,10 @@ using Unity.Cinemachine;
|
||||
using Chameleon.Components;
|
||||
|
||||
/// <summary>
|
||||
/// First-person camera. Drives its OWN dedicated <see cref="CinemachineCamera"/>: each frame it snaps
|
||||
/// to the player's dedicated <c>FirstPersonTarget</c> (the eye anchor) for position and
|
||||
/// applies free mouse-look (yaw/pitch) for rotation. While active the player's mesh is hidden so the
|
||||
/// body isn't seen from the inside. Movement stays with the player controller, which reads the
|
||||
/// brain-driven Main Camera forward, so WASD is automatically relative to this view.
|
||||
///
|
||||
/// Activation is driven by <see cref="FirstPersonMode"/> via <see cref="SetActive"/>: it raises the
|
||||
/// vcam priority so the CinemachineBrain blends to it, snaps look to the current view (no jump), hides
|
||||
/// the mesh and locks the cursor. While inactive the vcam rests at a low priority and this component
|
||||
/// does nothing.
|
||||
/// First-person camera on its own dedicated <see cref="CinemachineCamera"/>: snaps to the player's eye
|
||||
/// anchor, applies free mouse-look, and hides the player mesh while active. Driven by
|
||||
/// <see cref="FirstPersonMode"/> via <see cref="SetActive"/>; WASD stays view-relative automatically
|
||||
/// because movement reads the brain-driven Main Camera forward.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(CinemachineCamera))]
|
||||
[DisallowMultipleComponent]
|
||||
|
||||
@@ -5,14 +5,10 @@ using UnityEngine.InputSystem;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Free-flying spectator camera. Drives its OWN dedicated <see cref="CinemachineCamera"/> by moving
|
||||
/// this transform with WASD (relative to where it looks), Q/E (or Space/Left Ctrl) for down/up
|
||||
/// and the mouse for look. Collision is a <see cref="CharacterController"/> capsule sized like the
|
||||
/// player's, so the freecam slides along world geometry instead of clipping through walls/floors.
|
||||
///
|
||||
/// Activation is driven by <see cref="FreeCamMode"/> via <see cref="SetActive"/>: it raises the vcam
|
||||
/// priority so the CinemachineBrain blends to it, snaps the rig to the current view (no jump) and
|
||||
/// locks the cursor. While inactive the vcam rests at a low priority and this component does nothing.
|
||||
/// Free-flying spectator camera on its own dedicated <see cref="CinemachineCamera"/>: WASD flight relative
|
||||
/// to the view, Q/E (or Space/Left Ctrl) for down/up, mouse look. A <see cref="CharacterController"/>
|
||||
/// capsule makes it slide along world geometry instead of clipping. Driven by <see cref="FreeCamMode"/>
|
||||
/// via <see cref="SetActive"/>; while inactive this component does nothing.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(CharacterController))]
|
||||
[DisallowMultipleComponent]
|
||||
@@ -194,5 +190,4 @@ public class FreeCamController : MonoBehaviour
|
||||
return Input.GetKey(KeyCode.LeftShift);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,16 +8,10 @@ using UnityEngine.InputSystem;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Holds a list of <see cref="PlayerPose"/>s plus the active index. The pose system owns ALL character
|
||||
/// animation (the Animator is left empty): standing / airborne / wall / ceiling holds the active pose's
|
||||
/// idle clip, walking/sprinting on the ground plays its optional walk/run clip (unset = idle held).
|
||||
///
|
||||
/// One key cycles poses (wrapping); sprinting in any state snaps back to the default pose, held
|
||||
/// continuously so the reset shows up immediately. Clips play through a tiny <see cref="PlayableGraph"/>
|
||||
/// (two-input mixer on an <see cref="AnimationPlayableOutput"/>) that crossfades every clip switch over
|
||||
/// <see cref="poseBlendDuration"/>. Each pose also carries its own CharacterController capsule (eased,
|
||||
/// so a low pose can crawl under furniture) and per-pose camera y-offsets (third-person framing and
|
||||
/// first-person eye).
|
||||
/// Owns ALL character animation (the Animator is left empty): plays the active <see cref="PlayerPose"/>'s
|
||||
/// idle or optional walk/run/fall clip through a two-input mixer <see cref="PlayableGraph"/> that
|
||||
/// crossfades every clip switch. One key cycles poses, sprinting resets to the default pose; each pose
|
||||
/// also carries its own eased CharacterController capsule and per-pose camera y-offsets.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(PlayerCharacterController))]
|
||||
[DisallowMultipleComponent]
|
||||
@@ -115,10 +109,9 @@ public class PlayerPoseController : MonoBehaviour
|
||||
private Quaternion[] _restRot;
|
||||
private Vector3[] _restScale;
|
||||
|
||||
/// <summary>Index of the currently selected pose.</summary>
|
||||
public int CurrentIndex => _currentIndex;
|
||||
|
||||
/// <summary>Locomotion category resolved this frame (which clip the active pose is playing). The owner publishes it for network sync.</summary>
|
||||
/// <summary>Locomotion category resolved this frame; the owner publishes it for network sync.</summary>
|
||||
public Locomotion CurrentLocomotion => _currentLocomotion;
|
||||
|
||||
/// <summary>
|
||||
@@ -219,7 +212,6 @@ public class PlayerPoseController : MonoBehaviour
|
||||
_firstPersonCameraOffset = EaseCameraOffset(_firstPersonCameraOffset, firstPersonTarget);
|
||||
}
|
||||
|
||||
/// <summary>Eases a camera y-offset toward its target at <see cref="cameraOffsetSpeed"/>; 0 snaps.</summary>
|
||||
private float EaseCameraOffset(float current, float target)
|
||||
{
|
||||
return cameraOffsetSpeed > 0f
|
||||
@@ -261,7 +253,6 @@ public class PlayerPoseController : MonoBehaviour
|
||||
return poses[Mathf.Clamp(_currentIndex, 0, poses.Count - 1)];
|
||||
}
|
||||
|
||||
/// <summary>Maps the controller's current movement state to a locomotion category for clip selection.</summary>
|
||||
private Locomotion ResolveLocalLocomotion()
|
||||
{
|
||||
if (_player.IsMovingOnGround)
|
||||
@@ -375,7 +366,6 @@ public class PlayerPoseController : MonoBehaviour
|
||||
_posePlaying = true;
|
||||
}
|
||||
|
||||
/// <summary>Builds the graph (a 2-input mixer feeding the Animator output) lazily if it isn't alive yet.</summary>
|
||||
private void EnsureGraph()
|
||||
{
|
||||
if (_graph.IsValid())
|
||||
@@ -426,7 +416,6 @@ public class PlayerPoseController : MonoBehaviour
|
||||
ApplyBlendWeights();
|
||||
}
|
||||
|
||||
/// <summary>Advances an in-flight crossfade and drops the outgoing pose once it has fully faded.</summary>
|
||||
private void AdvanceBlend(float dt)
|
||||
{
|
||||
if (!_blending)
|
||||
@@ -446,7 +435,6 @@ public class PlayerPoseController : MonoBehaviour
|
||||
ApplyBlendWeights();
|
||||
}
|
||||
|
||||
/// <summary>Pushes the current blend onto the mixer inputs (smoothstepped while a "from" pose exists).</summary>
|
||||
private void ApplyBlendWeights()
|
||||
{
|
||||
if (!_mixer.IsValid())
|
||||
|
||||
@@ -2,10 +2,9 @@ using UnityEngine;
|
||||
using Unity.Cinemachine;
|
||||
|
||||
/// <summary>
|
||||
/// Feeds mouse input into a <see cref="CinemachineOrbitalFollow"/> so the camera
|
||||
/// orbits the follow target. Mouse movement drives yaw/pitch, the wheel zooms.
|
||||
/// The orbit behaviour depends on <see cref="LookMode"/> (usually set by the
|
||||
/// GameModeManager). Attach to the camera rig (the CinemachineOrbitalFollow).
|
||||
/// Feeds mouse input into a <see cref="CinemachineOrbitalFollow"/>: mouse movement drives yaw/pitch,
|
||||
/// the wheel zooms, middle-mouse pans (paint mode only). Orbit behaviour depends on
|
||||
/// <see cref="LookMode"/> (usually set by the GameModeManager). Attach to the camera rig.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(CinemachineOrbitalFollow))]
|
||||
[DisallowMultipleComponent]
|
||||
@@ -346,5 +345,4 @@ public class ThirdPersonOrbitInput : MonoBehaviour
|
||||
// Offset is camera-local (x=right, y=up); the extension translates eye + look-at together -> true pan.
|
||||
_camOffset.Offset = new Vector3(_pan.x, _pan.y, 0f);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,7 +32,6 @@ public class FrameRateLimiter : MonoBehaviour
|
||||
Apply();
|
||||
}
|
||||
|
||||
/// <summary>Applies the current FPS + VSync settings.</summary>
|
||||
public void Apply()
|
||||
{
|
||||
if (limitFrameRate)
|
||||
|
||||
@@ -3,17 +3,14 @@ using UnityEngine.Rendering;
|
||||
|
||||
namespace Chameleon.Components
|
||||
{
|
||||
// Drives the shell-texturing fur look for FurShell.shader. Draws the source mesh _shellCount times
|
||||
// via GPU instancing (Graphics.RenderMeshInstanced); every instance is one shell layer and reads its
|
||||
// index from SV_InstanceID in the shader to pick its extrusion height. Put this next to a MeshFilter
|
||||
// (static mesh) OR on an animated character's SkinnedMeshRenderer that already carries a
|
||||
// SkinnedColliderBaker - then the fur borrows that component's per-frame baked pose and follows the
|
||||
// animation. Assign the FurShell material either way.
|
||||
// Shell-texturing fur for FurShell.shader: draws the source mesh _shellCount times via GPU instancing
|
||||
// (Graphics.RenderMeshInstanced); each instance is one shell layer and picks its extrusion height from
|
||||
// SV_InstanceID in the shader. Put this next to a MeshFilter (static mesh) OR on a skinned avatar whose
|
||||
// SkinnedColliderBaker provides the per-frame baked pose the fur follows.
|
||||
//
|
||||
// This component is the SINGLE source of truth for the fur look: every tunable shader parameter is a
|
||||
// serialized field here and gets pushed via a MaterialPropertyBlock. The material's own inspector
|
||||
// sliders are therefore only template defaults - they are overridden live. (Use the "Copy Values From
|
||||
// Material" context menu to pull a material you already tuned into these fields.)
|
||||
// SINGLE source of truth for the fur look: every tunable shader parameter is a serialized field here,
|
||||
// pushed via a MaterialPropertyBlock, so the material's own inspector sliders are only template
|
||||
// defaults. (Use the "Copy Values From Material" context menu to pull in a hand-tuned material.)
|
||||
//
|
||||
// The draw is issued from RenderPipelineManager.beginCameraRendering (not Update/LateUpdate) so it
|
||||
// renders reliably for EVERY camera pass - including the Scene View and edit-mode repaints, where
|
||||
@@ -80,11 +77,9 @@ namespace Chameleon.Components
|
||||
[SerializeField] Vector2 _windDirection = new Vector2(1f, 0f);
|
||||
|
||||
[Header("X-Ray Reveal")]
|
||||
// The sibling MeshPainter's body see-through reveal is mirrored onto the fur so the strands glow WITH
|
||||
// the reveal instead of hiding it under opaque fur (colour/pulse are pulled from the painter, in phase
|
||||
// with the body). The fur draws up to _shellCount stacked TRANSPARENT shells, so that additive glow
|
||||
// composites once per overlapping layer and blows out; this scales the per-shell glow down to tame the
|
||||
// stacking so the total reads at roughly the body's intensity. Lower = subtler.
|
||||
// The fur draws up to _shellCount stacked TRANSPARENT shells, so the mirrored X-ray glow composites
|
||||
// once per overlapping layer and blows out; this scales the per-shell glow down so the total reads
|
||||
// at roughly the body's intensity. Lower = subtler.
|
||||
[SerializeField, Range(0f, 1f)] float _xrayFurStrength = 0.2f;
|
||||
|
||||
[Header("Rendering")]
|
||||
@@ -265,18 +260,16 @@ namespace Chameleon.Components
|
||||
_mpb.SetFloat(FurObjScaleId, transform.lossyScale.y);
|
||||
|
||||
// Mirror the sibling painter's live body X-ray reveal onto the fur so the strands glow WITH the
|
||||
// reveal (see-through camo) instead of hiding it under opaque fur. Same colour/pulse/flash the
|
||||
// body uses => in phase. ToRenderColor matches the raw MPB upload to Material.SetColor's gamma
|
||||
// handling (same convention as the fur tint above). Off (alpha 0) when no painter or not revealed
|
||||
// => the shader's additive term is zero, so the fur is unchanged.
|
||||
// reveal instead of hiding it under opaque fur. Same colour/pulse the body uses => in phase.
|
||||
// ToRenderColor matches the raw MPB upload to Material.SetColor's gamma handling (same
|
||||
// convention as the fur tint above). Off (alpha 0) when no painter or not revealed => the
|
||||
// shader's additive term is zero, so the fur is unchanged.
|
||||
if (_painter)
|
||||
{
|
||||
_mpb.SetColor(XRayColorId, ToRenderColor(_painter.CurrentXRayColor));
|
||||
_mpb.SetFloat(XRayPulseSpeedId, _painter.XRayPulseSpeed);
|
||||
_mpb.SetFloat(XRayPulseStrengthId, _painter.XRayPulseStrength);
|
||||
_mpb.SetFloat(XRayPulseShapeId, _painter.XRayPulseShape);
|
||||
// Per-shell glow scale: the body writes the reveal once (opaque) but the fur stacks many
|
||||
// transparent shells that each composite it, so scale it down to keep the total near the body.
|
||||
_mpb.SetFloat(XRayFurStrengthId, _xrayFurStrength);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,16 +2,15 @@ using UnityEngine;
|
||||
using Unity.Cinemachine;
|
||||
|
||||
/// <summary>
|
||||
/// Drives the world-space "hero cutout": occluder geometry inside a cone from the third-person camera
|
||||
/// (apex) to the player is clipped away, punching a see-through funnel toward the character. Each late
|
||||
/// frame this feeds two global shader properties (<c>_HeroCutoutTarget</c>, <c>_HeroCutoutParams</c>)
|
||||
/// that <c>Custom/HeroCutoutLit</c> reads.
|
||||
/// World-space "hero cutout": occluder geometry inside a cone from the third-person camera (apex) to
|
||||
/// the player is clipped away, punching a see-through funnel toward the character. Each late frame this
|
||||
/// feeds two globals (<c>_HeroCutoutTarget</c>, <c>_HeroCutoutParams</c>) that <c>Custom/HeroCutoutLit</c> reads.
|
||||
///
|
||||
/// Arming is deliberately simple: ONE ray from the camera toward the player - blocked = armed, clear
|
||||
/// view = occluders stay solid. The cut volume lives entirely in the shader: everything between camera
|
||||
/// and player (within the cone) is cut, geometry behind the player stays solid, and the far end fades
|
||||
/// across a soft depth band (<see cref="depthFeather"/>) instead of a hard plane. The cone radius grows
|
||||
/// with camera distance, so the hole keeps a constant on-screen size at every occluder depth.
|
||||
/// Arming is deliberately simple: ONE ray camera -> player; blocked = armed, clear view = occluders stay
|
||||
/// solid. The cut volume lives entirely in the shader: everything between camera and player (within the
|
||||
/// cone) is cut, geometry behind the player stays solid, and the far end fades across a soft depth band
|
||||
/// (<see cref="depthFeather"/>) instead of a hard plane. The cone tapers toward the camera, so the hole
|
||||
/// keeps a constant on-screen size at every occluder depth.
|
||||
///
|
||||
/// Active only while the third-person orbit vcam is the live Cinemachine camera; disarming clears the
|
||||
/// global flag (also the zero-initialised default), so scenes without this driver render cutout
|
||||
@@ -193,9 +192,8 @@ public class HeroCutout : MonoBehaviour
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lazily resolves the orbit vcam and its Cinemachine Deoccluder (they may live in a scene that
|
||||
/// loads after this driver). Captures the deoccluder's authored obstacle-avoidance state once so it
|
||||
/// can be restored later.
|
||||
/// Lazily resolves the orbit vcam and its Deoccluder (they may live in a scene that loads after this
|
||||
/// driver); captures the deoccluder's authored obstacle-avoidance state once so it can be restored.
|
||||
/// </summary>
|
||||
private void EnsureRefs()
|
||||
{
|
||||
@@ -209,9 +207,9 @@ public class HeroCutout : MonoBehaviour
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Switches the deoccluder's obstacle avoidance (the camera pull-in) off while this driver replaces
|
||||
/// it with the cutout, or restores the authored state when the replacement is turned off. Only
|
||||
/// toggles on transitions, so it never fights a value changed elsewhere mid-frame.
|
||||
/// Toggles the deoccluder's obstacle avoidance (the camera pull-in) off/back per
|
||||
/// <see cref="replaceDeoccluderPullIn"/>. Only acts on transitions, so it never fights a value
|
||||
/// changed elsewhere mid-frame.
|
||||
/// </summary>
|
||||
private void ApplyPullInSuppression()
|
||||
{
|
||||
@@ -229,7 +227,6 @@ public class HeroCutout : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Restores the deoccluder's authored pull-in if this driver had suppressed it.</summary>
|
||||
private void RestoreDeoccluderPullIn()
|
||||
{
|
||||
if (_pullInSuppressed && _deoccluder)
|
||||
|
||||
@@ -2,12 +2,11 @@ using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
/// <summary>
|
||||
/// Momentary world-space laser beam for a hunter's shot. Purely visual (no networking): a LineRenderer drawn
|
||||
/// from the muzzle to the hit point that fades IN fast then OUT over its lifetime, animating both opacity
|
||||
/// (via per-vertex colour alpha, read by the Chameleon/Laser shader) and thickness (via widthMultiplier).
|
||||
/// Self-contained: it builds its own LineRenderer child + an additive material at runtime, so it can be added
|
||||
/// to a hunter avatar purely in code with no prefab authoring. Call <see cref="Fire"/> to trigger; the beam
|
||||
/// recycles itself (disables the renderer) once it expires.
|
||||
/// Momentary world-space laser beam for a hunter's shot. Purely visual (no networking): a LineRenderer
|
||||
/// from muzzle to hit point that fades IN fast then OUT, animating opacity (per-vertex colour alpha, read
|
||||
/// by the Chameleon/Laser shader) and thickness (widthMultiplier). Self-contained: builds its own
|
||||
/// LineRenderer child + additive material at runtime, so it can be added to an avatar purely in code with
|
||||
/// no prefab authoring. Call <see cref="Fire"/> to trigger; the beam disables its renderer once expired.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public sealed class HunterLaserBeam : MonoBehaviour
|
||||
|
||||
@@ -20,7 +20,6 @@ public partial class MeshPainter
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Switches the backend at runtime, carrying over all painted maps.</summary>
|
||||
private void SwitchBackend(bool toGpu)
|
||||
{
|
||||
var canonical = new Color32[MAP_COUNT][];
|
||||
@@ -66,7 +65,6 @@ public partial class MeshPainter
|
||||
if (!_backendSeeded) ReseedCanonical(null);
|
||||
}
|
||||
|
||||
/// <summary>Tears down the active backend and sets up the requested one on the given maps.</summary>
|
||||
private void RebuildBackend(bool gpu, Color32[][] canonical)
|
||||
{
|
||||
if (_gpuActive) TeardownGpu();
|
||||
@@ -75,7 +73,6 @@ public partial class MeshPainter
|
||||
_hasLastScreen = false;
|
||||
}
|
||||
|
||||
/// <summary>Seed source for a map: the supplied canonical data when valid, else the material seed.</summary>
|
||||
private Color32[] ResolveSeed(int mi, Color32[][] canonical)
|
||||
{
|
||||
return (canonical != null && canonical[mi] != null && canonical[mi].Length == textureSize * textureSize)
|
||||
|
||||
@@ -85,7 +85,7 @@ public partial class MeshPainter
|
||||
if (!DeferInitialSeed) SeedGpuMap(mi, canonical); // deferred: RTs stay unseeded until the snapshot re-seed
|
||||
|
||||
// Color maps render to the display RT; the MS map exposes its packed final RT.
|
||||
AssignMapTexture(mi, gm.final != null ? gm.final : gm.display);
|
||||
AssignMapTexture(mi, gm.final ? gm.final : gm.display);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -133,7 +133,7 @@ public partial class MeshPainter
|
||||
{
|
||||
SeedGpuMap(mi, canonical);
|
||||
var gm = _gpu[mi];
|
||||
AssignMapTexture(mi, gm.final != null ? gm.final : gm.display);
|
||||
AssignMapTexture(mi, gm.final ? gm.final : gm.display);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,11 +426,11 @@ public partial class MeshPainter
|
||||
Graphics.Blit(gm.display, gm.temp, _dilateMat, 0);
|
||||
|
||||
var swap = gm.display; gm.display = gm.temp; gm.temp = swap;
|
||||
if (gm.final == null) AssignMapTexture(mi, gm.display);
|
||||
if (!gm.final) AssignMapTexture(mi, gm.display);
|
||||
}
|
||||
|
||||
// MS: pack the work layout (R=metallic, G=smoothness) into the URP final layout.
|
||||
if (gm.final != null) PackMs(gm);
|
||||
if (gm.final) PackMs(gm);
|
||||
}
|
||||
|
||||
GL.sRGBWrite = _prevSRGB;
|
||||
|
||||
@@ -73,7 +73,6 @@ public partial class MeshPainter
|
||||
/// <summary>True if anyone is listening for replicated stamps (the owner is networked + painting).</summary>
|
||||
private bool WantsStampCapture => OnStampCommitted != null;
|
||||
|
||||
/// <summary>Builds the replicated stamp from the current brush state + resolved placement and raises the event.</summary>
|
||||
private void EmitStamp(Vector3 p, float r, Vector3 u, Vector3 v, Vector3 n)
|
||||
{
|
||||
var handler = OnStampCommitted;
|
||||
|
||||
@@ -381,7 +381,6 @@ public partial class MeshPainter
|
||||
return dst;
|
||||
}
|
||||
|
||||
/// <summary>Bilinear blend of four byte samples (rounded, clamped to 0..255).</summary>
|
||||
private static byte Bilerp(byte c00, byte c10, byte c01, byte c11, float fx, float fy)
|
||||
{
|
||||
float top = c00 + (c10 - c00) * fx;
|
||||
|
||||
@@ -296,7 +296,7 @@ public partial class MeshPainter
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
TerrainLayer L = layers[i];
|
||||
if (L == null) continue;
|
||||
if (!L) continue;
|
||||
float w = weights[0, 0, i];
|
||||
if (w <= 0f) continue;
|
||||
|
||||
@@ -437,7 +437,7 @@ public partial class MeshPainter
|
||||
private static Color SampleTexel(Texture tex, Vector2 uv, bool srgb)
|
||||
{
|
||||
Texture2D t2d = tex as Texture2D;
|
||||
if (t2d != null && t2d.isReadable)
|
||||
if (t2d && t2d.isReadable)
|
||||
return t2d.GetPixelBilinear(uv.x, uv.y);
|
||||
|
||||
int w = Mathf.Max(1, tex.width), h = Mathf.Max(1, tex.height);
|
||||
|
||||
@@ -260,8 +260,8 @@ public partial class MeshPainter
|
||||
{
|
||||
_materialInstance.EnableKeyword("_PARALLAXMAP");
|
||||
_materialInstance.EnableKeyword("_NORMALMAP");
|
||||
if (GetMatFloat(ParallaxId, 0.02f) < 0.02f)
|
||||
SetMatFloat(ParallaxId, 0.02f);
|
||||
if (GetMatFloat(ParallaxId, ParallaxBase) < ParallaxBase)
|
||||
SetMatFloat(ParallaxId, ParallaxBase);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,16 +7,9 @@ using UnityEngine.InputSystem;
|
||||
using Random = UnityEngine.Random; // System (for Action) also defines Random -> keep the UnityEngine one
|
||||
|
||||
/// <summary>
|
||||
/// Live mesh painting in Play mode. Three paint maps are driven at once (each toggleable): albedo
|
||||
/// (_BaseMap), an HDRP-style mask map (R = metallic, G = occlusion, A = smoothness; one texture bound
|
||||
/// to BOTH _MetallicGlossMap and _OcclusionMap) and emission (_EmissionMap). Two backends, switchable
|
||||
/// live via <see cref="useGpu"/>: GPU shader painting into RenderTextures (fast, default) and CPU
|
||||
/// rasterization (identical result). Both paint seamlessly across UV seams because the brush is
|
||||
/// evaluated in 3D local space around the hit point, capturing triangles on both sides of a seam.
|
||||
///
|
||||
/// The brush is a square stamp in the surface tangent plane shaped by a grayscale mask (mask value =
|
||||
/// per-texel opacity); an empty mask list falls back to a procedural circle (classic round brush).
|
||||
/// A non-destructive preview (<see cref="previewMode"/>) shows size + mask at the hover point.
|
||||
/// Live Play-mode mesh painting of albedo, an HDRP-style mask map (metallic/occlusion/smoothness)
|
||||
/// and emission, with live-switchable GPU/CPU backends (<see cref="useGpu"/>) that paint identically
|
||||
/// and seamlessly across UV seams: the masked square brush is evaluated in 3D local space at the hit.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public partial class MeshPainter : MonoBehaviour
|
||||
@@ -727,7 +720,6 @@ public partial class MeshPainter : MonoBehaviour
|
||||
|
||||
// --- Brush preview ---
|
||||
|
||||
/// <summary>Creates the preview material and the unit quad used by the flat-decal mode.</summary>
|
||||
private void SetupPreview()
|
||||
{
|
||||
if (!previewShader) previewShader = Shader.Find("Hidden/MeshPaintPreview");
|
||||
@@ -860,7 +852,7 @@ public partial class MeshPainter : MonoBehaviour
|
||||
return;
|
||||
|
||||
// Don't paint through the in-game UI (a HUD click must not also stamp the mesh behind it).
|
||||
if (pressed && IsPointerOverUi())
|
||||
if (pressed && InputUtil.PointerOverUi())
|
||||
pressed = false;
|
||||
|
||||
// Brush preview at the hover point - independent of painting AND of the pipette, so the
|
||||
@@ -946,7 +938,6 @@ public partial class MeshPainter : MonoBehaviour
|
||||
_hasLastScreen = true;
|
||||
}
|
||||
|
||||
/// <summary>Raycasts at a screen position and stamps the masked square brush onto the mesh hit point.</summary>
|
||||
private void PaintAtScreen(Vector2 screenPos)
|
||||
{
|
||||
if (!TryComputeBrushPlacement(screenPos, out Vector3 pLocal, out float radiusLocal,
|
||||
@@ -1132,7 +1123,6 @@ public partial class MeshPainter : MonoBehaviour
|
||||
|
||||
// --- Channels (shared by both backends) ---
|
||||
|
||||
/// <summary>True if at least one paint channel is enabled (used to gate the per-frame stroke).</summary>
|
||||
private bool AnyChannelActive()
|
||||
{
|
||||
for (int mi = 0; mi < MAP_COUNT; mi++)
|
||||
@@ -1224,9 +1214,6 @@ public partial class MeshPainter : MonoBehaviour
|
||||
BrushSizeChanged?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>True if the pointer is currently over an interactive UI element (raycast target).</summary>
|
||||
private static bool IsPointerOverUi() => InputUtil.PointerOverUi();
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (_bakedMesh && !_baker) Destroy(_bakedMesh); // _baker owns its mesh; only free our own
|
||||
|
||||
@@ -2,17 +2,16 @@ using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// One knob that resizes the whole player. Setting <see cref="Scale"/> drives <c>transform.localScale</c>
|
||||
/// — which Unity already propagates to the visible mesh, the CharacterController capsule, the per-pose
|
||||
/// capsules (written as local values) and the camera target — and additionally rescales the movement,
|
||||
/// jump, slide and wall tunables on <see cref="PlayerCharacterController"/> proportionally, so a smaller
|
||||
/// or larger player still moves at the same body-relative pace instead of zipping or crawling. Without
|
||||
/// this the speeds (units/second, not geometry) stay put and a half-size player feels twice as fast.
|
||||
/// (mesh, CharacterController capsule, per-pose capsules and camera target follow automatically) and
|
||||
/// rescales the movement/jump/slide/wall tunables on <see cref="PlayerCharacterController"/>
|
||||
/// proportionally — the speeds are units/second, not geometry, so without this a half-size player feels
|
||||
/// twice as fast.
|
||||
///
|
||||
/// The authored values on the controller ARE the scale-1 design values: tune them normally there. They are
|
||||
/// captured as the base in Awake and multiplied by Scale at runtime, so the serialized design values are
|
||||
/// never overwritten on disk. In the editor only <c>localScale</c> is previewed live (geometry is the only
|
||||
/// visual part); the speed rescale happens on play. The skinned mesh collider scales correctly because
|
||||
/// <see cref="SkinnedColliderBaker"/> divides the player scale back out of its bake.
|
||||
/// The authored controller values ARE the scale-1 design values: they are captured as the base in Awake
|
||||
/// and multiplied by Scale at runtime, so the serialized values are never overwritten on disk. In the
|
||||
/// editor only <c>localScale</c> is previewed live; the speed rescale happens on play. The skinned mesh
|
||||
/// collider scales correctly because <see cref="SkinnedColliderBaker"/> divides the player scale back
|
||||
/// out of its bake.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
[RequireComponent(typeof(PlayerCharacterController))]
|
||||
|
||||
@@ -3,15 +3,14 @@ using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Sits on the "Radar" overlay object (a disc rendered by the Radar Camera in front of the main camera).
|
||||
/// Spawns "Target" blips - clones of the child "Target Template" - each of which continuously points from
|
||||
/// the reference camera toward a world-space transform (visualising the bearing camera -> target) and then
|
||||
/// destroys itself after a given lifetime. See <see cref="RadarTarget"/> for the per-blip behaviour.
|
||||
/// Spawns "Target" blips - clones of the child "Target Template" - each pointing from the reference
|
||||
/// camera toward a world-space transform until its lifetime elapses (see <see cref="RadarTarget"/>).
|
||||
///
|
||||
/// The whole radar overlay is only shown while it is useful: at least one live target, first-person is active,
|
||||
/// AND the match is in its Seek phase. Instead of snapping on/off it fades: a shared 0..1 factor (<see cref="CurrentFade"/>)
|
||||
/// drives the alpha of every (transparent) radar renderer - the disc here, the blip points in <see cref="RadarTarget"/> -
|
||||
/// and scales the disc from <see cref="hiddenScale"/> up to 1. Fade-in and fade-out speeds are independent. The overlay
|
||||
/// camera is switched off only once fully faded out, so hidden blips keep counting down their lifetime meanwhile.
|
||||
/// Shown only while useful: at least one live target, first-person active, AND the match in its Seek
|
||||
/// phase. Instead of snapping on/off it fades: a shared 0..1 factor (<see cref="CurrentFade"/>) drives
|
||||
/// the alpha of every radar renderer - the disc here, the blip points in RadarTarget - and scales the
|
||||
/// disc from <see cref="hiddenScale"/> up to 1. The overlay camera is switched off only once fully faded
|
||||
/// out, so hidden blips keep counting down their lifetime meanwhile.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public sealed class Radar : MonoBehaviour
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// A single radar blip spawned by <see cref="Radar"/>. Each frame it orients itself so its forward (+Z, where
|
||||
/// the "Point" marker sits) faces the direction from the reference camera to a world-space target, visualising
|
||||
/// the bearing camera -> target. Tracks continuously (the camera turns / the target moves) and self-destructs
|
||||
/// once its lifetime elapses - or earlier if its target is destroyed. The point's base colour and size encode
|
||||
/// the two things the flat radar disc can't show via position alone: <see cref="forwardGradient"/> tints it by
|
||||
/// how far the target sits ahead of / behind the camera, and its scale shrinks with camera -> target distance.
|
||||
/// A single radar blip spawned by <see cref="Radar"/>. Each frame it orients its forward (+Z, where the
|
||||
/// "Point" marker sits) along the bearing reference camera -> world target, and self-destructs once its
|
||||
/// lifetime elapses - or earlier if the target is destroyed. The point encodes the two things the flat
|
||||
/// disc can't show via position alone: <see cref="forwardGradient"/> tints it by how far the target sits
|
||||
/// ahead of / behind the camera, and its scale shrinks with camera -> target distance.
|
||||
///
|
||||
/// On top of the radar-wide fade, each blip fades in when it spawns and fades out at the end of its life (or when
|
||||
/// its world target vanishes) via its own <see cref="_fade"/> factor, so blips appear/disappear smoothly instead
|
||||
/// of popping. Fade-in and fade-out speeds are independent; the factor persists across <see cref="Refresh"/> so a
|
||||
/// refreshed blip is pulled back to full opacity rather than flickering to zero first.
|
||||
/// On top of the radar-wide fade, each blip ramps its own <see cref="_fade"/> in on spawn and out at end
|
||||
/// of life (or when its world target vanishes), so blips never pop. The factor persists across
|
||||
/// <see cref="Refresh"/> so a refreshed blip is pulled back to full opacity rather than flickering to
|
||||
/// zero first.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public sealed class RadarTarget : MonoBehaviour
|
||||
|
||||
@@ -3,16 +3,15 @@ using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Sole owner of the animated character's collider bake: keeps a non-convex MeshCollider in sync with
|
||||
/// the SkinnedMeshRenderer's current pose every frame, independent of any paint/game mode. MeshPainter's
|
||||
/// own per-frame bake sits behind its PaintingEnabled gate, so the collider would otherwise freeze in
|
||||
/// player-control / insect mode. When this component is present MeshPainter delegates to it (it stops
|
||||
/// baking and only borrows <see cref="BakedMesh"/> for its GPU raster path), so the deformed-pose
|
||||
/// collider stays fresh for everyone - paint raycasts, physics, future seeker / line-of-sight queries.
|
||||
/// the SkinnedMeshRenderer's current pose every frame, independent of any paint/game mode (MeshPainter's
|
||||
/// own bake sits behind its PaintingEnabled gate, so the collider would otherwise freeze in
|
||||
/// player-control / insect mode). When present, MeshPainter delegates to it and only borrows
|
||||
/// <see cref="BakedMesh"/> for its GPU raster path.
|
||||
///
|
||||
/// Bakes in the renderer's LOCAL space (BakeMesh, scale = 1) - the same convention MeshPainter and
|
||||
/// ConcealmentMeter use (world = transform.localToWorldMatrix). Runs at a negative execution order so
|
||||
/// the collider is fresh before MeshPainter (and other consumers) read it in the same frame. Owns the
|
||||
/// baked Mesh (creates + destroys it); MeshPainter never destroys a borrowed mesh.
|
||||
/// the collider is fresh before consumers read it in the same frame. Owns the baked Mesh (creates +
|
||||
/// destroys it); MeshPainter never destroys a borrowed mesh.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
[DefaultExecutionOrder(-50)]
|
||||
@@ -66,13 +65,12 @@ public class SkinnedColliderBaker : MonoBehaviour
|
||||
_skinned.BakeMesh(_baked);
|
||||
|
||||
// BakeMesh freezes the bone deformation — which already carries this transform's world scale —
|
||||
// into the mesh. The MeshCollider below then applies the same lossyScale a SECOND time, so a
|
||||
// scaled player (see PlayerScaler) would get a collider half/double the size of the visible
|
||||
// mesh. Divide the scale back out so the baked mesh is true scale-1 local geometry (the contract
|
||||
// this class already documents): the MeshCollider re-applies it exactly once (matching the
|
||||
// renderer), and MeshPainter / ConcealmentMeter — which raise the baked mesh to world via
|
||||
// localToWorldMatrix — stay correct at any player scale. No-op (and untouched) at scale 1; the
|
||||
// uniform divide is rotation-independent, which the player's uniform scale guarantees.
|
||||
// into the mesh, and the MeshCollider below applies the same lossyScale a SECOND time, so a
|
||||
// scaled player (see PlayerScaler) would get a collider half/double the visible mesh. Divide the
|
||||
// scale back out so the baked mesh is true scale-1 local geometry: the MeshCollider re-applies it
|
||||
// exactly once, and MeshPainter / ConcealmentMeter (world via localToWorldMatrix) stay correct at
|
||||
// any player scale. No-op at scale 1; the uniform divide is rotation-independent, which the
|
||||
// player's uniform scale guarantees.
|
||||
GeometryUtil.RescaleToUnitScale(_baked, transform.lossyScale, _vbuf);
|
||||
|
||||
_collider.sharedMesh = null; // force a physics re-cook of the new geometry
|
||||
|
||||
@@ -4,26 +4,22 @@ using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
/// <summary>
|
||||
/// Builds the environment reflection from the current Skybox in memory (renders
|
||||
/// the sky into a cubemap and assigns it as the Custom reflection), so
|
||||
/// smooth/metallic materials reflect the sky WITHOUT a baked LightingData.asset.
|
||||
/// Runs in Edit and Play mode ([ExecuteAlways]); drop it on one object per scene
|
||||
/// (or a prefab that lives in every scene) and you never have to assign a foreign
|
||||
/// LightingData again — also works for runtime-loaded / procedural scenes.
|
||||
/// Builds the environment reflection from the current Skybox in memory (renders the sky into a cubemap
|
||||
/// assigned as the Custom reflection), so smooth/metallic materials reflect the sky WITHOUT a baked
|
||||
/// LightingData.asset. Runs in Edit and Play mode ([ExecuteAlways]); drop it on one object per scene —
|
||||
/// also works for runtime-loaded / procedural scenes.
|
||||
///
|
||||
/// Why a rendered cubemap and not <see cref="DynamicGI.UpdateEnvironment"/>:
|
||||
/// in this project UpdateEnvironment does NOT produce a valid skybox reflection
|
||||
/// probe (a mirror stays black), so <see cref="RenderSettings.reflectionIntensity"/>
|
||||
/// had nothing to scale. Rendering the sky into a Custom reflection cubemap gives a
|
||||
/// real reflection that intensity can actually drive.
|
||||
/// Why a rendered cubemap and not <see cref="DynamicGI.UpdateEnvironment"/>: in this project
|
||||
/// UpdateEnvironment does NOT produce a valid skybox reflection probe (a mirror stays black), so
|
||||
/// <see cref="RenderSettings.reflectionIntensity"/> had nothing to scale. Rendering the sky into a
|
||||
/// Custom reflection cubemap gives a real reflection that intensity can actually drive.
|
||||
///
|
||||
/// The skybox AMBIENT probe still comes from UpdateEnvironment — but it only convolves a
|
||||
/// non-zero probe once the sky has actually been rendered this session. Straight after a
|
||||
/// domain reload / scene load / play-start the sky hasn't rendered yet, so the probe is
|
||||
/// ZERO and the Environment intensity multiplier has nothing to scale ("doesn't take
|
||||
/// effect" until a manual re-enable). Fix: do one synchronous full-screen sky render to
|
||||
/// "warm" the sky right before UpdateEnvironment — deterministic, no waiting on renders.
|
||||
/// (RenderToCubemap does NOT warm the ambient path; a normal Camera.Render does.)
|
||||
/// The skybox AMBIENT probe still comes from UpdateEnvironment — but it only convolves a non-zero probe
|
||||
/// once the sky has actually been rendered this session. Straight after a domain reload / scene load /
|
||||
/// play-start the sky hasn't rendered yet, so the probe is ZERO and the Environment intensity multiplier
|
||||
/// has nothing to scale ("doesn't take effect" until a manual re-enable). Fix: one synchronous
|
||||
/// full-screen sky render "warms" the sky right before UpdateEnvironment — deterministic, no waiting on
|
||||
/// renders. (RenderToCubemap does NOT warm the ambient path; a normal Camera.Render does.)
|
||||
/// </summary>
|
||||
[ExecuteAlways]
|
||||
[DisallowMultipleComponent]
|
||||
|
||||
@@ -6,19 +6,16 @@ using UnityEngine;
|
||||
/// AUTHORITY's avatar stands inside it, it asks the <see cref="MatchManager"/> to start the match.
|
||||
///
|
||||
/// Detection POLLS a server-side physics overlap every frame instead of relying on OnTriggerEnter.
|
||||
/// Why: the authority can be a REMOTE client (a dedicated server with no host player hands authority to
|
||||
/// the first client). On the server that client's avatar is a NetworkTransform-driven replica - its
|
||||
/// CharacterController is disabled (<see cref="NetworkPlayerSetup"/>) and it carries no Rigidbody, so it
|
||||
/// is moved by direct transform writes. Unity only sends OnTriggerEnter when one of the two colliders has
|
||||
/// a Rigidbody (or is a CharacterController moving via Move()); a transform-moved non-Rigidbody collider
|
||||
/// against a static trigger fires nothing. That is exactly why a host's own (Move()-driven) avatar tripped
|
||||
/// the trigger but a remote authority never did. An explicit overlap query finds the avatar's (baked)
|
||||
/// collider regardless of HOW it is moved, so start works for a host and a dedicated server alike.
|
||||
/// Why: the authority can be a REMOTE client, whose server-side avatar is a NetworkTransform-driven
|
||||
/// replica - CharacterController disabled (<see cref="NetworkPlayerSetup"/>), no Rigidbody, moved by
|
||||
/// direct transform writes. Unity only sends OnTriggerEnter when one of the two colliders has a
|
||||
/// Rigidbody (or is a CharacterController moving via Move()), so a host's own (Move()-driven) avatar
|
||||
/// tripped the trigger but a remote authority never did. An explicit overlap query finds the avatar's
|
||||
/// (baked) collider regardless of HOW it is moved.
|
||||
///
|
||||
/// Server-gated: pure clients query nothing. Only the authority avatar starts the match; a passing
|
||||
/// non-authority avatar is ignored. Runs only while in the lobby phase and stops once it has started a
|
||||
/// match. The trigger lives in the additively-loaded Lobby and is destroyed when the lobby unloads, so a
|
||||
/// re-match (back in the lobby) gets a fresh instance with its guard reset.
|
||||
/// Server-gated: pure clients query nothing. Only the authority avatar starts the match, only during
|
||||
/// the lobby phase, once. The trigger lives in the additively-loaded Lobby and is destroyed when the
|
||||
/// lobby unloads, so a re-match gets a fresh instance with its guard reset.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Collider))]
|
||||
[DisallowMultipleComponent]
|
||||
|
||||
@@ -3,15 +3,9 @@ using UnityEngine.UI;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
/// <summary>
|
||||
/// Makes a UI panel draggable. Attach to the panel itself: it follows the pointer from any of the panel's
|
||||
/// non-interactive surface (title bar, padding, labels, background) but ignores presses that begin on a
|
||||
/// widget - a <see cref="Selectable"/> (toggle, slider, ...) or a <see cref="UiPointerSurface"/> (the
|
||||
/// colour-picker field) - so those keep their own click/drag behaviour. It keeps a slice of the panel on
|
||||
/// screen and raises it above its siblings while interacting.
|
||||
///
|
||||
/// Works under any canvas scale (it tracks the pointer in the drag area's local space and applies the
|
||||
/// movement delta) and under any anchor/pivot (the on-screen clamp is derived from the panel's real
|
||||
/// corners in its parent, not from a fixed top-left assumption).
|
||||
/// Makes a UI panel draggable from any non-interactive surface; presses that begin on a widget
|
||||
/// (<see cref="Selectable"/> or <see cref="UiPointerSurface"/>) keep their own click/drag behaviour.
|
||||
/// Keeps a slice of the panel on screen and raises it above siblings; canvas-scale/anchor/pivot-agnostic.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
public class DraggablePanel : MonoBehaviour, IPointerDownHandler, IBeginDragHandler, IDragHandler, IEndDragHandler
|
||||
@@ -40,8 +34,6 @@ public class DraggablePanel : MonoBehaviour, IPointerDownHandler, IBeginDragHand
|
||||
|
||||
public void OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
// Raise the panel when pressing its own surface. (A press that lands on a widget is consumed by
|
||||
// that widget's own handler and never reaches us, so this rarely needs the guard - but it is cheap.)
|
||||
if (!StartedOnWidget(eventData)) BringToFront();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,17 +3,11 @@ 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 texture rebuilds. Hue is
|
||||
/// never lost while dragging the square (the square only edits S/V, the bar only edits H). Alpha is
|
||||
/// edited separately by a standalone slider in the HUD, not by this picker; <see cref="SetColor"/>
|
||||
/// still stores the incoming alpha so <see cref="Value"/> round-trips it.
|
||||
///
|
||||
/// The child structure (stable names) comes from the baked HUD prefab. <see cref="Rebind"/>
|
||||
/// (re)assigns the runtime gradient sprites (which a prefab cannot store) and (re)hooks the drag
|
||||
/// relays. A leftover alpha bar from a pre-split prefab is hidden by <see cref="Rebind"/>, so no
|
||||
/// re-bake is required.
|
||||
/// Runtime HSV colour picker: SV square (pure-hue base + static gradient overlays, so no per-frame
|
||||
/// texture rebuilds; edits only S/V) plus hue bar (edits only H), so hue is never lost while dragging.
|
||||
/// Alpha is edited by a separate HUD slider, but <see cref="SetColor"/> stores it so <see cref="Value"/>
|
||||
/// round-trips it. Child structure (stable names) comes from the baked HUD prefab; <see cref="Rebind"/>
|
||||
/// (re)assigns the runtime gradient sprites (which a prefab cannot store) and (re)hooks the drag relays.
|
||||
/// </summary>
|
||||
public class HsvColorPicker : MonoBehaviour
|
||||
{
|
||||
@@ -49,8 +43,7 @@ public class HsvColorPicker : MonoBehaviour
|
||||
|
||||
/// <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
|
||||
/// built in code or instantiated from a prefab. Call before <see cref="SetColor"/>.
|
||||
/// relays. Idempotent. Call before <see cref="SetColor"/>.
|
||||
/// </summary>
|
||||
public void Rebind()
|
||||
{
|
||||
@@ -87,7 +80,6 @@ public class HsvColorPicker : MonoBehaviour
|
||||
Refresh();
|
||||
}
|
||||
|
||||
// Idempotently hooks the handler to the surface's drag relay (unsubscribe-then-subscribe).
|
||||
private static void Rehook(Component surface, Action<Vector2> handler)
|
||||
{
|
||||
if (surface.TryGetComponent(out UiPointerSurface pointer))
|
||||
|
||||
@@ -5,14 +5,10 @@ using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Binder for the "Paintings" library panel. The panel's visual layout (Panel_Paintings, the scrollable
|
||||
/// list, an entry template, and the Save / Confirm dialogs) is authored inside the PaintHudPanel layout
|
||||
/// prefab - this code only finds those objects by their stable names and wires the behaviour, exactly like
|
||||
/// <see cref="WirePanel"/> does for the brush widgets. The list is populated by cloning the authored
|
||||
/// Lib_EntryTemplate per saved slot. Save opens a name dialog (an existing name is overwritten, with a
|
||||
/// warning shown while typing); Load applies the selected slot immediately; Delete goes through a
|
||||
/// confirmation dialog. Loading replaces the current painting and, when networked, re-broadcasts the
|
||||
/// loaded state to every other peer.
|
||||
/// Binder for the "Paintings" library panel, authored in the PaintHudPanel layout prefab and wired here
|
||||
/// by stable names (like <see cref="WirePanel"/> for the brush widgets). The list clones Lib_EntryTemplate
|
||||
/// per saved slot; Save overwrites by name (warned while typing), Load applies immediately (re-broadcast
|
||||
/// to peers when networked), Delete confirms.
|
||||
/// </summary>
|
||||
public partial class PaintHud
|
||||
{
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.EventSystems;
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
using UnityEngine.InputSystem.UI;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// In-game paint UI. Shows its panels only while the active <see cref="GameMode"/> is
|
||||
/// <see cref="PaintMode"/> (follows the manager's ModeChanged) and binds its widgets directly to the
|
||||
/// live <see cref="MeshPainter"/> fields, so edits apply immediately.
|
||||
///
|
||||
/// The layout (independently draggable sub-panels per channel + a global brush card) comes from the
|
||||
/// authored prefab (<see cref="layoutPrefab"/>). The BINDINGS live in code and are (re)applied at
|
||||
/// runtime by <see cref="WirePanel"/> against stable widget names - the prefab stores only the
|
||||
/// visual layout, never behaviour.
|
||||
/// In-game paint UI. Visible only while the active <see cref="GameMode"/> is <see cref="PaintMode"/>;
|
||||
/// binds its widgets directly to the live <see cref="MeshPainter"/> fields. The prefab stores only the
|
||||
/// visual layout; the bindings live in code, (re)applied by <see cref="WirePanel"/> against stable widget names.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public partial class PaintHud : MonoBehaviour
|
||||
@@ -119,7 +113,6 @@ public partial class PaintHud : MonoBehaviour
|
||||
// (in BindPainter) overwrites them. The reset button restores exactly these.
|
||||
CaptureAuthoredLayout();
|
||||
|
||||
// The paint library now lives inside the authored layout prefab; bind its widgets like the brush panel.
|
||||
BindLibrary(_panel ? _panel.transform : null);
|
||||
|
||||
// Bind as soon as a painter exists. With networked spawn-on-connect there is none at scene
|
||||
@@ -443,11 +436,8 @@ public partial class PaintHud : MonoBehaviour
|
||||
return s;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches (idempotently) a <see cref="DraggablePanel"/> to the whole panel so it can be dragged
|
||||
/// from any of its non-interactive surface (header, padding, labels, background); the component
|
||||
/// ignores presses that start on a widget (toggle, slider, colour picker).
|
||||
/// </summary>
|
||||
/// <summary>Attaches (idempotently) a <see cref="DraggablePanel"/> so the whole panel can be
|
||||
/// dragged from any non-widget surface.</summary>
|
||||
private void MakeDraggable(Transform root, string panelName)
|
||||
{
|
||||
var panel = GeometryUtil.FindDeep(root, panelName) as RectTransform;
|
||||
|
||||
@@ -3,19 +3,10 @@ using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Reshapes a UI <see cref="Slider"/>'s response with an <see cref="AnimationCurve"/> so the value runs
|
||||
/// non-linearly between the slider's authored <c>minValue</c>/<c>maxValue</c>. The curve maps the handle
|
||||
/// position (x, 0..1 - linear in screen space) to a value fraction (y, 0..1) that is then scaled to the
|
||||
/// slider's range. The slider's own min/max stays the single source of truth for the range; this only
|
||||
/// bends the mapping inside it - so dropping the component onto an existing slider needs no range re-entry.
|
||||
///
|
||||
/// <see cref="Value"/> is the curved output, <see cref="SetOutput"/> positions the handle to show a given
|
||||
/// output (inverting the curve), and <see cref="ValueChanged"/> raises the curved value whenever the
|
||||
/// handle moves - so the component is usable both standalone (wire <see cref="ValueChanged"/>) and from
|
||||
/// code that drives the raw slider (read <see cref="Value"/>).
|
||||
///
|
||||
/// The curve should be monotonic increasing (the default linear is); the inverse used by
|
||||
/// <see cref="SetOutput"/> assumes that.
|
||||
/// Reshapes a UI <see cref="Slider"/>'s response with an <see cref="AnimationCurve"/> mapping handle
|
||||
/// position (x, 0..1) to a value fraction (y, 0..1) scaled to the slider's own minValue/maxValue, which
|
||||
/// stays the single source of truth for the range. The curve must be monotonic increasing: the inverse
|
||||
/// used by <see cref="SetOutput"/> assumes that.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Slider))]
|
||||
[DisallowMultipleComponent]
|
||||
|
||||
@@ -3,10 +3,9 @@ using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
/// <summary>
|
||||
/// Turns a pointer press/drag on a UI graphic into a normalized (x, y) position in
|
||||
/// [0,1] within its RectTransform (origin bottom-left). Added in code to the colour
|
||||
/// picker's saturation/value field and hue bar so they react to both clicking and
|
||||
/// dragging. The graphic on the same object must be a raycast target.
|
||||
/// Turns a pointer press/drag on a UI graphic into a normalized (x, y) position in [0,1] within its
|
||||
/// RectTransform (origin bottom-left). Used by the colour picker's SV field and hue bar. The graphic
|
||||
/// on the same object must be a raycast target.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
public class UiPointerSurface : MonoBehaviour, IPointerDownHandler, IDragHandler
|
||||
|
||||
@@ -9,13 +9,13 @@ using UnityEditor.SceneManagement;
|
||||
/// <summary>
|
||||
/// Editor tool that runs V-HACD (approximate convex decomposition via the native <see cref="VHACD"/>
|
||||
/// plugin) on an environment mesh and bakes the resulting convex hulls into SAVED mesh assets + child
|
||||
/// MeshCollider GameObjects — the exact same output plumbing as <see cref="ConcealmentColliderBaker"/>,
|
||||
/// only the decomposition backend differs (V-HACD's voxel solver instead of the surface-clustering
|
||||
/// MeshCollider GameObjects — the same output plumbing as <see cref="ConcealmentColliderBaker"/>, only
|
||||
/// the decomposition backend differs (V-HACD's voxel solver instead of the surface-clustering
|
||||
/// heuristic). Tune the decomposition on the sibling <see cref="VHACD"/> component (its m_parameters),
|
||||
/// then run "Bake VHACD Colliders".
|
||||
///
|
||||
/// The hulls are real child GameObjects referencing hull-mesh assets under
|
||||
/// <c>Assets/Generated/VHACDColliders</c>, so they survive domain reload / play-mode / scene save
|
||||
/// <c>Assets/Generated/VHACDColliders</c>, so they survive domain reload / play mode / scene save
|
||||
/// (unlike an in-memory-only bake).
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
@@ -143,7 +143,7 @@ public class VHACDColliderBaker : MonoBehaviour
|
||||
{
|
||||
string raw;
|
||||
PrefabStage stage = PrefabStageUtility.GetPrefabStage(gameObject);
|
||||
if (stage != null)
|
||||
if (stage)
|
||||
{
|
||||
string ap = stage.assetPath;
|
||||
if (ap.StartsWith("Assets/")) ap = ap.Substring("Assets/".Length);
|
||||
|
||||
Reference in new issue
Block a user