dev/glyph-bank-alignment #3
@ -1,4 +1,6 @@
|
||||
{"type":"meta","next_id":{"DSC":24,"AGD":26,"DEC":24,"PLN":47,"LSN":38,"CLSN":1}}
|
||||
{"type":"meta","next_id":{"DSC":27,"AGD":29,"DEC":26,"PLN":49,"LSN":40,"CLSN":1}}
|
||||
{"type":"discussion","id":"DSC-0026","status":"done","ticket":"glyph-bank-naming-alignment-with-runtime","title":"Glyph Bank Naming Alignment with Runtime DEC-0006","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["packer","studio","naming","asset-contract","runtime-alignment","glyph-bank"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0040","file":"discussion/lessons/DSC-0026-glyph-bank-naming-alignment-with-runtime/LSN-0040-glyph-bank-artifact-naming-alignment.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]}
|
||||
{"type":"discussion","id":"DSC-0025","status":"done","ticket":"packer-pipeline-metadata-ownership","title":"Pipeline Metadata Ownership and Runtime Contract","created_at":"2026-04-09","updated_at":"2026-04-10","tags":["packer","metadata","runtime-contract","tooling","studio"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0039","file":"discussion/lessons/DSC-0025-packer-pipeline-metadata-ownership/LSN-0039-runtime-header-boundary-and-tooling-owned-pipeline-metadata.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]}
|
||||
{"type":"discussion","id":"DSC-0024","status":"done","ticket":"jacoco-reports-consolidation","title":"JaCoCo Reports Consolidation in Gradle","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["infra","gradle","jacoco","coverage","jenkins"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0038","file":"discussion/lessons/DSC-0024-jacoco-reports-consolidation/LSN-0038-jacoco-reports-consolidation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]}
|
||||
{"type":"discussion","id":"DSC-0001","status":"done","ticket":"studio-docs-import","title":"Import docs/studio into discussion-framework artifacts","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["studio","migration","discussion-framework","docs-import"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0001","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0001-assets-workspace-execution-wave-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0002","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0002-bank-composition-editor-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0003","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0003-mental-model-asset-mutations-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0004","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0004-mental-model-assets-workspace-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0005","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0005-mental-model-studio-events-and-components-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0006","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0006-mental-model-studio-shell-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0007","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0007-pack-wizard-shell-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0008","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0008-project-scoped-state-and-activity-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0016","file":"discussion/lessons/DSC-0001-studio-docs-import/LSN-0016-studio-docs-import-pattern.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"}]}
|
||||
{"type":"discussion","id":"DSC-0002","status":"open","ticket":"palette-management-in-studio","title":"Palette Management in Studio","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["studio","legacy-import","palette-management","tile-bank","packer-boundary"],"agendas":[{"id":"AGD-0002","file":"AGD-0002-palette-management-in-studio.md","status":"open","created_at":"2026-03-26","updated_at":"2026-03-26"}],"decisions":[],"plans":[],"lessons":[]}
|
||||
|
||||
@ -20,8 +20,8 @@ This lesson preserves the first-wave producer contract for tile-bank assets.
|
||||
|
||||
### Tile bank packing emits one canonical payload per asset with explicit normalization and early structural validation
|
||||
|
||||
**What:** The first-wave contract emits one canonical `TILES/indexed_v1` payload per asset, normalizes artifacts by explicit `artifacts[*].index`, packs one fixed `256 x 256` row-major sheet with packed `u4` pixels and `RGB565` palette blocks, treats palette identity as semantic rather than positional, and surfaces structural blockers early in walker/materialization.
|
||||
**Why:** The repository needed one explicit producer contract for tile banks rather than a mix of implicit artifact ordering, late byte-emission surprises, and unstable palette identity.
|
||||
**What:** The first-wave contract emits one canonical `GLYPH/indexed_v1` payload per asset, normalizes artifacts by explicit `artifacts[*].index`, packs one fixed `256 x 256` row-major sheet with packed `u4` pixels and `RGB565` palette blocks, treats palette identity as semantic rather than positional, and surfaces structural blockers early in walker/materialization.
|
||||
**Why:** The repository needed one explicit producer contract for glyph banks rather than a mix of implicit artifact ordering, late byte-emission surprises, and unstable palette identity.
|
||||
**Trade-offs:** This contract is more explicit and restrictive than ad hoc per-artifact packing, but it yields stable reviewability, predictable runtime metadata, and earlier validation.
|
||||
|
||||
## Patterns and Algorithms
|
||||
|
||||
@ -0,0 +1,71 @@
|
||||
---
|
||||
id: LSN-0039
|
||||
ticket: packer-pipeline-metadata-ownership
|
||||
title: Runtime Header Boundary and Tooling-Owned Pipeline Metadata
|
||||
created: 2026-04-10
|
||||
tags: [packer, metadata, runtime-contract, tooling, codec, assets-pa]
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
The packer had drift between implementation and spec around `output.pipeline` ownership. The runtime-facing artifact `assets.pa` was documented as if pipeline metadata should materialize under `asset_table[].metadata.pipeline`, while the implementation already treated at least part of that surface as tooling-oriented and excluded palette payloads from the runtime header.
|
||||
|
||||
This created the wrong architectural pressure: Studio and third-party tools could start depending on `assets.pa` for authoring/build provenance, and runtime-facing specs could silently absorb build-time structures that do not belong in the execution contract.
|
||||
|
||||
The discussion resolved that split explicitly and implemented it end to end in specs, packer materialization, and tests.
|
||||
|
||||
## Key Decisions
|
||||
|
||||
### Tooling Metadata Must Not Leak Into the Runtime Header
|
||||
|
||||
**What:**
|
||||
`output.pipeline` remains tooling-only metadata in `asset.json` and related sidecar outputs. The runtime-facing `assets.pa` header must not mirror `output.pipeline` by default. Runtime-owned metadata remains under explicit, normalized fields in `asset_table[].metadata`.
|
||||
|
||||
**Why:**
|
||||
`assets.pa` is the authoritative runtime artifact. If it starts carrying build provenance, editor-oriented structures, or reverse-sync payloads merely because they exist in the authoring manifest, the runtime contract becomes larger, noisier, and harder to evolve safely.
|
||||
|
||||
**Trade-offs:**
|
||||
Tools that want provenance cannot rely on one binary artifact alone. They must read `asset.json` and/or `build/asset_table_metadata.json`. That is a deliberate trade-off in favor of a cleaner runtime boundary.
|
||||
|
||||
### Promotion Must Be Explicit, Not a Bulk Mirror
|
||||
|
||||
**What:**
|
||||
If pipeline-derived information is needed at runtime, it must be promoted into an explicit normative field under runtime-owned metadata. The packer must not reintroduce a generic mirrored `pipeline` object under another name.
|
||||
|
||||
**Why:**
|
||||
Bulk mirroring hides product decisions inside implementation convenience. Explicit promotion forces each field to justify its presence in the runtime contract.
|
||||
|
||||
**Trade-offs:**
|
||||
This adds small editorial and implementation work whenever a new runtime-facing derived field is introduced, but it keeps the contract intentional and testable.
|
||||
|
||||
### Codec Serialization Must Stay in `SCREAMING_SNAKE_CASE`
|
||||
|
||||
**What:**
|
||||
Serialized asset entry `codec` values are locked to `SCREAMING_SNAKE_CASE`. The currently known `NONE` value remains serialized as `NONE`.
|
||||
|
||||
**Why:**
|
||||
The implementation already had a stable manifest/runtime representation through `OutputCodecCatalog`. Recording that casing in spec prevents future drift when more codecs are added.
|
||||
|
||||
**Trade-offs:**
|
||||
Future codec additions must preserve the established serialized style unless a new decision explicitly revises the contract.
|
||||
|
||||
## Patterns and Algorithms
|
||||
|
||||
- Use separate surfaces for separate consumers: `assets.pa` for runtime contract, `asset_table_metadata.json` for tooling-oriented provenance, and `asset.json` for authoring intent.
|
||||
- Build runtime metadata from explicit normalized fields first, then derive tooling sidecars from a superset when needed.
|
||||
- Keep the runtime header free of raw authoring/build objects even if the sidecar keeps them intact for Studio or inspection tools.
|
||||
- Lock serialized enum casing in spec as soon as the first value ships, even if the current catalog is minimal.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- Do not treat “already present in the manifest” as sufficient reason to serialize a field into the runtime header.
|
||||
- Do not let sidecar convenience become implicit runtime compatibility.
|
||||
- Do not partially filter a tooling object and leave an empty placeholder in runtime metadata unless that placeholder has explicit runtime meaning.
|
||||
- Do not leave enum casing as an implementation accident. If the manifest value matters, the spec must say so.
|
||||
|
||||
## Takeaways
|
||||
|
||||
- The runtime header is a contract surface, not a metadata dump.
|
||||
- Tooling provenance belongs in `asset.json` and `build/asset_table_metadata.json`, not in `assets.pa`.
|
||||
- Runtime-facing derived metadata must be promoted explicitly field by field.
|
||||
- Stable serialized casing rules should be fixed in spec before the value set grows.
|
||||
@ -0,0 +1,41 @@
|
||||
# Glyph Bank Artifact Naming Alignment
|
||||
|
||||
The graphical bank artifact, which stores indexed graphical data (glyphs) for runtime consumption, has been aligned with the unified project vocabulary across `packer`, `studio`, and `runtime`.
|
||||
|
||||
### Original Problem
|
||||
|
||||
Previously, the project used inconsistent terminology for the same concrete artifact:
|
||||
- `tile_bank` in some packer configurations.
|
||||
- `TILES/indexed_v1` as an output format.
|
||||
- `TILES` as a runtime-facing `bank_type`.
|
||||
- `TileBank` or `tile bank` in Studio UI and internal code.
|
||||
|
||||
This created a conceptual overlap with the "tile" domain, which properly refers to geometric sizes, map layers, and grid-based rendering logic. Keeping the artifact named "tile bank" made it difficult to distinguish between the storage container (the bank) and the geometric entities (the tiles) stored within or referenced by it.
|
||||
|
||||
### Consolidated Decision
|
||||
|
||||
Following `DEC-0025` (this repository) and `DEC-0006` (runtime repository), the concrete graphical bank artifact is now exclusively referred to as **Glyph Bank**.
|
||||
|
||||
1. **Artifact Name**: `glyph_bank` (serialized asset-family).
|
||||
2. **Output Format**: `GLYPH/indexed_v1`.
|
||||
3. **Runtime ID**: `GLYPH` (bank_type).
|
||||
4. **Vocabulary Split**:
|
||||
- Use **Glyph** when referring to the graphical data bank, its capacity, or its storage format.
|
||||
- Use **Tile** only for geometric concepts (tile size, tile maps, tile layers, coordinate grids).
|
||||
|
||||
### Implementation Results
|
||||
|
||||
- **Packer**: All `AssetFamilyCatalog`, `OutputFormatCatalog`, and walker implementations now use the `Glyph*` vocabulary. Parser validation now expects `type: glyph_bank`.
|
||||
- **Studio**: UI labels, composition coordinators, and internal services have been renamed to `Glyph Bank` to match the packer and runtime contracts.
|
||||
- **Contract**: No compatibility layer was introduced. All fixtures and test assets were migrated in a single wave to ensure a clean, unified contract.
|
||||
|
||||
### Common Pitfalls and Anti-patterns
|
||||
|
||||
- **Incorrect Rename**: Do not rename `tileSize` to `glyphSize`. A tile is a geometric unit; a glyph is the graphical content within that unit or the bank that stores it.
|
||||
- **Legacy Fixtures**: Since there is no compatibility layer, old `asset.json` files with `type: tile_bank` will fail to parse. They must be updated to `type: glyph_bank`.
|
||||
|
||||
### References
|
||||
|
||||
- `DEC-0025`: Local decision for Glyph Bank alignment.
|
||||
- `PLN-0048`: Implementation plan for the naming migration.
|
||||
- `docs/specs/packer/`: Updated specifications for asset declarations.
|
||||
@ -16,7 +16,7 @@ tags:
|
||||
|
||||
## Pain
|
||||
|
||||
Studio already has a retained agenda for palette management in the `tile bank` form session, but that agenda exists only under `docs/studio/agendas/` and therefore sits outside the discussion-framework lifecycle.
|
||||
Studio already has a retained agenda for palette management in the `glyph bank` form session, but that agenda exists only under `docs/studio/agendas/` and therefore sits outside the discussion-framework lifecycle.
|
||||
|
||||
Without importing it into `discussion/`, the canonical discussion surface would lose the still-open Studio question about:
|
||||
|
||||
@ -32,11 +32,11 @@ Legacy source: `docs/studio/agendas/Palette Management in Studio Agenda.md`
|
||||
Domain owner: `docs/studio`
|
||||
Cross-domain impact: `docs/packer`
|
||||
|
||||
The retained agenda is intentionally scoped to `tile bank`.
|
||||
The retained agenda is intentionally scoped to `glyph bank`.
|
||||
|
||||
The imported context keeps the same baseline:
|
||||
|
||||
- every `*.png` input of a tile bank is one tile;
|
||||
- every `*.png` input of a glyph bank is one tile;
|
||||
- each tile yields one extracted palette during cache construction;
|
||||
- developers may also work with favorite palettes and asset palettes;
|
||||
- each bank may integrate at most `64` palettes;
|
||||
@ -57,8 +57,8 @@ The current UI shape already converged on:
|
||||
- [ ] Should extracted tile palettes always enter the candidate set automatically, or can the developer suppress some of them before curation?
|
||||
- [ ] Should clicking a palette on the `selected` side be the only interaction needed to change the previewed palette in the first wave?
|
||||
- [ ] Which palette operations are read-only in the first wave, and which require staged preview/apply treatment?
|
||||
- [ ] Which parts of this interaction model should remain explicitly `tile bank`-specific, and which parts are reusable Studio form-session primitives?
|
||||
- [ ] Does the first release need only an asset-scoped `tile bank` section, or also a project-level palette browser for discovery and favorites management?
|
||||
- [ ] Which parts of this interaction model should remain explicitly `glyph bank`-specific, and which parts are reusable Studio form-session primitives?
|
||||
- [ ] Does the first release need only an asset-scoped `glyph bank` section, or also a project-level palette browser for discovery and favorites management?
|
||||
|
||||
## Options
|
||||
|
||||
@ -70,7 +70,7 @@ The current UI shape already converged on:
|
||||
|
||||
### Option B - Asset-Scoped Curated Selection
|
||||
- **Approach:** Treat bank palettes as an asset-scoped curated selection built from tile-derived palettes, favorites, and asset palettes, with Studio owning the curation UX and packer owning persistence.
|
||||
- **Pro:** Matches the concrete `tile bank` workflow and keeps authority boundaries clear.
|
||||
- **Pro:** Matches the concrete `glyph bank` workflow and keeps authority boundaries clear.
|
||||
- **Con:** Requires explicit UX for capacity, identity, deduplication, and preview semantics.
|
||||
- **Maintainability:** Strong. It makes responsibilities explicit and keeps family-specific rules close to the workflow that needs them.
|
||||
|
||||
@ -82,7 +82,7 @@ The current UI shape already converged on:
|
||||
|
||||
## Discussion
|
||||
|
||||
The imported agenda keeps the original recommendation intact: palette management should be first-class for `tile bank`, but Studio must not become the persistence authority.
|
||||
The imported agenda keeps the original recommendation intact: palette management should be first-class for `glyph bank`, but Studio must not become the persistence authority.
|
||||
|
||||
That means the key open problem is not whether palettes exist. It is how Studio explains and curates them safely:
|
||||
|
||||
@ -90,7 +90,7 @@ That means the key open problem is not whether palettes exist. It is how Studio
|
||||
- clear source and identity display;
|
||||
- explicit `1..64` capacity feedback;
|
||||
- a preview model that stays tied to the selected bank palettes;
|
||||
- a boundary between `tile bank`-specific UX and reusable Studio primitives.
|
||||
- a boundary between `glyph bank`-specific UX and reusable Studio primitives.
|
||||
|
||||
The retained source already converged on a pragmatic first wave:
|
||||
|
||||
@ -106,7 +106,7 @@ Recommended direction: adopt **Option B**.
|
||||
|
||||
The imported agenda preserves the retained Studio direction:
|
||||
|
||||
1. palette management belongs to the `tile bank` asset workflow;
|
||||
1. palette management belongs to the `glyph bank` asset workflow;
|
||||
2. Studio aggregates candidate palettes from tile-derived extraction, favorites, and asset palettes;
|
||||
3. the first wave uses a custom-rendered transfer list with an explicit `1..64` selected range;
|
||||
4. the right-side preview panel stays tied to the current selected palette set;
|
||||
|
||||
@ -53,7 +53,7 @@ The packer now exposes:
|
||||
### Option A - Keep fixed 64-palette serialization
|
||||
- **Approach:** Preserve the current fixed v1 payload with zero-filled unused palette slots.
|
||||
- **Pro:** No runtime change and current specs/tests remain stable.
|
||||
- **Con:** Every tile bank keeps paying the full palette cost.
|
||||
- **Con:** Every glyph bank keeps paying the full palette cost.
|
||||
- **Maintainability:** Medium.
|
||||
|
||||
### Option B - Move to variable palette serialization now
|
||||
|
||||
@ -58,7 +58,7 @@ Rules:
|
||||
|
||||
Examples:
|
||||
|
||||
- `tile_bank`
|
||||
- `glyph_bank`
|
||||
- `sound_bank`
|
||||
|
||||
## Inputs
|
||||
@ -92,8 +92,9 @@ Rules:
|
||||
|
||||
- `output.format` defines the semantic/runtime format contract;
|
||||
- `output.codec` defines storage/extraction behavior;
|
||||
- `output.codec` serialized values must use `SCREAMING_SNAKE_CASE`;
|
||||
- `output.metadata` carries runtime-relevant format-specific detail;
|
||||
- `output.pipeline` carries pipeline-injected metadata kept separate at authoring time;
|
||||
- `output.pipeline` carries tooling/build metadata kept separate at authoring time;
|
||||
- codec must remain explicit and must not be hidden inside format naming.
|
||||
|
||||
### Explicit Index Collections
|
||||
@ -139,6 +140,8 @@ Rules:
|
||||
- declaration-time metadata may come from multiple sources under the asset contract (for example, format metadata, codec-related metadata, and build/pipeline-derived declarations);
|
||||
- `output.pipeline` may carry nested pipeline-derived metadata objects;
|
||||
- this segmentation exists for authoring clarity and does not define multiple runtime sinks;
|
||||
- `output.pipeline` is tooling-only and must not become part of the runtime-facing asset header by default;
|
||||
- pipeline-derived values required at runtime must be promoted explicitly into normative runtime-owned metadata fields;
|
||||
- runtime consumers must read effective metadata from the runtime asset entry metadata sink (`AssetEntry.metadata`);
|
||||
- convergence/normalization behavior is normative in the build artifact specification.
|
||||
|
||||
@ -200,7 +203,7 @@ The `asset.json` schema is versioned independently from:
|
||||
- runtime-facing artifact schema
|
||||
- format contract versions
|
||||
|
||||
Format-specific contracts evolve through values such as `TILES/indexed_v1`.
|
||||
Format-specific contracts evolve through values such as `GLYPH/indexed_v1`.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
|
||||
@ -95,9 +95,15 @@ Baseline normalized metadata segmentation:
|
||||
|
||||
- `output.metadata` materializes at the metadata root;
|
||||
- `output.codec_configuration` materializes under `metadata.codec`;
|
||||
- `output.pipeline` materializes under `metadata.pipeline`;
|
||||
- `output.pipeline` remains tooling-only and must not materialize under `asset_table[].metadata` by default;
|
||||
- pipeline-derived runtime-required values must be promoted explicitly into normative runtime-owned metadata fields;
|
||||
- format-specific runtime-required fields may remain directly readable at the metadata root when the runtime consumer requires them there.
|
||||
|
||||
Asset entry codec serialization:
|
||||
|
||||
- `asset_table[].codec` MUST use `SCREAMING_SNAKE_CASE`;
|
||||
- the current known `NONE` value serializes as `NONE`.
|
||||
|
||||
### Preload
|
||||
|
||||
Preload is emitted deterministically from per-asset declaration.
|
||||
@ -120,7 +126,7 @@ Rules:
|
||||
|
||||
- `build/asset_table.json` mirrors `header.asset_table` 1:1;
|
||||
- `build/preload.json` mirrors `header.preload` 1:1;
|
||||
- `build/asset_table_metadata.json` is tooling-only;
|
||||
- `build/asset_table_metadata.json` is tooling-only and may retain pipeline/build provenance that is intentionally excluded from the runtime header;
|
||||
- richer tooling data must not be added to the 1:1 mirror files.
|
||||
|
||||
## Alignment and Offsets
|
||||
@ -139,15 +145,15 @@ Offset ambiguity guardrail:
|
||||
- internal pipeline indexing data (for example per-sample ranges for audio banks) must live under `asset_table[].metadata`;
|
||||
- internal indexing fields must not be interpreted as payload slicing fields.
|
||||
|
||||
## Format-Specific Baseline: `TILES/indexed_v1`
|
||||
## Format-Specific Baseline: `GLYPH/indexed_v1`
|
||||
|
||||
The first-wave producer contract for `TILES/indexed_v1` is fixed and runtime-aligned.
|
||||
The first-wave producer contract for `GLYPH/indexed_v1` is fixed and runtime-aligned.
|
||||
|
||||
### Tile Selection and Identity
|
||||
|
||||
Rules:
|
||||
|
||||
- only selected artifacts participate in the emitted tile bank;
|
||||
- only selected artifacts participate in the emitted glyph bank;
|
||||
- `1 artifact = 1 tile` in v1;
|
||||
- artifacts are normalized by ascending `artifacts[*].index`;
|
||||
- emitted `tile_id` equals the normalized artifact index;
|
||||
@ -170,7 +176,7 @@ Rules:
|
||||
|
||||
Rules:
|
||||
|
||||
- the serialized payload for `TILES/indexed_v1` is:
|
||||
- the serialized payload for `GLYPH/indexed_v1` is:
|
||||
1. one packed `u4` pixel plane for the full emitted sheet;
|
||||
2. one palette block of `64 * 16 * 2 = 2048` bytes;
|
||||
- packed pixel bytes must be emitted by the packer, not inferred later by the runtime;
|
||||
@ -183,7 +189,7 @@ Rules:
|
||||
Rules:
|
||||
|
||||
- emitted tile-bank entries use:
|
||||
- `bank_type = TILES`
|
||||
- `bank_type = GLYPH`
|
||||
- `codec = NONE`
|
||||
- tile-bank v1 metadata must expose at least:
|
||||
- `tile_size`
|
||||
@ -202,7 +208,7 @@ Rules:
|
||||
- bank palettes are declared under `output.pipeline.palettes`;
|
||||
- each palette declaration uses the shape `{ "index": <int>, "palette": { ... } }`;
|
||||
- palette ordering is ascending numeric `index`, never raw array position;
|
||||
- palette ids in the emitted tile bank are the normalized declared palette indices;
|
||||
- palette ids in the emitted glyph bank are the normalized declared palette indices;
|
||||
- any tile in the bank may be rendered with any palette in the bank at runtime;
|
||||
- palette selection is a runtime draw concern, not a tile-payload embedding concern.
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ package p.packer.messages.assets;
|
||||
import java.util.Locale;
|
||||
|
||||
public enum AssetFamilyCatalog {
|
||||
TILE_BANK("tile_bank"),
|
||||
GLYPH_BANK("glyph_bank"),
|
||||
SOUND_BANK("sound_bank"),
|
||||
UNKNOWN("unknown");
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public enum OutputFormatCatalog {
|
||||
TILES_INDEXED_V1(AssetFamilyCatalog.TILE_BANK, "TILES/indexed_v1", "TILES/indexed_v1"),
|
||||
GLYPH_INDEXED_V1(AssetFamilyCatalog.GLYPH_BANK, "GLYPH/indexed_v1", "GLYPH/indexed_v1"),
|
||||
SOUND_V1(AssetFamilyCatalog.SOUND_BANK, "SOUND/v1", "SOUND/v1"),
|
||||
UNKNOWN(AssetFamilyCatalog.UNKNOWN, "unknown", "Unknown");
|
||||
|
||||
|
||||
@ -0,0 +1,4 @@
|
||||
package p.packer.models;
|
||||
|
||||
public record PackerGlyphBankRequirements(int tileSize) {
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
package p.packer.models;
|
||||
|
||||
public record PackerTileBankRequirements(int tileSize) {
|
||||
}
|
||||
@ -17,21 +17,21 @@ import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public class PackerAssetWalker {
|
||||
private static final int TILE_BANK_EMITTED_SHEET_SIZE = 256;
|
||||
private static final int TILE_BANK_MAX_PALETTES = 64;
|
||||
private static final Set<OutputFormatCatalog> TILE_BANK_SUPPORTED_FORMATS = Set.of(
|
||||
OutputFormatCatalog.TILES_INDEXED_V1
|
||||
private static final int GLYPH_BANK_EMITTED_SHEET_SIZE = 256;
|
||||
private static final int GLYPH_BANK_MAX_PALETTES = 64;
|
||||
private static final Set<OutputFormatCatalog> GLYPH_BANK_SUPPORTED_FORMATS = Set.of(
|
||||
OutputFormatCatalog.GLYPH_INDEXED_V1
|
||||
);
|
||||
|
||||
private static final Set<OutputFormatCatalog> SOUND_BANK_SUPPORTED_FORMATS = Set.of(
|
||||
OutputFormatCatalog.SOUND_V1
|
||||
);
|
||||
|
||||
private final PackerTileBankWalker tileBankWalker;
|
||||
private final PackerGlyphBankWalker glyphBankWalker;
|
||||
private final PackerSoundBankWalker soundBankWalker;
|
||||
|
||||
public PackerAssetWalker(final ObjectMapper mapper) {
|
||||
this.tileBankWalker = new PackerTileBankWalker(mapper);
|
||||
this.glyphBankWalker = new PackerGlyphBankWalker(mapper);
|
||||
this.soundBankWalker = new PackerSoundBankWalker(mapper);
|
||||
}
|
||||
|
||||
@ -47,18 +47,18 @@ public class PackerAssetWalker {
|
||||
final Optional<PackerAssetCacheEntry> priorAssetCache) {
|
||||
final List<PackerDiagnostic> diagnostics = new ArrayList<>();
|
||||
switch (declaration.assetFamily()) {
|
||||
case TILE_BANK -> {
|
||||
case GLYPH_BANK -> {
|
||||
var metadata = declaration.outputMetadata();
|
||||
if (MapUtils.isEmpty(metadata)) {
|
||||
diagnostics.add(new PackerDiagnostic(
|
||||
PackerDiagnosticSeverity.WARNING,
|
||||
PackerDiagnosticCategory.HYGIENE,
|
||||
"Output metadata for tile bank cannot be empty, using default values",
|
||||
"Output metadata for glyph bank cannot be empty, using default values",
|
||||
assetRoot,
|
||||
false));
|
||||
metadata = Map.of("tile_size", "16x16");
|
||||
}
|
||||
final var requirementBuildResult = buildTileBankRequirements(declaration, metadata);
|
||||
final var requirementBuildResult = buildGlyphBankRequirements(declaration, metadata);
|
||||
if (requirementBuildResult.hasError()) {
|
||||
diagnostics.add(new PackerDiagnostic(
|
||||
PackerDiagnosticSeverity.ERROR,
|
||||
@ -68,11 +68,11 @@ public class PackerAssetWalker {
|
||||
true));
|
||||
return new PackerWalkResult(List.of(), diagnostics);
|
||||
}
|
||||
diagnostics.addAll(tileBankDeclarationDiagnostics(assetRoot, declaration, requirementBuildResult.requirements));
|
||||
final var walkResult = tileBankWalker.walk(assetRoot, requirementBuildResult.requirements, priorAssetCache);
|
||||
diagnostics.addAll(glyphBankDeclarationDiagnostics(assetRoot, declaration, requirementBuildResult.requirements));
|
||||
final var walkResult = glyphBankWalker.walk(assetRoot, requirementBuildResult.requirements, priorAssetCache);
|
||||
diagnostics.addAll(walkResult.diagnostics());
|
||||
diagnostics.addAll(tileBankSelectedArtifactDiagnostics(assetRoot, declaration, walkResult));
|
||||
diagnostics.addAll(tileBankFragileIndexDiagnostics(declaration, walkResult));
|
||||
diagnostics.addAll(glyphBankSelectedArtifactDiagnostics(assetRoot, declaration, walkResult));
|
||||
diagnostics.addAll(glyphBankFragileIndexDiagnostics(declaration, walkResult));
|
||||
return new PackerWalkResult(walkResult.probeResults(), diagnostics);
|
||||
}
|
||||
case SOUND_BANK -> {
|
||||
@ -81,7 +81,7 @@ public class PackerAssetWalker {
|
||||
diagnostics.add(new PackerDiagnostic(
|
||||
PackerDiagnosticSeverity.WARNING,
|
||||
PackerDiagnosticCategory.HYGIENE,
|
||||
"Output metadata for tile bank cannot be empty, using default values",
|
||||
"Output metadata for sound bank cannot be empty, using default values",
|
||||
assetRoot,
|
||||
false));
|
||||
metadata = Map.of("sample_rate", "44100", "channels", "1");
|
||||
@ -113,15 +113,15 @@ public class PackerAssetWalker {
|
||||
return PackerWalkResult.EMPTY;
|
||||
}
|
||||
|
||||
private RequirementBuildResult<PackerTileBankRequirements> buildTileBankRequirements(
|
||||
private RequirementBuildResult<PackerGlyphBankRequirements> buildGlyphBankRequirements(
|
||||
final PackerAssetDeclaration declaration,
|
||||
final Map<String, String> metadata) {
|
||||
if (!TILE_BANK_SUPPORTED_FORMATS.contains(declaration.outputFormat())) {
|
||||
return RequirementBuildResult.fail("Unsupported output format for tile bank: " + declaration.outputFormat());
|
||||
if (!GLYPH_BANK_SUPPORTED_FORMATS.contains(declaration.outputFormat())) {
|
||||
return RequirementBuildResult.fail("Unsupported output format for glyph bank: " + declaration.outputFormat());
|
||||
}
|
||||
final var tileSizeStr = metadata.get("tile_size");
|
||||
if (StringUtils.isBlank(tileSizeStr)) {
|
||||
return RequirementBuildResult.fail("Tile size metadata for tile bank cannot be empty");
|
||||
return RequirementBuildResult.fail("Tile size metadata for glyph bank cannot be empty");
|
||||
}
|
||||
final int tileSize;
|
||||
switch (tileSizeStr) {
|
||||
@ -135,16 +135,16 @@ public class PackerAssetWalker {
|
||||
tileSize = 32;
|
||||
} break;
|
||||
default: {
|
||||
return RequirementBuildResult.fail("Unsupported tile size for tile bank: " + tileSizeStr);
|
||||
return RequirementBuildResult.fail("Unsupported tile size for glyph bank: " + tileSizeStr);
|
||||
}
|
||||
}
|
||||
return RequirementBuildResult.success(new PackerTileBankRequirements(tileSize));
|
||||
return RequirementBuildResult.success(new PackerGlyphBankRequirements(tileSize));
|
||||
}
|
||||
|
||||
private List<PackerDiagnostic> tileBankDeclarationDiagnostics(
|
||||
private List<PackerDiagnostic> glyphBankDeclarationDiagnostics(
|
||||
final Path assetRoot,
|
||||
final PackerAssetDeclaration declaration,
|
||||
final PackerTileBankRequirements requirements) {
|
||||
final PackerGlyphBankRequirements requirements) {
|
||||
final List<PackerDiagnostic> diagnostics = new ArrayList<>();
|
||||
final List<PackerAssetArtifactSelection> artifacts = declaration.artifacts();
|
||||
final Set<Integer> seenIndices = new java.util.HashSet<>();
|
||||
@ -169,8 +169,8 @@ public class PackerAssetWalker {
|
||||
}
|
||||
}
|
||||
|
||||
final int tileCapacity = (TILE_BANK_EMITTED_SHEET_SIZE / requirements.tileSize())
|
||||
* (TILE_BANK_EMITTED_SHEET_SIZE / requirements.tileSize());
|
||||
final int tileCapacity = (GLYPH_BANK_EMITTED_SHEET_SIZE / requirements.tileSize())
|
||||
* (GLYPH_BANK_EMITTED_SHEET_SIZE / requirements.tileSize());
|
||||
if (artifacts.size() > tileCapacity) {
|
||||
diagnostics.add(new PackerDiagnostic(
|
||||
PackerDiagnosticSeverity.ERROR,
|
||||
@ -191,18 +191,18 @@ public class PackerAssetWalker {
|
||||
assetRoot,
|
||||
true));
|
||||
}
|
||||
if (declaredPalettes.size() > TILE_BANK_MAX_PALETTES) {
|
||||
if (declaredPalettes.size() > GLYPH_BANK_MAX_PALETTES) {
|
||||
diagnostics.add(new PackerDiagnostic(
|
||||
PackerDiagnosticSeverity.ERROR,
|
||||
PackerDiagnosticCategory.STRUCTURAL,
|
||||
"Tile bank cannot declare more than " + TILE_BANK_MAX_PALETTES + " palettes.",
|
||||
"Glyph bank cannot declare more than " + GLYPH_BANK_MAX_PALETTES + " palettes.",
|
||||
assetRoot,
|
||||
true));
|
||||
}
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
private List<PackerDiagnostic> tileBankSelectedArtifactDiagnostics(
|
||||
private List<PackerDiagnostic> glyphBankSelectedArtifactDiagnostics(
|
||||
final Path assetRoot,
|
||||
final PackerAssetDeclaration declaration,
|
||||
final PackerWalkResult walkResult) {
|
||||
@ -225,7 +225,7 @@ public class PackerAssetWalker {
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
private List<PackerDiagnostic> tileBankFragileIndexDiagnostics(
|
||||
private List<PackerDiagnostic> glyphBankFragileIndexDiagnostics(
|
||||
final PackerAssetDeclaration declaration,
|
||||
final PackerWalkResult walkResult) {
|
||||
final List<DeclaredPalette> declaredPalettes = declaredPalettes(declaration);
|
||||
|
||||
@ -7,19 +7,17 @@ import p.packer.models.*;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
public class PackerTileBankWalker extends PackerAbstractBankWalker<PackerTileBankRequirements> {
|
||||
public class PackerGlyphBankWalker extends PackerAbstractBankWalker<PackerGlyphBankRequirements> {
|
||||
private static final int MAX_COLORS_PER_PALETTE = 15;
|
||||
private static final int COLOR_KEY_RGB = 0x00FF00FF;
|
||||
private static final Set<String> SUPPORTED_MIME_TYPES = Set.of(
|
||||
"image/png"
|
||||
);
|
||||
|
||||
public PackerTileBankWalker(ObjectMapper mapper) {
|
||||
public PackerGlyphBankWalker(ObjectMapper mapper) {
|
||||
super(mapper);
|
||||
}
|
||||
|
||||
@ -42,7 +40,7 @@ public class PackerTileBankWalker extends PackerAbstractBankWalker<PackerTileBan
|
||||
protected PackerProbeResult processFileProbe(
|
||||
final PackerFileProbe fileProbe,
|
||||
final Path assetRoot,
|
||||
final PackerTileBankRequirements requirements) {
|
||||
final PackerGlyphBankRequirements requirements) {
|
||||
final List<PackerDiagnostic> diagnostics = new ArrayList<>();
|
||||
|
||||
final var image = readImage(fileProbe, diagnostics);
|
||||
@ -63,7 +61,7 @@ public class PackerTileBankWalker extends PackerAbstractBankWalker<PackerTileBan
|
||||
diagnostics.add(new PackerDiagnostic(
|
||||
PackerDiagnosticSeverity.ERROR,
|
||||
PackerDiagnosticCategory.STRUCTURAL,
|
||||
"Invalid tile dimensions for " + fileProbe.path().getFileName()
|
||||
"Invalid glyph dimensions for " + fileProbe.path().getFileName()
|
||||
+ ": expected " + requirements.tileSize() + "x" + requirements.tileSize()
|
||||
+ " but got " + width + "x" + height,
|
||||
fileProbe.path(),
|
||||
@ -140,7 +138,6 @@ public class PackerTileBankWalker extends PackerAbstractBankWalker<PackerTileBan
|
||||
return new PackerProbeResult(fileProbe, Map.of("tile", tile, "palette", palette), diagnostics);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean hasReusableTileMetadata(final Map<String, Object> metadata) {
|
||||
final Object tileValue = metadata.get("tile");
|
||||
if (!(tileValue instanceof Map<?, ?> tile)) {
|
||||
@ -37,7 +37,7 @@ import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class FileSystemPackerWorkspaceService implements PackerWorkspaceService {
|
||||
private static final int TILE_BANK_COLOR_KEY_RGB565 = 0xF81F;
|
||||
private static final int GLYPH_BANK_COLOR_KEY_RGB565 = 0xF81F;
|
||||
|
||||
private final ObjectMapper mapper;
|
||||
private final PackerWorkspaceFoundation workspaceFoundation;
|
||||
@ -816,7 +816,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
if (declaration == null) {
|
||||
return List.of();
|
||||
}
|
||||
if (declaration.assetFamily() == AssetFamilyCatalog.TILE_BANK) {
|
||||
if (declaration.assetFamily() == AssetFamilyCatalog.GLYPH_BANK) {
|
||||
return List.of();
|
||||
}
|
||||
return List.of(new PackerDiagnostic(
|
||||
@ -878,7 +878,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
"size", packedAsset.payload().length,
|
||||
"decoded_size", packedAsset.decodedSize(),
|
||||
"codec", packedAsset.codec(),
|
||||
"metadata", packedAsset.metadata())));
|
||||
"metadata", packedAsset.runtimeMetadata())));
|
||||
if (packedAsset.preloadEnabled()) {
|
||||
final int slot = nextPreloadSlotByBankType.getOrDefault(packedAsset.bankType(), 0);
|
||||
nextPreloadSlotByBankType.put(packedAsset.bankType(), slot + 1);
|
||||
@ -888,7 +888,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
}
|
||||
assetTableMetadata.add(new LinkedHashMap<>(Map.of(
|
||||
"asset_id", packedAsset.assetId(),
|
||||
"metadata", packedAsset.metadata())));
|
||||
"metadata", packedAsset.toolingMetadata())));
|
||||
}
|
||||
|
||||
final byte[] headerBytes = canonicalJsonBytes(Map.of(
|
||||
@ -909,10 +909,10 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
|
||||
private PackedAsset packRuntimeAsset(PackerRuntimeAsset runtimeAsset) {
|
||||
final PackerAssetDeclaration declaration = runtimeAsset.parsedDeclaration().declaration();
|
||||
if (declaration == null || declaration.assetFamily() != AssetFamilyCatalog.TILE_BANK) {
|
||||
if (declaration == null || declaration.assetFamily() != AssetFamilyCatalog.GLYPH_BANK) {
|
||||
throw new IllegalStateException("Unsupported pack output family for current implementation.");
|
||||
}
|
||||
return packTileBank(runtimeAsset, declaration);
|
||||
return packGlyphBank(runtimeAsset, declaration);
|
||||
}
|
||||
|
||||
private boolean isEffectivelyIncludedInPackSet(PackerRuntimeAsset runtimeAsset) {
|
||||
@ -923,7 +923,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
return declaration == null || !declaration.artifacts().isEmpty();
|
||||
}
|
||||
|
||||
private PackedAsset packTileBank(PackerRuntimeAsset runtimeAsset, PackerAssetDeclaration declaration) {
|
||||
private PackedAsset packGlyphBank(PackerRuntimeAsset runtimeAsset, PackerAssetDeclaration declaration) {
|
||||
final int tileSize = parseTileSize(declaration.outputMetadata().get("tile_size"));
|
||||
final int width = 256;
|
||||
final int height = 256;
|
||||
@ -955,35 +955,38 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
});
|
||||
|
||||
final byte[] packedPixels = packNibbles(sheetPixels);
|
||||
final byte[] paletteBytes = emitTileBankPalettes(declaration);
|
||||
final byte[] paletteBytes = emitGlyphBankPalettes(declaration);
|
||||
final byte[] payload = new byte[packedPixels.length + paletteBytes.length];
|
||||
System.arraycopy(packedPixels, 0, payload, 0, packedPixels.length);
|
||||
System.arraycopy(paletteBytes, 0, payload, packedPixels.length, paletteBytes.length);
|
||||
|
||||
final LinkedHashMap<String, Object> metadata = new LinkedHashMap<>();
|
||||
metadata.put("tile_size", tileSize);
|
||||
metadata.put("width", width);
|
||||
metadata.put("height", height);
|
||||
metadata.put("palette_count", 64);
|
||||
metadata.put("palette_authored", countAuthoredTileBankPalettes(declaration));
|
||||
metadata.put("codec", Map.of());
|
||||
metadata.put("pipeline", normalizeTileBankRuntimePipelineMetadata(declaration));
|
||||
final LinkedHashMap<String, Object> runtimeMetadata = new LinkedHashMap<>();
|
||||
runtimeMetadata.put("tile_size", tileSize);
|
||||
runtimeMetadata.put("width", width);
|
||||
runtimeMetadata.put("height", height);
|
||||
runtimeMetadata.put("palette_count", 64);
|
||||
runtimeMetadata.put("palette_authored", countAuthoredGlyphBankPalettes(declaration));
|
||||
runtimeMetadata.put("codec", Map.of());
|
||||
declaration.outputMetadata().forEach((key, value) -> {
|
||||
if (!"tile_size".equals(key)) {
|
||||
metadata.putIfAbsent(key, value);
|
||||
runtimeMetadata.putIfAbsent(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
final LinkedHashMap<String, Object> toolingMetadata = new LinkedHashMap<>(runtimeMetadata);
|
||||
toolingMetadata.put("pipeline", normalizeToolingPipelineMetadata(declaration));
|
||||
|
||||
final PackerRegistryEntry registryEntry = runtimeAsset.registryEntry()
|
||||
.orElseThrow(() -> new IllegalStateException("Packed runtime asset must be registered"));
|
||||
return new PackedAsset(
|
||||
registryEntry.assetId(),
|
||||
declaration.name(),
|
||||
"TILES",
|
||||
"GLYPH",
|
||||
"NONE",
|
||||
payload,
|
||||
width * height + 2048,
|
||||
metadata,
|
||||
runtimeMetadata,
|
||||
toolingMetadata,
|
||||
declaration.preloadEnabled());
|
||||
}
|
||||
|
||||
@ -1006,7 +1009,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
return packed;
|
||||
}
|
||||
|
||||
private byte[] emitTileBankPalettes(PackerAssetDeclaration declaration) {
|
||||
private byte[] emitGlyphBankPalettes(PackerAssetDeclaration declaration) {
|
||||
final byte[] bytes = new byte[64 * 16 * 2];
|
||||
final JsonNode palettesNode = declaration.outputPipelineMetadata().get("palettes");
|
||||
if (!(palettesNode instanceof com.fasterxml.jackson.databind.node.ArrayNode palettesArray)) {
|
||||
@ -1023,8 +1026,8 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
continue;
|
||||
}
|
||||
final int paletteBaseOffset = (paletteIndex * 16) * 2;
|
||||
bytes[paletteBaseOffset] = (byte) (TILE_BANK_COLOR_KEY_RGB565 & 0xFF);
|
||||
bytes[paletteBaseOffset + 1] = (byte) ((TILE_BANK_COLOR_KEY_RGB565 >>> 8) & 0xFF);
|
||||
bytes[paletteBaseOffset] = (byte) (GLYPH_BANK_COLOR_KEY_RGB565 & 0xFF);
|
||||
bytes[paletteBaseOffset + 1] = (byte) ((GLYPH_BANK_COLOR_KEY_RGB565 >>> 8) & 0xFF);
|
||||
final JsonNode convertedNode = paletteNode.path("convertedRgb565");
|
||||
if (!convertedNode.isArray()) {
|
||||
continue;
|
||||
@ -1039,16 +1042,13 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private int countAuthoredTileBankPalettes(PackerAssetDeclaration declaration) {
|
||||
private int countAuthoredGlyphBankPalettes(PackerAssetDeclaration declaration) {
|
||||
final JsonNode palettesNode = declaration.outputPipelineMetadata().get("palettes");
|
||||
return palettesNode instanceof ArrayNode palettesArray ? palettesArray.size() : 0;
|
||||
}
|
||||
|
||||
private Map<String, Object> normalizeTileBankRuntimePipelineMetadata(PackerAssetDeclaration declaration) {
|
||||
final LinkedHashMap<String, Object> pipeline = new LinkedHashMap<>(
|
||||
PackerReadMessageMapper.normalizeMetadata(declaration.outputPipelineMetadata()));
|
||||
pipeline.remove("palettes");
|
||||
return pipeline;
|
||||
private Map<String, Object> normalizeToolingPipelineMetadata(PackerAssetDeclaration declaration) {
|
||||
return new LinkedHashMap<>(PackerReadMessageMapper.normalizeMetadata(declaration.outputPipelineMetadata()));
|
||||
}
|
||||
|
||||
private byte[] buildPrelude(int headerLength) {
|
||||
@ -1111,7 +1111,8 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
String codec,
|
||||
byte[] payload,
|
||||
int decodedSize,
|
||||
Map<String, Object> metadata,
|
||||
Map<String, Object> runtimeMetadata,
|
||||
Map<String, Object> toolingMetadata,
|
||||
boolean preloadEnabled) {
|
||||
}
|
||||
|
||||
|
||||
@ -121,7 +121,7 @@ public final class PackerAssetDeclarationParser {
|
||||
diagnostics.add(new PackerDiagnostic(
|
||||
PackerDiagnosticSeverity.ERROR,
|
||||
PackerDiagnosticCategory.STRUCTURAL,
|
||||
"Field 'type' must be one of: tile_bank, palette_bank, sound_bank.",
|
||||
"Field 'type' must be one of: glyph_bank, palette_bank, sound_bank.",
|
||||
manifestPath,
|
||||
true));
|
||||
return null;
|
||||
|
||||
@ -29,7 +29,7 @@ final class PackerOutputContractCatalog {
|
||||
codecs.addAll(outputFormat.availableCodecs());
|
||||
} else {
|
||||
final String normalizedFormat = outputFormat.manifestValue().trim().toUpperCase(Locale.ROOT);
|
||||
if (normalizedFormat.startsWith("TILES/")
|
||||
if (normalizedFormat.startsWith("GLYPH/")
|
||||
|| normalizedFormat.startsWith("PALETTE/")
|
||||
|| normalizedFormat.startsWith("SOUND/")
|
||||
|| normalizedFormat.startsWith("AUDIO/")) {
|
||||
@ -54,7 +54,7 @@ final class PackerOutputContractCatalog {
|
||||
|
||||
private static List<PackerCodecConfigurationField> metadataFieldsFor(OutputFormatCatalog outputFormat) {
|
||||
return switch (outputFormat) {
|
||||
case TILES_INDEXED_V1 -> List.of(new PackerCodecConfigurationField(
|
||||
case GLYPH_INDEXED_V1 -> List.of(new PackerCodecConfigurationField(
|
||||
"tile_size",
|
||||
"TileSize",
|
||||
PackerCodecConfigurationFieldType.ENUM,
|
||||
|
||||
@ -6,7 +6,7 @@ import org.junit.jupiter.api.io.TempDir;
|
||||
import p.packer.models.PackerAssetCacheEntry;
|
||||
import p.packer.models.PackerFileCacheEntry;
|
||||
import p.packer.models.PackerTileIndexedV1;
|
||||
import p.packer.models.PackerTileBankRequirements;
|
||||
import p.packer.models.PackerGlyphBankRequirements;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
@ -18,7 +18,7 @@ import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
final class PackerTileBankWalkerTest {
|
||||
final class PackerGlyphBankWalkerTest {
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
|
||||
@TempDir
|
||||
@ -30,7 +30,7 @@ final class PackerTileBankWalkerTest {
|
||||
final Path filePath = assetRoot.resolve("tile.png");
|
||||
writeTile(filePath);
|
||||
|
||||
final PackerTileBankWalker walker = new PackerTileBankWalker(MAPPER);
|
||||
final PackerGlyphBankWalker walker = new PackerGlyphBankWalker(MAPPER);
|
||||
final PackerAssetCacheEntry priorCache = new PackerAssetCacheEntry(
|
||||
1,
|
||||
"contract",
|
||||
@ -45,7 +45,7 @@ final class PackerTileBankWalkerTest {
|
||||
"palette", Map.of("originalArgb8888", List.of(0xFFFFFFFF))),
|
||||
List.of())));
|
||||
|
||||
final var result = walker.walk(assetRoot, new PackerTileBankRequirements(16), Optional.of(priorCache));
|
||||
final var result = walker.walk(assetRoot, new PackerGlyphBankRequirements(16), Optional.of(priorCache));
|
||||
|
||||
final PackerTileIndexedV1 tile = (PackerTileIndexedV1) result.probeResults().getFirst().metadata().get("tile");
|
||||
assertNotNull(tile);
|
||||
@ -135,9 +135,9 @@ final class PackerRuntimeAssetMaterializerTest {
|
||||
1,
|
||||
"uuid",
|
||||
"asset",
|
||||
AssetFamilyCatalog.TILE_BANK,
|
||||
AssetFamilyCatalog.GLYPH_BANK,
|
||||
artifacts,
|
||||
OutputFormatCatalog.TILES_INDEXED_V1,
|
||||
OutputFormatCatalog.GLYPH_INDEXED_V1,
|
||||
OutputCodecCatalog.NONE,
|
||||
metadata,
|
||||
pipelineMetadata,
|
||||
|
||||
@ -141,7 +141,7 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
assertEquals(PackerOperationStatus.SUCCESS, result.status());
|
||||
assertEquals(1, result.packSummary().assets().size());
|
||||
assertEquals(1, result.packSummary().assets().getFirst().artifactCount());
|
||||
assertEquals(OutputFormatCatalog.TILES_INDEXED_V1, result.packSummary().assets().getFirst().outputFormat());
|
||||
assertEquals(OutputFormatCatalog.GLYPH_INDEXED_V1, result.packSummary().assets().getFirst().outputFormat());
|
||||
assertEquals("16x16", result.packSummary().assets().getFirst().outputMetadata().get("tile_size"));
|
||||
assertEquals(64L, ((Map<?, ?>) ((Map<?, ?>) result.packSummary().assets().getFirst().outputPipeline().get("samples")).get("1")).get("length"));
|
||||
assertEquals(0L, result.packSummary().assets().getFirst().lastModified());
|
||||
@ -232,14 +232,14 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void packValidationBlocksTileBanksWithoutDeclaredPalettes() throws Exception {
|
||||
void packValidationBlocksGlyphBanksWithoutDeclaredPalettes() throws Exception {
|
||||
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("pack-validation-no-palettes"));
|
||||
final Path assetRoot = projectRoot.resolve("assets/ui/atlas");
|
||||
final Path manifestPath = assetRoot.resolve("asset.json");
|
||||
writeTilePng(assetRoot.resolve("confirm.png"), 16);
|
||||
|
||||
final ObjectNode manifest = (ObjectNode) MAPPER.readTree(manifestPath.toFile());
|
||||
manifest.putObject("output").put("format", "TILES/indexed_v1").put("codec", "NONE");
|
||||
manifest.putObject("output").put("format", "GLYPH/indexed_v1").put("codec", "NONE");
|
||||
final var artifacts = manifest.putArray("artifacts");
|
||||
artifacts.addObject().put("file", "confirm.png").put("index", 0);
|
||||
MAPPER.writerWithDefaultPrettyPrinter().writeValue(manifestPath.toFile(), manifest);
|
||||
@ -278,7 +278,7 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void packWorkspaceEmitsTileBankArtifactsFromFrozenSnapshot() throws Exception {
|
||||
void packWorkspaceEmitsGlyphBankArtifactsFromFrozenSnapshot() throws Exception {
|
||||
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("pack-workspace-success"));
|
||||
final Path assetRoot = projectRoot.resolve("assets/ui/atlas");
|
||||
final Path manifestPath = assetRoot.resolve("asset.json");
|
||||
@ -330,15 +330,14 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
assertEquals(0xF81F, readLeUnsignedShort(assetsPa, paletteStart));
|
||||
assertEquals(0xF800, readLeUnsignedShort(assetsPa, paletteStart + 2));
|
||||
assertEquals(1, assetTable.get(0).path("asset_id").asInt());
|
||||
assertEquals("TILES", assetTable.get(0).path("bank_type").asText());
|
||||
assertEquals("GLYPH", assetTable.get(0).path("bank_type").asText());
|
||||
assertEquals("NONE", assetTable.get(0).path("codec").asText());
|
||||
assertEquals(16, assetTable.get(0).path("metadata").path("tile_size").asInt());
|
||||
assertEquals(256, assetTable.get(0).path("metadata").path("width").asInt());
|
||||
assertEquals(256, assetTable.get(0).path("metadata").path("height").asInt());
|
||||
assertEquals(64, assetTable.get(0).path("metadata").path("palette_count").asInt());
|
||||
assertEquals(1, assetTable.get(0).path("metadata").path("palette_authored").asInt());
|
||||
assertTrue(assetTable.get(0).path("metadata").path("pipeline").isObject());
|
||||
assertTrue(assetTable.get(0).path("metadata").path("pipeline").path("palettes").isMissingNode());
|
||||
assertTrue(assetTable.get(0).path("metadata").path("pipeline").isMissingNode());
|
||||
|
||||
final var preload = MAPPER.readTree(projectRoot.resolve("build/preload.json").toFile());
|
||||
assertEquals(1, preload.size());
|
||||
@ -348,7 +347,8 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
final var assetTableMetadata = MAPPER.readTree(projectRoot.resolve("build/asset_table_metadata.json").toFile());
|
||||
assertEquals(1, assetTableMetadata.size());
|
||||
assertEquals(1, assetTableMetadata.get(0).path("metadata").path("palette_authored").asInt());
|
||||
assertTrue(assetTableMetadata.get(0).path("metadata").path("pipeline").path("palettes").isMissingNode());
|
||||
assertEquals(1, assetTableMetadata.get(0).path("metadata").path("pipeline").path("palettes").size());
|
||||
assertEquals(0, assetTableMetadata.get(0).path("metadata").path("pipeline").path("palettes").get(0).path("index").asInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -446,7 +446,7 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
assertEquals(PackerOperationStatus.PARTIAL, result.status());
|
||||
assertFalse(result.canPack());
|
||||
assertTrue(result.assets().getFirst().diagnostics().stream()
|
||||
.anyMatch(diagnostic -> diagnostic.message().contains("Invalid tile dimensions for oversized.png")));
|
||||
.anyMatch(diagnostic -> diagnostic.message().contains("Invalid glyph dimensions for oversized.png")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -491,8 +491,8 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
project(projectRoot),
|
||||
"ui/new-atlas",
|
||||
"new_atlas",
|
||||
AssetFamilyCatalog.TILE_BANK,
|
||||
OutputFormatCatalog.TILES_INDEXED_V1,
|
||||
AssetFamilyCatalog.GLYPH_BANK,
|
||||
OutputFormatCatalog.GLYPH_INDEXED_V1,
|
||||
OutputCodecCatalog.NONE,
|
||||
true));
|
||||
|
||||
@ -521,8 +521,8 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
project(projectRoot),
|
||||
"ui/new-atlas",
|
||||
"new_atlas",
|
||||
AssetFamilyCatalog.TILE_BANK,
|
||||
OutputFormatCatalog.TILES_INDEXED_V1,
|
||||
AssetFamilyCatalog.GLYPH_BANK,
|
||||
OutputFormatCatalog.GLYPH_INDEXED_V1,
|
||||
OutputCodecCatalog.NONE,
|
||||
true));
|
||||
|
||||
@ -539,7 +539,7 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
assertEquals("new_atlas", detailsResult.details().summary().identity().assetName());
|
||||
assertNotNull(detailsResult.details().summary().identity().assetUuid());
|
||||
assertTrue(detailsResult.diagnostics().stream().noneMatch(diagnostic -> diagnostic.blocking()));
|
||||
assertTrue(detailsResult.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("Output metadata for tile bank cannot be empty")));
|
||||
assertTrue(detailsResult.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("Output metadata for glyph bank cannot be empty")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -555,8 +555,8 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
project(projectRoot),
|
||||
"ui/new-atlas",
|
||||
"new_atlas",
|
||||
AssetFamilyCatalog.TILE_BANK,
|
||||
OutputFormatCatalog.TILES_INDEXED_V1,
|
||||
AssetFamilyCatalog.GLYPH_BANK,
|
||||
OutputFormatCatalog.GLYPH_INDEXED_V1,
|
||||
OutputCodecCatalog.NONE,
|
||||
true));
|
||||
|
||||
@ -987,7 +987,7 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
"name": "ui_atlas",
|
||||
"type": "IMAGE/bank",
|
||||
"output": {
|
||||
"format": "TILES/indexed_v1",
|
||||
"format": "GLYPH/indexed_v1",
|
||||
"codec": "none"
|
||||
},
|
||||
"preload": {
|
||||
@ -1002,7 +1002,7 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
"name": "ui_clone",
|
||||
"type": "IMAGE/bank",
|
||||
"output": {
|
||||
"format": "TILES/indexed_v1",
|
||||
"format": "GLYPH/indexed_v1",
|
||||
"codec": "none"
|
||||
},
|
||||
"preload": {
|
||||
@ -1053,8 +1053,8 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
project(projectRoot),
|
||||
"ui/delete-me",
|
||||
"delete_me",
|
||||
AssetFamilyCatalog.TILE_BANK,
|
||||
OutputFormatCatalog.TILES_INDEXED_V1,
|
||||
AssetFamilyCatalog.GLYPH_BANK,
|
||||
OutputFormatCatalog.GLYPH_INDEXED_V1,
|
||||
OutputCodecCatalog.NONE,
|
||||
false));
|
||||
final Path assetRoot = projectRoot.resolve("assets/ui/delete-me");
|
||||
@ -1081,8 +1081,8 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
project(projectRoot),
|
||||
"ui/source-atlas",
|
||||
"source_atlas",
|
||||
AssetFamilyCatalog.TILE_BANK,
|
||||
OutputFormatCatalog.TILES_INDEXED_V1,
|
||||
AssetFamilyCatalog.GLYPH_BANK,
|
||||
OutputFormatCatalog.GLYPH_INDEXED_V1,
|
||||
OutputCodecCatalog.NONE,
|
||||
true));
|
||||
Files.writeString(projectRoot.resolve("assets/ui/source-atlas/atlas.png"), "fixture");
|
||||
@ -1148,16 +1148,16 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
project(projectRoot),
|
||||
"ui/source",
|
||||
"source",
|
||||
AssetFamilyCatalog.TILE_BANK,
|
||||
OutputFormatCatalog.TILES_INDEXED_V1,
|
||||
AssetFamilyCatalog.GLYPH_BANK,
|
||||
OutputFormatCatalog.GLYPH_INDEXED_V1,
|
||||
OutputCodecCatalog.NONE,
|
||||
false));
|
||||
final CreateAssetResult target = service.createAsset(new CreateAssetRequest(
|
||||
project(projectRoot),
|
||||
"ui/target",
|
||||
"target",
|
||||
AssetFamilyCatalog.TILE_BANK,
|
||||
OutputFormatCatalog.TILES_INDEXED_V1,
|
||||
AssetFamilyCatalog.GLYPH_BANK,
|
||||
OutputFormatCatalog.GLYPH_INDEXED_V1,
|
||||
OutputCodecCatalog.NONE,
|
||||
false));
|
||||
|
||||
@ -1223,7 +1223,7 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
project(projectRoot),
|
||||
"audio/bad",
|
||||
"bad_asset",
|
||||
AssetFamilyCatalog.TILE_BANK,
|
||||
AssetFamilyCatalog.GLYPH_BANK,
|
||||
OutputFormatCatalog.SOUND_V1,
|
||||
OutputCodecCatalog.NONE,
|
||||
false));
|
||||
@ -1243,16 +1243,16 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
project(projectRoot),
|
||||
"ui/atlas-a",
|
||||
"atlas_a",
|
||||
AssetFamilyCatalog.TILE_BANK,
|
||||
OutputFormatCatalog.TILES_INDEXED_V1,
|
||||
AssetFamilyCatalog.GLYPH_BANK,
|
||||
OutputFormatCatalog.GLYPH_INDEXED_V1,
|
||||
OutputCodecCatalog.NONE,
|
||||
true)));
|
||||
final Future<CreateAssetResult> second = executor.submit(() -> service.createAsset(new CreateAssetRequest(
|
||||
project(projectRoot),
|
||||
"ui/atlas-b",
|
||||
"atlas_b",
|
||||
AssetFamilyCatalog.TILE_BANK,
|
||||
OutputFormatCatalog.TILES_INDEXED_V1,
|
||||
AssetFamilyCatalog.GLYPH_BANK,
|
||||
OutputFormatCatalog.GLYPH_INDEXED_V1,
|
||||
OutputCodecCatalog.NONE,
|
||||
false)));
|
||||
|
||||
|
||||
@ -29,8 +29,8 @@ final class PackerAssetDeclarationParserTest {
|
||||
assertEquals(1, result.declaration().schemaVersion());
|
||||
assertEquals("fixture-uuid-1", result.declaration().assetUuid());
|
||||
assertEquals("ui_atlas", result.declaration().name());
|
||||
assertEquals(AssetFamilyCatalog.TILE_BANK, result.declaration().assetFamily());
|
||||
assertEquals("TILES/indexed_v1", result.declaration().outputFormat().displayName());
|
||||
assertEquals(AssetFamilyCatalog.GLYPH_BANK, result.declaration().assetFamily());
|
||||
assertEquals("GLYPH/indexed_v1", result.declaration().outputFormat().displayName());
|
||||
assertEquals(OutputCodecCatalog.NONE, result.declaration().outputCodec());
|
||||
assertEquals(Map.of(), result.declaration().outputPipelineMetadata());
|
||||
assertTrue(result.declaration().preloadEnabled());
|
||||
@ -75,9 +75,9 @@ final class PackerAssetDeclarationParserTest {
|
||||
"schema_version": 1,
|
||||
"asset_uuid": "uuid-pipeline",
|
||||
"name": "pipeline_asset",
|
||||
"type": "tile_bank",
|
||||
"type": "glyph_bank",
|
||||
"output": {
|
||||
"format": "TILES/indexed_v1",
|
||||
"format": "GLYPH/indexed_v1",
|
||||
"codec": "NONE",
|
||||
"pipeline": []
|
||||
},
|
||||
@ -99,9 +99,9 @@ final class PackerAssetDeclarationParserTest {
|
||||
"schema_version": 1,
|
||||
"asset_uuid": "uuid-palette-contract",
|
||||
"name": "palette_asset",
|
||||
"type": "tile_bank",
|
||||
"type": "glyph_bank",
|
||||
"output": {
|
||||
"format": "TILES/indexed_v1",
|
||||
"format": "GLYPH/indexed_v1",
|
||||
"codec": "NONE",
|
||||
"pipeline": {
|
||||
"palettes": [
|
||||
@ -135,9 +135,9 @@ final class PackerAssetDeclarationParserTest {
|
||||
"schema_version": 1,
|
||||
"asset_uuid": "uuid-palette-duplicate",
|
||||
"name": "palette_asset",
|
||||
"type": "tile_bank",
|
||||
"type": "glyph_bank",
|
||||
"output": {
|
||||
"format": "TILES/indexed_v1",
|
||||
"format": "GLYPH/indexed_v1",
|
||||
"codec": "NONE",
|
||||
"pipeline": {
|
||||
"palettes": [
|
||||
@ -203,9 +203,9 @@ final class PackerAssetDeclarationParserTest {
|
||||
"schema_version": 1,
|
||||
"asset_uuid": "uuid-outside",
|
||||
"name": "bad_asset",
|
||||
"type": "tile_bank",
|
||||
"type": "glyph_bank",
|
||||
"inputs": { "sprites": ["../outside.png"] },
|
||||
"output": { "format": "TILES/indexed_v1", "codec": "NONE" },
|
||||
"output": { "format": "GLYPH/indexed_v1", "codec": "NONE" },
|
||||
"preload": { "enabled": true }
|
||||
}
|
||||
""");
|
||||
@ -225,7 +225,7 @@ final class PackerAssetDeclarationParserTest {
|
||||
"asset_uuid": "uuid-video",
|
||||
"name": "bad_asset",
|
||||
"type": "video_bank",
|
||||
"output": { "format": "TILES/indexed_v1", "codec": "NONE" },
|
||||
"output": { "format": "GLYPH/indexed_v1", "codec": "NONE" },
|
||||
"preload": { "enabled": true }
|
||||
}
|
||||
""");
|
||||
|
||||
@ -44,14 +44,14 @@ final class PackerAssetDetailsServiceTest {
|
||||
assertEquals(PackerBuildParticipation.INCLUDED, result.details().summary().buildParticipation());
|
||||
assertEquals("fixture-uuid-1", result.details().summary().identity().assetUuid());
|
||||
assertEquals("ui_atlas", result.details().summary().identity().assetName());
|
||||
assertEquals("TILES/indexed_v1", result.details().outputFormat().displayName());
|
||||
assertEquals("GLYPH/indexed_v1", result.details().outputFormat().displayName());
|
||||
assertEquals(List.of(OutputCodecCatalog.NONE), result.details().availableOutputCodecs());
|
||||
assertEquals(List.of(), result.details().codecConfigurationFieldsByCodec().get(OutputCodecCatalog.NONE));
|
||||
assertNotNull(result.details().bankComposition());
|
||||
assertTrue(result.details().bankComposition().selectedFiles().isEmpty());
|
||||
assertTrue(result.details().pipelinePalettes().isEmpty());
|
||||
assertTrue(result.diagnostics().stream().noneMatch(diagnostic -> diagnostic.blocking()));
|
||||
assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("Output metadata for tile bank cannot be empty")));
|
||||
assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("Output metadata for glyph bank cannot be empty")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -152,9 +152,9 @@ final class PackerAssetDetailsServiceTest {
|
||||
|
||||
assertEquals(PackerOperationStatus.PARTIAL, result.status());
|
||||
assertTrue(result.diagnostics().stream()
|
||||
.anyMatch(diagnostic -> diagnostic.message().contains("Invalid tile dimensions for wrong-palette.png")));
|
||||
.anyMatch(diagnostic -> diagnostic.message().contains("Invalid glyph dimensions for wrong-palette.png")));
|
||||
assertTrue(result.details().diagnostics().stream()
|
||||
.anyMatch(diagnostic -> diagnostic.message().contains("Invalid tile dimensions for wrong-palette.png")));
|
||||
.anyMatch(diagnostic -> diagnostic.message().contains("Invalid glyph dimensions for wrong-palette.png")));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"type": "tile_bank",
|
||||
"type": "glyph_bank",
|
||||
"inputs": "wrong",
|
||||
"output": {
|
||||
"codec": ""
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
"schema_version": 9,
|
||||
"asset_uuid": "future-uuid-1",
|
||||
"name": "future_asset",
|
||||
"type": "tile_bank",
|
||||
"type": "glyph_bank",
|
||||
"output": {
|
||||
"format": "TILES/indexed_v1",
|
||||
"format": "GLYPH/indexed_v1",
|
||||
"codec": "NONE"
|
||||
},
|
||||
"preload": {
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
"schema_version": 1,
|
||||
"asset_uuid": "fixture-uuid-1",
|
||||
"name": "ui_atlas",
|
||||
"type": "tile_bank",
|
||||
"type": "glyph_bank",
|
||||
"output": {
|
||||
"format": "TILES/indexed_v1",
|
||||
"format": "GLYPH/indexed_v1",
|
||||
"codec": "NONE"
|
||||
},
|
||||
"preload": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"type": "tile_bank",
|
||||
"type": "glyph_bank",
|
||||
"inputs": "wrong",
|
||||
"output": {
|
||||
"codec": ""
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
"schema_version": 1,
|
||||
"asset_uuid": "uuid-1",
|
||||
"name": "ui_atlas",
|
||||
"type": "tile_bank",
|
||||
"type": "glyph_bank",
|
||||
"output": {
|
||||
"format": "TILES/indexed_v1",
|
||||
"format": "GLYPH/indexed_v1",
|
||||
"codec": "NONE"
|
||||
},
|
||||
"preload": {
|
||||
|
||||
@ -190,7 +190,7 @@ public enum I18n {
|
||||
ASSETS_LABEL_BUILD_PARTICIPATION("assets.label.buildParticipation"),
|
||||
ASSETS_LABEL_ASSET_ID("assets.label.assetId"),
|
||||
ASSETS_LABEL_TYPE("assets.label.type"),
|
||||
ASSETS_TYPE_TILE_BANK("assets.type.tileBank"),
|
||||
ASSETS_TYPE_GLYPH_BANK("assets.type.glyphBank"),
|
||||
ASSETS_TYPE_PALETTE_BANK("assets.type.paletteBank"),
|
||||
ASSETS_TYPE_SOUND_BANK("assets.type.soundBank"),
|
||||
ASSETS_TYPE_UNKNOWN("assets.type.unknown"),
|
||||
|
||||
@ -113,7 +113,7 @@ public final class AssetDetailsUiSupport {
|
||||
|
||||
public static String typeLabel(AssetFamilyCatalog assetFamily) {
|
||||
return switch (assetFamily) {
|
||||
case TILE_BANK -> Container.i18n().text(I18n.ASSETS_TYPE_TILE_BANK);
|
||||
case GLYPH_BANK -> Container.i18n().text(I18n.ASSETS_TYPE_GLYPH_BANK);
|
||||
case SOUND_BANK -> Container.i18n().text(I18n.ASSETS_TYPE_SOUND_BANK);
|
||||
case UNKNOWN -> Container.i18n().text(I18n.ASSETS_TYPE_UNKNOWN);
|
||||
};
|
||||
@ -121,7 +121,7 @@ public final class AssetDetailsUiSupport {
|
||||
|
||||
public static String typeChipTone(AssetFamilyCatalog assetFamily) {
|
||||
return switch (assetFamily) {
|
||||
case TILE_BANK -> "assets-details-chip-image";
|
||||
case GLYPH_BANK -> "assets-details-chip-image";
|
||||
case SOUND_BANK -> "assets-details-chip-audio";
|
||||
case UNKNOWN -> "assets-details-chip-generic";
|
||||
};
|
||||
|
||||
@ -20,7 +20,7 @@ public final class AssetBankCapacityService {
|
||||
final Map<String, String> safeMetadata = Map.copyOf(Objects.requireNonNull(outputMetadata, "outputMetadata"));
|
||||
final Map<String, Object> safePipeline = Map.copyOf(Objects.requireNonNull(outputPipeline, "outputPipeline"));
|
||||
return switch (safeFamily) {
|
||||
case TILE_BANK -> evaluateTileBank(artifactCount, safeMetadata);
|
||||
case GLYPH_BANK -> evaluateGlyphBank(artifactCount, safeMetadata);
|
||||
case SOUND_BANK -> evaluateSoundBank(resolveSoundBankUsedBytes(safePipeline, usedBytes));
|
||||
case UNKNOWN -> new AssetDetailsBankCompositionCapacityState(
|
||||
0.0d,
|
||||
@ -31,7 +31,7 @@ public final class AssetBankCapacityService {
|
||||
};
|
||||
}
|
||||
|
||||
public int maxArtifactsForTileBank(Map<String, String> outputMetadata) {
|
||||
public int maxArtifactsForGlyphBank(Map<String, String> outputMetadata) {
|
||||
final String tileSizeValue = Objects.requireNonNull(outputMetadata, "outputMetadata")
|
||||
.getOrDefault("tile_size", "16x16");
|
||||
return switch (tileSizeValue) {
|
||||
@ -41,10 +41,10 @@ public final class AssetBankCapacityService {
|
||||
};
|
||||
}
|
||||
|
||||
private AssetDetailsBankCompositionCapacityState evaluateTileBank(
|
||||
private AssetDetailsBankCompositionCapacityState evaluateGlyphBank(
|
||||
int artifactCount,
|
||||
Map<String, String> outputMetadata) {
|
||||
final int maxSlots = maxArtifactsForTileBank(outputMetadata);
|
||||
final int maxSlots = maxArtifactsForGlyphBank(outputMetadata);
|
||||
final int safeArtifactCount = Math.max(0, artifactCount);
|
||||
final double progress = maxSlots <= 0 ? 0.0d : (double) safeArtifactCount / (double) maxSlots;
|
||||
final boolean blocked = safeArtifactCount >= maxSlots;
|
||||
@ -53,7 +53,7 @@ public final class AssetBankCapacityService {
|
||||
severityFor(progress),
|
||||
blocked,
|
||||
safeArtifactCount + " / " + maxSlots,
|
||||
blocked ? "Tile bank slot capacity reached." : "");
|
||||
blocked ? "Glyph bank slot capacity reached." : "");
|
||||
}
|
||||
|
||||
private AssetDetailsBankCompositionCapacityState evaluateSoundBank(long usedBytes) {
|
||||
|
||||
@ -10,7 +10,7 @@ import java.util.Objects;
|
||||
|
||||
public final class AssetDetailsBankCompositionCoordinator {
|
||||
private final List<AssetDetailsBankCompositionFamilySupport> familySupports = List.of(
|
||||
new AssetDetailsTileBankCompositionFamilySupport(),
|
||||
new AssetDetailsGlyphBankCompositionFamilySupport(),
|
||||
new AssetDetailsSoundBankCompositionFamilySupport(),
|
||||
new AssetDetailsFallbackBankCompositionFamilySupport());
|
||||
|
||||
|
||||
@ -7,21 +7,21 @@ import p.studio.workspaces.assets.messages.AssetWorkspaceBankCompositionFile;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
final class AssetDetailsTileBankCompositionFamilySupport extends AssetDetailsAbstractBankCompositionFamilySupport {
|
||||
final class AssetDetailsGlyphBankCompositionFamilySupport extends AssetDetailsAbstractBankCompositionFamilySupport {
|
||||
private final AssetBankCapacityService capacityService = new AssetBankCapacityService();
|
||||
|
||||
private int maxSlots = capacityService.maxArtifactsForTileBank(Map.of());
|
||||
private int maxSlots = capacityService.maxArtifactsForGlyphBank(Map.of());
|
||||
private Map<String, String> outputMetadata = Map.of();
|
||||
|
||||
@Override
|
||||
public boolean supports(AssetFamilyCatalog assetFamily) {
|
||||
return assetFamily == AssetFamilyCatalog.TILE_BANK;
|
||||
return assetFamily == AssetFamilyCatalog.GLYPH_BANK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetDetailsBankCompositionDraft createDraft(AssetWorkspaceAssetDetails details) {
|
||||
outputMetadata = metadataMap(details);
|
||||
maxSlots = capacityService.maxArtifactsForTileBank(outputMetadata);
|
||||
maxSlots = capacityService.maxArtifactsForGlyphBank(outputMetadata);
|
||||
return super.createDraft(details);
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ final class AssetDetailsTileBankCompositionFamilySupport extends AssetDetailsAbs
|
||||
@Override
|
||||
public AssetDetailsBankCompositionCapacityState evaluate(AssetDetailsBankCompositionDraft draft) {
|
||||
return capacityService.evaluate(
|
||||
AssetFamilyCatalog.TILE_BANK,
|
||||
AssetFamilyCatalog.GLYPH_BANK,
|
||||
draft.selectedFiles().size(),
|
||||
outputMetadata,
|
||||
Map.of(),
|
||||
@ -18,7 +18,7 @@ public final class AssetDetailsPaletteOverhaulingCoordinator {
|
||||
private StudioFormSession<AssetDetailsPaletteOverhaulingDraft> formSession;
|
||||
|
||||
public void replaceDetails(AssetWorkspaceAssetDetails details) {
|
||||
if (details == null || details.summary().assetFamily() != AssetFamilyCatalog.TILE_BANK) {
|
||||
if (details == null || details.summary().assetFamily() != AssetFamilyCatalog.GLYPH_BANK) {
|
||||
formSession = null;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -112,7 +112,7 @@ public final class AssetListItemControl extends VBox {
|
||||
|
||||
private String assetRowToneClass(AssetFamilyCatalog assetFamily) {
|
||||
return switch (assetFamily) {
|
||||
case TILE_BANK -> "assets-workspace-asset-row-tone-image";
|
||||
case GLYPH_BANK -> "assets-workspace-asset-row-tone-image";
|
||||
case SOUND_BANK -> "assets-workspace-asset-row-tone-audio";
|
||||
default -> "assets-workspace-asset-row-tone-generic";
|
||||
};
|
||||
@ -120,7 +120,7 @@ public final class AssetListItemControl extends VBox {
|
||||
|
||||
private String assetNameToneClass(AssetFamilyCatalog assetFamily) {
|
||||
return switch (assetFamily) {
|
||||
case TILE_BANK -> "assets-workspace-asset-name-tone-image";
|
||||
case GLYPH_BANK -> "assets-workspace-asset-name-tone-image";
|
||||
case SOUND_BANK -> "assets-workspace-asset-name-tone-audio";
|
||||
default -> "assets-workspace-asset-name-tone-generic";
|
||||
};
|
||||
|
||||
@ -181,7 +181,7 @@ assets.label.registration=Registration
|
||||
assets.label.buildParticipation=Build Participation
|
||||
assets.label.assetId=Asset ID
|
||||
assets.label.type=Type
|
||||
assets.type.tileBank=Tile Bank
|
||||
assets.type.glyphBank=Glyph Bank
|
||||
assets.type.paletteBank=Palette Bank
|
||||
assets.type.soundBank=Sound Bank
|
||||
assets.type.unknown=Unknown
|
||||
|
||||
@ -101,9 +101,9 @@ final class AssetDetailsBankCompositionCoordinatorTest {
|
||||
|
||||
private AssetWorkspaceAssetDetails tileDetails(String tileSize, int fileCount) {
|
||||
return new AssetWorkspaceAssetDetails(
|
||||
summary(AssetFamilyCatalog.TILE_BANK),
|
||||
summary(AssetFamilyCatalog.GLYPH_BANK),
|
||||
List.of(),
|
||||
OutputFormatCatalog.TILES_INDEXED_V1,
|
||||
OutputFormatCatalog.GLYPH_INDEXED_V1,
|
||||
OutputCodecCatalog.NONE,
|
||||
List.of(OutputCodecCatalog.NONE),
|
||||
Map.of(OutputCodecCatalog.NONE, List.of()),
|
||||
|
||||
@ -80,12 +80,12 @@ final class AssetDetailsPaletteOverhaulingCoordinatorTest {
|
||||
AssetWorkspaceAssetState.REGISTERED,
|
||||
AssetWorkspaceBuildParticipation.INCLUDED,
|
||||
1,
|
||||
AssetFamilyCatalog.TILE_BANK,
|
||||
AssetFamilyCatalog.GLYPH_BANK,
|
||||
Path.of("/tmp/bank"),
|
||||
false,
|
||||
false),
|
||||
List.of(),
|
||||
OutputFormatCatalog.TILES_INDEXED_V1,
|
||||
OutputFormatCatalog.GLYPH_INDEXED_V1,
|
||||
OutputCodecCatalog.NONE,
|
||||
List.of(OutputCodecCatalog.NONE),
|
||||
Map.of(OutputCodecCatalog.NONE, List.of()),
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
"relative_path" : "confirm.png",
|
||||
"mime_type" : "image/png",
|
||||
"size" : 137,
|
||||
"last_modified" : 1774359768919,
|
||||
"last_modified" : 1775797786944,
|
||||
"fingerprint" : "aa7d241deabcebe29a6096e14eaf16fdc06cf06380c11a507620b00fc7bff094",
|
||||
"metadata" : { },
|
||||
"diagnostics" : [ {
|
||||
@ -1263,7 +1263,7 @@
|
||||
"relative_path" : "right-palette.png",
|
||||
"mime_type" : "image/png",
|
||||
"size" : 327,
|
||||
"last_modified" : 1774359773932,
|
||||
"last_modified" : 1775797786950,
|
||||
"fingerprint" : "0526996900bdeef23c72fe987fb45800f11895946dcd5f0f7f8390c879069a37",
|
||||
"metadata" : {
|
||||
"palette" : {
|
||||
@ -1281,7 +1281,7 @@
|
||||
"relative_path" : "wrong-palette.png",
|
||||
"mime_type" : "image/png",
|
||||
"size" : 248,
|
||||
"last_modified" : 1774359771814,
|
||||
"last_modified" : 1775797786946,
|
||||
"fingerprint" : "15850f68547775866b01a0fe0b0012bb0243dec303ce1f9c3e02220e05b593e6",
|
||||
"metadata" : { },
|
||||
"diagnostics" : [ {
|
||||
@ -1311,7 +1311,7 @@
|
||||
"relative_path" : "confirm.png",
|
||||
"mime_type" : "image/png",
|
||||
"size" : 137,
|
||||
"last_modified" : 1774359768918,
|
||||
"last_modified" : 1775797786770,
|
||||
"fingerprint" : "aa7d241deabcebe29a6096e14eaf16fdc06cf06380c11a507620b00fc7bff094",
|
||||
"metadata" : {
|
||||
"palette" : {
|
||||
@ -1341,7 +1341,7 @@
|
||||
"relative_path" : "flag00.png",
|
||||
"mime_type" : "image/png",
|
||||
"size" : 507,
|
||||
"last_modified" : 1774359777378,
|
||||
"last_modified" : 1775797786709,
|
||||
"fingerprint" : "6ca635a7906067b6a2a8460a8562718c445783e6df18b9f2c80c83c5ac569db6",
|
||||
"metadata" : {
|
||||
"palette" : {
|
||||
@ -1365,7 +1365,7 @@
|
||||
"relative_path" : "flag01.png",
|
||||
"mime_type" : "image/png",
|
||||
"size" : 766,
|
||||
"last_modified" : 1774359777379,
|
||||
"last_modified" : 1775797786708,
|
||||
"fingerprint" : "79257d8437747a68e9e8eb8f43fced7aa55940dc8eca98f95767994abbacdd81",
|
||||
"metadata" : { },
|
||||
"diagnostics" : [ {
|
||||
@ -1379,7 +1379,7 @@
|
||||
"relative_path" : "flag02.png",
|
||||
"mime_type" : "image/png",
|
||||
"size" : 684,
|
||||
"last_modified" : 1774359777379,
|
||||
"last_modified" : 1775797786703,
|
||||
"fingerprint" : "5fd43c447cf6dd9b164458860239427d2f4544ff6e007814731b50c17ffe75bf",
|
||||
"metadata" : { },
|
||||
"diagnostics" : [ {
|
||||
@ -1393,7 +1393,7 @@
|
||||
"relative_path" : "flag03.png",
|
||||
"mime_type" : "image/png",
|
||||
"size" : 669,
|
||||
"last_modified" : 1774359777380,
|
||||
"last_modified" : 1775797786706,
|
||||
"fingerprint" : "7feab8c868bb446afd3dd7250e70504fb0ca228f463650d2dd7c525144e0b321",
|
||||
"metadata" : {
|
||||
"palette" : {
|
||||
@ -1411,7 +1411,7 @@
|
||||
"relative_path" : "link00.png",
|
||||
"mime_type" : "image/png",
|
||||
"size" : 497,
|
||||
"last_modified" : 1774359777381,
|
||||
"last_modified" : 1775797786710,
|
||||
"fingerprint" : "c363f3ef7f32d9b249c7dc8babdb3eb5aae68c524099e5e97290202b054dda71",
|
||||
"metadata" : {
|
||||
"palette" : {
|
||||
@ -1429,7 +1429,7 @@
|
||||
"relative_path" : "link01.png",
|
||||
"mime_type" : "image/png",
|
||||
"size" : 500,
|
||||
"last_modified" : 1774359777382,
|
||||
"last_modified" : 1775797786711,
|
||||
"fingerprint" : "e2130efbb9643b41a4fb62619f2713402f9622c7db040fe18d929609ef70b89a",
|
||||
"metadata" : {
|
||||
"palette" : {
|
||||
@ -1447,7 +1447,7 @@
|
||||
"relative_path" : "link02.png",
|
||||
"mime_type" : "image/png",
|
||||
"size" : 517,
|
||||
"last_modified" : 1774359777383,
|
||||
"last_modified" : 1775797786713,
|
||||
"fingerprint" : "9216dfc6a6226fac398e9f781550d23fbfaa65bd377cdda69187149beca114ec",
|
||||
"metadata" : {
|
||||
"palette" : {
|
||||
@ -1465,7 +1465,7 @@
|
||||
"relative_path" : "link03.png",
|
||||
"mime_type" : "image/png",
|
||||
"size" : 507,
|
||||
"last_modified" : 1774359777383,
|
||||
"last_modified" : 1775797786712,
|
||||
"fingerprint" : "ebc4b7ca7bb1455288681c5d71424f60f658a44cb343d0c3934329cec676c867",
|
||||
"metadata" : {
|
||||
"palette" : {
|
||||
@ -1483,7 +1483,7 @@
|
||||
"relative_path" : "link04.png",
|
||||
"mime_type" : "image/png",
|
||||
"size" : 497,
|
||||
"last_modified" : 1774359777384,
|
||||
"last_modified" : 1775797786718,
|
||||
"fingerprint" : "c363f3ef7f32d9b249c7dc8babdb3eb5aae68c524099e5e97290202b054dda71",
|
||||
"metadata" : {
|
||||
"palette" : {
|
||||
@ -1501,7 +1501,7 @@
|
||||
"relative_path" : "link05.png",
|
||||
"mime_type" : "image/png",
|
||||
"size" : 495,
|
||||
"last_modified" : 1774359777384,
|
||||
"last_modified" : 1775797786717,
|
||||
"fingerprint" : "1ba8ce75c445396334737143e0cfa56c87a1fb64cca8ad0962192594849249eb",
|
||||
"metadata" : {
|
||||
"palette" : {
|
||||
@ -1519,7 +1519,7 @@
|
||||
"relative_path" : "link06.png",
|
||||
"mime_type" : "image/png",
|
||||
"size" : 518,
|
||||
"last_modified" : 1774359777385,
|
||||
"last_modified" : 1775797786714,
|
||||
"fingerprint" : "5e0954447699c6d5eac7550f1a10cf520efcddad0b9fbd9a716434da9d9550d4",
|
||||
"metadata" : {
|
||||
"palette" : {
|
||||
@ -1537,7 +1537,7 @@
|
||||
"relative_path" : "link07.png",
|
||||
"mime_type" : "image/png",
|
||||
"size" : 517,
|
||||
"last_modified" : 1774359777385,
|
||||
"last_modified" : 1775797786716,
|
||||
"fingerprint" : "6104ba7f216a937994d478ed07766ec28f48ad72fdd6620508535a1f877b8ddb",
|
||||
"metadata" : {
|
||||
"palette" : {
|
||||
@ -1555,7 +1555,7 @@
|
||||
"relative_path" : "t1381s1.png",
|
||||
"mime_type" : "image/png",
|
||||
"size" : 4970,
|
||||
"last_modified" : 1774359777386,
|
||||
"last_modified" : 1775797786715,
|
||||
"fingerprint" : "c8b667527b32436ab97fd57b3215917f7065d6d1b7f6d71be1827351bdc61fa6",
|
||||
"metadata" : { },
|
||||
"diagnostics" : [ {
|
||||
@ -1569,7 +1569,7 @@
|
||||
"relative_path" : "t1489s1.png",
|
||||
"mime_type" : "image/png",
|
||||
"size" : 6489,
|
||||
"last_modified" : 1774359777387,
|
||||
"last_modified" : 1775797786719,
|
||||
"fingerprint" : "678c6f48c6d1fcd1209c9d0ab1c46f20eea4871946135263b7363488cceb74f0",
|
||||
"metadata" : { },
|
||||
"diagnostics" : [ {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"schema_version" : 1,
|
||||
"asset_uuid" : "87396aab-337e-479e-b1f4-ec296678389e",
|
||||
"name" : "Zelda",
|
||||
"type" : "tile_bank",
|
||||
"type" : "glyph_bank",
|
||||
"output" : {
|
||||
"codec" : "NONE",
|
||||
"pipeline" : {
|
||||
@ -14,7 +14,7 @@
|
||||
}
|
||||
} ]
|
||||
},
|
||||
"format" : "TILES/indexed_v1",
|
||||
"format" : "GLYPH/indexed_v1",
|
||||
"codec_configuration" : { },
|
||||
"metadata" : {
|
||||
"tile_size" : "32x32"
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
"schema_version" : 1,
|
||||
"asset_uuid" : "64147d33-e8bf-4272-bb5c-b4c07c0276b3",
|
||||
"name" : "Bigode",
|
||||
"type" : "tile_bank",
|
||||
"type" : "glyph_bank",
|
||||
"output" : {
|
||||
"format" : "TILES/indexed_v1",
|
||||
"format" : "GLYPH/indexed_v1",
|
||||
"codec" : "NONE",
|
||||
"codec_configuration" : { },
|
||||
"metadata" : {
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
"schema_version" : 1,
|
||||
"asset_uuid" : "b15b319f-5cab-4254-93ea-d83f4742d204",
|
||||
"name" : "ui_atlas",
|
||||
"type" : "tile_bank",
|
||||
"type" : "glyph_bank",
|
||||
"output" : {
|
||||
"format" : "TILES/indexed_v1",
|
||||
"format" : "GLYPH/indexed_v1",
|
||||
"codec" : "NONE",
|
||||
"codec_configuration" : { },
|
||||
"metadata" : {
|
||||
|
||||
@ -2,10 +2,10 @@
|
||||
"schema_version" : 1,
|
||||
"asset_uuid" : "f64d3bfe-443d-4703-b62a-face19a32cac",
|
||||
"name" : "bbb2",
|
||||
"type" : "tile_bank",
|
||||
"type" : "glyph_bank",
|
||||
"output" : {
|
||||
"codec" : "NONE",
|
||||
"format" : "TILES/indexed_v1"
|
||||
"format" : "GLYPH/indexed_v1"
|
||||
},
|
||||
"preload" : {
|
||||
"enabled" : false
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
"schema_version" : 1,
|
||||
"asset_uuid" : "4d9847b0-5a23-421f-8b78-bf3909ca2281",
|
||||
"name" : "one-more-atlas",
|
||||
"type" : "tile_bank",
|
||||
"type" : "glyph_bank",
|
||||
"output" : {
|
||||
"format" : "TILES/indexed_v1",
|
||||
"format" : "GLYPH/indexed_v1",
|
||||
"codec" : "NONE"
|
||||
},
|
||||
"preload" : {
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
"schema_version" : 1,
|
||||
"asset_uuid" : "21953cb8-4101-4790-9e5e-d95f5fbc9b5a",
|
||||
"name" : "ui_atlas",
|
||||
"type" : "tile_bank",
|
||||
"type" : "glyph_bank",
|
||||
"output" : {
|
||||
"format" : "TILES/indexed_v1",
|
||||
"format" : "GLYPH/indexed_v1",
|
||||
"codec" : "NONE",
|
||||
"codec_configuration" : { },
|
||||
"metadata" : {
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
"schema_version" : 1,
|
||||
"asset_uuid" : "62a81570-8f47-4612-9288-6060e6c9a2e2",
|
||||
"name" : "one-more-atlas",
|
||||
"type" : "tile_bank",
|
||||
"type" : "glyph_bank",
|
||||
"output" : {
|
||||
"format" : "TILES/indexed_v1",
|
||||
"format" : "GLYPH/indexed_v1",
|
||||
"codec" : "NONE"
|
||||
},
|
||||
"preload" : {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user