lecodes-sdk 1.2.0 → 2.0.1
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 +5 -2
- package/dist/editor.d.ts +12 -0
- package/dist/global.d.ts +4 -8
- package/dist/host.d.ts +2 -3
- package/dist/types/animate/tween/Animation.d.ts +0 -3
- package/dist/types/animate/tween/animateValue.d.ts +4 -2
- package/dist/types/animate/tween/easing.d.ts +8 -0
- package/dist/types/animate/tween/spec.d.ts +15 -7
- package/dist/types/audio/audio.d.ts +2 -1
- package/dist/types/canvas/Canvas.d.ts +40 -108
- package/dist/types/canvas/gen/cssColor.d.ts +17 -0
- package/dist/types/canvas/gen/recorder.d.ts +118 -0
- package/dist/types/canvas/gen/spec.d.ts +144 -0
- package/dist/types/core/color.d.ts +3 -1
- package/dist/types/core/pins.d.ts +18 -0
- package/dist/types/g2/Node2D.d.ts +5 -8
- package/dist/types/g2/Scene2D.d.ts +6 -2
- package/dist/types/gl/Foliage.d.ts +30 -7
- package/dist/types/gl/Light.d.ts +8 -0
- package/dist/types/gl/Lightmap.d.ts +13 -2
- package/dist/types/gl/Material.d.ts +17 -3
- package/dist/types/gl/Model.d.ts +6 -2
- package/dist/types/gl/Node.d.ts +3 -6
- package/dist/types/gl/Scene.d.ts +41 -13
- package/dist/types/gl/Texture.d.ts +1 -1
- package/dist/types/gl/animation/Locomotion.d.ts +8 -1
- package/dist/types/inject.d.ts +11 -11
- package/dist/types/inject.editor.d.ts +1 -0
- package/dist/types/net/core.d.ts +7 -0
- package/dist/types/plugin.d.ts +76 -0
- package/dist/types/plugins/gen/camera/sdk/camera.d.ts +24 -0
- package/dist/types/plugins/gen/camera/sdk/camera.gen.d.ts +25 -0
- package/dist/types/plugins/{geolocation.d.ts → gen/geolocation/sdk/geolocation.d.ts} +2 -20
- package/dist/types/plugins/gen/geolocation/sdk/geolocation.gen.d.ts +31 -0
- package/dist/types/plugins/{map.d.ts → gen/map/sdk/map.d.ts} +9 -50
- package/dist/types/plugins/gen/map/sdk/map.gen.d.ts +53 -0
- package/dist/types/plugins/gen/push/sdk/push.d.ts +23 -0
- package/dist/types/plugins/gen/push/sdk/push.gen.d.ts +35 -0
- package/dist/types/plugins/{qr.d.ts → gen/qr-scanner/sdk/qr-scanner.d.ts} +2 -3
- package/dist/types/plugins/gen/qr-scanner/sdk/qr-scanner.gen.d.ts +15 -0
- package/dist/types/runtime/app.d.ts +9 -2
- package/dist/types/runtime/fetch.d.ts +2 -0
- package/dist/types/runtime/input.d.ts +1 -1
- package/dist/types/runtime/media.d.ts +6 -10
- package/dist/types/runtime/misc.d.ts +4 -1
- package/dist/types/runtime/net.d.ts +3 -2
- package/dist/types/runtime/touch.d.ts +32 -0
- package/dist/types/scene/defineScene.d.ts +43 -2
- package/dist/types/scene/editor.d.ts +52 -0
- package/dist/types/scene/gizmos.d.ts +7 -4
- package/dist/types/ui/NativeView.d.ts +6 -4
- package/dist/types/ui/UI.d.ts +1 -1
- package/dist/types/ui/UIBottomSheet.d.ts +6 -12
- package/dist/types/ui/UIButton.d.ts +12 -14
- package/dist/types/ui/UIContainer.d.ts +0 -6
- package/dist/types/ui/UIImage.d.ts +1 -4
- package/dist/types/ui/UIInput.d.ts +8 -24
- package/dist/types/ui/UIModal.d.ts +0 -2
- package/dist/types/ui/UINode.d.ts +70 -56
- package/dist/types/ui/UIPager.d.ts +28 -27
- package/dist/types/ui/UIPopover.d.ts +0 -2
- package/dist/types/ui/UIScreen.d.ts +12 -17
- package/dist/types/ui/UIScrollable.d.ts +1 -4
- package/dist/types/ui/UIText.d.ts +0 -2
- package/dist/types/ui/UIVideo.d.ts +3 -5
- package/dist/types/ui/UIVirtualizedList.d.ts +14 -16
- package/dist/types/ui/UIWidget.d.ts +10 -9
- package/dist/types/ui/colorKeys.gen.d.ts +9 -0
- package/dist/types/ui/presentable.d.ts +46 -32
- package/dist/types/ui/router.d.ts +18 -7
- package/dist/types/ui/styleColor.d.ts +1 -0
- package/dist/types/ui/transitions.d.ts +18 -0
- package/dist/types/ui/tree.d.ts +75 -0
- package/dist/types/version.d.ts +10 -0
- package/dist/types.json +1 -1
- package/package.json +12 -3
- package/prompts/2d.md +2 -6
- package/prompts/3d.md +1 -5
- package/prompts/README.md +1 -1
- package/prompts/canvas.md +9 -8
- package/prompts/compose.ts +1 -1
- package/prompts/core.md +3 -3
- package/prompts/design.md +1 -1
- package/prompts/dist/2d-game.md +473 -239
- package/prompts/dist/3d-app.md +553 -205
- package/prompts/dist/ar-app.md +435 -202
- package/prompts/dist/design.md +113 -95
- package/prompts/dist/ui-app.md +386 -170
- package/prompts/ui-design.md +2 -3
- package/prompts/ui.md +45 -37
- package/src/animate/tween/Animation.ts +34 -150
- package/src/animate/tween/Timeline.ts +175 -175
- package/src/animate/tween/animateValue.ts +6 -3
- package/src/animate/tween/easing.ts +10 -3
- package/src/animate/tween/spec.ts +41 -15
- package/src/audio/Sound.ts +3 -3
- package/src/audio/audio.ts +2 -1
- package/src/bridges/2d.d.ts +317 -0
- package/src/bridges/app.d.ts +91 -0
- package/src/bridges/audio.d.ts +97 -0
- package/src/bridges/canvas.d.ts +79 -0
- package/src/bridges/device.d.ts +72 -0
- package/src/bridges/fetch.d.ts +80 -0
- package/src/bridges/files.d.ts +70 -0
- package/src/bridges/gl.d.ts +1133 -0
- package/src/bridges/input.d.ts +72 -0
- package/src/bridges/media.d.ts +55 -0
- package/src/bridges/nav.d.ts +71 -0
- package/src/bridges/net.d.ts +52 -0
- package/src/bridges/service.d.ts +52 -0
- package/src/bridges/socket.d.ts +31 -0
- package/src/bridges/storage.d.ts +33 -0
- package/src/bridges/tree.d.ts +301 -0
- package/src/bridges/types.d.ts +49 -0
- package/src/canvas/Canvas.ts +114 -159
- package/src/canvas/gen/cssColor.ts +224 -0
- package/src/canvas/gen/recorder.ts +212 -0
- package/src/canvas/gen/spec.ts +201 -0
- package/src/chisel.ts +193 -0
- package/src/compile/assetMacro.ts +1 -1
- package/src/compile/bundler.ts +11 -2
- package/src/compile/compileProject.ts +43 -4
- package/src/compile/fontMacro.ts +3 -4
- package/src/compile/header.ts +26 -5
- package/src/compile/index.ts +3 -1
- package/src/compile/liteMaterial.ts +1 -1
- package/src/compile/sceneEditor.ts +11 -26
- package/src/core/color.ts +73 -30
- package/src/core/pins.ts +51 -0
- package/src/core/signals.ts +8 -1
- package/src/g2/CharacterController2D.ts +3 -3
- package/src/g2/Node2D.ts +57 -39
- package/src/g2/Physics2D.ts +2 -2
- package/src/g2/Scene2D.ts +35 -23
- package/src/g2/Texture2D.ts +1 -1
- package/src/g2/loop.ts +4 -4
- package/src/gl/CameraPlace.ts +52 -52
- package/src/gl/Foliage.ts +72 -17
- package/src/gl/Geometry.ts +1 -2
- package/src/gl/Light.ts +10 -0
- package/src/gl/Lightmap.ts +45 -29
- package/src/gl/Material.ts +95 -51
- package/src/gl/Mesh.ts +120 -120
- package/src/gl/Model.ts +21 -16
- package/src/gl/Node.ts +91 -24
- package/src/gl/Particles.ts +1 -1
- package/src/gl/Scene.ts +100 -44
- package/src/gl/Texture.ts +8 -7
- package/src/gl/animation/AnimationClip.ts +1 -1
- package/src/gl/animation/DynamicBone.ts +482 -482
- package/src/gl/animation/Locomotion.ts +8 -3
- package/src/gl/nav/NavMesh.ts +3 -4
- package/src/gl/physics/Physics.ts +2 -2
- package/src/gl/physics/physicsEvents.ts +3 -3
- package/src/gl/scenarios.ts +291 -291
- package/src/gl/terrain/Terrain.ts +4 -5
- package/src/gl/touch.ts +14 -15
- package/src/host.d.ts +2 -3
- package/src/inject.editor.ts +7 -0
- package/src/inject.ts +13 -16
- package/src/net/core.ts +6 -5
- package/src/net/index.ts +1 -1
- package/src/net/replication.ts +1 -1
- package/src/plugin.ts +191 -0
- package/src/plugins/gen/camera/contract.d.ts +27 -0
- package/src/plugins/gen/camera/sdk/camera.gen.ts +46 -0
- package/src/plugins/gen/camera/sdk/camera.ts +57 -0
- package/src/plugins/gen/geolocation/contract.d.ts +50 -0
- package/src/plugins/gen/geolocation/sdk/geolocation.gen.ts +54 -0
- package/src/plugins/{geolocation.ts → gen/geolocation/sdk/geolocation.ts} +22 -43
- package/src/plugins/gen/map/contract.d.ts +144 -0
- package/src/plugins/gen/map/sdk/map.gen.ts +88 -0
- package/src/plugins/{map.ts → gen/map/sdk/map.ts} +68 -102
- package/src/plugins/gen/push/contract.d.ts +61 -0
- package/src/plugins/gen/push/sdk/push.gen.ts +60 -0
- package/src/plugins/gen/push/sdk/push.ts +105 -0
- package/src/plugins/gen/qr-scanner/contract.d.ts +16 -0
- package/src/plugins/gen/qr-scanner/sdk/qr-scanner.gen.ts +29 -0
- package/src/plugins/gen/qr-scanner/sdk/qr-scanner.ts +52 -0
- package/src/plugins/permission.ts +5 -4
- package/src/runtime/app.ts +20 -9
- package/src/runtime/appEvents.ts +5 -4
- package/src/runtime/channel.ts +18 -15
- package/src/runtime/clipboard.ts +4 -3
- package/src/runtime/datetime.ts +2 -1
- package/src/runtime/device.ts +17 -15
- package/src/runtime/fetch.ts +30 -20
- package/src/runtime/files.ts +16 -15
- package/src/runtime/input.ts +12 -10
- package/src/runtime/media.ts +50 -46
- package/src/runtime/misc.ts +7 -3
- package/src/runtime/net.ts +8 -7
- package/src/runtime/rpc.ts +1 -3
- package/src/runtime/service.ts +19 -14
- package/src/runtime/share.ts +4 -3
- package/src/runtime/storage.ts +6 -4
- package/src/runtime/touch.ts +32 -0
- package/src/scene/defineScene.ts +61 -365
- package/src/scene/editor.ts +408 -0
- package/src/scene/editorPlugins.ts +3 -3
- package/src/scene/gizmos.ts +15 -9
- package/src/server/db/marci/query.ts +1 -1
- package/src/server/host.ts +1 -1
- package/src/server/runtime.ts +1 -1
- package/src/ui/NativeView.ts +72 -25
- package/src/ui/UI.ts +3 -3
- package/src/ui/UIBottomSheet.ts +16 -17
- package/src/ui/UIButton.ts +54 -16
- package/src/ui/UIContainer.ts +0 -6
- package/src/ui/UIImage.ts +34 -30
- package/src/ui/UIInput.ts +29 -37
- package/src/ui/UIModal.ts +1 -3
- package/src/ui/UINode.ts +347 -297
- package/src/ui/UIPager.ts +93 -78
- package/src/ui/UIPopover.ts +0 -2
- package/src/ui/UIScreen.ts +41 -36
- package/src/ui/UIScrollable.ts +19 -13
- package/src/ui/UISpacer.ts +1 -1
- package/src/ui/UITabs.ts +8 -6
- package/src/ui/UIText.ts +7 -17
- package/src/ui/UIVideo.ts +30 -27
- package/src/ui/UIVirtualizedList.ts +58 -59
- package/src/ui/UIWidget.ts +38 -18
- package/src/ui/colorKeys.gen.ts +37 -0
- package/src/ui/fonts.ts +2 -2
- package/src/ui/presentable.ts +59 -42
- package/src/ui/router.ts +52 -35
- package/src/ui/styleColor.ts +56 -0
- package/src/ui/theme.ts +7 -6
- package/src/ui/transitions.ts +249 -0
- package/src/ui/tree.ts +346 -0
- package/src/version.ts +24 -0
- package/tests/helpers/engineWorld.ts +11 -0
- package/tests/helpers/fakeTree.ts +353 -0
- package/tests/helpers/hostStubs.ts +31 -0
- package/tests/helpers/index.ts +14 -0
- package/tests/helpers/memoryMarci.ts +124 -0
- package/tests/helpers/phases.ts +23 -0
- package/tests/helpers/preload.ts +18 -0
- package/tests/helpers/stubApp.ts +2 -0
- package/tests/helpers/stubDevice.ts +2 -0
- package/tests/helpers/stubFetch.ts +2 -0
- package/tests/helpers/stubInput.ts +2 -0
- package/dist/inject.js +0 -4629
- package/dist/types/core/registry.d.ts +0 -7
- package/dist/types/plugins/camera.d.ts +0 -25
- package/dist/types/plugins/push.d.ts +0 -46
- package/src/bridges.d.ts +0 -1769
- package/src/compile/__tests__/assetIconMacro.test.ts +0 -219
- package/src/compile/__tests__/assetMacro.test.ts +0 -100
- package/src/compile/__tests__/assetName.test.ts +0 -55
- package/src/compile/__tests__/compile.test.ts +0 -310
- package/src/compile/__tests__/detectEntry.test.ts +0 -151
- package/src/compile/__tests__/fontMacro.test.ts +0 -199
- package/src/compile/__tests__/serverSplit.test.ts +0 -27
- package/src/core/__tests__/stateMachine.test.ts +0 -132
- package/src/core/registry.ts +0 -23
- package/src/plugins/camera.ts +0 -81
- package/src/plugins/push.ts +0 -132
- package/src/plugins/qr.ts +0 -73
package/src/gl/Lightmap.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Baked lighting for static 3D geometry (
|
|
1
|
+
// Baked lighting for static 3D geometry (engines/bake). A scene FILE does all of this by
|
|
2
2
|
// itself: `env: { lightmap: { data, light, aux } }` marks the level as baked, every model/mesh node
|
|
3
3
|
// that no Physics aspect moves is a static (keys = node paths, `lightmap: true|false` overrides),
|
|
4
4
|
// and the runtime applies the bake when the level finishes loading (scene/defineScene.ts).
|
|
@@ -39,12 +39,13 @@
|
|
|
39
39
|
// is untouched — it is the same material reading other pages). The bake's `--debug-dir` keeps the
|
|
40
40
|
// float layers `lecodes lightmap inspect` reads.
|
|
41
41
|
|
|
42
|
+
import { Color } from "../core/color"
|
|
42
43
|
import { fetch } from "../runtime/fetch"
|
|
43
44
|
import { Texture } from "./Texture"
|
|
44
45
|
import { Material } from "./Material"
|
|
45
46
|
import { Mesh } from "./Mesh"
|
|
46
47
|
import { Model } from "./Model"
|
|
47
|
-
import { Node,
|
|
48
|
+
import { Node, nodes } from "./Node"
|
|
48
49
|
import { Light } from "./Light"
|
|
49
50
|
import type { Scene } from "./Scene"
|
|
50
51
|
|
|
@@ -266,12 +267,13 @@ export class Lightmap {
|
|
|
266
267
|
// by entity id through the bridge, not `node.children`: the bundler drops that accessor from a build where
|
|
267
268
|
// nothing else reads it (the leash in lightcheck, 2026-09-18: "children" was undefined on a Mesh)
|
|
268
269
|
const walk = (id: number): void => {
|
|
269
|
-
const n =
|
|
270
|
+
const n = nodes.get(id)
|
|
270
271
|
if (n instanceof Model || n instanceof Mesh) {
|
|
271
272
|
if (on) Lightmap.movers.add(n); else Lightmap.movers.delete(n)
|
|
272
273
|
if (Lightmap.volumeOn || !on) _creator.lightVolumeTrack?.(id, on)
|
|
273
274
|
}
|
|
274
|
-
|
|
275
|
+
const count = _creator.getChildCount(id)
|
|
276
|
+
for (let i = 0; i < count; i++) walk(_creator.getChild(id, i))
|
|
275
277
|
}
|
|
276
278
|
walk(node.id)
|
|
277
279
|
}
|
|
@@ -285,8 +287,7 @@ export class Lightmap {
|
|
|
285
287
|
try {
|
|
286
288
|
const resp = await fetch(files.volume, { useOnce: true })
|
|
287
289
|
if (resp.status >= 400) { console.warn(`Lightmap: no light grid at ${files.volume} (HTTP ${resp.status})`); return }
|
|
288
|
-
const ok = _creator.lightVolumeLoad((resp as unknown as {
|
|
289
|
-
resp.dispose()
|
|
290
|
+
const ok = _creator.lightVolumeLoad((resp as unknown as { _h: Handle })._h.id)
|
|
290
291
|
if (!ok) { console.warn(`Lightmap: ${files.volume} is not a light grid — rebake`); return }
|
|
291
292
|
} catch (e) {
|
|
292
293
|
console.warn(`Lightmap: could not read ${files.volume}: ${String(e)}`)
|
|
@@ -313,10 +314,31 @@ export class Lightmap {
|
|
|
313
314
|
Lightmap.bound = null
|
|
314
315
|
if (Lightmap.balls) { Lightmap.balls.destroy(); Lightmap.balls = null }
|
|
315
316
|
_creator.lightmapSetOptions?.(1, 0)
|
|
317
|
+
Lightmap.sunArgs = null
|
|
316
318
|
_creator.lightmapSunSet?.(0, 1, 0, 0, 0, 0, false)
|
|
317
319
|
_creator.lightmapDebug?.(0)
|
|
318
320
|
}
|
|
319
321
|
|
|
322
|
+
/** THE MOVERS' SUN SHADOWS ON THE BAKED STATICS (a bot, a barrel, the player's rifle darkening the ground under
|
|
323
|
+
* it): the lightmap shader reads the real-time shadow map and the bake's sun map for it, per pixel. Off, a baked
|
|
324
|
+
* surface takes no real-time shadow at all — the cheapest lighting tier (some 4 ms of a 16 ms 720p frame on an
|
|
325
|
+
* integrated GPU, the yard 2026-09-26); a mover still shades ITSELF and the other movers. Live: it rebinds the
|
|
326
|
+
* level's material instances at once. */
|
|
327
|
+
static get moverShadows(): boolean { return Lightmap._moverShadows }
|
|
328
|
+
static set moverShadows(on: boolean) {
|
|
329
|
+
if (Lightmap._moverShadows === on) return
|
|
330
|
+
Lightmap._moverShadows = on
|
|
331
|
+
Lightmap.applySun()
|
|
332
|
+
}
|
|
333
|
+
private static _moverShadows = true
|
|
334
|
+
/** The loaded bake's sun (TO the sun ×3, its illuminance rgb, the sun map's levels), null without a sun map. */
|
|
335
|
+
private static sunArgs: [number, number, number, number, number, number, number] | null = null
|
|
336
|
+
private static applySun(): void {
|
|
337
|
+
const a = Lightmap.sunArgs
|
|
338
|
+
if (a && Lightmap._moverShadows) _creator.lightmapSunSet?.(a[0], a[1], a[2], a[3], a[4], a[5], true, a[6])
|
|
339
|
+
else _creator.lightmapSunSet?.(0, 1, 0, 0, 0, 0, false)
|
|
340
|
+
}
|
|
341
|
+
|
|
320
342
|
/** Apply a bake — or, under `lecodes lightmap bake`, run it. Resolves to null when nothing was
|
|
321
343
|
* applied (no bake yet, a host without the feature, bake mode). */
|
|
322
344
|
private static scene: Scene | null = null
|
|
@@ -333,7 +355,6 @@ export class Lightmap {
|
|
|
333
355
|
const resp = await fetch(files.data, { useOnce: true })
|
|
334
356
|
if (resp.status >= 400) { console.warn(`Lightmap: no bake at ${files.data} (HTTP ${resp.status}) — run \`lecodes lightmap bake\``); return null }
|
|
335
357
|
data = resp.json()
|
|
336
|
-
resp.dispose()
|
|
337
358
|
} catch (e) {
|
|
338
359
|
console.warn(`Lightmap: could not read ${files.data}: ${String(e)}`)
|
|
339
360
|
return null
|
|
@@ -357,8 +378,8 @@ export class Lightmap {
|
|
|
357
378
|
_creator.lightmapSetOptions?.(lightScale, data.emissiveNits ?? 0)
|
|
358
379
|
// a mover's sun shadow on the statics: only a bake that wrote the sun map under its aux pages
|
|
359
380
|
const sun = data.sunMap ? data.sun : null
|
|
360
|
-
|
|
361
|
-
|
|
381
|
+
Lightmap.sunArgs = sun ? [-sun.direction[0], -sun.direction[1], -sun.direction[2], sun.color[0] * sun.lux, sun.color[1] * sun.lux, sun.color[2] * sun.lux, data.sunMapLevels ?? 1] : null
|
|
382
|
+
Lightmap.applySun()
|
|
362
383
|
// the reflection probes: the atlas loads like a page, the grid travels as numbers; level-wide on the host
|
|
363
384
|
let probes = 0
|
|
364
385
|
let probeAtlas: Texture | undefined
|
|
@@ -370,7 +391,7 @@ export class Lightmap {
|
|
|
370
391
|
else {
|
|
371
392
|
probeAtlas = await Texture.load(files.probes, { fullSize: true, srgb: false })
|
|
372
393
|
// which probes a pixel reflects is in the aux pages (their probe maps); the host needs the atlas and the records' range
|
|
373
|
-
probes = _creator.lightmapProbesSet(probeAtlas.
|
|
394
|
+
probes = _creator.lightmapProbesSet(probeAtlas._h.id, pb.oct, pb.levels, pb.perRow, pb.count,
|
|
374
395
|
pb.encode[0], pb.encode[1], pb.encode[2], pb.encode[3], pb.encode[4], pb.encode[5])
|
|
375
396
|
if (probes !== pb.count) console.warn(`Lightmap: the host took ${probes} of ${pb.count} probe(s) — rebake`)
|
|
376
397
|
}
|
|
@@ -400,9 +421,9 @@ export class Lightmap {
|
|
|
400
421
|
for (const [ k, inst ] of groups) {
|
|
401
422
|
if (inst.small && inst.light && _creator.lightmapObjectApply && n > 0) {
|
|
402
423
|
const l = inst.light
|
|
403
|
-
took += _creator.lightmapObjectApply(node.id, light[0].
|
|
424
|
+
took += _creator.lightmapObjectApply(node.id, light[0]._h.id, aux[0]._h.id, Float32Array.from([ ...l.cube.flat(), probeId(l.probes[0]), probeId(l.probes[1]), l.probes[2] ]), k)
|
|
404
425
|
} else if (inst.receiver && inst.st && inst.page !== undefined && inst.page < n) {
|
|
405
|
-
took += _creator.lightmapApply(node.id, light[inst.page].
|
|
426
|
+
took += _creator.lightmapApply(node.id, light[inst.page]._h.id, aux[inst.page]._h.id, inst.st[0], inst.st[1], inst.st[2], inst.st[3], k)
|
|
406
427
|
}
|
|
407
428
|
}
|
|
408
429
|
if (took === 0) console.warn(`Lightmap: ${e.key} (${groups.size} groups) was not loaded lightmapped, or the host binds no groups (rebuild it)`)
|
|
@@ -417,7 +438,7 @@ export class Lightmap {
|
|
|
417
438
|
if (!l || !_creator.lightmapObjectApply || n === 0) continue
|
|
418
439
|
if (e.mesh) e.mesh.material = Lightmap.meshMaterial(e.mesh)
|
|
419
440
|
const d = Float32Array.from([ ...l.cube.flat(), probeId(l.probes[0]), probeId(l.probes[1]), l.probes[2] ])
|
|
420
|
-
if (_creator.lightmapObjectApply(node.id, light[0].
|
|
441
|
+
if (_creator.lightmapObjectApply(node.id, light[0]._h.id, aux[0]._h.id, d) > 0) smallLit++
|
|
421
442
|
continue
|
|
422
443
|
}
|
|
423
444
|
if (!r && declined.has(e.key)) continue // glass / a declined material: the bake knows it as an occluder - the light grid lights it (loadVolume)
|
|
@@ -425,7 +446,7 @@ export class Lightmap {
|
|
|
425
446
|
const st = r.st!
|
|
426
447
|
// a Mesh receiver: its lit material becomes a lightmap material carrying the same colour / map / factors
|
|
427
448
|
if (e.mesh) e.mesh.material = Lightmap.meshMaterial(e.mesh)
|
|
428
|
-
const took = _creator.lightmapApply(node.id, light[r.page!].
|
|
449
|
+
const took = _creator.lightmapApply(node.id, light[r.page!]._h.id, aux[r.page!]._h.id, st[0], st[1], st[2], st[3])
|
|
429
450
|
if (took === 0) { console.warn(`Lightmap: ${e.key} was not loaded lightmapped (Model.load { lightmap: true } / Material.lightmap())`); continue }
|
|
430
451
|
applied++
|
|
431
452
|
}
|
|
@@ -479,12 +500,12 @@ export class Lightmap {
|
|
|
479
500
|
const parts = Lightmap.groupsOf(b.data.instances)
|
|
480
501
|
for (const [ id, e ] of b.byEntity) {
|
|
481
502
|
for (const [ k, inst ] of parts.get(e.key) ?? []) {
|
|
482
|
-
if (inst.receiver && inst.st && inst.page !== undefined && inst.page < b.pages) _creator.lightmapApply(id, tex[inst.page].
|
|
503
|
+
if (inst.receiver && inst.st && inst.page !== undefined && inst.page < b.pages) _creator.lightmapApply(id, tex[inst.page]._h.id, b.aux[inst.page]._h.id, inst.st[0], inst.st[1], inst.st[2], inst.st[3], k)
|
|
483
504
|
}
|
|
484
505
|
const r = b.rects.get(e.key)
|
|
485
506
|
if (!r || r.page! >= b.pages) continue
|
|
486
507
|
const st = r.st!
|
|
487
|
-
_creator.lightmapApply(id, tex[r.page!].
|
|
508
|
+
_creator.lightmapApply(id, tex[r.page!]._h.id, b.aux[r.page!]._h.id, st[0], st[1], st[2], st[3])
|
|
488
509
|
}
|
|
489
510
|
console.log(`[lightmap] showing the ${kind} pages`)
|
|
490
511
|
}
|
|
@@ -508,7 +529,7 @@ export class Lightmap {
|
|
|
508
529
|
ball.name = `probe#${k}`
|
|
509
530
|
ball.position = [ x, y, z ]
|
|
510
531
|
holder.add(ball)
|
|
511
|
-
_creator.lightmapApply(ball.id, b.light[0].
|
|
532
|
+
_creator.lightmapApply(ball.id, b.light[0]._h.id, b.aux[0]._h.id, 0, 0, 0, 0)
|
|
512
533
|
m.set("probeBall", k + 1) // probe k alone, unoccluded
|
|
513
534
|
n++
|
|
514
535
|
}
|
|
@@ -527,15 +548,13 @@ export class Lightmap {
|
|
|
527
548
|
static probe(screenX: number, screenY: number): LightmapProbe | null {
|
|
528
549
|
const b = Lightmap.bound
|
|
529
550
|
if (!b || !_creator.pickTriangle) return null
|
|
530
|
-
type Hit = { entity: number, nodeName: string, material: string, point: number[], distance: number, uv1?: number
|
|
551
|
+
type Hit = { entity: number, nodeName: string, material: string, point: number[], distance: number, uv1?: [number, number], lightmapGroup?: number }
|
|
531
552
|
// the nearest hit over the registered statics: one pick per entry (the all-instances pick, entity 0, walks
|
|
532
553
|
// every GLB instance including the off-scene templates and is what a scene file's clones confuse)
|
|
533
554
|
let hit: Hit | null = null
|
|
534
555
|
for (const id of b.byEntity.keys()) {
|
|
535
|
-
const
|
|
536
|
-
if (!
|
|
537
|
-
const h = JSON.parse(j) as Hit
|
|
538
|
-
if (!hit || h.distance < hit.distance) hit = h
|
|
556
|
+
const h: Hit | null = _creator.pickTriangle(id, screenX, screenY)
|
|
557
|
+
if (h && (!hit || h.distance < hit.distance)) hit = h
|
|
539
558
|
}
|
|
540
559
|
if (!hit) { console.log(`[probe] nothing baked under ${screenX.toFixed(0)},${screenY.toFixed(0)}`); return null }
|
|
541
560
|
const entry = b.byEntity.get(hit.entity)
|
|
@@ -553,19 +572,16 @@ export class Lightmap {
|
|
|
553
572
|
}
|
|
554
573
|
}
|
|
555
574
|
|
|
556
|
-
/** A
|
|
557
|
-
private static
|
|
558
|
-
|
|
559
|
-
if (c.length === 7) return c + "ff"
|
|
560
|
-
if (c.length === 4) return "#" + c[1] + c[1] + c[2] + c[2] + c[3] + c[3] + "ff"
|
|
561
|
-
return c
|
|
575
|
+
/** A material's colour uniform as "#rrggbbaa" (the float4 uniform path sets its alpha); white without one. */
|
|
576
|
+
private static colorUniform(c: unknown): string {
|
|
577
|
+
return typeof c === "string" ? Color.toHexString(c) : "#ffffffff"
|
|
562
578
|
}
|
|
563
579
|
|
|
564
580
|
/** The lightmap material for a static Mesh, from whatever lit material it carried (colour, map, roughness, metallic). */
|
|
565
581
|
private static meshMaterial(mesh: Mesh): Material {
|
|
566
582
|
const u = mesh.material?.uniforms ?? {}
|
|
567
583
|
const m = Material.lightmap()
|
|
568
|
-
m.set("baseColorFactor", Lightmap.
|
|
584
|
+
m.set("baseColorFactor", Lightmap.colorUniform(u.baseColor ?? u.color))
|
|
569
585
|
if (u.baseColorMap instanceof Texture) m.set("baseColorMap", u.baseColorMap)
|
|
570
586
|
if (typeof u.roughness === "number") m.set("roughnessFactor", u.roughness)
|
|
571
587
|
if (typeof u.metallic === "number") m.set("metallicFactor", u.metallic)
|
package/src/gl/Material.ts
CHANGED
|
@@ -15,16 +15,25 @@ const isCanvasSource = (v: unknown): v is Canvas =>
|
|
|
15
15
|
|
|
16
16
|
type UniformValue = string | Texture | number | boolean | number[] | Float32Array | null
|
|
17
17
|
|
|
18
|
+
// A color string → the RGB or the RGBA slot. The arity is semantic (engines/gl material.cpp): RGB on a
|
|
19
|
+
// float4 keeps the instance's alpha, RGBA sets it — so a color that carries alpha (below 1, or written
|
|
20
|
+
// '#rgba' / '#rrggbbaa') goes RGBA, every other one RGB. Not a color → throws with the value.
|
|
21
|
+
const applyColorUniform = (id: number, key: string, value: string): void => {
|
|
22
|
+
const rgba = Color.toPackedRgba(value)
|
|
23
|
+
const t = value.trim()
|
|
24
|
+
if ((rgba & 0xff) !== 0xff || (t[0] === "#" && (t.length === 5 || t.length === 9))) _creator.setUniformRgba(id, key, rgba)
|
|
25
|
+
else _creator.setUniformRgb(id, key, rgba >>> 8)
|
|
26
|
+
}
|
|
27
|
+
|
|
18
28
|
const applyUniform = (id: number, key: string, value: UniformValue): void => {
|
|
19
29
|
if (typeof value === "string") {
|
|
20
|
-
|
|
21
|
-
else _creator.setUniformRgb(id, key, parseInt(value.slice(1), 16))
|
|
30
|
+
applyColorUniform(id, key, value)
|
|
22
31
|
} else if (typeof value === "number") {
|
|
23
32
|
_creator.setUniformFloat(id, key, value)
|
|
24
33
|
} else if (typeof value === "boolean") {
|
|
25
34
|
_creator.setUniformBoolean(id, key, value)
|
|
26
35
|
} else if (value instanceof Texture) {
|
|
27
|
-
_creator.setUniformTexture(id, key, value.
|
|
36
|
+
_creator.setUniformTexture(id, key, value._h.id, value.wrapS, value.wrapT)
|
|
28
37
|
} else if (value instanceof Float32Array) {
|
|
29
38
|
_creator.setUniformArray(id, key, value)
|
|
30
39
|
} else if (Array.isArray(value)) {
|
|
@@ -163,26 +172,30 @@ export type LitMaterialOptions = MaterialColorOptions & MaterialStateOptions & {
|
|
|
163
172
|
|
|
164
173
|
|
|
165
174
|
export class Material {
|
|
166
|
-
/** @internal
|
|
167
|
-
|
|
175
|
+
/** @internal The material instance's owned handle (`_h.id` = the engine id). An instance the
|
|
176
|
+
* engine created itself (a glTF part's) is wrapped from its number and never freed by us. */
|
|
177
|
+
_h: Handle
|
|
168
178
|
readonly shader: FetchResponse | "unknown"
|
|
169
179
|
readonly uniforms: Record<string, UniformValue>
|
|
170
180
|
|
|
171
181
|
private _colorKey: string | null = null
|
|
172
182
|
|
|
173
|
-
constructor(shader: FetchResponse | number) {
|
|
183
|
+
constructor(shader: FetchResponse | Handle | number) {
|
|
174
184
|
if (typeof shader === "number") {
|
|
175
185
|
this.shader = "unknown"
|
|
176
|
-
this.
|
|
186
|
+
this._h = { id: shader } // an engine-owned instance: not ours to free
|
|
187
|
+
} else if ("_h" in (shader as any)) {
|
|
188
|
+
this.shader = shader as FetchResponse
|
|
189
|
+
this._h = _creator.createMaterial((shader as any)._h.id) // owned: dropped and unused → freed
|
|
177
190
|
} else {
|
|
178
|
-
this.shader =
|
|
179
|
-
this.
|
|
191
|
+
this.shader = "unknown"
|
|
192
|
+
this._h = shader as Handle // an instance the runtime already wrapped for us
|
|
180
193
|
}
|
|
181
194
|
const store: Record<string, UniformValue> = {}
|
|
182
|
-
const id = () => this.
|
|
195
|
+
const id = () => this._h.id
|
|
183
196
|
this.uniforms = new Proxy(store, {
|
|
184
197
|
get: (t, k: string) => t[k],
|
|
185
|
-
set: (t, k: string, v: UniformValue) => {
|
|
198
|
+
set: (t, k: string, v: UniformValue) => { applyUniform(id(), k, v); t[k] = v; return true },
|
|
186
199
|
})
|
|
187
200
|
}
|
|
188
201
|
|
|
@@ -199,18 +212,18 @@ export class Material {
|
|
|
199
212
|
/** Depth test against the scene (write-only; see `MaterialDepthOptions`). A host that predates
|
|
200
213
|
* the call leaves the material as the shader has it. */
|
|
201
214
|
set depthTest(on: boolean) {
|
|
202
|
-
if (_creator.setMaterialDepthTest) _creator.setMaterialDepthTest(this.
|
|
215
|
+
if (_creator.setMaterialDepthTest) _creator.setMaterialDepthTest(this._h.id, on)
|
|
203
216
|
else warnOnce("depthTest", "[material] this host has no setMaterialDepthTest — depthTest ignored")
|
|
204
217
|
}
|
|
205
218
|
/** Depth write (write-only; see `MaterialStateOptions`). */
|
|
206
219
|
set depthWrite(on: boolean) {
|
|
207
|
-
if (_creator.setMaterialDepthWrite) _creator.setMaterialDepthWrite(this.
|
|
220
|
+
if (_creator.setMaterialDepthWrite) _creator.setMaterialDepthWrite(this._h.id, on)
|
|
208
221
|
else warnOnce("depthWrite", "[material] this host has no setMaterialDepthWrite — depthWrite ignored")
|
|
209
222
|
}
|
|
210
223
|
/** Both faces drawn (write-only; see `MaterialStateOptions`). */
|
|
211
224
|
set doubleSided(on: boolean) {
|
|
212
225
|
// 0 = no culling, 2 = back faces culled (the shader's usual default)
|
|
213
|
-
if (_creator.setMaterialCulling) _creator.setMaterialCulling(this.
|
|
226
|
+
if (_creator.setMaterialCulling) _creator.setMaterialCulling(this._h.id, on ? 0 : 2)
|
|
214
227
|
else warnOnce("doubleSided", "[material] this host has no setMaterialCulling — doubleSided ignored")
|
|
215
228
|
}
|
|
216
229
|
/** The stencil test / write (write-only; `null` = back to none). See `MaterialStencil`. */
|
|
@@ -218,7 +231,7 @@ export class Material {
|
|
|
218
231
|
if (!_creator.setMaterialStencil) { warnOnce("stencil", "[material] this host has no setMaterialStencil — stencil ignored"); return }
|
|
219
232
|
const o = s ?? {}
|
|
220
233
|
const clamp = (v: number | undefined, d: number): number => Math.max(0, Math.min(255, Math.round(v ?? d)))
|
|
221
|
-
_creator.setMaterialStencil(this.
|
|
234
|
+
_creator.setMaterialStencil(this._h.id, !!o.write, STENCIL_TEST.indexOf(o.test ?? "always"), clamp(o.ref, 0),
|
|
222
235
|
STENCIL_OP.indexOf(o.onPass ?? "keep"), STENCIL_OP.indexOf(o.onFail ?? "keep"), STENCIL_OP.indexOf(o.onDepthFail ?? "keep"),
|
|
223
236
|
clamp(o.readMask, 255), clamp(o.writeMask, 255))
|
|
224
237
|
}
|
|
@@ -238,11 +251,20 @@ export class Material {
|
|
|
238
251
|
}
|
|
239
252
|
|
|
240
253
|
// --- factories ---
|
|
254
|
+
// The engine's own materials are asked for by NAME (`_creator.builtinMaterial`): the runtime finds the
|
|
255
|
+
// shader — in the archive its host ships, or in the host's file of that name. Every call below passes
|
|
256
|
+
// a string LITERAL: the compile reads a bundle's preload list out of them (compile/header.ts), so a
|
|
257
|
+
// name that reaches the call through a variable is a material a web host never fetches.
|
|
258
|
+
|
|
259
|
+
/** The material an instance handle is of; a host that carries none under `name` is an error naming it. */
|
|
260
|
+
private static _builtin(h: Handle | null, name: string): Material {
|
|
261
|
+
if (!h) throw new Error(`Material: this host carries no "${name}" material`)
|
|
262
|
+
return new Material(h)
|
|
263
|
+
}
|
|
241
264
|
|
|
242
265
|
/** PBR lit material. */
|
|
243
266
|
static lit(options: LitMaterialOptions = {}): Material {
|
|
244
|
-
|
|
245
|
-
const m = new Material({ _id: _creatorUtils.fetchLocal("lit.filamat") } as any)
|
|
267
|
+
const m = Material._builtin(_creator.builtinMaterial("lit"), "lit")
|
|
246
268
|
m._colorKey = "baseColor"
|
|
247
269
|
if (options.color !== undefined) m.color = options.color
|
|
248
270
|
if (options.map) m.map = options.map
|
|
@@ -255,16 +277,13 @@ export class Material {
|
|
|
255
277
|
/** Unlit (flat) material — opaque unless `transparent` asks for alpha blending. */
|
|
256
278
|
static unlit(options: UnlitMaterialOptions = {}): Material {
|
|
257
279
|
// Blending is baked into the shader package (a material instance can't change it), so the
|
|
258
|
-
// blended twin is its own
|
|
259
|
-
|
|
260
|
-
// -1; fall back to the opaque one rather than handing createMaterial a bad handle.
|
|
261
|
-
let id = -1
|
|
280
|
+
// blended twin is its own material. A host without it draws opaque.
|
|
281
|
+
let h: Handle | null = null
|
|
262
282
|
if (options.transparent) {
|
|
263
|
-
|
|
264
|
-
if (
|
|
283
|
+
h = _creator.builtinMaterial("unlit-transparent")
|
|
284
|
+
if (!h) console.warn("[material] this host carries no unlit-transparent material — drawing opaque")
|
|
265
285
|
}
|
|
266
|
-
|
|
267
|
-
const m = new Material({ _id: id } as any)
|
|
286
|
+
const m = Material._builtin(h ?? _creator.builtinMaterial("unlit"), "unlit")
|
|
268
287
|
m._colorKey = "baseColor"
|
|
269
288
|
if (options.color !== undefined) m.color = options.color
|
|
270
289
|
if (options.map) m.map = options.map
|
|
@@ -277,10 +296,9 @@ export class Material {
|
|
|
277
296
|
* knob is primed here; JS writes after construction override them. */
|
|
278
297
|
static particles(options: ParticlesMaterialOptions = {}): Material {
|
|
279
298
|
const quad = options.render !== undefined && options.render !== "point"
|
|
280
|
-
// Both fetchLocal literals inline so the compiler's preload header captures the shader names.
|
|
281
299
|
const m = quad
|
|
282
|
-
?
|
|
283
|
-
:
|
|
300
|
+
? Material._builtin(_creator.builtinMaterial("particles-quad"), "particles-quad")
|
|
301
|
+
: Material._builtin(_creator.builtinMaterial("particles"), "particles")
|
|
284
302
|
m.uniforms.sheetSize = _sheetColsRows(options.sheet)
|
|
285
303
|
m.uniforms.emissive = options.emissive ?? 0
|
|
286
304
|
m.uniforms.additive = options.blend === "add" ? 1 : 0
|
|
@@ -301,10 +319,9 @@ export class Material {
|
|
|
301
319
|
* scene behind it. Blended, no depth write, no shadows — the engine keeps the scene depth bound
|
|
302
320
|
* while a set with live decals is on screen. */
|
|
303
321
|
static decal(options: DecalMaterialOptions = {}): Material {
|
|
304
|
-
// Both fetchLocal literals inline so the compiler's preload header captures the shader names.
|
|
305
322
|
const m = options.normalMap
|
|
306
|
-
?
|
|
307
|
-
:
|
|
323
|
+
? Material._builtin(_creator.builtinMaterial("decal-relief"), "decal-relief")
|
|
324
|
+
: Material._builtin(_creator.builtinMaterial("decal"), "decal")
|
|
308
325
|
m.uniforms.time = 0
|
|
309
326
|
m.uniforms.edge = options.edge ?? 0.3
|
|
310
327
|
m.uniforms.angleFade = options.angleFade ?? 0.3
|
|
@@ -321,20 +338,21 @@ export class Material {
|
|
|
321
338
|
|
|
322
339
|
/** Material that samples a VideoPlayer's texture. */
|
|
323
340
|
static video(map?: Texture): Material {
|
|
324
|
-
const m =
|
|
341
|
+
const m = Material._builtin(_creator.builtinMaterial("video"), "video")
|
|
325
342
|
if (map) m.uniforms.baseColorMap = map
|
|
326
343
|
return m
|
|
327
344
|
}
|
|
328
345
|
|
|
329
|
-
/** The lightmap material (
|
|
346
|
+
/** The lightmap material (engines/bake): a lit PBR material whose whole DIFFUSE light is the
|
|
330
347
|
* baked irradiance atlas on UV1 (`lightmapLight`, lux) — the real-time sun, lamps and diffuse IBL do not
|
|
331
348
|
* touch it; the specular IBL stays, occluded by the baked sky visibility. Models take it through
|
|
332
349
|
* `Model.load(…, { lightmap: true })`; a static Mesh gets one from here. Parameters use gltfio's names
|
|
333
350
|
* (`baseColorFactor`, `baseColorMap`, `roughnessFactor`, `metallicFactor`) plus `lightmapLight`, `lightmapAux`,
|
|
334
351
|
* `lightmapST`, `lightScale` (the level-wide value `Lightmap.load` sets) and the debug view knobs. */
|
|
335
352
|
static lightmap(): Material {
|
|
336
|
-
//
|
|
337
|
-
const
|
|
353
|
+
// The lite material is only asked for (200 KB of shader) when the tier wants it.
|
|
354
|
+
const lite = Material.lightmapShading === "lite" ? Material._liteTier(_creator.builtinMaterial("lightmap-lite")) : null
|
|
355
|
+
const m = Material._builtin(lite ?? _creator.builtinMaterial("lightmap"), "lightmap")
|
|
338
356
|
Material._lightmapDefaults(m)
|
|
339
357
|
return m
|
|
340
358
|
}
|
|
@@ -342,18 +360,33 @@ export class Material {
|
|
|
342
360
|
/** `Material.lightmap()`'s masked twin (`blending: masked`, same shader and parameters): what the engine
|
|
343
361
|
* gives a lightmapped model's alpha-MASK materials. The cutoff comes from the glTF material. */
|
|
344
362
|
static lightmapMasked(): Material {
|
|
345
|
-
const
|
|
363
|
+
const lite = Material.lightmapShading === "lite" ? Material._liteTier(_creator.builtinMaterial("lightmap-masked-lite")) : null
|
|
364
|
+
const m = Material._builtin(lite ?? _creator.builtinMaterial("lightmap-masked"), "lightmap-masked")
|
|
346
365
|
Material._lightmapDefaults(m)
|
|
347
366
|
return m
|
|
348
367
|
}
|
|
349
368
|
|
|
369
|
+
/** THE LIGHTMAP SHADER TIER, engine-wide: `"full"` = the target's own blob (desktop: the bicubic atlas fetch, eight
|
|
370
|
+
* probe corners, the sun's highlight on metal), `"lite"` = the blob the phones ship (bilinear fetch, one probe
|
|
371
|
+
* corner, no sun highlight; the movers' shadows on the statics stay) — on an integrated GPU some 30 % less per
|
|
372
|
+
* pixel on a baked level, the picture a phone shows. Read when a lightmap material is FIRST made, which
|
|
373
|
+
* `Model.load({ lightmap })` does once per world: set it BEFORE the level loads (a graphics preset applied at
|
|
374
|
+
* boot, like `Texture.maxSize`); a change afterwards takes the next level. A host without the lite material
|
|
375
|
+
* (a phone: its own blob is that shader already) takes the full one. */
|
|
376
|
+
static lightmapShading: "full" | "lite" = "full"
|
|
377
|
+
/** The lite tier's instance, or — said once — that this host has none and the full shader is used. */
|
|
378
|
+
private static _liteTier(lite: Handle | null): Handle | null {
|
|
379
|
+
if (!lite && !Material._liteWarned) { Material._liteWarned = true; console.warn("Material.lightmapShading = 'lite': this host carries no lite lightmap material — the full shader is used") }
|
|
380
|
+
return lite
|
|
381
|
+
}
|
|
382
|
+
private static _liteWarned = false
|
|
383
|
+
|
|
350
384
|
/** The terrain splat material (docs/terrain-plan.md §1.5): four albedo (+ normal-map) layers blended by
|
|
351
385
|
* a control map on the terrain's own grid, per-layer `tiling` (metres per repeat) / `roughness` /
|
|
352
386
|
* `normalScale` / `triplanar`. `Terrain` builds and owns one per terrain; the uniforms are primed here so an
|
|
353
387
|
* unset layer is white. Lit real-time; a BAKED terrain takes `Material.terrainLightmap()`. */
|
|
354
388
|
static terrain(): Material {
|
|
355
|
-
|
|
356
|
-
const m = new Material({ _id: _creatorUtils.fetchLocal("terrain.filamat") } as any)
|
|
389
|
+
const m = Material._builtin(_creator.builtinMaterial("terrain"), "terrain")
|
|
357
390
|
m.set("tiling", [ 8, 8, 8, 8 ]).set("roughness", [ 1, 1, 1, 1 ]).set("normalScale", [ 0, 0, 0, 0 ]).set("triplanar", [ 0, 0, 0, 0 ])
|
|
358
391
|
m.set("gridSize", [ 2, 2 ]).set("cellSize", 1).set("tint", "#ffffff")
|
|
359
392
|
return m
|
|
@@ -365,8 +398,7 @@ export class Material {
|
|
|
365
398
|
* bound bake it draws black, like any lightmapped static. No reflection probes (the sampler budget): it reflects the
|
|
366
399
|
* sky through the baked sky visibility. */
|
|
367
400
|
static terrainLightmap(): Material {
|
|
368
|
-
|
|
369
|
-
const m = new Material({ _id: _creatorUtils.fetchLocal("terrain-lightmap.filamat") } as any)
|
|
401
|
+
const m = Material._builtin(_creator.builtinMaterial("terrain-lightmap"), "terrain-lightmap")
|
|
370
402
|
m.set("tiling", [ 8, 8, 8, 8 ]).set("roughness", [ 1, 1, 1, 1 ]).set("normalScale", [ 0, 0, 0, 0 ]).set("triplanar", [ 0, 0, 0, 0 ])
|
|
371
403
|
m.set("gridSize", [ 2, 2 ]).set("cellSize", 1).set("tint", "#ffffff")
|
|
372
404
|
m.set("lightmapST", [ 1, 1, 0, 0 ]).set("lightScale", 1).set("debugMode", 0).set("debugParams", [ 0.001, 0, 5, 0 ])
|
|
@@ -386,7 +418,7 @@ export class Material {
|
|
|
386
418
|
* and a per-copy tint. glTF parameters like `Material.lightmap()` plus `wind` / `sway` / `fade` / `benders`,
|
|
387
419
|
* which the engine writes itself. Not baked by the rewritten bake yet. */
|
|
388
420
|
static foliage(): Material {
|
|
389
|
-
const m =
|
|
421
|
+
const m = Material._builtin(_creator.builtinMaterial("foliage"), "foliage")
|
|
390
422
|
Material._glbDefaults(m)
|
|
391
423
|
return m
|
|
392
424
|
}
|
|
@@ -394,7 +426,7 @@ export class Material {
|
|
|
394
426
|
/** `Material.foliage()`'s masked twin (`blending: masked`): what the engine gives a foliage model's alpha-MASK
|
|
395
427
|
* materials — the cards. */
|
|
396
428
|
static foliageMasked(): Material {
|
|
397
|
-
const m =
|
|
429
|
+
const m = Material._builtin(_creator.builtinMaterial("foliage-masked"), "foliage-masked")
|
|
398
430
|
Material._glbDefaults(m)
|
|
399
431
|
return m
|
|
400
432
|
}
|
|
@@ -409,16 +441,28 @@ export class Material {
|
|
|
409
441
|
m.set("lightmapST", [ 1, 1, 0, 0 ]).set("lightScale", 1).set("debugMode", 0).set("debugParams", [ 0.001, 0, 5, 0 ])
|
|
410
442
|
}
|
|
411
443
|
|
|
412
|
-
/** @internal the foliage template pair, like the lightmap one
|
|
413
|
-
*
|
|
414
|
-
static _foliageTemplate(): Material
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
444
|
+
/** @internal the foliage template pair, like the lightmap one; null on a host that carries no foliage
|
|
445
|
+
* tier — `Model.load` then falls back to the lightmap tier. */
|
|
446
|
+
static _foliageTemplate(): Material | null {
|
|
447
|
+
if (Material._folTemplate === undefined) Material._folTemplate = Material._glbTemplate(_creator.builtinMaterial("foliage"))
|
|
448
|
+
return Material._folTemplate
|
|
449
|
+
}
|
|
450
|
+
private static _folTemplate: Material | null | undefined
|
|
451
|
+
static _foliageMaskedTemplate(): Material | null {
|
|
452
|
+
if (Material._folMaskedTemplate === undefined) Material._folMaskedTemplate = Material._glbTemplate(_creator.builtinMaterial("foliage-masked"))
|
|
453
|
+
return Material._folMaskedTemplate
|
|
454
|
+
}
|
|
455
|
+
private static _folMaskedTemplate: Material | null | undefined
|
|
456
|
+
private static _glbTemplate(h: Handle | null): Material | null {
|
|
457
|
+
if (!h) return null
|
|
458
|
+
const m = new Material(h)
|
|
459
|
+
Material._glbDefaults(m)
|
|
460
|
+
return m
|
|
461
|
+
}
|
|
418
462
|
|
|
419
463
|
/** Shadow-catcher material (transparent except where shadows fall). */
|
|
420
464
|
static shadow(color: ColorInput = "#000000aa"): Material {
|
|
421
|
-
const m =
|
|
465
|
+
const m = Material._builtin(_creator.builtinMaterial("shadow"), "shadow")
|
|
422
466
|
m._colorKey = "color"
|
|
423
467
|
m.color = color
|
|
424
468
|
return m
|
|
@@ -435,7 +479,7 @@ export class Material {
|
|
|
435
479
|
if (!(id >= 0)) {
|
|
436
480
|
return Promise.reject(new Error(`Shader not found next to the app (${url}). Compile it with \`lecodes shaders compile\`.`))
|
|
437
481
|
}
|
|
438
|
-
return Promise.resolve(new Material({
|
|
482
|
+
return Promise.resolve(new Material({ _h: { id: id } } as any))
|
|
439
483
|
}
|
|
440
484
|
return fetch(url, { useOnce: true }).then((resp) => {
|
|
441
485
|
if (resp.status >= 400) return Promise.reject(new Error(`Failed to fetch shader from ${url}. HTTP ${resp.status}`))
|
|
@@ -444,7 +488,7 @@ export class Material {
|
|
|
444
488
|
}
|
|
445
489
|
|
|
446
490
|
/** @internal */
|
|
447
|
-
static idOf(material: Material): number { return material.
|
|
491
|
+
static idOf(material: Material): number { return material._h.id }
|
|
448
492
|
}
|
|
449
493
|
|
|
450
494
|
const applyDepth = (m: Material, o: MaterialStateOptions): void => {
|