Package and reeadme updates

This commit is contained in:
Savya Bikram Shah
2026-05-27 10:36:39 +05:45
parent e6391e0b32
commit 3791708a17
12 changed files with 560 additions and 46 deletions

155
Readme.md
View File

@@ -466,7 +466,7 @@ public readonly struct ArtworkSavedSignal {
### `Capture`
- Bound to the "Capture" button.
- Calls `ICaptureService.CaptureAsync(artCamera, template.PaperBackground)` → PNG bytes.
- Calls `ICaptureService.CaptureAsync()` → PNG bytes. Capture reads `IPaperRig.Surface` directly; no camera or paper-bg args needed.
- Hands bytes to `IGalleryService.SaveAsync(...)`.
- Fires `ArtworkCapturedSignal` then `ArtworkSavedSignal`.
- Shows a quick "saved!" toast with a thumbnail of the new entry.
@@ -572,20 +572,23 @@ persistentDataPath/Gallery/
## 12. Capture Pipeline
With the RT-paper-rig, capture has no setup phase. The RT is already the final image at all times.
```
[Capture button or Next button]
ICaptureService.CaptureAsync(artCamera, paperBg)
ICaptureService.CaptureAsync()
├─ Allocate RenderTexture (2048×2048, ARGB32)
├─ artCamera.targetTexture = rt
├─ Force render (artCamera.Render())
├─ ReadPixels into Texture2D
├─ Composite paperBg underneath (single shader pass or CPU blend)
├─ Encode PNG (Texture2D.EncodeToPNG)
├─ Release RT + temp texture
return byte[]
├─ rt = _paperRig.Surface (already populated each frame)
├─ prev = RenderTexture.active
├─ RenderTexture.active = rt
├─ tex = new Texture2D(rt.width, rt.height, RGBA32, false)
├─ tex.ReadPixels(full rect, 0, 0); tex.Apply()
├─ RenderTexture.active = prev
├─ bytes = tex.EncodeToPNG() (on worker via UniTask.RunOnThreadPool)
Object.Destroy(tex)
└─ return bytes
IGalleryService.SaveAsync(bytes, templateId)
@@ -599,8 +602,10 @@ EventBus.Publish(new ArtworkSavedSignal(dto))
Notes:
- HUD never appears in capture because `ArtCamera` only renders the `Artwork` layer.
- Paper background can either be already present in the scene (cheap) or composited at capture time (lets the same drawing be saved with different papers).
- HUD never appears in capture because the HUD is on `UICamera` / Canvas — it is physically in a different render path. The RT only ever sees `ArtCamera`'s output.
- Paper background is a sprite parented under `IPaperRig.PaperRoot` and is rendered into the RT every frame — already baked in.
- Saved PNGs are byte-comparable across devices because the RT dimensions and ArtCamera matrix never depend on screen size.
- `CaptureAsync` is safe to call repeatedly — no camera state is ever mutated.
---
@@ -726,6 +731,7 @@ Every folder under `Code/` is its own `.asmdef`. References follow the layer rul
| `Darkmatter.Services.Capture` | `Services/Capture/` | `Darkmatter.Core` |
| `Darkmatter.Features.MainMenu` | `Features/MainMenu/` | `Darkmatter.Core`, Libs |
| `Darkmatter.Features.DrawingCatalog` | `Features/DrawingCatalog/` | `Darkmatter.Core`, Libs |
| `Darkmatter.Features.Paper` | `Features/Paper/` | `Darkmatter.Core`, `Lib.Installers` |
| `Darkmatter.Features.ShapeBuilder` | `Features/ShapeBuilder/` | `Darkmatter.Core`, Libs |
| `Darkmatter.Features.Coloring` | `Features/Coloring/` | `Darkmatter.Core`, `Lib.CommandStack` |
| `Darkmatter.Features.History` | `Features/History/` | `Darkmatter.Core`, `Lib.CommandStack` |
@@ -1101,11 +1107,13 @@ Toddler-mode error UI:
| Class | Layer | Asmdef |
|---|---|---|
| `IDrawingTemplate`, `ShapePieceDTO`, `ColorRegionDTO` | Core | `Darkmatter.Core` |
| `IPaperRig`, `IArtInputBridge` | Core | `Darkmatter.Core` |
| `ICommand`, `IUndoStack` | Core | `Darkmatter.Core` |
| `BoundedUndoStack` | Libs | `Darkmatter.Lib.CommandStack` |
| `AddressableAssetProviderService` | Services | `Darkmatter.Services.Assets` |
| `FileGalleryService` | Services | `Darkmatter.Services.Gallery` |
| `RenderTextureCaptureService` | Services | `Darkmatter.Services.Capture` |
| `PaperRig`, `ArtInputBridge`, `PaperRigModule` | Features | `Darkmatter.Features.Paper` |
| `ColoringController`, `PaintRegionCommand` | Features | `Darkmatter.Features.Coloring` |
| `ShapeBuilderController`, `ShapePieceView` | Features | `Darkmatter.Features.ShapeBuilder` |
| `HistoryController` | Features | `Darkmatter.Features.History` |
@@ -1185,17 +1193,34 @@ public interface IGalleryService {
```
#### `ICaptureService` *(Core/Capture)*
Snapshots the artwork camera to a PNG blob.
Snapshots the paper RT to a PNG blob. No arguments — dimensions and content come from `IPaperRig.Surface`.
```csharp
public interface ICaptureService {
UniTask<byte[]> CaptureAsync(
Camera artCamera,
Sprite paperBackground,
int width = 2048,
int height = 2048);
UniTask<byte[]> CaptureAsync();
}
```
#### `IPaperRig` *(Core/Paper)*
Shared art rig. The single source of truth for everything that lives in the drawing world.
```csharp
public interface IPaperRig {
Camera ArtCamera { get; } // offscreen, targetTexture = Surface
RenderTexture Surface { get; } // 2048×2048 ARGB32 — the paper itself
Transform PaperRoot { get; } // parent of regions/pieces/paper bg
Vector2 DesignSize { get; } // world units, e.g. (20, 20)
Rect DesignRect { get; } // centered on origin
}
```
#### `IArtInputBridge` *(Core/Paper)*
Converts screen-space pointer coords to art-world coords inside the RT.
```csharp
public interface IArtInputBridge {
bool TryScreenToArtWorld(Vector2 screenPos, out Vector2 artWorldPos);
}
```
Returns `false` when the pointer is outside the displayed RawImage rect (toddler tapped the HUD or backdrop). Every art-world raycast goes through this.
#### `IProgressionService` *(Core/Progression)*
Tracks which templates the child has completed and what they last opened.
```csharp
@@ -1412,6 +1437,77 @@ public sealed class ShapePieceFactory {
---
### 32.5b Feature — `Paper`
The shared art rig — RT, offscreen camera, screen↔world bridge. Every other feature in the ColorBook scene resolves `IPaperRig` and `IArtInputBridge` from DI and never touches `Screen.*` or `Camera.*` directly.
#### `PaperRig : MonoBehaviour, IPaperRig` *(Rig)*
Scene-bound component placed on a GameObject in `ColorBook.unity`. Owns the RT lifecycle.
```csharp
// inspector fields:
// Camera _artCamera (Orthographic, aspect=1, fixed ortho size)
// Transform _paperRoot (parent of regions/pieces)
// Vector2 _designSize = (20, 20) (world units; matches 2048×2048 at PPU=100)
// int _surfaceSize = 2048 (RT side length, square)
public sealed class PaperRig : MonoBehaviour, IPaperRig {
public Camera ArtCamera => _artCamera;
public RenderTexture Surface => _surface;
public Transform PaperRoot => _paperRoot;
public Vector2 DesignSize => _designSize;
public Rect DesignRect => new(-_designSize / 2f, _designSize);
}
```
- **Awake:** allocate `_surface = new RenderTexture(_surfaceSize, _surfaceSize, 0, ARGB32) { name = "PaperSurface" };` then `_surface.Create()` and `_artCamera.targetTexture = _surface; _artCamera.aspect = 1f; _artCamera.orthographicSize = _designSize.y / 2f;`.
- **OnDestroy:** `_surface.Release(); Object.Destroy(_surface);`.
- **No update logic** — the camera renders every frame automatically because `targetTexture` is set.
- **Important:** `_artCamera`'s `orthographicSize` and `aspect` are set once and never touched again. The RT contents are deterministic.
#### `ArtInputBridge : MonoBehaviour, IArtInputBridge` *(Input)*
Lives on the same UI Canvas as the paper `RawImage`.
```csharp
// inspector fields:
// RawImage _paperImage (the on-screen paper)
// RectTransform _paperRect (== _paperImage.rectTransform)
// Camera _uiCamera (Canvas event camera)
// IPaperRig _rig (injected via VContainer + IInjectable, or resolved in Start)
public bool TryScreenToArtWorld(Vector2 screenPos, out Vector2 artWorldPos) {
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(
_paperRect, screenPos, _uiCamera, out var local)) {
artWorldPos = default; return false;
}
var rect = _paperRect.rect;
var uv = new Vector2(
(local.x - rect.xMin) / rect.width,
(local.y - rect.yMin) / rect.height);
if (uv.x < 0 || uv.x > 1 || uv.y < 0 || uv.y > 1) {
artWorldPos = default; return false;
}
artWorldPos = _rig.ArtCamera.ViewportToWorldPoint(uv);
return true;
}
```
- Returns `false` when the toddler tapped outside the RawImage (HUD button area, backdrop, off-screen).
- Used by every feature that does world-space picking — `Coloring`, `ShapeBuilder`, and any future feature like stickers.
#### `PaperRigModule : MonoBehaviour, IServiceModule` *(Installers)*
Scene-scoped installer. Dragged onto `ColorBookLifetimeScope._installers[]`.
```csharp
// inspector fields:
// PaperRig _rig
// ArtInputBridge _bridge
public void Register(IContainerBuilder builder) {
builder.RegisterInstance<IPaperRig>(_rig);
builder.RegisterInstance<IArtInputBridge>(_bridge);
}
```
- Registers as `Instance` because both are MonoBehaviours already in the scene.
- Lifetime is implicitly tied to the scene (Unity destroys them on unload).
---
### 32.6 Feature — `Coloring`
#### `ColoringStateRepository` *(Repository)*
@@ -1453,7 +1549,15 @@ public sealed class ColorRegionView : MonoBehaviour {
```
#### `ColoringInputBinder` *(Systems)* — `IStartable, IDisposable`
Subscribes to `IInputReader.PointerDown`, raycasts on the `Artwork` layer mask, calls `ColoringController.PaintRegion(view)` on hit.
Subscribes to `IInputReader.PointerDown`. On each tap:
1. `_bridge.TryScreenToArtWorld(screenPos, out var artPos)` — bail if outside the paper.
2. `Physics2D.OverlapPoint(artPos, _artworkMask)` against the `Artwork` layer.
3. If hit, `ColoringController.PaintRegion(hit.GetComponent<ColorRegionView>())`.
```csharp
// fields: IInputReader _input, IArtInputBridge _bridge, IColoringController _coloring, LayerMask _artworkMask
```
Note: `_bridge` is the same instance the entire scene uses — no per-feature coordinate math.
#### `PaintRegionCommand` *(Commands)*
Source in section 23. Holds `view`, `fromColor`, `toColor`, `bus`. Symmetrical execute/undo.
@@ -1503,14 +1607,16 @@ Wires controller `StateChanged` ↔ view enable/disable; view click events → c
#### `CaptureController` *(Systems)*
The orchestrator behind the "Capture" button. Stateless other than guarding against concurrent captures.
```csharp
// fields: ICaptureService _capture, IGalleryService _gallery, IEventBus _bus, ColorBookSceneRefs _refs
// fields: ICaptureService _capture, IGalleryService _gallery, IEventBus _bus
public sealed class CaptureController {
public bool IsCapturing { get; }
public UniTask<SavedArtworkDTO> CaptureCurrentAsync(string templateId, Sprite paperBg);
public UniTask<SavedArtworkDTO> CaptureCurrentAsync(string templateId);
}
// pub: ArtworkCapturedSignal (mid-flow), ArtworkSavedSignal (post-save)
```
- **Flow:** `_capture.CaptureAsync()``_gallery.SaveAsync(bytes, templateId)` → publish signals.
- **Concurrency:** sets `IsCapturing = true` on entry; UI binds button enabled to `!IsCapturing` to prevent double-tap.
- **No camera or sprite args** — capture reads `IPaperRig.Surface` directly inside the service.
#### `CaptureButtonPresenter` *(UI)*
Wires button click → `CaptureController.CaptureCurrentAsync`. Disables button while in progress. Shows toast on `ArtworkSavedSignal`.
@@ -1641,8 +1747,11 @@ Implemented as `ScriptableObject` per feature so scopes can drag them in the ins
| `ColoringController` | Feature | Region spawn + paint cmd | undo, state, factory, bus |
| `ColorRegionView` | Feature | Region sprite MB | — |
| `PaintRegionCommand` | Feature | Undoable paint | view, bus |
| `PaperRig` | Feature | RT + ArtCamera owner | — |
| `ArtInputBridge` | Feature | Screen→art-world picking | rig, raw image, ui cam |
| `PaperRigModule` | Feature | DI registration | rig, bridge |
| `HistoryController` | Feature | Undo/redo facade | undo stack, bus |
| `CaptureController` | Feature | Capture+save orchestration | capture svc, gallery, bus, refs |
| `CaptureController` | Feature | Capture+save orchestration | capture svc, gallery, bus |
| `ColorBookFlowController` | Feature | Scene FSM | bus, catalog, builder, coloring, capture, progression |
| `GalleryPresenter` | Feature | Art book listing | gallery, share, view, bus |
| `BoundedUndoStack` | Lib | Capped undo store | — |
@@ -1650,7 +1759,7 @@ Implemented as `ScriptableObject` per feature so scopes can drag them in the ins
| `Fsm<TState>` | Lib | Generic FSM | — |
| `AddressableAssetProviderService` | Service | Addressables wrapper | — |
| `FileGalleryService` | Service | Gallery file IO | paths, thumb gen, bus |
| `RenderTextureCaptureService` | Service | PNG render | — |
| `RenderTextureCaptureService` | Service | PNG render from rig.Surface | paper rig |
| `JsonPersistenceService` | Service | Settings/progression IO | — |
| `SceneService` | Service | Async scene loads | — |
| `AudioService` | Service | SFX playback | assets |