Class ScyllaShapeBase3D
Abstract base class for all concrete Scylla 3D shape components. A shape owns its UnityEngine.Mesh, regenerates it on demand from serialized parameters, and exposes a uniform set of features: tint, double-sided rendering, collider mode, bounds query.
Inheritance
Inherited Members
Namespace: Scylla.Core.Util.Geom
Assembly: ScyllaCore.dll
Syntax
[ExecuteAlways]
[DisallowMultipleComponent]
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public abstract class ScyllaShapeBase3D : MonoBehaviour
Remarks
Each shape subclass implements BuildMesh(Mesh) to populate the owned mesh from its serialized parameters. The base class handles the Unity plumbing (mesh allocation, component lifecycle, editor-time rebuild scheduling, collider attachment, tint application).
The owned UnityEngine.Mesh is created lazily in Awake() and the first Rebuild() call. It is destroyed in OnDestroy(). The mesh is marked UnityEngine.HideFlags.DontSaveInEditor so it does not bloat scene and prefab files.
In the editor, OnValidate() defers Rebuild() via DelayCall(Action) so the rebuild runs after the property change has settled and Unity allows mesh mutation and component changes. This applies in Edit Mode and Play Mode alike, so Inspector edits rebuild the shape in both. Code changes go through the property setters, which rebuild immediately.
The shape parameter setters apply the same fixed bounds the mesh build applies, such as a segment count range or a minimum dimension, before they store the value, so the getter returns the value the mesh is built with. A bound that depends on another parameter, for example a door width limited by the wall length, is applied only at build time, because applying it in the setter would make the result depend on the order in which the parameters are set. Assigning a value equal to the current one does not rebuild the mesh. Inspector edits bypass the setters and are clamped at build time only.
With a ColliderMode other than None, the shape manages exactly one collider: the one it attached. Other colliders on the GameObject are left alone. The one exception is a shape that has no collider of its own on record, for example one saved by an earlier version: if the GameObject holds exactly one non-trigger collider of the type the mode needs, the shape adopts it instead of adding a second one.
Properties
ActiveFaceColorResolver
The per-face color resolver active for the current rebuild, or null when
per-face coloring is disabled or the palette could not be resolved. Subclass
BuildMesh(Mesh) implementations that support per-face coloring forward
this to their static builder so it emits the mesh's vertex color channel; shapes that
do not yet support per-face coloring simply ignore it.
Declaration
protected IFaceColorResolver ActiveFaceColorResolver { get; }
Property Value
| Type | Description |
|---|---|
| IFaceColorResolver |
Bounds
Local-space axis-aligned bounding box of the most recently rebuilt mesh. Returns Empty when no mesh has been built. Implicit conversion is defined between AABB3D and UnityEngine.Bounds so callers can treat the result as either type.
Declaration
public AABB3D Bounds { get; }
Property Value
| Type | Description |
|---|---|
| AABB3D |
ColliderMode
Controls the collider attached to this shape's UnityEngine.GameObject when the mesh is rebuilt. See ColliderMode for variant behaviour.
Declaration
public ColliderMode ColliderMode { get; set; }
Property Value
| Type | Description |
|---|---|
| ColliderMode |
ColorPaletteName
Name of the ColorPalette (resolved via ColorAssetRegistry) used for per-face coloring. Ignored when FaceColoringMode is None or the name does not resolve to a palette. Setting this triggers a rebuild.
Declaration
public string ColorPaletteName { get; set; }
Property Value
| Type | Description |
|---|---|
| string |
ColorSeed
Seed for Hash. Changing it reshuffles the per-face color assignment deterministically and triggers a rebuild.
Declaration
public int ColorSeed { get; set; }
Property Value
| Type | Description |
|---|---|
| int |
DoubleSided
When true, the shape renders both faces by sharing
GetDoubleSided(). Useful for flat or single-sided
meshes (planes, quads, single-quad floor tiles). Changing this triggers a material
swap on the next Rebuild().
Declaration
public bool DoubleSided { get; set; }
Property Value
| Type | Description |
|---|---|
| bool |
FaceColoringMode
Selects how the shape's faces are colored from the palette named by ColorPaletteName. None (the default) disables per-face coloring entirely. Setting this triggers a rebuild.
Declaration
public FaceColoringMode FaceColoringMode { get; set; }
Property Value
| Type | Description |
|---|---|
| FaceColoringMode |
FaceColoringPalette
Runtime override palette for per-face coloring. When non-null, it takes precedence over ColorPaletteName, letting code drive coloring with a ColorPalette it already holds (no registry lookup). This is not serialized; the Inspector path uses the palette name instead. Setting this triggers a rebuild.
Declaration
public ColorPalette FaceColoringPalette { get; set; }
Property Value
| Type | Description |
|---|---|
| ColorPalette |
IsManuallyEdited
When true, this shape has been manually edited (e.g. a vertex was moved by
an external tool) and subsequent parameter changes no longer trigger an automatic
rebuild. This is reserved for future tooling and is always false in the
initial release.
Declaration
public bool IsManuallyEdited { get; set; }
Property Value
| Type | Description |
|---|---|
| bool |
SharedMesh
The UnityEngine.Mesh instance owned and rebuilt by this shape. Becomes non-null after the first Rebuild() call (or, in the editor, after the shape's OnValidate() deferred rebuild has run).
Declaration
public Mesh SharedMesh { get; }
Property Value
| Type | Description |
|---|---|
| Mesh |
Tint
Per-renderer tint applied via UnityEngine.MaterialPropertyBlock. Defaults to UnityEngine.Color.white. Setting this property refreshes the property block immediately.
Declaration
public Color Tint { get; set; }
Property Value
| Type | Description |
|---|---|
| Color |
Remarks
On the shared default shape material the tint is always written and sets the shape's color. On a custom material, opaque white means "no tint": the shape removes its color override so the material's own color shows. Any other color replaces the material's color for this renderer only.
The tint is written to _BaseColor (URP and HDRP Lit) or, when the material's
shader has no _BaseColor but has _Color, to _Color (Built-in
Standard). A shader with neither property ignores the tint. Removing the
override clears the renderer's whole property block.
TintEntryName
Name of the entry within TintPaletteName to use as the whole-shape tint. Ignored when TintPaletteName is empty or does not resolve. Setting this re-applies the tint.
Declaration
public string TintEntryName { get; set; }
Property Value
| Type | Description |
|---|---|
| string |
TintPaletteName
Name of a ColorPalette (resolved via ColorAssetRegistry) to draw the whole-shape tint from. When this and TintEntryName both resolve, the matching palette color overrides the inline Tint; otherwise Tint is used. Setting this re-applies the tint.
Declaration
public string TintPaletteName { get; set; }
Property Value
| Type | Description |
|---|---|
| string |
UniformColorIndex
Palette index used by Uniform. Clamped to the palette range at resolve time. Setting this triggers a rebuild.
Declaration
public int UniformColorIndex { get; set; }
Property Value
| Type | Description |
|---|---|
| int |
Methods
Awake()
Caches the UnityEngine.MeshFilter and UnityEngine.MeshRenderer references, ensures the default material is assigned, then performs an initial mesh rebuild.
Declaration
protected virtual void Awake()
BuildMesh(Mesh)
Subclass extension point: write vertex / triangle / normal / uv data into
target from the shape's serialized parameters. The base class
calls Clear(bool) before invoking this method and
UnityEngine.Mesh.RecalculateBounds() afterwards, so subclasses should not invoke
either themselves.
Declaration
protected abstract void BuildMesh(Mesh target)
Parameters
| Type | Name | Description |
|---|---|---|
| Mesh | target | The owned mesh to populate. Already cleared by the base class. |
GetMeshName()
Returns the shape's mesh name as it should appear on the generated mesh asset. Defaults to the subclass type name; override to provide a friendlier label.
Declaration
protected virtual string GetMeshName()
Returns
| Type | Description |
|---|---|
| string | A short identifier for the generated mesh, used as UnityEngine.Object.name. |
GetOrAddManagedCollider<T>()
Returns the collider this shape manages, as a T. Reuses the
managed collider when it already has that exact type; otherwise destroys only the
managed collider and attaches a new T. Colliders the user
added to the GameObject are never touched.
Declaration
protected T GetOrAddManagedCollider<T>() where T : Collider
Returns
| Type | Description |
|---|---|
| T | The managed collider, ready to be sized by the caller. |
Type Parameters
| Name | Description |
|---|---|
| T | The concrete collider type the current mode needs. |
Remarks
When the shape has no managed collider on record (a scene saved by an earlier
version, or a managed collider the user deleted), it adopts an existing collider
instead of adding one, but only when the GameObject holds exactly one non-trigger
collider of exactly type T. This keeps an upgraded scene
from gaining a duplicate of the collider the shape attached before.
OnDestroy()
Releases the owned UnityEngine.Mesh instance and unsubscribes from
DelayCall(Action) if a deferred rebuild was pending.
Subclasses overriding this method must call base.OnDestroy() to avoid
leaking the mesh or the editor callback.
Declaration
protected virtual void OnDestroy()
OnValidate()
Editor-only validation hook. Schedules a deferred Rebuild() so the mesh
reflects the latest Inspector values without violating Unity's restriction on
mutating objects during OnValidate. Runs in Edit Mode and Play Mode.
Declaration
protected virtual void OnValidate()
Rebuild()
Rebuilds the owned mesh from the shape's serialized parameters by invoking BuildMesh(Mesh), then refreshes the collider and tint.
Declaration
public void Rebuild()
Remarks
Safe to call from runtime, editor, and tests. When IsManuallyEdited
is true, the call is a no-op so manual mesh edits are preserved.
RebuildAndGet()
Calls Rebuild() and returns the resulting owned UnityEngine.Mesh. Intended for tween extensions and procedural systems that need a direct reference to the mesh immediately after a parameter change. The returned mesh is owned by this component and must not be destroyed by the caller.
Declaration
public Mesh RebuildAndGet()
Returns
| Type | Description |
|---|---|
| Mesh | The owned UnityEngine.Mesh after the rebuild completes. |
SetAndRebuild(ref float, float)
Stores value in field and rebuilds the mesh, unless
the two are approximately equal, in which case nothing happens. Shape parameter
setters call this with the value already clamped to the build's fixed bounds.
Declaration
protected bool SetAndRebuild(ref float field, float value)
Parameters
| Type | Name | Description |
|---|---|---|
| float | field | The serialized backing field of the parameter. |
| float | value | The new, already clamped value. |
Returns
| Type | Description |
|---|---|
| bool |
|
SetAndRebuild<T>(ref T, T)
Stores value in field and rebuilds the mesh, unless
the two are equal according to Default, in which case
nothing happens. Used for integer, boolean, enum and size parameters.
Declaration
protected bool SetAndRebuild<T>(ref T field, T value)
Parameters
| Type | Name | Description |
|---|---|---|
| T | field | The serialized backing field of the parameter. |
| T | value | The new, already clamped value. |
Returns
| Type | Description |
|---|---|
| bool |
|
Type Parameters
| Name | Description |
|---|---|
| T | The parameter type. |
TryAttachPrimitiveCollider()
Subclass extension point: when ColliderMode is
MatchingPrimitive, obtain the appropriate primitive
collider through GetOrAddManagedCollider<T>(), size it to the current
parameters, and return true. Return false to let the base class fall
back to UnityEngine.MeshCollider.
Declaration
protected virtual bool TryAttachPrimitiveCollider()
Returns
| Type | Description |
|---|---|
| bool |
|
Remarks
Runs on every rebuild. Always go through GetOrAddManagedCollider<T>()
rather than AddComponent: it reuses the collider from the previous rebuild,
so the override only has to update its size and center. A collider added directly
is not tracked by the shape and a new one would be added on every rebuild.