@three-flatland/normals 0.1.0-alpha.1 → 0.1.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/NormalMapLoader.d.ts +99 -0
- package/dist/NormalMapLoader.d.ts.map +1 -0
- package/dist/NormalMapLoader.js +128 -125
- package/dist/NormalMapLoader.js.map +1 -1
- package/dist/bake.d.ts +57 -0
- package/dist/bake.d.ts.map +1 -0
- package/dist/bake.js +149 -114
- package/dist/bake.js.map +1 -1
- package/dist/bake.node.d.ts +20 -0
- package/dist/bake.node.d.ts.map +1 -0
- package/dist/bake.node.js +37 -30
- package/dist/bake.node.js.map +1 -1
- package/dist/cli.d.ts +4 -3
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +109 -122
- package/dist/cli.js.map +1 -1
- package/dist/descriptor.d.ts +129 -0
- package/dist/descriptor.d.ts.map +1 -0
- package/dist/descriptor.js +45 -52
- package/dist/descriptor.js.map +1 -1
- package/dist/index.d.ts +5 -320
- package/dist/index.js +5 -33
- package/dist/node.d.ts +7 -21
- package/dist/node.js +5 -5
- package/dist/resolveNormalMap.d.ts +51 -0
- package/dist/resolveNormalMap.d.ts.map +1 -0
- package/dist/resolveNormalMap.js +67 -88
- package/dist/resolveNormalMap.js.map +1 -1
- package/package.json +27 -9
- package/dist/index.js.map +0 -1
- package/dist/node.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,320 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* surfaces:
|
|
7
|
-
*
|
|
8
|
-
* 1. Library API `bakeNormalMap(pixels, w, h, descriptor)`
|
|
9
|
-
* 2. CLI `flatland-bake normal --descriptor <file>`
|
|
10
|
-
* 3. Loader API `LDtkLoader.load(url, { normals: descriptor })`
|
|
11
|
-
*
|
|
12
|
-
* The descriptor carries enough information for the baker to emit a
|
|
13
|
-
* 1:1 co-registered normal map: where the regions live, how each
|
|
14
|
-
* region tilts, and what per-texel bump source to use.
|
|
15
|
-
*
|
|
16
|
-
* All types are pure and browser-safe — consumed identically by node
|
|
17
|
-
* bakers and browser runtime loaders.
|
|
18
|
-
*/
|
|
19
|
-
/**
|
|
20
|
-
* Screen-relative direction the surface normal's XY component points.
|
|
21
|
-
*
|
|
22
|
-
* Mental model: imagine the baked normal as an arrow. `NormalDirection`
|
|
23
|
-
* is the screen-space direction that arrow tilts toward — equivalently,
|
|
24
|
-
* the direction the tile's visible face is "pointing." For a wall at
|
|
25
|
-
* the top of the map (visible face toward the camera below), that's
|
|
26
|
-
* `'south'` / `'down'`.
|
|
27
|
-
*
|
|
28
|
-
* Cardinal and compass aliases are equivalent; the canonical form in
|
|
29
|
-
* the codebase is NSEW. Numbers are interpreted as radians in standard
|
|
30
|
-
* math convention (0 = +X / right, CCW positive).
|
|
31
|
-
*/
|
|
32
|
-
type NormalDirection = 'flat' | 'up' | 'north' | 'down' | 'south' | 'left' | 'west' | 'right' | 'east' | 'up-left' | 'north-west' | 'up-right' | 'north-east' | 'down-left' | 'south-west' | 'down-right' | 'south-east' | number;
|
|
33
|
-
/**
|
|
34
|
-
* Resolve a `NormalDirection` to an angle in radians, or `null` when
|
|
35
|
-
* the direction is `'flat'` (no tilt).
|
|
36
|
-
*
|
|
37
|
-
* Convention: 0 = +X / right, π/2 = +Y / up, π = -X / left,
|
|
38
|
-
* -π/2 = -Y / down. Matches `Math.atan2` output.
|
|
39
|
-
*/
|
|
40
|
-
declare function directionToAngle(direction: NormalDirection | undefined): number | null;
|
|
41
|
-
/**
|
|
42
|
-
* Per-texel bump source inside a region. Each non-'none' mode runs a
|
|
43
|
-
* central-difference gradient on the named channel; the result is
|
|
44
|
-
* treated as a height field where high values are raised and low
|
|
45
|
-
* values are sunken. Negative `strength` inverts (dark = raised).
|
|
46
|
-
*
|
|
47
|
-
* - `'alpha'` — gradient on the source alpha channel (default).
|
|
48
|
-
* Preserves sprite silhouette edges — the classic path for
|
|
49
|
-
* transparent sprites. Solid opaque regions produce no bump.
|
|
50
|
-
* - `'luminance'` — gradient on Rec. 709 luminance `(0.2126·R +
|
|
51
|
-
* 0.7152·G + 0.0722·B)`. Brick faces read as raised; dark mortar
|
|
52
|
-
* lines read as sunken grooves. Right mode for solid opaque
|
|
53
|
-
* tilesets.
|
|
54
|
-
* - `'red'` / `'green'` / `'blue'` — gradient on a single color
|
|
55
|
-
* channel. Use when your art treats one channel as a height map
|
|
56
|
-
* (e.g., packing height into the red channel of a data texture).
|
|
57
|
-
* - `'none'` — flat fill at the region's tilt direction. No per-texel
|
|
58
|
-
* variation; cheapest and useful for uniform surfaces.
|
|
59
|
-
*/
|
|
60
|
-
type NormalBump = 'alpha' | 'luminance' | 'red' | 'green' | 'blue' | 'none';
|
|
61
|
-
interface NormalRegion {
|
|
62
|
-
x: number;
|
|
63
|
-
y: number;
|
|
64
|
-
w: number;
|
|
65
|
-
h: number;
|
|
66
|
-
/** Per-texel bump source. Default inherits from descriptor (`'alpha'`). */
|
|
67
|
-
bump?: NormalBump;
|
|
68
|
-
/** Base tilt direction. Default inherits from descriptor (`'flat'`). */
|
|
69
|
-
direction?: NormalDirection;
|
|
70
|
-
/**
|
|
71
|
-
* Tilt angle in radians from flat. Ignored when `direction === 'flat'`.
|
|
72
|
-
* Default inherits from descriptor, which defaults to `Math.PI / 4`.
|
|
73
|
-
*/
|
|
74
|
-
pitch?: number;
|
|
75
|
-
/** Gradient strength multiplier for this region. Default 1. */
|
|
76
|
-
strength?: number;
|
|
77
|
-
/**
|
|
78
|
-
* World-space elevation of the region in [0, 1], where 0 = ground
|
|
79
|
-
* plane (floor) and 1 = top-of-wall (cap). Baked into the normal
|
|
80
|
-
* atlas's B channel and consumed by the light pass to compute
|
|
81
|
-
* per-fragment light direction (`L.z = lightHeight − elevation`).
|
|
82
|
-
*
|
|
83
|
-
* Default inherits from the descriptor, which defaults to 0. Cap
|
|
84
|
-
* regions typically set 1; tilted face regions typically set 0.5.
|
|
85
|
-
*/
|
|
86
|
-
elevation?: number;
|
|
87
|
-
}
|
|
88
|
-
interface NormalSourceDescriptor {
|
|
89
|
-
/** Reserved for future schema evolution. Currently always `1`. */
|
|
90
|
-
version?: 1;
|
|
91
|
-
/** Default bump source for regions that don't specify one. Default `'alpha'`. */
|
|
92
|
-
bump?: NormalBump;
|
|
93
|
-
/** Default tilt for regions that don't specify one. Default `'flat'`. */
|
|
94
|
-
direction?: NormalDirection;
|
|
95
|
-
/** Default tilt pitch in radians. Default `Math.PI / 4`. */
|
|
96
|
-
pitch?: number;
|
|
97
|
-
/** Default gradient strength. Default `1`. */
|
|
98
|
-
strength?: number;
|
|
99
|
-
/** Default elevation for regions that don't specify one. Default 0 (ground). */
|
|
100
|
-
elevation?: number;
|
|
101
|
-
/**
|
|
102
|
-
* Regions of the source texture. When omitted, the whole texture is
|
|
103
|
-
* treated as a single region inheriting all descriptor defaults.
|
|
104
|
-
*/
|
|
105
|
-
regions?: NormalRegion[];
|
|
106
|
-
}
|
|
107
|
-
declare const DEFAULT_PITCH: number;
|
|
108
|
-
declare const DEFAULT_STRENGTH = 1;
|
|
109
|
-
declare const DEFAULT_BUMP: NormalBump;
|
|
110
|
-
declare const DEFAULT_ELEVATION = 0;
|
|
111
|
-
/**
|
|
112
|
-
* Merge a region against its parent descriptor defaults. Returns a
|
|
113
|
-
* fully-populated region suitable for direct consumption by the baker.
|
|
114
|
-
*/
|
|
115
|
-
interface ResolvedNormalRegion {
|
|
116
|
-
x: number;
|
|
117
|
-
y: number;
|
|
118
|
-
w: number;
|
|
119
|
-
h: number;
|
|
120
|
-
bump: NormalBump;
|
|
121
|
-
/** Tilt angle in radians, or `null` when the region is flat. */
|
|
122
|
-
angle: number | null;
|
|
123
|
-
pitch: number;
|
|
124
|
-
strength: number;
|
|
125
|
-
/** World-space elevation in [0, 1]. Written to the output B channel. */
|
|
126
|
-
elevation: number;
|
|
127
|
-
}
|
|
128
|
-
declare function resolveRegion(region: NormalRegion, descriptor?: NormalSourceDescriptor): ResolvedNormalRegion;
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* @deprecated — legacy flat-texture bake options. Use
|
|
132
|
-
* `bakeNormalMap(pixels, w, h, descriptor)` for region-aware control.
|
|
133
|
-
*/
|
|
134
|
-
interface BakeOptions {
|
|
135
|
-
/** Scales the alpha gradient before normalization. Default 1. */
|
|
136
|
-
strength?: number;
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Produce a tangent-space normal map from source pixels + a descriptor.
|
|
140
|
-
*
|
|
141
|
-
* Cross-platform: browser + node. No filesystem, no pngjs. Used by
|
|
142
|
-
* the CLI baker (wrapped with file I/O) and the runtime loader
|
|
143
|
-
* (called directly on decoded image pixels).
|
|
144
|
-
*
|
|
145
|
-
* Per texel in each region:
|
|
146
|
-
* 1. `bump: 'alpha'` — central-difference gradient on the source
|
|
147
|
-
* alpha channel, clamped to the region bounds so adjacent regions
|
|
148
|
-
* (e.g. atlas cells) can't bleed into each other.
|
|
149
|
-
* 2. Compose with a tilt rotation that takes `+Z` to the region's
|
|
150
|
-
* `direction` at `pitch` radians.
|
|
151
|
-
* 3. Normalize and encode to `rgb = normal * 0.5 + 0.5`. Alpha is
|
|
152
|
-
* copied from the source so the baked map carries its own
|
|
153
|
-
* silhouette.
|
|
154
|
-
*
|
|
155
|
-
* Texels outside every region receive the flat normal `(0, 0, 1)`
|
|
156
|
-
* with the source's alpha — safe default for sparse descriptors.
|
|
157
|
-
*
|
|
158
|
-
* @param pixels RGBA pixel buffer, row-major, 4 bytes per pixel.
|
|
159
|
-
* @param width Pixel width.
|
|
160
|
-
* @param height Pixel height.
|
|
161
|
-
* @param descriptor Region / tilt / bump control. Defaults to a single
|
|
162
|
-
* whole-texture flat region with alpha-derived bump.
|
|
163
|
-
* @returns RGBA pixel buffer containing the encoded normal map.
|
|
164
|
-
*/
|
|
165
|
-
declare function bakeNormalMap(pixels: Uint8Array, width: number, height: number, descriptor?: NormalSourceDescriptor): Uint8Array;
|
|
166
|
-
/**
|
|
167
|
-
* Legacy back-compat wrapper. Use `bakeNormalMap(pixels, w, h, {
|
|
168
|
-
* strength })` for equivalent behavior in new code.
|
|
169
|
-
*
|
|
170
|
-
* @deprecated
|
|
171
|
-
*/
|
|
172
|
-
declare function bakeNormalMapFromPixels(pixels: Uint8Array, width: number, height: number, options?: BakeOptions): Uint8Array;
|
|
173
|
-
/**
|
|
174
|
-
* Derive the conventional `.normal.png` sibling URL for a sprite PNG.
|
|
175
|
-
*
|
|
176
|
-
* Runtime loaders call this to try the baked output before falling
|
|
177
|
-
* back to the runtime TSL path.
|
|
178
|
-
*
|
|
179
|
-
* Pure string rewrite — browser-safe, no filesystem.
|
|
180
|
-
*/
|
|
181
|
-
declare function bakedNormalURL(spriteURL: string): string;
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Options accepted by `NormalMapLoader.load()`. Inherits `forceRuntime`
|
|
185
|
-
* from the shared {@link BakedAssetLoaderOptions} so every baked-asset
|
|
186
|
-
* loader in the codebase advertises the same option shape.
|
|
187
|
-
*/
|
|
188
|
-
interface NormalMapLoaderStaticOptions extends BakedAssetLoaderOptions {
|
|
189
|
-
/**
|
|
190
|
-
* When provided, missing sidecars trigger an in-memory bake via
|
|
191
|
-
* `resolveNormalMap`. Without a descriptor, `NormalMapLoader.load()` can
|
|
192
|
-
* only probe the baked sibling and returns `null` on miss (legacy
|
|
193
|
-
* behavior, preserved for backward compat).
|
|
194
|
-
*/
|
|
195
|
-
descriptor?: NormalSourceDescriptor;
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Result of loading a sprite's normal data.
|
|
199
|
-
*
|
|
200
|
-
* Either a baked normal texture (fast path) or `null`, signalling the caller
|
|
201
|
-
* to use the runtime TSL `normalFromSprite` helper against the sprite's own
|
|
202
|
-
* alpha channel.
|
|
203
|
-
*/
|
|
204
|
-
type NormalMapResult = Texture | null;
|
|
205
|
-
/**
|
|
206
|
-
* Loader for per-sprite normal maps following the canonical three-flatland
|
|
207
|
-
* "try baked → fall back to runtime" pattern documented in
|
|
208
|
-
* `planning/bake/loader-pattern.md`.
|
|
209
|
-
*
|
|
210
|
-
* Given a sprite PNG URL, this loader fetches the sibling `.normal.png` that
|
|
211
|
-
* `flatland-bake normal` produces. If it is absent, the loader resolves to
|
|
212
|
-
* `null` so the lit material can switch to its TSL runtime fallback.
|
|
213
|
-
*
|
|
214
|
-
* Only the baked path uses the network — the runtime branch is a shader
|
|
215
|
-
* concern and lives in `@three-flatland/nodes/lighting/normalFromSprite`.
|
|
216
|
-
*
|
|
217
|
-
* @example
|
|
218
|
-
* ```ts
|
|
219
|
-
* // Vanilla static API
|
|
220
|
-
* const normalTex = await NormalMapLoader.load('/sprites/knight.png')
|
|
221
|
-
* if (normalTex) material.normalMap = normalTex
|
|
222
|
-
* else material.useRuntimeNormals = true
|
|
223
|
-
*
|
|
224
|
-
* // Skip the baked probe — go straight to runtime (or null without descriptor)
|
|
225
|
-
* const tex = await NormalMapLoader.load(url, { forceRuntime: true })
|
|
226
|
-
*
|
|
227
|
-
* // R3F useLoader
|
|
228
|
-
* const tex = useLoader(NormalMapLoader, '/sprites/knight.png')
|
|
229
|
-
* ```
|
|
230
|
-
*/
|
|
231
|
-
declare class NormalMapLoader extends Loader<NormalMapResult> {
|
|
232
|
-
/**
|
|
233
|
-
* Generate this asset's normal map in the browser on every load
|
|
234
|
-
* instead of loading a pre-baked `.normal.png` sidecar. With a
|
|
235
|
-
* `descriptor`, the in-memory bake runs on every load. Without a
|
|
236
|
-
* descriptor, the loader resolves directly to `null` and the caller
|
|
237
|
-
* uses the TSL runtime fallback. Suppresses the "no baked sibling"
|
|
238
|
-
* warn either way.
|
|
239
|
-
*
|
|
240
|
-
* Use when runtime is the right home for the bake. Not a dev-iteration
|
|
241
|
-
* knob — the default path (probe → bake on miss + warn) already
|
|
242
|
-
* handles iteration. See {@link BakedAssetLoaderOptions.forceRuntime}.
|
|
243
|
-
*/
|
|
244
|
-
forceRuntime: boolean;
|
|
245
|
-
/**
|
|
246
|
-
* When set, missing sidecars trigger an in-memory bake via
|
|
247
|
-
* `resolveNormalMap`. Without a descriptor, the loader can only probe
|
|
248
|
-
* the baked sibling and returns `null` on miss (legacy behavior).
|
|
249
|
-
*
|
|
250
|
-
* Set via the `useLoader` callback:
|
|
251
|
-
* ```ts
|
|
252
|
-
* useLoader(NormalMapLoader, url, undefined, (l) => { l.descriptor = desc })
|
|
253
|
-
* ```
|
|
254
|
-
*/
|
|
255
|
-
descriptor: NormalSourceDescriptor | undefined;
|
|
256
|
-
constructor(manager?: LoadingManager);
|
|
257
|
-
load(url: string, onLoad?: (data: NormalMapResult) => void, onProgress?: (event: ProgressEvent) => void, onError?: (err: unknown) => void): NormalMapResult;
|
|
258
|
-
loadAsync(url: string): Promise<NormalMapResult>;
|
|
259
|
-
/**
|
|
260
|
-
* Vanilla cache: keyed by `(url, forceRuntime, descriptor hash)` so
|
|
261
|
-
* two callers passing *different* descriptors for the same URL get
|
|
262
|
-
* distinct results instead of colliding on the first one's bake.
|
|
263
|
-
*
|
|
264
|
-
* Instance API (R3F `useLoader`) bypasses this map — R3F has its own
|
|
265
|
-
* suspense cache and we don't want double-caching to fight the lifecycle.
|
|
266
|
-
*/
|
|
267
|
-
private static _cache;
|
|
268
|
-
static load(url: string, options?: NormalMapLoaderStaticOptions): Promise<NormalMapResult>;
|
|
269
|
-
static clearCache(): void;
|
|
270
|
-
private static _loadImpl;
|
|
271
|
-
private static _tryLoadBaked;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
interface ResolveNormalMapOptions {
|
|
275
|
-
/**
|
|
276
|
-
* Generate this asset's normal map in the browser on every load
|
|
277
|
-
* instead of loading a pre-baked `.normal.png` sidecar. Fetches the
|
|
278
|
-
* source image, decodes its pixels, and runs `bakeNormalMap` in
|
|
279
|
-
* memory each time — no HEAD probe, no devtime "no baked sibling"
|
|
280
|
-
* warning.
|
|
281
|
-
*
|
|
282
|
-
* The returned data is always a valid normal map: this flag chooses
|
|
283
|
-
* *where* the bake happens (browser vs CI), not whether you get one.
|
|
284
|
-
*
|
|
285
|
-
* Use when runtime is the right home for the bake — procedurally
|
|
286
|
-
* varied content, throwaway prototypes, asset bundles where shipping
|
|
287
|
-
* the sidecar isn't worth the bytes. Not a dev-iteration knob: the
|
|
288
|
-
* default path (probe → bake on miss + warn) already handles that.
|
|
289
|
-
*
|
|
290
|
-
* Mirrors `SlugFontLoader.forceRuntime` and every other baked-asset
|
|
291
|
-
* loader in the codebase.
|
|
292
|
-
*/
|
|
293
|
-
forceRuntime?: boolean;
|
|
294
|
-
/**
|
|
295
|
-
* Force the returned texture's `flipY` to this value. Pass the
|
|
296
|
-
* diffuse texture's `flipY` so the normal map samples 1:1 with the
|
|
297
|
-
* diffuse on the GPU regardless of whether the normal came from the
|
|
298
|
-
* image-loader path (default true) or the in-memory `DataTexture`
|
|
299
|
-
* path (default false).
|
|
300
|
-
*/
|
|
301
|
-
flipY?: boolean;
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Resolve the normal map for an asset URL + descriptor.
|
|
305
|
-
*
|
|
306
|
-
* 1. Hash the descriptor.
|
|
307
|
-
* 2. Probe the baked sibling `<source>.normal.png` (unless
|
|
308
|
-
* `forceRuntime`). If present and its stamped hash matches, load
|
|
309
|
-
* and return that texture directly.
|
|
310
|
-
* 3. On miss (or stale hash), fetch the source, decode its pixels,
|
|
311
|
-
* lazy-import + run `bakeNormalMap` in memory, and wrap the result
|
|
312
|
-
* in a `DataTexture`. Runtime bake is the always-on fallback when
|
|
313
|
-
* normals were requested.
|
|
314
|
-
*
|
|
315
|
-
* Browser-only — uses `fetch`, `createImageBitmap`, `OffscreenCanvas`.
|
|
316
|
-
* Safe to call from any Three.js loader runtime.
|
|
317
|
-
*/
|
|
318
|
-
declare function resolveNormalMap(sourceURL: string, descriptor: NormalSourceDescriptor, options?: ResolveNormalMapOptions): Promise<Texture>;
|
|
319
|
-
|
|
320
|
-
export { type BakeOptions, DEFAULT_BUMP, DEFAULT_ELEVATION, DEFAULT_PITCH, DEFAULT_STRENGTH, type NormalBump, type NormalDirection, NormalMapLoader, type NormalMapResult, type NormalRegion, type NormalSourceDescriptor, type ResolveNormalMapOptions, type ResolvedNormalRegion, bakeNormalMap, bakeNormalMapFromPixels, bakedNormalURL, directionToAngle, resolveNormalMap, resolveRegion };
|
|
1
|
+
import { DEFAULT_BUMP, DEFAULT_ELEVATION, DEFAULT_PITCH, DEFAULT_STRENGTH, NormalBump, NormalDirection, NormalRegion, NormalSourceDescriptor, ResolvedNormalRegion, directionToAngle, resolveRegion } from "./descriptor.js";
|
|
2
|
+
import { NormalMapLoader, NormalMapResult } from "./NormalMapLoader.js";
|
|
3
|
+
import { BakeOptions, bakeNormalMap, bakeNormalMapFromPixels, bakedNormalURL } from "./bake.js";
|
|
4
|
+
import { ResolveNormalMapOptions, resolveNormalMap } from "./resolveNormalMap.js";
|
|
5
|
+
export { type BakeOptions, DEFAULT_BUMP, DEFAULT_ELEVATION, DEFAULT_PITCH, DEFAULT_STRENGTH, type NormalBump, type NormalDirection, NormalMapLoader, type NormalMapResult, type NormalRegion, type NormalSourceDescriptor, type ResolveNormalMapOptions, type ResolvedNormalRegion, bakeNormalMap, bakeNormalMapFromPixels, bakedNormalURL, directionToAngle, resolveNormalMap, resolveRegion };
|
package/dist/index.js
CHANGED
|
@@ -1,33 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
NormalMapLoader
|
|
8
|
-
} from "./NormalMapLoader.js";
|
|
9
|
-
import {
|
|
10
|
-
resolveNormalMap
|
|
11
|
-
} from "./resolveNormalMap.js";
|
|
12
|
-
import {
|
|
13
|
-
directionToAngle,
|
|
14
|
-
resolveRegion,
|
|
15
|
-
DEFAULT_PITCH,
|
|
16
|
-
DEFAULT_STRENGTH,
|
|
17
|
-
DEFAULT_BUMP,
|
|
18
|
-
DEFAULT_ELEVATION
|
|
19
|
-
} from "./descriptor.js";
|
|
20
|
-
export {
|
|
21
|
-
DEFAULT_BUMP,
|
|
22
|
-
DEFAULT_ELEVATION,
|
|
23
|
-
DEFAULT_PITCH,
|
|
24
|
-
DEFAULT_STRENGTH,
|
|
25
|
-
NormalMapLoader,
|
|
26
|
-
bakeNormalMap,
|
|
27
|
-
bakeNormalMapFromPixels,
|
|
28
|
-
bakedNormalURL,
|
|
29
|
-
directionToAngle,
|
|
30
|
-
resolveNormalMap,
|
|
31
|
-
resolveRegion
|
|
32
|
-
};
|
|
33
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
import { DEFAULT_BUMP, DEFAULT_ELEVATION, DEFAULT_PITCH, DEFAULT_STRENGTH, directionToAngle, resolveRegion } from "./descriptor.js";
|
|
2
|
+
import { bakeNormalMap, bakeNormalMapFromPixels, bakedNormalURL } from "./bake.js";
|
|
3
|
+
import { NormalMapLoader } from "./NormalMapLoader.js";
|
|
4
|
+
import { resolveNormalMap } from "./resolveNormalMap.js";
|
|
5
|
+
export { DEFAULT_BUMP, DEFAULT_ELEVATION, DEFAULT_PITCH, DEFAULT_STRENGTH, NormalMapLoader, bakeNormalMap, bakeNormalMapFromPixels, bakedNormalURL, directionToAngle, resolveNormalMap, resolveRegion };
|
package/dist/node.d.ts
CHANGED
|
@@ -1,21 +1,7 @@
|
|
|
1
|
-
import { NormalSourceDescriptor,
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
*
|
|
9
|
-
* Node-only wrapper around `bakeNormalMap` — handles file I/O and
|
|
10
|
-
* stamps the output PNG with a `tEXt` chunk containing the descriptor
|
|
11
|
-
* hash, so `probeBakedSibling` can invalidate stale outputs.
|
|
12
|
-
*
|
|
13
|
-
* Second argument accepts either:
|
|
14
|
-
* - A full `NormalSourceDescriptor` (region-aware).
|
|
15
|
-
* - A legacy `BakeOptions` (`{ strength }`) — for back-compat with
|
|
16
|
-
* existing callers; promoted to a zero-region descriptor.
|
|
17
|
-
* - A path to a descriptor JSON file.
|
|
18
|
-
*/
|
|
19
|
-
declare function bakeNormalMapFile(inputPath: string, descriptorOrOptions?: NormalSourceDescriptor | BakeOptions | string, outputPath?: string): string;
|
|
20
|
-
|
|
21
|
-
export { BakeOptions, NormalSourceDescriptor, bakeNormalMapFile };
|
|
1
|
+
import { DEFAULT_BUMP, DEFAULT_ELEVATION, DEFAULT_PITCH, DEFAULT_STRENGTH, NormalBump, NormalDirection, NormalRegion, NormalSourceDescriptor, ResolvedNormalRegion, directionToAngle, resolveRegion } from "./descriptor.js";
|
|
2
|
+
import { NormalMapLoader, NormalMapResult } from "./NormalMapLoader.js";
|
|
3
|
+
import { BakeOptions, bakeNormalMap, bakeNormalMapFromPixels, bakedNormalURL } from "./bake.js";
|
|
4
|
+
import { bakeNormalMapFile } from "./bake.node.js";
|
|
5
|
+
import { ResolveNormalMapOptions, resolveNormalMap } from "./resolveNormalMap.js";
|
|
6
|
+
import "./index.js";
|
|
7
|
+
export { type BakeOptions, DEFAULT_BUMP, DEFAULT_ELEVATION, DEFAULT_PITCH, DEFAULT_STRENGTH, type NormalBump, type NormalDirection, NormalMapLoader, type NormalMapResult, type NormalRegion, type NormalSourceDescriptor, type ResolveNormalMapOptions, type ResolvedNormalRegion, bakeNormalMap, bakeNormalMapFile, bakeNormalMapFromPixels, bakedNormalURL, directionToAngle, resolveNormalMap, resolveRegion };
|
package/dist/node.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { DEFAULT_BUMP, DEFAULT_ELEVATION, DEFAULT_PITCH, DEFAULT_STRENGTH, directionToAngle, resolveRegion } from "./descriptor.js";
|
|
2
|
+
import { bakeNormalMap, bakeNormalMapFromPixels, bakedNormalURL } from "./bake.js";
|
|
3
|
+
import { NormalMapLoader } from "./NormalMapLoader.js";
|
|
2
4
|
import { bakeNormalMapFile } from "./bake.node.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
};
|
|
6
|
-
//# sourceMappingURL=node.js.map
|
|
5
|
+
import { resolveNormalMap } from "./resolveNormalMap.js";
|
|
6
|
+
export { DEFAULT_BUMP, DEFAULT_ELEVATION, DEFAULT_PITCH, DEFAULT_STRENGTH, NormalMapLoader, bakeNormalMap, bakeNormalMapFile, bakeNormalMapFromPixels, bakedNormalURL, directionToAngle, resolveNormalMap, resolveRegion };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { NormalSourceDescriptor } from "./descriptor.js";
|
|
2
|
+
import { Texture } from "three";
|
|
3
|
+
//#region src/resolveNormalMap.d.ts
|
|
4
|
+
interface ResolveNormalMapOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Generate this asset's normal map in the browser on every load
|
|
7
|
+
* instead of loading a pre-baked `.normal.png` sidecar. Fetches the
|
|
8
|
+
* source image, decodes its pixels, and runs `bakeNormalMap` in
|
|
9
|
+
* memory each time — no HEAD probe, no devtime "no baked sibling"
|
|
10
|
+
* warning.
|
|
11
|
+
*
|
|
12
|
+
* The returned data is always a valid normal map: this flag chooses
|
|
13
|
+
* *where* the bake happens (browser vs CI), not whether you get one.
|
|
14
|
+
*
|
|
15
|
+
* Use when runtime is the right home for the bake — procedurally
|
|
16
|
+
* varied content, throwaway prototypes, asset bundles where shipping
|
|
17
|
+
* the sidecar isn't worth the bytes. Not a dev-iteration knob: the
|
|
18
|
+
* default path (probe → bake on miss + warn) already handles that.
|
|
19
|
+
*
|
|
20
|
+
* Mirrors `SlugFontLoader.forceRuntime` and every other baked-asset
|
|
21
|
+
* loader in the codebase.
|
|
22
|
+
*/
|
|
23
|
+
forceRuntime?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Force the returned texture's `flipY` to this value. Pass the
|
|
26
|
+
* diffuse texture's `flipY` so the normal map samples 1:1 with the
|
|
27
|
+
* diffuse on the GPU regardless of whether the normal came from the
|
|
28
|
+
* image-loader path (default true) or the in-memory `DataTexture`
|
|
29
|
+
* path (default false).
|
|
30
|
+
*/
|
|
31
|
+
flipY?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Resolve the normal map for an asset URL + descriptor.
|
|
35
|
+
*
|
|
36
|
+
* 1. Hash the descriptor.
|
|
37
|
+
* 2. Probe the baked sibling `<source>.normal.png` (unless
|
|
38
|
+
* `forceRuntime`). If present and its stamped hash matches, load
|
|
39
|
+
* and return that texture directly.
|
|
40
|
+
* 3. On miss (or stale hash), fetch the source, decode its pixels,
|
|
41
|
+
* lazy-import + run `bakeNormalMap` in memory, and wrap the result
|
|
42
|
+
* in a `DataTexture`. Runtime bake is the always-on fallback when
|
|
43
|
+
* normals were requested.
|
|
44
|
+
*
|
|
45
|
+
* Browser-only — uses `fetch`, `createImageBitmap`, `OffscreenCanvas`.
|
|
46
|
+
* Safe to call from any Three.js loader runtime.
|
|
47
|
+
*/
|
|
48
|
+
declare function resolveNormalMap(sourceURL: string, descriptor: NormalSourceDescriptor, options?: ResolveNormalMapOptions): Promise<Texture>;
|
|
49
|
+
//#endregion
|
|
50
|
+
export { ResolveNormalMapOptions, resolveNormalMap };
|
|
51
|
+
//# sourceMappingURL=resolveNormalMap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveNormalMap.d.ts","names":[],"sources":["../src/resolveNormalMap.ts"],"mappings":";;;UAIiB;;;;;;;;;;;;;;;;;;;EAmBf;;;;;;;;EAQA;;;;;;;;;;;;;;;;;iBAkBoB,iBACpB,mBACA,YAAY,wBACZ,UAAS,0BACR,QAAQ"}
|
package/dist/resolveNormalMap.js
CHANGED
|
@@ -1,97 +1,76 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import { DataTexture, RGBAFormat, TextureLoader, UnsignedByteType } from "three";
|
|
2
|
+
import { bakedSiblingURL, devtimeWarn, hashDescriptor, probeBakedSibling } from "@three-flatland/bake";
|
|
3
|
+
//#region src/resolveNormalMap.ts
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the normal map for an asset URL + descriptor.
|
|
6
|
+
*
|
|
7
|
+
* 1. Hash the descriptor.
|
|
8
|
+
* 2. Probe the baked sibling `<source>.normal.png` (unless
|
|
9
|
+
* `forceRuntime`). If present and its stamped hash matches, load
|
|
10
|
+
* and return that texture directly.
|
|
11
|
+
* 3. On miss (or stale hash), fetch the source, decode its pixels,
|
|
12
|
+
* lazy-import + run `bakeNormalMap` in memory, and wrap the result
|
|
13
|
+
* in a `DataTexture`. Runtime bake is the always-on fallback when
|
|
14
|
+
* normals were requested.
|
|
15
|
+
*
|
|
16
|
+
* Browser-only — uses `fetch`, `createImageBitmap`, `OffscreenCanvas`.
|
|
17
|
+
* Safe to call from any Three.js loader runtime.
|
|
18
|
+
*/
|
|
13
19
|
async function resolveNormalMap(sourceURL, descriptor, options = {}) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
devtimeWarn(
|
|
32
|
-
"normal",
|
|
33
|
-
sourceURL,
|
|
34
|
-
`No baked sibling at ${bakedURL} \u2014 baking in memory. Run \`npx flatland-bake normal\` for production.`
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
const tex = await bakeInMemory(sourceURL, descriptor);
|
|
39
|
-
if (options.flipY !== void 0) tex.flipY = options.flipY;
|
|
40
|
-
tex.needsUpdate = true;
|
|
41
|
-
return tex;
|
|
20
|
+
const hash = hashDescriptor(descriptor);
|
|
21
|
+
if (!options.forceRuntime) {
|
|
22
|
+
const bakedURL = bakedSiblingURL(sourceURL, ".normal.png");
|
|
23
|
+
const probe = await probeBakedSibling(bakedURL, { expectedHash: hash });
|
|
24
|
+
if (probe.ok && probe.hashMatches) {
|
|
25
|
+
const tex = await loadTextureURL(bakedURL);
|
|
26
|
+
if (options.flipY !== void 0) tex.flipY = options.flipY;
|
|
27
|
+
tex.needsUpdate = true;
|
|
28
|
+
return tex;
|
|
29
|
+
}
|
|
30
|
+
if (probe.ok && !probe.hashMatches) devtimeWarn("normal", sourceURL, `${bakedURL} exists but its descriptor hash is stale — re-baking in memory. Run \`npx flatland-bake normal ${sourceURL} --descriptor <descriptor>.json\` to refresh.`);
|
|
31
|
+
else devtimeWarn("normal", sourceURL, `No baked sibling at ${bakedURL} — baking in memory. Run \`npx flatland-bake normal\` for production.`);
|
|
32
|
+
}
|
|
33
|
+
const tex = await bakeInMemory(sourceURL, descriptor);
|
|
34
|
+
if (options.flipY !== void 0) tex.flipY = options.flipY;
|
|
35
|
+
tex.needsUpdate = true;
|
|
36
|
+
return tex;
|
|
42
37
|
}
|
|
43
38
|
async function loadTextureURL(url) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
url,
|
|
48
|
-
(tex) => resolve(tex),
|
|
49
|
-
void 0,
|
|
50
|
-
(err) => reject(err)
|
|
51
|
-
);
|
|
52
|
-
});
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
new TextureLoader().load(url, (tex) => resolve(tex), void 0, (err) => reject(err));
|
|
41
|
+
});
|
|
53
42
|
}
|
|
54
43
|
async function bakeInMemory(sourceURL, descriptor) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const texture = new DataTexture(
|
|
67
|
-
normalPixels,
|
|
68
|
-
width,
|
|
69
|
-
height,
|
|
70
|
-
RGBAFormat,
|
|
71
|
-
UnsignedByteType
|
|
72
|
-
);
|
|
73
|
-
texture.needsUpdate = true;
|
|
74
|
-
return texture;
|
|
44
|
+
const res = await fetch(sourceURL);
|
|
45
|
+
if (!res.ok) throw new Error(`resolveNormalMap: failed to fetch ${sourceURL} (${res.status})`);
|
|
46
|
+
const blob = await res.blob();
|
|
47
|
+
const bitmap = await createImageBitmap(blob);
|
|
48
|
+
const { width, height } = bitmap;
|
|
49
|
+
const pixels = imageBitmapToRGBA(bitmap, width, height);
|
|
50
|
+
bitmap.close();
|
|
51
|
+
const { bakeNormalMap } = await import("./bake.js");
|
|
52
|
+
const texture = new DataTexture(bakeNormalMap(pixels, width, height, descriptor), width, height, RGBAFormat, UnsignedByteType);
|
|
53
|
+
texture.needsUpdate = true;
|
|
54
|
+
return texture;
|
|
75
55
|
}
|
|
76
56
|
function imageBitmapToRGBA(bitmap, width, height) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return new Uint8Array(data.data.buffer, data.data.byteOffset, data.data.byteLength);
|
|
57
|
+
if (typeof OffscreenCanvas !== "undefined") {
|
|
58
|
+
const ctx = new OffscreenCanvas(width, height).getContext("2d");
|
|
59
|
+
if (!ctx) throw new Error("resolveNormalMap: OffscreenCanvas 2D context unavailable");
|
|
60
|
+
ctx.drawImage(bitmap, 0, 0);
|
|
61
|
+
const data = ctx.getImageData(0, 0, width, height);
|
|
62
|
+
return new Uint8Array(data.data.buffer, data.data.byteOffset, data.data.byteLength);
|
|
63
|
+
}
|
|
64
|
+
const canvas = document.createElement("canvas");
|
|
65
|
+
canvas.width = width;
|
|
66
|
+
canvas.height = height;
|
|
67
|
+
const ctx = canvas.getContext("2d");
|
|
68
|
+
if (!ctx) throw new Error("resolveNormalMap: Canvas 2D context unavailable");
|
|
69
|
+
ctx.drawImage(bitmap, 0, 0);
|
|
70
|
+
const data = ctx.getImageData(0, 0, width, height);
|
|
71
|
+
return new Uint8Array(data.data.buffer, data.data.byteOffset, data.data.byteLength);
|
|
93
72
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
73
|
+
//#endregion
|
|
74
|
+
export { resolveNormalMap };
|
|
75
|
+
|
|
97
76
|
//# sourceMappingURL=resolveNormalMap.js.map
|