@jgengine/shell 0.18.1 → 0.21.0
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/CHANGELOG.md +504 -7
- package/dist/GameHost.d.ts +12 -1
- package/dist/GameHost.js +14 -1
- package/dist/GamePlayerShell.js +132 -17
- package/dist/Shell3dPresentation.d.ts +5 -11
- package/dist/Shell3dPresentation.js +73 -11
- package/dist/ShellChrome.d.ts +2 -1
- package/dist/ShellChrome.js +22 -6
- package/dist/ShellHudPresentation.d.ts +20 -2
- package/dist/ShellHudPresentation.js +41 -6
- package/dist/audio/AudioComponents.js +6 -2
- package/dist/audio/audioEngine.d.ts +24 -0
- package/dist/audio/audioEngine.js +231 -83
- package/dist/audio/audioWire.d.ts +3 -0
- package/dist/audio/audioWire.js +9 -3
- package/dist/audio/loopParams.d.ts +39 -0
- package/dist/audio/loopParams.js +43 -0
- package/dist/audio/synthEngine.d.ts +7 -1
- package/dist/audio/synthEngine.js +103 -38
- package/dist/camera/GameCameraRig.js +3 -2
- package/dist/camera/GameFirstPersonCamera.d.ts +6 -4
- package/dist/camera/GameFirstPersonCamera.js +68 -27
- package/dist/camera/Viewports.d.ts +34 -0
- package/dist/camera/Viewports.js +123 -0
- package/dist/camera/cameraRigs.js +76 -62
- package/dist/camera/chaseHeading.d.ts +9 -0
- package/dist/camera/chaseHeading.js +10 -0
- package/dist/camera/customMuzzle.d.ts +14 -0
- package/dist/camera/customMuzzle.js +68 -0
- package/dist/camera/index.d.ts +1 -0
- package/dist/camera/index.js +1 -0
- package/dist/camera/orbitCameraMath.js +2 -2
- package/dist/camera/rigMath.d.ts +82 -4
- package/dist/camera/rigMath.js +133 -15
- package/dist/camera/rigResolve.d.ts +3 -0
- package/dist/camera/rigResolve.js +11 -0
- package/dist/combat/ProjectileModels.d.ts +31 -0
- package/dist/combat/ProjectileModels.js +70 -0
- package/dist/commandSink.d.ts +1 -2
- package/dist/commandSink.js +3 -2
- package/dist/defineGame.d.ts +19 -3
- package/dist/defineGame.js +39 -7
- package/dist/devtools/DevtoolsOverlay.d.ts +2 -69
- package/dist/devtools/DevtoolsOverlay.js +15 -167
- package/dist/devtools/DevtoolsRuntime.d.ts +7 -0
- package/dist/devtools/DevtoolsRuntime.js +61 -0
- package/dist/devtools/agentBridge.d.ts +6 -0
- package/dist/devtools/agentBridge.js +16 -2
- package/dist/devtools/devtoolsReports.d.ts +68 -0
- package/dist/devtools/devtoolsReports.js +100 -0
- package/dist/devtools/latencyInstrumentation.d.ts +3 -0
- package/dist/devtools/latencyInstrumentation.js +11 -0
- package/dist/devtools/playtestProbe.d.ts +4 -0
- package/dist/devtools/playtestProbe.js +22 -0
- package/dist/diagnostics/FpsCounter.d.ts +17 -0
- package/dist/diagnostics/FpsCounter.js +32 -0
- package/dist/drivers/FrameDriver.js +35 -20
- package/dist/drivers/FrameRateLimiter.d.ts +9 -0
- package/dist/drivers/FrameRateLimiter.js +39 -0
- package/dist/drivers/HudOnlyDriver.d.ts +6 -1
- package/dist/drivers/HudOnlyDriver.js +32 -14
- package/dist/environment/DayNightSky.js +5 -5
- package/dist/environment/Daylight.d.ts +35 -1
- package/dist/environment/Daylight.js +98 -28
- package/dist/environment/EnvironmentScene.js +11 -10
- package/dist/environment/VolumetricClouds.js +20 -2
- package/dist/environment/daylightCycle.d.ts +20 -0
- package/dist/environment/daylightCycle.js +59 -7
- package/dist/environment/sunShadowMath.d.ts +45 -0
- package/dist/environment/sunShadowMath.js +50 -0
- package/dist/input/gamepadPoll.d.ts +47 -0
- package/dist/input/gamepadPoll.js +142 -0
- package/dist/input/gamepadSource.d.ts +11 -1
- package/dist/input/gamepadSource.js +117 -71
- package/dist/input/mouseLook.js +2 -1
- package/dist/input/padHaptics.d.ts +17 -0
- package/dist/input/padHaptics.js +32 -0
- package/dist/input/pointerLock.d.ts +11 -0
- package/dist/input/pointerLock.js +19 -0
- package/dist/inputSink.d.ts +6 -6
- package/dist/inputSink.js +60 -21
- package/dist/lazyShell3dPresentation.d.ts +9 -0
- package/dist/lazyShell3dPresentation.js +24 -0
- package/dist/materialOverride.d.ts +42 -0
- package/dist/materialOverride.js +95 -36
- package/dist/multiplayer.d.ts +33 -3
- package/dist/multiplayer.js +105 -22
- package/dist/pointer/pointerService.d.ts +2 -0
- package/dist/pointer/pointerService.js +12 -2
- package/dist/postfx/PostProcessing.d.ts +11 -1
- package/dist/postfx/PostProcessing.js +14 -42
- package/dist/postfx/gradeShader.d.ts +1 -1
- package/dist/postfx/gradeShader.js +30 -2
- package/dist/postfx/shadowPrepass.d.ts +3 -0
- package/dist/postfx/shadowPrepass.js +18 -0
- package/dist/postfx/stylizePass.d.ts +23 -0
- package/dist/postfx/stylizePass.js +216 -0
- package/dist/presentationRecovery.d.ts +75 -0
- package/dist/presentationRecovery.js +204 -0
- package/dist/presentationRecovery3d.d.ts +4 -0
- package/dist/presentationRecovery3d.js +32 -0
- package/dist/presentationResolve.d.ts +8 -1
- package/dist/presentationResolve.js +5 -2
- package/dist/render/CascadedShadows.d.ts +7 -2
- package/dist/render/CascadedShadows.js +98 -20
- package/dist/render/EnvironmentLighting.js +74 -35
- package/dist/render/HairCards.d.ts +7 -0
- package/dist/render/HairCards.js +23 -0
- package/dist/render/SceneModels.js +52 -78
- package/dist/render/SkinnedInstances.d.ts +91 -0
- package/dist/render/SkinnedInstances.js +249 -0
- package/dist/render/TextureFiltering.d.ts +16 -0
- package/dist/render/TextureFiltering.js +64 -0
- package/dist/render/authoredSurfaceMaterial.d.ts +43 -0
- package/dist/render/authoredSurfaceMaterial.js +108 -0
- package/dist/render/displayColor.d.ts +33 -0
- package/dist/render/displayColor.js +138 -0
- package/dist/render/materialAppearanceSignals.d.ts +23 -0
- package/dist/render/materialAppearanceSignals.js +58 -0
- package/dist/render/materialAsset.d.ts +98 -0
- package/dist/render/materialAsset.js +454 -0
- package/dist/render/measureBounds.js +16 -3
- package/dist/render/measureCollisionMesh.js +8 -1
- package/dist/render/modelAssets.d.ts +8 -0
- package/dist/render/modelAssets.js +43 -0
- package/dist/render/modelBindPose.d.ts +5 -0
- package/dist/render/modelBindPose.js +35 -0
- package/dist/render/modelLoad.d.ts +32 -0
- package/dist/render/modelLoad.js +70 -1
- package/dist/render/modelNodeVisibility.d.ts +6 -0
- package/dist/render/modelNodeVisibility.js +18 -0
- package/dist/render/modelRender.d.ts +18 -1
- package/dist/render/modelRender.js +81 -3
- package/dist/render/projectedOverdraw.d.ts +35 -0
- package/dist/render/projectedOverdraw.js +204 -0
- package/dist/render/resolveModel.d.ts +12 -2
- package/dist/render/resolveModel.js +25 -4
- package/dist/render/rigNode.d.ts +26 -0
- package/dist/render/rigNode.js +32 -0
- package/dist/render/spriteRender.d.ts +4 -0
- package/dist/render/spriteRender.js +5 -0
- package/dist/render/staticShapeInstances.d.ts +34 -0
- package/dist/render/staticShapeInstances.js +78 -0
- package/dist/render/useFootIk.d.ts +53 -4
- package/dist/render/useFootIk.js +262 -70
- package/dist/render/useModelAnimation.d.ts +50 -0
- package/dist/render/useModelAnimation.js +245 -99
- package/dist/render/useModelInstance.d.ts +25 -0
- package/dist/render/useModelInstance.js +43 -0
- package/dist/scatter/scatterModels.d.ts +3 -2
- package/dist/scatter/scatterModels.js +15 -7
- package/dist/scene/AuthoredScene.d.ts +23 -6
- package/dist/scene/AuthoredScene.js +46 -9
- package/dist/scene/CityRenderer.js +6 -6
- package/dist/scene/GeneratedAssetRenderer.js +11 -42
- package/dist/settings/QuickControls.js +1 -1
- package/dist/settings/SettingsMenu.js +11 -18
- package/dist/settings/SettingsRuntime.d.ts +5 -0
- package/dist/settings/SettingsRuntime.js +29 -2
- package/dist/settings/appliedSettings.d.ts +9 -3
- package/dist/settings/appliedSettings.js +6 -5
- package/dist/settings/settingsController.d.ts +3 -0
- package/dist/settings/settingsController.js +47 -3
- package/dist/shellControlSuspension.d.ts +18 -0
- package/dist/shellControlSuspension.js +28 -0
- package/dist/shellInputPublication.d.ts +28 -0
- package/dist/shellInputPublication.js +49 -0
- package/dist/shellPointerInput.d.ts +18 -0
- package/dist/shellPointerInput.js +49 -0
- package/dist/structures/BuildingKitBatch.d.ts +3 -1
- package/dist/structures/BuildingKitBatch.js +35 -56
- package/dist/structures/BuildingRenderBudget.d.ts +23 -0
- package/dist/structures/BuildingRenderBudget.js +54 -0
- package/dist/structures/GeneratedBuilding.d.ts +3 -15
- package/dist/structures/GeneratedBuilding.js +33 -22
- package/dist/structures/buildingKitMaterials.d.ts +20 -0
- package/dist/structures/buildingKitMaterials.js +70 -0
- package/dist/structures/buildingSpatialBatch.d.ts +11 -0
- package/dist/structures/buildingSpatialBatch.js +45 -0
- package/dist/structures/buildingSurface.d.ts +21 -0
- package/dist/structures/buildingSurface.js +134 -0
- package/dist/terrain/GrassField.d.ts +2 -1
- package/dist/terrain/GrassField.js +59 -5
- package/dist/terrain/grassGeometry.d.ts +10 -0
- package/dist/terrain/grassGeometry.js +55 -0
- package/dist/terrain/grassMaterial.js +2 -2
- package/dist/terrain/terrainDetailMaterial.d.ts +3 -3
- package/dist/terrain/terrainDetailMaterial.js +7 -7
- package/dist/touch/TouchControlsOverlay.js +4 -4
- package/dist/useShellMultiplayerSync.d.ts +1 -1
- package/dist/useShellMultiplayerSync.js +9 -3
- package/dist/vfx/ParticleField.d.ts +23 -20
- package/dist/vfx/ParticleField.js +168 -50
- package/dist/visibility/CullingProvider.js +2 -62
- package/dist/visibility/cullingDriver.d.ts +9 -0
- package/dist/visibility/cullingDriver.js +68 -0
- package/dist/weather/AuthoredWeatherLayer.d.ts +16 -0
- package/dist/weather/AuthoredWeatherLayer.js +31 -0
- package/dist/weather/DustField.d.ts +2 -1
- package/dist/weather/DustField.js +17 -3
- package/dist/weather/FireSpreadLayer.d.ts +4 -2
- package/dist/weather/FireSpreadLayer.js +5 -11
- package/dist/weather/LightningStrike.d.ts +4 -1
- package/dist/weather/LightningStrike.js +13 -17
- package/dist/weather/RainField.d.ts +2 -1
- package/dist/weather/RainField.js +17 -3
- package/dist/weather/RainImpactField.d.ts +16 -0
- package/dist/weather/RainImpactField.js +89 -0
- package/dist/weather/SnowField.d.ts +2 -1
- package/dist/weather/SnowField.js +17 -3
- package/dist/weather/WeatherLayer.d.ts +11 -2
- package/dist/weather/WeatherLayer.js +30 -6
- package/dist/weather/index.d.ts +2 -0
- package/dist/weather/index.js +2 -0
- package/dist/weather/weatherMath.d.ts +2 -0
- package/dist/weather/weatherMath.js +8 -0
- package/dist/weather/weatherShelter.d.ts +18 -0
- package/dist/weather/weatherShelter.js +75 -0
- package/dist/weather/weatherUniforms.d.ts +40 -1
- package/dist/weather/weatherUniforms.js +29 -9
- package/dist/world/AuthoredFlocks.d.ts +22 -0
- package/dist/world/AuthoredFlocks.js +87 -0
- package/dist/world/StaticObjectModels.d.ts +8 -0
- package/dist/world/StaticObjectModels.js +75 -0
- package/dist/world/WorldHud.d.ts +14 -6
- package/dist/world/WorldHud.js +25 -12
- package/dist/world/WorldParticles.d.ts +10 -23
- package/dist/world/WorldParticles.js +210 -62
- package/dist/world/WorldScene.d.ts +3 -1
- package/dist/world/WorldScene.js +41 -26
- package/dist/world/staticObjectBatches.d.ts +35 -0
- package/dist/world/staticObjectBatches.js +163 -0
- package/dist/world/worldBarSamples.d.ts +37 -33
- package/dist/world/worldBarSamples.js +117 -75
- package/package.json +4 -4
- package/skills/ce-handoff/SKILL.md +2 -2
- package/skills/game-design/SKILL.md +2 -2
- package/skills/jgengine/SKILL.md +28 -6
- package/skills/jgengine/api.md +139 -99
- package/skills/jgengine/capabilities.md +28 -0
- package/skills/jgengine/recipes/minimal-game.md +10 -2
- package/skills/jgengine-ui/SKILL.md +16 -17
- package/skills/jgengine-ui/api.md +603 -365
- package/skills/jgengine-ui/capabilities.md +131 -1
- package/skills/jgengine-ui/reference-primitives.md +179 -0
- package/skills/jgengine-ui/reference.md +4 -81
- package/skills/jgengine-world/SKILL.md +25 -4
- package/skills/jgengine-world/api.md +1045 -655
- package/skills/jgengine-world/capabilities.md +246 -9
- package/skills/jgengine-world/recipes/character-animation.md +62 -0
- package/skills/jgengine-world/recipes/controls.md +48 -0
- package/skills/jgengine-world/recipes/flight-feel.md +76 -0
- package/skills/jgengine-world/recipes/npc-senses.md +55 -0
- package/skills/jgengine-world/recipes/vehicle-feel.md +87 -0
- package/skills/jgengine-world/reference.md +180 -4
- package/skills/jgengine-world/visibility.md +139 -0
- package/skills/level-design/SKILL.md +1 -1
- package/dist/render/testFixtures/proceduralSpriteAtlas.d.ts +0 -15
- package/dist/render/testFixtures/proceduralSpriteAtlas.js +0 -30
- package/dist/render/testFixtures/proceduralTileLayer.d.ts +0 -13
- package/dist/render/testFixtures/proceduralTileLayer.js +0 -30
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import type { ModelMaterialOverride } from "@jgengine/core/game/playableGame";
|
|
3
|
+
import { type MaterialOverrideTextures } from "../materialOverride.js";
|
|
4
|
+
/** Serializable PBR settings for an authored primitive surface. Map roles use the model-material contract. */
|
|
5
|
+
export interface AuthoredSurfaceConfig extends Omit<ModelMaterialOverride, "anisotropy" | "normalScale"> {
|
|
6
|
+
/** Physical metres per texture tile on unit boxes/cylinders, including instance and parent scale. Omit to retain authored UVs. */
|
|
7
|
+
repeatMetres?: number;
|
|
8
|
+
/** Texture sampler policy on owned clones; defaults to repeat on both axes and anisotropy 1. */
|
|
9
|
+
wrapping?: "repeat" | "clamp" | "mirror";
|
|
10
|
+
anisotropy?: number;
|
|
11
|
+
/** Physical directional highlight strength [0,1]; `anisotropy` remains the legacy texture sampler setting. */
|
|
12
|
+
surfaceAnisotropy?: number;
|
|
13
|
+
normalScale?: readonly [number, number];
|
|
14
|
+
/** Normalized [0,1] height maps displace in local shape units (Three defaults: scale 1, bias 0). */
|
|
15
|
+
displacementScale?: number;
|
|
16
|
+
displacementBias?: number;
|
|
17
|
+
transparent?: boolean;
|
|
18
|
+
opacity?: number;
|
|
19
|
+
depthWrite?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/** Primitive UV layout whose physical dimensions drive `repeatMetres`. */
|
|
22
|
+
export type SurfaceShape = "box" | "cylinder";
|
|
23
|
+
/** Stable material identity, independent of object/map property insertion order. @internal */
|
|
24
|
+
export declare function authoredSurfaceKey(config: AuthoredSurfaceConfig): string;
|
|
25
|
+
/** Configure owned map clones without changing textures retained in a loader cache. @internal */
|
|
26
|
+
export declare function authoredSurfaceTextures(entries: Record<string, string>, sources: readonly THREE.Texture[], config?: AuthoredSurfaceConfig): MaterialOverrideTextures;
|
|
27
|
+
/** Apply PBR settings and optional metre UVs to an owned standard material. @internal */
|
|
28
|
+
export declare function configureAuthoredSurface(material: THREE.MeshStandardMaterial, config: AuthoredSurfaceConfig, textures: MaterialOverrideTextures, shape: SurfaceShape): void;
|
|
29
|
+
/**
|
|
30
|
+
* Load declared surface-map roles and own the material/map clones (cached textures remain untouched).
|
|
31
|
+
* Colour, emissive, sheen-colour and specular-colour maps are sRGB; numeric maps are linear. Cylinder side metres use the RMS ellipse circumference approximation.
|
|
32
|
+
* Static batches and custom meshes share this lifecycle.
|
|
33
|
+
* @capability authored-surface-material render declared PBR maps with colour-space handling and metre-scale box/cylinder UVs
|
|
34
|
+
*/
|
|
35
|
+
export declare function useAuthoredSurfaceMaterial(config: AuthoredSurfaceConfig, shape?: SurfaceShape): THREE.MeshStandardMaterial;
|
|
36
|
+
/**
|
|
37
|
+
* Mount an owned PBR surface material on a mesh.
|
|
38
|
+
* @capability authored-surface-material mount an owned PBR surface material on a mesh
|
|
39
|
+
*/
|
|
40
|
+
export declare function AuthoredSurfaceMaterial({ surface, shape }: {
|
|
41
|
+
surface: AuthoredSurfaceConfig;
|
|
42
|
+
shape?: SurfaceShape;
|
|
43
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useLoader } from "@react-three/fiber";
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import * as THREE from "three";
|
|
5
|
+
import { MATERIAL_TEXTURE_SEMANTICS } from "@jgengine/core/material/materialAsset";
|
|
6
|
+
import { applyMaterialOverrideToMaterial, requiresPhysicalMaterial } from "../materialOverride.js";
|
|
7
|
+
import { modelMapEntries } from "./modelAssets.js";
|
|
8
|
+
import { useDisposable } from "./useDisposable.js";
|
|
9
|
+
/** Stable material identity, independent of object/map property insertion order. @internal */
|
|
10
|
+
export function authoredSurfaceKey(config) {
|
|
11
|
+
return JSON.stringify(Object.entries({ ...config, maps: modelMapEntries(config.maps ?? {}) }).sort(([a], [b]) => a.localeCompare(b)));
|
|
12
|
+
}
|
|
13
|
+
/** Configure owned map clones without changing textures retained in a loader cache. @internal */
|
|
14
|
+
export function authoredSurfaceTextures(entries, sources, config = {}) {
|
|
15
|
+
const wrapping = config.wrapping === "clamp" ? THREE.ClampToEdgeWrapping : config.wrapping === "mirror" ? THREE.MirroredRepeatWrapping : THREE.RepeatWrapping;
|
|
16
|
+
return Object.fromEntries(Object.keys(entries).map((role, index) => {
|
|
17
|
+
const texture = sources[index].clone();
|
|
18
|
+
texture.colorSpace = MATERIAL_TEXTURE_SEMANTICS[role]?.colorSpace === "srgb" ? THREE.SRGBColorSpace : THREE.NoColorSpace;
|
|
19
|
+
texture.wrapS = wrapping;
|
|
20
|
+
texture.wrapT = wrapping;
|
|
21
|
+
texture.anisotropy = config.anisotropy ?? 1;
|
|
22
|
+
texture.needsUpdate = true;
|
|
23
|
+
return [role, texture];
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
/** Apply PBR settings and optional metre UVs to an owned standard material. @internal */
|
|
27
|
+
export function configureAuthoredSurface(material, config, textures, shape) {
|
|
28
|
+
const tile = config.repeatMetres;
|
|
29
|
+
if (tile !== undefined && (!Number.isFinite(tile) || tile <= 0))
|
|
30
|
+
throw new Error("repeatMetres must be finite and positive");
|
|
31
|
+
if (config.normalScale?.some(value => !Number.isFinite(value)))
|
|
32
|
+
throw new Error("normalScale must be finite");
|
|
33
|
+
if (config.anisotropy !== undefined && (!Number.isFinite(config.anisotropy) || config.anisotropy < 1))
|
|
34
|
+
throw new Error("anisotropy must be finite and >= 1");
|
|
35
|
+
if ([config.displacementScale, config.displacementBias].some(value => value !== undefined && !Number.isFinite(value)))
|
|
36
|
+
throw new Error("displacement settings must be finite");
|
|
37
|
+
const { anisotropy: _samplerAnisotropy, surfaceAnisotropy, ...surface } = config;
|
|
38
|
+
if (requiresPhysicalMaterial({ ...surface, normalScale: surface.normalScale === undefined ? undefined : [...surface.normalScale], anisotropy: surfaceAnisotropy }, textures) && !material.isMeshPhysicalMaterial)
|
|
39
|
+
throw new Error("Physical authored surface settings require MeshPhysicalMaterial");
|
|
40
|
+
applyMaterialOverrideToMaterial(material, { ...surface, normalScale: surface.normalScale === undefined ? undefined : [...surface.normalScale], anisotropy: surfaceAnisotropy }, false, textures);
|
|
41
|
+
if (config.displacementScale !== undefined)
|
|
42
|
+
material.displacementScale = config.displacementScale;
|
|
43
|
+
if (config.displacementBias !== undefined)
|
|
44
|
+
material.displacementBias = config.displacementBias;
|
|
45
|
+
if (config.transparent !== undefined)
|
|
46
|
+
material.transparent = config.transparent;
|
|
47
|
+
if (config.opacity !== undefined)
|
|
48
|
+
material.opacity = config.opacity;
|
|
49
|
+
if (config.depthWrite !== undefined)
|
|
50
|
+
material.depthWrite = config.depthWrite;
|
|
51
|
+
if (config.normalScale !== undefined)
|
|
52
|
+
material.normalScale.set(...config.normalScale);
|
|
53
|
+
if (tile === undefined)
|
|
54
|
+
return;
|
|
55
|
+
const previousCompile = material.onBeforeCompile;
|
|
56
|
+
const previousKey = material.customProgramCacheKey();
|
|
57
|
+
material.onBeforeCompile = (shader, renderer) => {
|
|
58
|
+
previousCompile.call(material, shader, renderer);
|
|
59
|
+
shader.vertexShader = shader.vertexShader.replace("#include <uv_vertex>", `#include <uv_vertex>
|
|
60
|
+
mat4 surfaceTransform = modelMatrix;
|
|
61
|
+
#ifdef USE_INSTANCING
|
|
62
|
+
surfaceTransform = modelMatrix * instanceMatrix;
|
|
63
|
+
#endif
|
|
64
|
+
vec3 surfaceExtent = vec3(length(surfaceTransform[0].xyz), length(surfaceTransform[1].xyz), length(surfaceTransform[2].xyz));
|
|
65
|
+
vec2 surfaceRepeat = ${shape === "box"
|
|
66
|
+
? "abs(normal.x) > 0.5 ? surfaceExtent.zy : abs(normal.y) > 0.5 ? surfaceExtent.xz : surfaceExtent.xy"
|
|
67
|
+
: "abs(normal.y) > 0.5 ? surfaceExtent.xz * 2.0 : vec2(6.28318530718 * sqrt((surfaceExtent.x * surfaceExtent.x + surfaceExtent.z * surfaceExtent.z) * 0.5), surfaceExtent.y)"};
|
|
68
|
+
surfaceRepeat /= ${tile.toExponential(8)};
|
|
69
|
+
${Object.entries({ MAP: "vMapUv", NORMALMAP: "vNormalMapUv", ROUGHNESSMAP: "vRoughnessMapUv", AOMAP: "vAoMapUv", METALNESSMAP: "vMetalnessMapUv", EMISSIVEMAP: "vEmissiveMapUv", DISPLACEMENTMAP: "vDisplacementMapUv", ALPHAMAP: "vAlphaMapUv", SHEENCOLORMAP: "vSheenColorMapUv", SHEENROUGHNESSMAP: "vSheenRoughnessMapUv", ANISOTROPYMAP: "vAnisotropyMapUv", CLEARCOATMAP: "vClearcoatMapUv", CLEARCOATROUGHNESSMAP: "vClearcoatRoughnessMapUv", CLEARCOATNORMALMAP: "vClearcoatNormalMapUv", SPECULARINTENSITYMAP: "vSpecularIntensityMapUv", SPECULARCOLORMAP: "vSpecularColorMapUv", TRANSMISSIONMAP: "vTransmissionMapUv", THICKNESSMAP: "vThicknessMapUv", IRIDESCENCEMAP: "vIridescenceMapUv", IRIDESCENCETHICKNESSMAP: "vIridescenceThicknessMapUv" }).map(([role, name]) => {
|
|
70
|
+
return `#ifdef USE_${role}\n ${name} *= surfaceRepeat;\n#endif`;
|
|
71
|
+
}).join("\n")}`);
|
|
72
|
+
};
|
|
73
|
+
material.customProgramCacheKey = () => `${previousKey}|authored-surface:${shape}:${tile}`;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Load declared surface-map roles and own the material/map clones (cached textures remain untouched).
|
|
77
|
+
* Colour, emissive, sheen-colour and specular-colour maps are sRGB; numeric maps are linear. Cylinder side metres use the RMS ellipse circumference approximation.
|
|
78
|
+
* Static batches and custom meshes share this lifecycle.
|
|
79
|
+
* @capability authored-surface-material render declared PBR maps with colour-space handling and metre-scale box/cylinder UVs
|
|
80
|
+
*/
|
|
81
|
+
export function useAuthoredSurfaceMaterial(config, shape = "box") {
|
|
82
|
+
const key = authoredSurfaceKey(config);
|
|
83
|
+
const entries = useMemo(() => modelMapEntries(config.maps ?? {}), [key]);
|
|
84
|
+
const loaded = useLoader(THREE.TextureLoader, Object.values(entries));
|
|
85
|
+
const resources = useDisposable(() => {
|
|
86
|
+
const textures = authoredSurfaceTextures(entries, loaded, config);
|
|
87
|
+
const { anisotropy: _samplerAnisotropy, surfaceAnisotropy, ...surface } = config;
|
|
88
|
+
const material = requiresPhysicalMaterial({ ...surface, normalScale: surface.normalScale === undefined ? undefined : [...surface.normalScale], anisotropy: surfaceAnisotropy }, textures) ? new THREE.MeshPhysicalMaterial() : new THREE.MeshStandardMaterial();
|
|
89
|
+
try {
|
|
90
|
+
configureAuthoredSurface(material, config, textures, shape);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
material.dispose();
|
|
94
|
+
Object.values(textures).forEach(texture => texture.dispose());
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
return [material, ...Object.values(textures)];
|
|
98
|
+
}, [key, entries, loaded, shape]);
|
|
99
|
+
return resources[0];
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Mount an owned PBR surface material on a mesh.
|
|
103
|
+
* @capability authored-surface-material mount an owned PBR surface material on a mesh
|
|
104
|
+
*/
|
|
105
|
+
export function AuthoredSurfaceMaterial({ surface, shape = "box" }) {
|
|
106
|
+
const material = useAuthoredSurfaceMaterial(surface, shape);
|
|
107
|
+
return _jsx("primitive", { object: material, attach: "material" });
|
|
108
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type Color, type ToneMapping } from "three";
|
|
2
|
+
type Rgb = [number, number, number];
|
|
3
|
+
/**
|
|
4
|
+
* three.js r182's tone-mapping curve for `mode`, applied to a linear scene color at `exposure`.
|
|
5
|
+
* Mirrors `tonemapping_pars_fragment`; `NoToneMapping` and custom curves pass through.
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
export declare function toneMapColor(color: readonly [number, number, number], mode: ToneMapping, exposure?: number): Rgb;
|
|
9
|
+
/**
|
|
10
|
+
* The linear scene color that `mode` at `exposure` displays as `display` (linear sRGB, 0..1).
|
|
11
|
+
* Sky and fog colors are picked as on-screen swatches, but the output pass tone-maps them like
|
|
12
|
+
* lit surfaces, which turns a pale horizon grey. Solved by fixed-point iteration because AgX and
|
|
13
|
+
* ACES mix channels; a swatch a curve cannot reach (pure white, a saturated highlight under AgX)
|
|
14
|
+
* lands on the closest color it can show.
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
export declare function sceneColorForDisplay(display: readonly [number, number, number], mode: ToneMapping, exposure?: number): Rgb;
|
|
18
|
+
/**
|
|
19
|
+
* Writes the scene color that displays as `display` into `target`, recomputing only when the
|
|
20
|
+
* swatch or the renderer's tone mapping changes, so per-frame sky drivers stay allocation-free.
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
export declare class DisplayColorWriter {
|
|
24
|
+
private hex;
|
|
25
|
+
private mode;
|
|
26
|
+
private exposure;
|
|
27
|
+
private scene;
|
|
28
|
+
write(target: Color, display: Color, renderer: {
|
|
29
|
+
toneMapping: ToneMapping;
|
|
30
|
+
toneMappingExposure: number;
|
|
31
|
+
}): void;
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { ACESFilmicToneMapping, AgXToneMapping, CineonToneMapping, LinearToneMapping, NeutralToneMapping, ReinhardToneMapping, } from "three";
|
|
2
|
+
const saturate = (x) => Math.min(1, Math.max(0, x));
|
|
3
|
+
/** GLSL `mat3(c0, c1, c2) * v` with the columns three's tonemapping chunk declares. */
|
|
4
|
+
function mat3(c, v) {
|
|
5
|
+
return [
|
|
6
|
+
c[0][0] * v[0] + c[1][0] * v[1] + c[2][0] * v[2],
|
|
7
|
+
c[0][1] * v[0] + c[1][1] * v[1] + c[2][1] * v[2],
|
|
8
|
+
c[0][2] * v[0] + c[1][2] * v[1] + c[2][2] * v[2],
|
|
9
|
+
];
|
|
10
|
+
}
|
|
11
|
+
const ACES_IN = [[0.59719, 0.076, 0.0284], [0.35458, 0.90834, 0.13383], [0.04823, 0.01566, 0.83777]];
|
|
12
|
+
const ACES_OUT = [[1.60475, -0.10208, -0.00327], [-0.53108, 1.10813, -0.07276], [-0.07367, -0.00605, 1.07602]];
|
|
13
|
+
const REC2020_TO_SRGB = [[1.6605, -0.1246, -0.0182], [-0.5876, 1.1329, -0.1006], [-0.0728, -0.0083, 1.1187]];
|
|
14
|
+
const SRGB_TO_REC2020 = [[0.6274, 0.0691, 0.0164], [0.3293, 0.9195, 0.088], [0.0433, 0.0113, 0.8956]];
|
|
15
|
+
const AGX_INSET = [
|
|
16
|
+
[0.856627153315983, 0.137318972929847, 0.11189821299995],
|
|
17
|
+
[0.0951212405381588, 0.761241990602591, 0.0767994186031903],
|
|
18
|
+
[0.0482516061458583, 0.101439036467562, 0.811302368396859],
|
|
19
|
+
];
|
|
20
|
+
const AGX_OUTSET = [
|
|
21
|
+
[1.1271005818144368, -0.1413297634984383, -0.14132976349843826],
|
|
22
|
+
[-0.11060664309660323, 1.157823702216272, -0.11060664309660294],
|
|
23
|
+
[-0.016493938717834573, -0.016493938717834257, 1.2519364065950405],
|
|
24
|
+
];
|
|
25
|
+
const AGX_MIN_EV = -12.47393;
|
|
26
|
+
const AGX_MAX_EV = 4.026069;
|
|
27
|
+
function aces(c) {
|
|
28
|
+
const v = mat3(ACES_IN, [c[0] / 0.6, c[1] / 0.6, c[2] / 0.6]);
|
|
29
|
+
const fit = v.map((x) => (x * (x + 0.0245786) - 0.000090537) / (x * (0.983729 * x + 0.432951) + 0.238081));
|
|
30
|
+
return mat3(ACES_OUT, fit).map(saturate);
|
|
31
|
+
}
|
|
32
|
+
function agx(c) {
|
|
33
|
+
const inset = mat3(AGX_INSET, mat3(SRGB_TO_REC2020, c));
|
|
34
|
+
const curved = inset.map((x) => {
|
|
35
|
+
const t = saturate((Math.log2(Math.max(x, 1e-10)) - AGX_MIN_EV) / (AGX_MAX_EV - AGX_MIN_EV));
|
|
36
|
+
const t2 = t * t;
|
|
37
|
+
const t4 = t2 * t2;
|
|
38
|
+
return 15.5 * t4 * t2 - 40.14 * t4 * t + 31.96 * t4 - 6.868 * t2 * t + 0.4298 * t2 + 0.1191 * t - 0.00232;
|
|
39
|
+
});
|
|
40
|
+
const out = mat3(AGX_OUTSET, curved).map((x) => Math.pow(Math.max(0, x), 2.2));
|
|
41
|
+
return mat3(REC2020_TO_SRGB, out).map(saturate);
|
|
42
|
+
}
|
|
43
|
+
function neutral(input) {
|
|
44
|
+
const x = Math.min(input[0], input[1], input[2]);
|
|
45
|
+
const offset = x < 0.08 ? x - 6.25 * x * x : 0.04;
|
|
46
|
+
const c = input.map((v) => v - offset);
|
|
47
|
+
const peak = Math.max(c[0], c[1], c[2]);
|
|
48
|
+
const start = 0.76;
|
|
49
|
+
if (peak < start)
|
|
50
|
+
return c;
|
|
51
|
+
const d = 1 - start;
|
|
52
|
+
const newPeak = 1 - (d * d) / (peak + d - start);
|
|
53
|
+
const g = 1 - 1 / (0.15 * (peak - newPeak) + 1);
|
|
54
|
+
return c.map((v) => v * (newPeak / peak) * (1 - g) + newPeak * g);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* three.js r182's tone-mapping curve for `mode`, applied to a linear scene color at `exposure`.
|
|
58
|
+
* Mirrors `tonemapping_pars_fragment`; `NoToneMapping` and custom curves pass through.
|
|
59
|
+
* @internal
|
|
60
|
+
*/
|
|
61
|
+
export function toneMapColor(color, mode, exposure = 1) {
|
|
62
|
+
const c = [color[0] * exposure, color[1] * exposure, color[2] * exposure];
|
|
63
|
+
switch (mode) {
|
|
64
|
+
case LinearToneMapping:
|
|
65
|
+
return c.map(saturate);
|
|
66
|
+
case ReinhardToneMapping:
|
|
67
|
+
return c.map((x) => saturate(x / (1 + x)));
|
|
68
|
+
case CineonToneMapping:
|
|
69
|
+
return c.map((v) => {
|
|
70
|
+
const x = Math.max(0, v - 0.004);
|
|
71
|
+
return Math.pow((x * (6.2 * x + 0.5)) / (x * (6.2 * x + 1.7) + 0.06), 2.2);
|
|
72
|
+
});
|
|
73
|
+
case ACESFilmicToneMapping:
|
|
74
|
+
return aces(c);
|
|
75
|
+
case AgXToneMapping:
|
|
76
|
+
return agx(c);
|
|
77
|
+
case NeutralToneMapping:
|
|
78
|
+
return neutral(c);
|
|
79
|
+
default:
|
|
80
|
+
return c;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const CURVED = new Set([ReinhardToneMapping, CineonToneMapping, ACESFilmicToneMapping, AgXToneMapping, NeutralToneMapping]);
|
|
84
|
+
/** Keeps a compensated sky inside half-float range; the brightest pastel under AgX needs ~13. */
|
|
85
|
+
const MAX_SCENE_VALUE = 32;
|
|
86
|
+
/**
|
|
87
|
+
* The linear scene color that `mode` at `exposure` displays as `display` (linear sRGB, 0..1).
|
|
88
|
+
* Sky and fog colors are picked as on-screen swatches, but the output pass tone-maps them like
|
|
89
|
+
* lit surfaces, which turns a pale horizon grey. Solved by fixed-point iteration because AgX and
|
|
90
|
+
* ACES mix channels; a swatch a curve cannot reach (pure white, a saturated highlight under AgX)
|
|
91
|
+
* lands on the closest color it can show.
|
|
92
|
+
* @internal
|
|
93
|
+
*/
|
|
94
|
+
export function sceneColorForDisplay(display, mode, exposure = 1) {
|
|
95
|
+
const target = [saturate(display[0]), saturate(display[1]), saturate(display[2])];
|
|
96
|
+
if (mode === LinearToneMapping)
|
|
97
|
+
return target.map((v) => v / Math.max(exposure, 1e-6));
|
|
98
|
+
if (!CURVED.has(mode))
|
|
99
|
+
return target;
|
|
100
|
+
const scene = [...target];
|
|
101
|
+
let best = [...target];
|
|
102
|
+
let bestError = Number.POSITIVE_INFINITY;
|
|
103
|
+
for (let iteration = 0; iteration < 48; iteration += 1) {
|
|
104
|
+
const shown = toneMapColor(scene, mode, exposure);
|
|
105
|
+
const error = Math.abs(shown[0] - target[0]) + Math.abs(shown[1] - target[1]) + Math.abs(shown[2] - target[2]);
|
|
106
|
+
if (error < bestError) {
|
|
107
|
+
bestError = error;
|
|
108
|
+
best = [scene[0], scene[1], scene[2]];
|
|
109
|
+
}
|
|
110
|
+
for (let channel = 0; channel < 3; channel += 1) {
|
|
111
|
+
// Clamped so a channel a curve's toe or shoulder cannot reach does not blow up its neighbors.
|
|
112
|
+
const ratio = Math.min(2, Math.max(0.5, Math.max(target[channel], 1e-5) / Math.max(shown[channel], 1e-5)));
|
|
113
|
+
scene[channel] = Math.min(MAX_SCENE_VALUE, Math.max(1e-5, scene[channel] * Math.pow(ratio, 0.8)));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return best;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Writes the scene color that displays as `display` into `target`, recomputing only when the
|
|
120
|
+
* swatch or the renderer's tone mapping changes, so per-frame sky drivers stay allocation-free.
|
|
121
|
+
* @internal
|
|
122
|
+
*/
|
|
123
|
+
export class DisplayColorWriter {
|
|
124
|
+
hex = -1;
|
|
125
|
+
mode = -1;
|
|
126
|
+
exposure = Number.NaN;
|
|
127
|
+
scene = [0, 0, 0];
|
|
128
|
+
write(target, display, renderer) {
|
|
129
|
+
const hex = display.getHex();
|
|
130
|
+
if (hex !== this.hex || renderer.toneMapping !== this.mode || renderer.toneMappingExposure !== this.exposure) {
|
|
131
|
+
this.hex = hex;
|
|
132
|
+
this.mode = renderer.toneMapping;
|
|
133
|
+
this.exposure = renderer.toneMappingExposure;
|
|
134
|
+
this.scene = sceneColorForDisplay([display.r, display.g, display.b], this.mode, this.exposure);
|
|
135
|
+
}
|
|
136
|
+
target.setRGB(this.scene[0], this.scene[1], this.scene[2]);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import { type MaterialAppearanceBaseline, type MaterialAppearancePolicy, type MaterialAppearanceResponse } from "@jgengine/core/material/appearanceSignals";
|
|
3
|
+
import type { EnvironmentSample } from "@jgengine/core/world/envField";
|
|
4
|
+
/** Immutable resolved baseline for one caller-owned assigned material, including its original card exposure. */
|
|
5
|
+
export interface RenderMaterialAppearanceBaseline extends MaterialAppearanceBaseline {
|
|
6
|
+
cardBacklightExposure: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Capture once after assignment; recapture after an authored change. Never capture an already wet material.
|
|
10
|
+
* @capability material-appearance-signals capture immutable resolved dry values from one owned assigned material
|
|
11
|
+
*/
|
|
12
|
+
export declare function captureMaterialAppearanceBaseline(material: THREE.MeshStandardMaterial): RenderMaterialAppearanceBaseline;
|
|
13
|
+
/**
|
|
14
|
+
* Apply one sample to an already owned material. Imported/cache materials must be cloned before use.
|
|
15
|
+
* Allocates no textures or materials; clearcoat/sheen require an existing physical material and opt-in policy.
|
|
16
|
+
* @capability material-appearance-signals update one owned material from wetness and local card exposure without global lighting edits
|
|
17
|
+
*/
|
|
18
|
+
export declare function applyMaterialAppearanceSignals(material: THREE.MeshStandardMaterial, baseline: MaterialAppearanceBaseline, sample: EnvironmentSample, policy?: MaterialAppearancePolicy): MaterialAppearanceResponse;
|
|
19
|
+
/**
|
|
20
|
+
* Restore the dry baseline when removing an appearance binding; maps and unrelated imported features remain borrowed.
|
|
21
|
+
* @capability material-appearance-signals restore dry material response when removing an environment appearance binding
|
|
22
|
+
*/
|
|
23
|
+
export declare function restoreMaterialAppearanceBaseline(material: THREE.MeshStandardMaterial, baseline: RenderMaterialAppearanceBaseline): void;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import { resolveMaterialAppearance } from "@jgengine/core/material/appearanceSignals";
|
|
3
|
+
import { setHairCardLightExposure } from "./materialAsset.js";
|
|
4
|
+
/**
|
|
5
|
+
* Capture once after assignment; recapture after an authored change. Never capture an already wet material.
|
|
6
|
+
* @capability material-appearance-signals capture immutable resolved dry values from one owned assigned material
|
|
7
|
+
*/
|
|
8
|
+
export function captureMaterialAppearanceBaseline(material) {
|
|
9
|
+
const physical = material;
|
|
10
|
+
return Object.freeze({
|
|
11
|
+
roughness: material.roughness,
|
|
12
|
+
colorLinear: Object.freeze([material.color.r, material.color.g, material.color.b]),
|
|
13
|
+
...(physical.isMeshPhysicalMaterial ? { sheen: physical.sheen, clearcoat: physical.clearcoat, clearcoatRoughness: physical.clearcoatRoughness } : {}),
|
|
14
|
+
cardBacklightExposure: material.userData.jgHairLightExposure ?? 1,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Apply one sample to an already owned material. Imported/cache materials must be cloned before use.
|
|
19
|
+
* Allocates no textures or materials; clearcoat/sheen require an existing physical material and opt-in policy.
|
|
20
|
+
* @capability material-appearance-signals update one owned material from wetness and local card exposure without global lighting edits
|
|
21
|
+
*/
|
|
22
|
+
export function applyMaterialAppearanceSignals(material, baseline, sample, policy = {}) {
|
|
23
|
+
if ((policy.wetness?.sheen !== undefined || policy.wetness?.clearcoat !== undefined) && !material.isMeshPhysicalMaterial)
|
|
24
|
+
throw new TypeError("Wet sheen or clearcoat requires an owned MeshPhysicalMaterial; declare the capability when assigning it.");
|
|
25
|
+
const response = resolveMaterialAppearance(sample, baseline, policy);
|
|
26
|
+
const physical = material;
|
|
27
|
+
if (response.surface.roughness !== undefined)
|
|
28
|
+
material.roughness = response.surface.roughness;
|
|
29
|
+
if (response.colorLinear !== undefined)
|
|
30
|
+
material.color.setRGB(...response.colorLinear, THREE.LinearSRGBColorSpace);
|
|
31
|
+
if (response.surface.sheen !== undefined)
|
|
32
|
+
physical.sheen = response.surface.sheen;
|
|
33
|
+
if (response.surface.clearcoat !== undefined)
|
|
34
|
+
physical.clearcoat = response.surface.clearcoat;
|
|
35
|
+
if (response.surface.clearcoatRoughness !== undefined)
|
|
36
|
+
physical.clearcoatRoughness = response.surface.clearcoatRoughness;
|
|
37
|
+
if (response.backlightExposure !== undefined)
|
|
38
|
+
setHairCardLightExposure(material, response.backlightExposure);
|
|
39
|
+
return response;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Restore the dry baseline when removing an appearance binding; maps and unrelated imported features remain borrowed.
|
|
43
|
+
* @capability material-appearance-signals restore dry material response when removing an environment appearance binding
|
|
44
|
+
*/
|
|
45
|
+
export function restoreMaterialAppearanceBaseline(material, baseline) {
|
|
46
|
+
if ((baseline.sheen !== undefined || baseline.clearcoat !== undefined) && !material.isMeshPhysicalMaterial)
|
|
47
|
+
throw new TypeError("The appearance baseline belongs to a physical material.");
|
|
48
|
+
material.roughness = baseline.roughness;
|
|
49
|
+
material.color.setRGB(...baseline.colorLinear, THREE.LinearSRGBColorSpace);
|
|
50
|
+
const physical = material;
|
|
51
|
+
if (baseline.sheen !== undefined)
|
|
52
|
+
physical.sheen = baseline.sheen;
|
|
53
|
+
if (baseline.clearcoat !== undefined)
|
|
54
|
+
physical.clearcoat = baseline.clearcoat;
|
|
55
|
+
if (baseline.clearcoatRoughness !== undefined)
|
|
56
|
+
physical.clearcoatRoughness = baseline.clearcoatRoughness;
|
|
57
|
+
setHairCardLightExposure(material, baseline.cardBacklightExposure);
|
|
58
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { ModelConfig } from "@jgengine/core/game/playableGame";
|
|
2
|
+
import * as THREE from "three";
|
|
3
|
+
import { type MaterialAsset, type MaterialAssignment, type MaterialAssignmentValidationOptions, type MaterialSurfaceParameters, type MaterialTextureMetadata, type MaterialTextureRole } from "@jgengine/core/material/materialAsset";
|
|
4
|
+
import { type MaterialOverrideTextures } from "../materialOverride.js";
|
|
5
|
+
import { type SurfaceShape } from "./authoredSurfaceMaterial.js";
|
|
6
|
+
/** Shares the model transcoder and loader cache while loading native authored images normally. @internal */
|
|
7
|
+
export declare class MaterialTextureLoader extends THREE.Loader<THREE.Texture> {
|
|
8
|
+
load(url: string, onLoad?: (texture: THREE.Texture) => void, onProgress?: (event: ProgressEvent) => void, onError?: (error: unknown) => void): void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Validate and configure an owned texture view while retaining the loader's immutable source.
|
|
12
|
+
* @capability material-texture-metadata configure owned map views with semantic color space, packed channels, UV transforms and sampling
|
|
13
|
+
*/
|
|
14
|
+
export declare function configureMaterialTexture(role: MaterialTextureRole, metadata: MaterialTextureMetadata, source: THREE.Texture): THREE.Texture;
|
|
15
|
+
/**
|
|
16
|
+
* Physical defaults adapt a declared construction; explicit authored parameters remain authoritative.
|
|
17
|
+
* @capability material-assets resolve fabric and hair construction into sparse physical appearance parameters
|
|
18
|
+
*/
|
|
19
|
+
export declare function materialAssetSurface(asset: MaterialAsset, overrides?: MaterialSurfaceParameters): MaterialSurfaceParameters;
|
|
20
|
+
/**
|
|
21
|
+
* Apply one reusable material's parameters, map views and construction to an imported PBR slot.
|
|
22
|
+
* @capability material-assets clone and configure an imported PBR slot while retaining its omitted physical features and maps
|
|
23
|
+
*/
|
|
24
|
+
export declare function applyMaterialAsset(material: THREE.Material, asset: MaterialAsset, textures?: MaterialOverrideTextures, overrides?: MaterialSurfaceParameters): THREE.MeshStandardMaterial;
|
|
25
|
+
/** Read-only target validation policy; source coverage can reflect an earlier all-slot override. */
|
|
26
|
+
export interface MaterialAssignmentTargetOptions extends MaterialAssignmentValidationOptions {
|
|
27
|
+
/** Coverage applied to every imported slot before named assignments; never mutates the source. */
|
|
28
|
+
baseAlphaMode?: MaterialSurfaceParameters["alphaMode"];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Check selectors, PBR slots, UVs and construction prerequisites before allocating material or texture views.
|
|
32
|
+
* Optional role restrictions apply only to referenced assets; imported maps remain untouched.
|
|
33
|
+
* @capability material-slot-selection preflight native model assignments without allocating or changing imported resources
|
|
34
|
+
*/
|
|
35
|
+
export declare function validateMaterialAssignmentTargets(root: THREE.Object3D, assets: readonly MaterialAsset[], assignments: readonly MaterialAssignment[], options?: MaterialAssignmentTargetOptions): void;
|
|
36
|
+
/**
|
|
37
|
+
* Apply intersecting mesh/slot selectors without changing omitted imported slots. Returns caller-owned replacements; model clones register them only after success.
|
|
38
|
+
* @capability material-assets assign reusable physical assets to independent imported model slots
|
|
39
|
+
*/
|
|
40
|
+
export declare function applyMaterialAssignments(root: THREE.Object3D, assets: readonly MaterialAsset[], assignments: readonly MaterialAssignment[], textures?: ReadonlyMap<string, MaterialOverrideTextures>): THREE.Material[];
|
|
41
|
+
/** Reusable primitive surface options; physical metre UVs reuse the instanced surface renderer. */
|
|
42
|
+
export interface MaterialAssetSurfaceOptions {
|
|
43
|
+
shape?: SurfaceShape;
|
|
44
|
+
repeatMetres?: number;
|
|
45
|
+
overrides?: MaterialSurfaceParameters;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Load map views, own their cleanup and build the same physical shader used by model assignments.
|
|
49
|
+
* @capability material-assets own a serializable material asset's textures and runtime physical shader for a custom renderer
|
|
50
|
+
*/
|
|
51
|
+
export declare function useMaterialAssetMaterial(asset: MaterialAsset, options?: MaterialAssetSurfaceOptions): THREE.MeshStandardMaterial;
|
|
52
|
+
/**
|
|
53
|
+
* Mount a serializable material asset on an authored primitive, sharing model-slot rendering and owned resource cleanup.
|
|
54
|
+
* @capability material-assets render authored physical surfaces, woven detail and approximate hair-card appearance
|
|
55
|
+
*/
|
|
56
|
+
export declare function MaterialAssetSurface({ asset, ...options }: {
|
|
57
|
+
asset: MaterialAsset;
|
|
58
|
+
} & MaterialAssetSurfaceOptions): import("react").JSX.Element;
|
|
59
|
+
/**
|
|
60
|
+
* Estimate texture allocation from loaded dimensions/mips and report pass/coverage risks. Compressed maps count actual mip bytes; uncompressed bytes bound texture views that may share uploads. This does not measure pixel overdraw.
|
|
61
|
+
* @capability material-resource-metrics inspect loaded texture byte bounds and physical, coverage and transmission participation
|
|
62
|
+
*/
|
|
63
|
+
export declare function materialResourceMetrics(materials: readonly THREE.Material[]): {
|
|
64
|
+
materials: number;
|
|
65
|
+
textures: number;
|
|
66
|
+
textureBytes: number;
|
|
67
|
+
unknownTextureSizes: number;
|
|
68
|
+
blendedMaterials: number;
|
|
69
|
+
maskedMaterials: number;
|
|
70
|
+
transmissionMaterials: number;
|
|
71
|
+
doubleSidedMaterials: number;
|
|
72
|
+
physicalMaterials: number;
|
|
73
|
+
};
|
|
74
|
+
/** Runtime imported-slot inventory for precise authoring selectors; indices address the mesh material array. */
|
|
75
|
+
export interface ModelMaterialSlotInfo {
|
|
76
|
+
mesh: string;
|
|
77
|
+
slot: string;
|
|
78
|
+
slotIndex: number;
|
|
79
|
+
materialType: string;
|
|
80
|
+
uvSets: number[];
|
|
81
|
+
tangents: boolean;
|
|
82
|
+
physical: boolean;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Inspect imported names, local slot indices and directional/map prerequisites without mutating the source.
|
|
86
|
+
* @capability material-slots discover exact imported mesh and material selectors with UV and tangent prerequisites
|
|
87
|
+
*/
|
|
88
|
+
export declare function inspectModelMaterialSlots(root: THREE.Object3D): ModelMaterialSlotInfo[];
|
|
89
|
+
/** Loaded, owned map views for one model configuration. Loader sources and imported maps remain borrowed. @internal */
|
|
90
|
+
export declare function useModelMaterialTextures(model: Pick<ModelConfig, "material" | "materialAssets" | "materialAssignments">): {
|
|
91
|
+
global: MaterialOverrideTextures;
|
|
92
|
+
assets: ReadonlyMap<string, MaterialOverrideTextures>;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Gate the existing thin-sheet hair-card scatter by sampled local light exposure, without changing scene exposure.
|
|
96
|
+
* @capability material-appearance-signals apply sampled local exposure to the hair-card backlight approximation without global lighting edits
|
|
97
|
+
*/
|
|
98
|
+
export declare function setHairCardLightExposure(material: THREE.Material, exposure: number): void;
|