genshin-engine 3.7.0 → 3.9.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/dist/index.d.ts CHANGED
@@ -1,27 +1,60 @@
1
- import { BufferAttribute, BufferGeometry, Camera, Color, ColorRepresentation, Data3DTexture, DataTexture, DirectionalLight, Frustum, HemisphereLight, InstancedBufferGeometry, OrthographicCamera, Scene, Vector2, Vector3, Vector3Like } from "three";
1
+ import { BufferAttribute, BufferGeometry, Camera, CanvasTexture, Color, ColorRepresentation, Data3DTexture, DataTexture, DirectionalLight, Frustum, HemisphereLight, InstancedBufferGeometry, Matrix4, OrthographicCamera, Scene, Sprite, Texture, ToneMapping, Vector2, Vector3, Vector3Like } from "three";
2
+ import { MeshBasicNodeMaterial, MeshPhysicalNodeMaterial, MeshToonNodeMaterial, MeshToonNodeMaterialParameters, Node, RenderPipeline, RenderTarget, Renderer, TextureNode, UniformNode, WebGPURenderer } from "three/webgpu";
2
3
  import { CSMShadowNode } from "three/examples/jsm/csm/CSMShadowNode.js";
3
- import { MeshBasicNodeMaterial, MeshToonNodeMaterial, MeshToonNodeMaterialParameters, Node, RenderPipeline, RenderTarget, Renderer, TextureNode, UniformNode, WebGPURenderer } from "three/webgpu";
4
4
  import BloomNode from "three/examples/jsm/tsl/display/BloomNode.js";
5
5
  import GodraysNode from "three/examples/jsm/tsl/display/GodraysNode.js";
6
+ //#region src/atmosphere/CloudBandOptions.d.ts
7
+ export interface CloudBandOptions {
8
+ count: number;
9
+ distanceRange: [number, number];
10
+ heightRange: [number, number];
11
+ seed: number;
12
+ widthRange: [number, number];
13
+ }
14
+ //#endregion
15
+ //#region src/atmosphere/CloudSprite.d.ts
16
+ export interface CloudSprite {
17
+ lit: [number, number][][];
18
+ outline: [number, number][][];
19
+ }
20
+ //#endregion
6
21
  //#region src/atmosphere/SkyKeyframe.d.ts
7
22
  export interface SkyKeyframe {
23
+ cloudLitBackColor?: Color;
8
24
  cloudLitColor: Color;
25
+ cloudShadeBackColor?: Color;
9
26
  cloudShadeColor: Color;
27
+ haloColor?: Color;
10
28
  hemisphereGroundColor: Color;
11
29
  hemisphereIntensity: number;
12
30
  hemisphereSkyColor: Color;
31
+ horizonBackColor?: Color;
13
32
  horizonColor: Color;
14
33
  lightColor: Color;
15
34
  lightIntensity: number;
16
35
  minutes: number;
17
36
  starIntensity: number;
37
+ sunHaloColor?: Color;
38
+ zenithBackColor?: Color;
18
39
  zenithColor: Color;
19
40
  }
20
41
  //#endregion
42
+ //#region src/atmosphere/SkyShape.d.ts
43
+ export interface SkyShape {
44
+ frontBackBlend: number;
45
+ haloHeight: number;
46
+ horizonBand: number;
47
+ moonSize: number;
48
+ sunHaloSize: number;
49
+ }
50
+ //#endregion
21
51
  //#region src/atmosphere/SkyState.d.ts
22
52
  export interface SkyState extends Omit<SkyKeyframe, "minutes"> {
53
+ fogColor?: Color;
54
+ fogDensity?: number;
23
55
  lightDirection: Vector3;
24
56
  moonDirection: Vector3;
57
+ shape?: SkyShape;
25
58
  sunDirection: Vector3;
26
59
  }
27
60
  //#endregion
@@ -29,13 +62,27 @@ export interface SkyState extends Omit<SkyKeyframe, "minutes"> {
29
62
  export interface SkyUniforms {
30
63
  cloudCoverage: UniformNode<"float", number>;
31
64
  cloudDrift: UniformNode<"vec2", Vector2>;
65
+ cloudFrontBackBlend: UniformNode<"float", number>;
66
+ cloudLitBackColor: UniformNode<"color", Color>;
32
67
  cloudLitColor: UniformNode<"color", Color>;
68
+ cloudShadeBackColor: UniformNode<"color", Color>;
33
69
  cloudShadeColor: UniformNode<"color", Color>;
70
+ cloudSunBrighten: UniformNode<"float", number>;
71
+ frontBackBlend: UniformNode<"float", number>;
72
+ haloColor: UniformNode<"color", Color>;
73
+ haloHeight: UniformNode<"float", number>;
74
+ horizonBackColor: UniformNode<"color", Color>;
75
+ horizonBand: UniformNode<"float", number>;
34
76
  horizonColor: UniformNode<"color", Color>;
35
77
  lightColor: UniformNode<"color", Color>;
36
78
  moonDirection: UniformNode<"vec3", Vector3>;
79
+ moonGlowColor: UniformNode<"color", Color>;
80
+ moonSize: UniformNode<"float", number>;
37
81
  starIntensity: UniformNode<"float", number>;
38
82
  sunDirection: UniformNode<"vec3", Vector3>;
83
+ sunHaloColor: UniformNode<"color", Color>;
84
+ sunHaloSize: UniformNode<"float", number>;
85
+ zenithBackColor: UniformNode<"color", Color>;
39
86
  zenithColor: UniformNode<"color", Color>;
40
87
  }
41
88
  //#endregion
@@ -53,6 +100,10 @@ export interface FogUniforms {
53
100
  color: UniformNode<"color", Color>;
54
101
  density: UniformNode<"float", number>;
55
102
  heightFalloff: UniformNode<"float", number>;
103
+ scatterColor: UniformNode<"color", Color>;
104
+ scatterDirection: UniformNode<"vec3", Vector3>;
105
+ scatterPower: UniformNode<"float", number>;
106
+ scatterStrength: UniformNode<"float", number>;
56
107
  startDistance: UniformNode<"float", number>;
57
108
  }
58
109
  //#endregion
@@ -96,6 +147,37 @@ export declare const applySkyState: (skyState: SkyState, { fogUniforms, godraysL
96
147
  //#region src/atmosphere/computeSunDirection.d.ts
97
148
  export declare const computeSunDirection: (minutes: number, tilt: number, sunDirection: Vector3) => Vector3;
98
149
  //#endregion
150
+ //#region src/atmosphere/constants.d.ts
151
+ export declare const DEFAULT_SKY_SHAPE: Readonly<SkyShape>;
152
+ export declare const DEFAULT_SKY_GRADIENT: {
153
+ green: number[];
154
+ red: number[];
155
+ };
156
+ //#endregion
157
+ //#region src/atmosphere/createCloudAtlasTexture.d.ts
158
+ export declare const getCloudAtlasColumns: (spriteCount: number) => number;
159
+ export declare const createCloudAtlasTexture: (sprites: readonly CloudSprite[], cellSize: number) => CanvasTexture<OffscreenCanvas>;
160
+ //#endregion
161
+ //#region src/atmosphere/createCloudBandSprite.d.ts
162
+ export declare const createCloudBandSprite: (atlas: Texture, clouds: readonly {
163
+ position: [number, number, number];
164
+ spriteIndex: number;
165
+ width: number;
166
+ }[], { aspect, spriteCount }: {
167
+ aspect: number;
168
+ spriteCount: number;
169
+ }, skyUniforms: SkyUniforms) => {
170
+ dispose: () => void;
171
+ places: [number, number, number][];
172
+ sprite: Sprite;
173
+ };
174
+ //#endregion
175
+ //#region src/atmosphere/createSkyGradientTexture.d.ts
176
+ export declare const createSkyGradientTexture: ({ green, red }: {
177
+ green: readonly number[];
178
+ red: readonly number[];
179
+ }) => DataTexture;
180
+ //#endregion
99
181
  //#region src/atmosphere/createSkyState.d.ts
100
182
  export declare const createSkyState: () => SkyState;
101
183
  //#endregion
@@ -105,6 +187,16 @@ export declare const createSkyUniforms: () => SkyUniforms;
105
187
  //#region src/atmosphere/createSunLight.d.ts
106
188
  export declare const createSunLight: ({ cascadeCount, maxFar, shadowMapSize }: SunLightOptions) => SunLight;
107
189
  //#endregion
190
+ //#region src/atmosphere/orderByViewDepth.d.ts
191
+ export declare const orderByViewDepth: (places: readonly (readonly number[])[], { elements }: Matrix4) => number[];
192
+ //#endregion
193
+ //#region src/atmosphere/placeCloudBand.d.ts
194
+ export declare const placeCloudBand: ({ count, distanceRange: [near, far], heightRange: [low, high], seed, widthRange: [narrow, wide] }: CloudBandOptions, spriteCount: number) => {
195
+ position: [number, number, number];
196
+ spriteIndex: number;
197
+ width: number;
198
+ }[];
199
+ //#endregion
108
200
  //#region src/atmosphere/sampleSkyState.d.ts
109
201
  export declare const sampleSkyState: (skyKeyframes: readonly SkyKeyframe[], minutes: number, tilt: number, skyState: SkyState) => SkyState;
110
202
  //#endregion
@@ -121,6 +213,26 @@ export declare const advanceGameClock: (gameClock: GameClock, deltaSeconds: numb
121
213
  export declare const MINUTES_PER_DAY = 1440;
122
214
  export declare const GAME_MINUTES_PER_SECOND = 1;
123
215
  //#endregion
216
+ //#region src/kits/architecture/LatheSection.d.ts
217
+ export interface LatheSection {
218
+ bottomRadius: number;
219
+ height: number;
220
+ topRadius: number;
221
+ }
222
+ //#endregion
223
+ //#region src/kits/architecture/LatheStackOptions.d.ts
224
+ export interface LatheStackOptions {
225
+ isFaceted: boolean;
226
+ radialSegments: number;
227
+ sections: LatheSection[];
228
+ }
229
+ //#endregion
230
+ //#region src/kits/architecture/createBoxesGeometry.d.ts
231
+ export declare const createBoxesGeometry: (boxes: readonly (readonly number[])[]) => BufferGeometry;
232
+ //#endregion
233
+ //#region src/kits/architecture/createLatheStackGeometry.d.ts
234
+ export declare const createLatheStackGeometry: ({ isFaceted, radialSegments, sections }: LatheStackOptions) => BufferGeometry;
235
+ //#endregion
124
236
  //#region src/kits/statue/createStatueGeometry.d.ts
125
237
  export declare const createStatueGeometry: () => BufferGeometry;
126
238
  //#endregion
@@ -180,12 +292,25 @@ export interface RampOptions {
180
292
  terminator: number;
181
293
  }
182
294
  //#endregion
295
+ //#region src/materials/addSmoothLoop.d.ts
296
+ export declare const addSmoothLoop: (path: Path2D, points: readonly (readonly [number, number])[]) => void;
297
+ //#endregion
183
298
  //#region src/materials/computeRampValues.d.ts
184
299
  export declare const computeRampValues: ({ resolution, softness, terminator }: RampOptions) => Uint8Array;
185
300
  //#endregion
186
301
  //#region src/materials/createRampTexture.d.ts
187
302
  export declare const createRampTexture: (rampOptions: RampOptions) => DataTexture;
188
303
  //#endregion
304
+ //#region src/nodes/StoneMaterialOptions.d.ts
305
+ export interface StoneMaterialOptions {
306
+ albedo: string;
307
+ rimColor: readonly number[];
308
+ rimPower: number;
309
+ rimStrength: number;
310
+ smoothness: number;
311
+ specularColor: readonly number[];
312
+ }
313
+ //#endregion
189
314
  //#region src/nodes/ToonMaterialOptions.d.ts
190
315
  export interface ToonMaterialOptions {
191
316
  color?: ColorRepresentation;
@@ -239,8 +364,14 @@ export declare const createLightUniforms: () => LightUniforms;
239
364
  //#region src/nodes/createRimNode.d.ts
240
365
  export declare const createRimNode: ({ rimColor, rimStrength, sunDirection }: LightUniforms) => Node<"vec3">;
241
366
  //#endregion
367
+ //#region src/nodes/createSkyColorNode.d.ts
368
+ export declare const createSkyColorNode: ({ frontBackBlend, haloColor, haloHeight, horizonBackColor, horizonBand, horizonColor, sunDirection, sunHaloColor, sunHaloSize, zenithBackColor, zenithColor }: SkyUniforms, gradient: Texture, direction: Node<"vec3">) => Node<"vec3">;
369
+ //#endregion
242
370
  //#region src/nodes/createSkyNode.d.ts
243
- export declare const createSkyNode: ({ cloudCoverage, cloudDrift, cloudLitColor, cloudShadeColor, horizonColor, lightColor, moonDirection, starIntensity, sunDirection, zenithColor }: SkyUniforms) => Node<"vec3">;
371
+ export declare const createSkyNode: (uniforms: SkyUniforms, gradient: Texture) => Node<"vec3">;
372
+ //#endregion
373
+ //#region src/nodes/createStoneMaterial.d.ts
374
+ export declare const createStoneMaterial: ({ albedo, rimColor, rimPower, rimStrength, smoothness, specularColor }: StoneMaterialOptions, glowNode?: Node<"vec3">) => MeshPhysicalNodeMaterial;
244
375
  //#endregion
245
376
  //#region src/nodes/createToonMaterial.d.ts
246
377
  export declare const createToonMaterial: ({ color, isOutlined, isVertexColors, lightUniforms, rampTexture }: ToonMaterialOptions) => ToonNodeMaterial;
@@ -312,7 +443,7 @@ export declare const createGodraysLight: (shadowMapSize: number, halfExtent: num
312
443
  export declare const createGradeLutTexture: (gradeOptions: GradeOptions) => Data3DTexture;
313
444
  //#endregion
314
445
  //#region src/post/createHeightFogNode.d.ts
315
- export declare const createHeightFogNode: (colorNode: Node<"vec4">, depthNode: TextureNode, camera: Camera, { baseHeight, color, density, heightFalloff, startDistance }: FogUniforms) => Node<"vec4">;
446
+ export declare const createHeightFogNode: (colorNode: Node<"vec4">, depthNode: TextureNode, camera: Camera, { baseHeight, color, density, heightFalloff, scatterColor, scatterDirection, scatterPower, scatterStrength, startDistance }: FogUniforms) => Node<"vec4">;
316
447
  //#endregion
317
448
  //#region src/post/createPostPipeline.d.ts
318
449
  export declare const createPostPipeline: ({ camera, fogUniforms, godraysLight, gradeLutTexture, postUniforms: { godraysColor, gradeIntensity, outlineColor, outlineFadeDistance, outlineThickness }, qualityTierSettings: { antialiasingMode, godraysStepCount, isBloomEnabled }, renderer, scene }: PostPipelineOptions) => PostPipeline;
@@ -320,6 +451,12 @@ export declare const createPostPipeline: ({ camera, fogUniforms, godraysLight, g
320
451
  //#region src/post/createPostUniforms.d.ts
321
452
  export declare const createPostUniforms: () => PostUniforms;
322
453
  //#endregion
454
+ //#region src/post/toSceneColor.d.ts
455
+ export declare const toSceneColor: (displayColor: Color) => Color;
456
+ //#endregion
457
+ //#region src/post/toneMapNeutral.d.ts
458
+ export declare const toneMapNeutral: ([red, green, blue]: [number, number, number]) => [number, number, number];
459
+ //#endregion
323
460
  //#region src/random/createSeededRandom.d.ts
324
461
  export declare const createSeededRandom: (seed: number) => (() => number);
325
462
  //#endregion
@@ -333,6 +470,44 @@ export declare enum QualityTier {
333
470
  //#region src/renderer/QualityTierSettingsMap.d.ts
334
471
  export declare const QualityTierSettingsMap: Readonly<Record<QualityTier, QualityTierSettings>>;
335
472
  //#endregion
473
+ //#region src/renderer/constants.d.ts
474
+ export declare const GENSHIN_TONE_MAPPING: ToneMapping;
475
+ //#endregion
476
+ //#region src/renderer/getScreenDirection.d.ts
477
+ export declare const getScreenDirection: ({ aspect, fov, pitch, yaw }: {
478
+ aspect: number;
479
+ fov: number;
480
+ pitch: number;
481
+ yaw: number;
482
+ }, [x, y]: [number, number]) => Vector3;
483
+ //#endregion
484
+ //#region src/scene/SceneMaterial.d.ts
485
+ export interface SceneMaterial {
486
+ colors: Record<string, [number, number, number, number]>;
487
+ floats: Record<string, number>;
488
+ name: string;
489
+ textures: Record<string, {
490
+ name: string;
491
+ offset: [number, number];
492
+ scale: [number, number];
493
+ }>;
494
+ }
495
+ //#endregion
496
+ //#region src/scene/ScenePlacement.d.ts
497
+ export interface ScenePlacement {
498
+ materials: string[];
499
+ mesh: string;
500
+ position: [number, number, number];
501
+ rotation: [number, number, number, number];
502
+ scale: [number, number, number];
503
+ }
504
+ //#endregion
505
+ //#region src/scene/SceneLayout.d.ts
506
+ export interface SceneLayout {
507
+ materials: Record<string, SceneMaterial>;
508
+ placements: ScenePlacement[];
509
+ }
510
+ //#endregion
336
511
  //#region src/terrain/TerrainSelection.d.ts
337
512
  export interface TerrainSelection {
338
513
  count: number;
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
- import { Box3, BufferAttribute, BufferGeometry, Color, CylinderGeometry, Data3DTexture, DataTexture, DirectionalLight, DoubleSide, HalfFloatType, InstancedBufferGeometry, LatheGeometry, LinearFilter, OrthographicCamera, Quaternion, RedFormat, Vector2, Vector3 } from "three";
2
- import { Fn, If, attribute, cameraFar, cameraNear, cameraPosition, cameraViewMatrix, cos, exp, exp2, float, getViewPosition, hash, instanceIndex, max, mix, modelViewMatrix, modelWorldMatrix, mrt, mx_cell_noise_float, mx_fractal_noise_float, mx_noise_float, normalView, normalWorldGeometry, output, perspectiveDepthToViewZ, positionGeometry, positionLocal, positionView, positionViewDirection, positionWorld, pow, reflect, renderOutput, screenUV, sin, smoothstep, step, texture, texture3D, time, uniform, uv, varying, vec2, vec3, vec4, velocity, viewportDepthTexture, viewportSharedTexture } from "three/tsl";
1
+ import { Box3, BoxGeometry, BufferAttribute, BufferGeometry, CanvasTexture, ClampToEdgeWrapping, Color, CylinderGeometry, Data3DTexture, DataTexture, DirectionalLight, DoubleSide, HalfFloatType, InstancedBufferAttribute, InstancedBufferGeometry, LatheGeometry, LinearFilter, MathUtils, Matrix4, NeutralToneMapping, NoColorSpace, OrthographicCamera, Quaternion, RGBAFormat, RedFormat, Sprite, UnsignedByteType, Vector2, Vector3 } from "three";
2
+ import { Fn, If, abs, asin, attribute, cameraFar, cameraNear, cameraPosition, cameraViewMatrix, clamp, color, cos, exp, exp2, float, getViewPosition, hash, instanceIndex, instancedBufferAttribute, max, min, mix, modelViewMatrix, modelWorldMatrix, mrt, mx_cell_noise_float, mx_fractal_noise_float, mx_noise_float, normalView, normalWorldGeometry, output, perspectiveDepthToViewZ, positionGeometry, positionLocal, positionView, positionViewDirection, positionWorld, pow, reflect, renderOutput, saturate, screenUV, sin, smoothstep, step, texture, texture3D, time, uniform, uv, varying, vec2, vec3, vec4, velocity, viewportDepthTexture, viewportSharedTexture } from "three/tsl";
3
+ import { MeshBasicNodeMaterial, MeshPhysicalNodeMaterial, MeshToonNodeMaterial, RenderPipeline, RenderTarget, SpriteNodeMaterial, ToonOutlinePassNode, WebGPURenderer } from "three/webgpu";
3
4
  import { CSMShadowNode } from "three/examples/jsm/csm/CSMShadowNode.js";
4
5
  import { mergeGeometries } from "three/examples/jsm/utils/BufferGeometryUtils.js";
5
- import { MeshBasicNodeMaterial, MeshToonNodeMaterial, NeutralToneMapping, RenderPipeline, RenderTarget, ToonOutlinePassNode, WebGPURenderer } from "three/webgpu";
6
6
  import { voronoi2d } from "three/examples/jsm/tsl/math/voronoiNoise.js";
7
7
  import { bilateralBlur } from "three/examples/jsm/tsl/display/BilateralBlurNode.js";
8
8
  import { bloom } from "three/examples/jsm/tsl/display/BloomNode.js";
@@ -11,9 +11,72 @@ import { godrays } from "three/examples/jsm/tsl/display/GodraysNode.js";
11
11
  import { lut3D } from "three/examples/jsm/tsl/display/Lut3DNode.js";
12
12
  import { smaa } from "three/examples/jsm/tsl/display/SMAANode.js";
13
13
  import { traa } from "three/examples/jsm/tsl/display/TRAANode.js";
14
+ //#region src/atmosphere/constants.ts
15
+ const DEFAULT_SKY_SHAPE = {
16
+ frontBackBlend: 1,
17
+ haloHeight: 1,
18
+ horizonBand: .45,
19
+ moonSize: 1,
20
+ sunHaloSize: 1
21
+ };
22
+ const DEFAULT_SAMPLE_COUNT = 33;
23
+ const DEFAULT_SKY_GRADIENT = {
24
+ green: Array.from({ length: DEFAULT_SAMPLE_COUNT }, () => 0),
25
+ red: Array.from({ length: DEFAULT_SAMPLE_COUNT }, (_, index) => {
26
+ const share = index / 32;
27
+ return 1 - share * share * (3 - 2 * share);
28
+ })
29
+ };
30
+ //#endregion
31
+ //#region src/post/toneMapNeutral.ts
32
+ const TOE_END = .08;
33
+ const TOE_OFFSET = .04;
34
+ const COMPRESSION_START = .76;
35
+ const DESATURATION = .15;
36
+ const toneMapNeutral = ([red, green, blue]) => {
37
+ const lowest = Math.min(red, green, blue);
38
+ const offset = lowest < TOE_END ? lowest - 6.25 * lowest * lowest : TOE_OFFSET;
39
+ const offsetColor = [
40
+ red - offset,
41
+ green - offset,
42
+ blue - offset
43
+ ];
44
+ const peak = Math.max(...offsetColor);
45
+ if (peak < COMPRESSION_START) return offsetColor;
46
+ const headroom = .24;
47
+ const newPeak = 1 - headroom * headroom / (peak + headroom - COMPRESSION_START);
48
+ const whiteShare = 1 - 1 / (DESATURATION * (peak - newPeak) + 1);
49
+ return offsetColor.map((channel) => channel * newPeak / peak + (newPeak - channel * newPeak / peak) * whiteShare);
50
+ };
51
+ //#endregion
52
+ //#region src/post/toSceneColor.ts
53
+ const INVERSE_STEP_COUNT = 16;
54
+ const SLOPE_NUDGE = 1e-4;
55
+ const SCENE_CHANNEL_LIMIT = 1.5;
56
+ const toSceneColor = (displayColor) => {
57
+ const target = [
58
+ displayColor.r,
59
+ displayColor.g,
60
+ displayColor.b
61
+ ];
62
+ let input = [...target];
63
+ for (let step = 0; step < INVERSE_STEP_COUNT; step++) {
64
+ const current = input;
65
+ const output = toneMapNeutral(current);
66
+ input = current.map((channel, index) => {
67
+ const nudged = [...current];
68
+ nudged[index] = channel + SLOPE_NUDGE;
69
+ const slope = ((toneMapNeutral(nudged)[index] ?? 0) - (output[index] ?? 0)) / SLOPE_NUDGE;
70
+ return Math.min(Math.max(channel + ((target[index] ?? 0) - (output[index] ?? 0)) / slope, 0), SCENE_CHANNEL_LIMIT);
71
+ });
72
+ }
73
+ return new Color(...input);
74
+ };
75
+ //#endregion
14
76
  //#region src/atmosphere/applySkyState.ts
15
77
  const GODRAYS_REDRAW_COSINE = Math.cos(.6 * Math.PI / 180);
16
78
  const godraysDirection = new Vector3();
79
+ const BLACK = new Color(0, 0, 0);
17
80
  const applySkyState = (skyState, { fogUniforms, godraysLight, hemisphere, light, lightDistance, lightUniforms, postUniforms, skyUniforms }) => {
18
81
  const { horizonColor, lightColor, lightDirection } = skyState;
19
82
  light.position.copy(light.target.position).addScaledVector(lightDirection, lightDistance);
@@ -25,16 +88,30 @@ const applySkyState = (skyState, { fogUniforms, godraysLight, hemisphere, light,
25
88
  lightUniforms.lightColor.value.copy(lightColor);
26
89
  lightUniforms.sunDirection.value.copy(lightDirection);
27
90
  lightUniforms.rimColor.value.copy(horizonColor);
28
- fogUniforms.color.value.copy(horizonColor);
91
+ fogUniforms.color.value.copy(toSceneColor(skyState.fogColor ?? horizonColor));
92
+ fogUniforms.scatterColor.value.copy(toSceneColor(lightColor));
93
+ fogUniforms.scatterDirection.value.copy(lightDirection);
29
94
  postUniforms.godraysColor.value.copy(lightColor);
30
- skyUniforms.cloudLitColor.value.copy(skyState.cloudLitColor);
31
- skyUniforms.cloudShadeColor.value.copy(skyState.cloudShadeColor);
32
- skyUniforms.horizonColor.value.copy(horizonColor);
95
+ skyUniforms.cloudLitColor.value.copy(toSceneColor(skyState.cloudLitColor));
96
+ skyUniforms.cloudShadeColor.value.copy(toSceneColor(skyState.cloudShadeColor));
97
+ skyUniforms.cloudLitBackColor.value.copy(toSceneColor(skyState.cloudLitBackColor ?? skyState.cloudLitColor));
98
+ skyUniforms.cloudShadeBackColor.value.copy(toSceneColor(skyState.cloudShadeBackColor ?? skyState.cloudShadeColor));
99
+ skyUniforms.horizonColor.value.copy(toSceneColor(horizonColor));
100
+ skyUniforms.horizonBackColor.value.copy(toSceneColor(skyState.horizonBackColor ?? horizonColor));
101
+ skyUniforms.zenithBackColor.value.copy(toSceneColor(skyState.zenithBackColor ?? skyState.zenithColor));
102
+ skyUniforms.haloColor.value.copy(toSceneColor(skyState.haloColor ?? BLACK));
103
+ skyUniforms.sunHaloColor.value.copy(toSceneColor(skyState.sunHaloColor ?? BLACK));
33
104
  skyUniforms.lightColor.value.copy(lightColor);
34
105
  skyUniforms.moonDirection.value.copy(skyState.moonDirection);
35
106
  skyUniforms.starIntensity.value = skyState.starIntensity;
36
107
  skyUniforms.sunDirection.value.copy(skyState.sunDirection);
37
- skyUniforms.zenithColor.value.copy(skyState.zenithColor);
108
+ skyUniforms.zenithColor.value.copy(toSceneColor(skyState.zenithColor));
109
+ const { frontBackBlend, haloHeight, horizonBand, moonSize, sunHaloSize } = skyState.shape ?? DEFAULT_SKY_SHAPE;
110
+ skyUniforms.frontBackBlend.value = frontBackBlend;
111
+ skyUniforms.haloHeight.value = haloHeight;
112
+ skyUniforms.horizonBand.value = horizonBand;
113
+ skyUniforms.moonSize.value = moonSize;
114
+ skyUniforms.sunHaloSize.value = sunHaloSize;
38
115
  godraysDirection.subVectors(godraysLight.position, godraysLight.target.position).normalize();
39
116
  if (godraysDirection.dot(lightDirection) >= GODRAYS_REDRAW_COSINE) return;
40
117
  godraysLight.position.copy(godraysLight.target.position).addScaledVector(lightDirection, lightDistance);
@@ -52,6 +129,135 @@ const computeSunDirection = (minutes, tilt, sunDirection) => {
52
129
  return sunDirection.set(Math.sin(hourAngle), height * Math.cos(tilt), height * Math.sin(tilt));
53
130
  };
54
131
  //#endregion
132
+ //#region src/materials/addSmoothLoop.ts
133
+ const addSmoothLoop = (path, points) => {
134
+ if (points.length < 3) return;
135
+ const readMidpoint = (index) => {
136
+ const [startX = 0, startY = 0] = points[index % points.length] ?? [];
137
+ const [endX = 0, endY = 0] = points[(index + 1) % points.length] ?? [];
138
+ return [(startX + endX) / 2, (startY + endY) / 2];
139
+ };
140
+ path.moveTo(...readMidpoint(points.length - 1));
141
+ for (const [index, [x, y]] of points.entries()) path.quadraticCurveTo(x, y, ...readMidpoint(index));
142
+ path.closePath();
143
+ };
144
+ //#endregion
145
+ //#region src/atmosphere/createCloudAtlasTexture.ts
146
+ const EDGE_BLUR_SHARE = .012;
147
+ const getCloudAtlasColumns = (spriteCount) => Math.ceil(Math.sqrt(spriteCount));
148
+ const createCloudAtlasTexture = (sprites, cellSize) => {
149
+ const columns = getCloudAtlasColumns(sprites.length);
150
+ const canvas = new OffscreenCanvas(columns * cellSize, columns * cellSize);
151
+ const context = canvas.getContext("2d");
152
+ if (context) {
153
+ context.globalCompositeOperation = "lighter";
154
+ context.filter = `blur(${cellSize * EDGE_BLUR_SHARE}px)`;
155
+ const fill = (loops, column, row, style) => {
156
+ const path = new Path2D();
157
+ const toCanvas = ([x, y]) => [(column + x) * cellSize, (row + 1 - y) * cellSize];
158
+ for (const loop of loops) addSmoothLoop(path, loop.map((point) => toCanvas(point)));
159
+ context.fillStyle = style;
160
+ context.fill(path, "evenodd");
161
+ };
162
+ for (const [index, { lit, outline }] of sprites.entries()) {
163
+ const column = index % columns;
164
+ const row = Math.floor(index / columns);
165
+ fill(outline, column, row, "#f00");
166
+ fill(lit, column, row, "#0f0");
167
+ }
168
+ }
169
+ const texture = new CanvasTexture(canvas);
170
+ texture.colorSpace = NoColorSpace;
171
+ texture.magFilter = LinearFilter;
172
+ texture.minFilter = LinearFilter;
173
+ texture.generateMipmaps = false;
174
+ return texture;
175
+ };
176
+ //#endregion
177
+ //#region src/atmosphere/orderByViewDepth.ts
178
+ const orderByViewDepth = (places, { elements }) => {
179
+ const depths = places.map(([x = 0, y = 0, z = 0]) => elements[2] * x + elements[6] * y + elements[10] * z + elements[14]);
180
+ return depths.map((_, index) => index).toSorted((first, second) => (depths[first] ?? 0) - (depths[second] ?? 0));
181
+ };
182
+ //#endregion
183
+ //#region src/atmosphere/createCloudBandSprite.ts
184
+ const BELOW_FADE_START = .1;
185
+ const BELOW_FADE_SCALE = 5;
186
+ const COVERAGE_LIGHT_SHARE = .4;
187
+ const EDGE_FADE_START = .15;
188
+ const EDGE_FADE_END = .85;
189
+ const DROPPED_COVERAGE = .01;
190
+ const createCloudBandSprite = (atlas, clouds, { aspect, spriteCount }, skyUniforms) => {
191
+ const { cloudCoverage, cloudFrontBackBlend, cloudLitBackColor, cloudLitColor, cloudShadeBackColor, cloudShadeColor, cloudSunBrighten, sunDirection } = skyUniforms;
192
+ const columns = getCloudAtlasColumns(spriteCount);
193
+ const places = clouds.map(({ position }) => [...position]);
194
+ const cloudScales = clouds.map(({ width }) => [width, width / aspect]);
195
+ const cloudCells = clouds.map(({ spriteIndex }) => [spriteIndex % columns, columns - 1 - Math.floor(spriteIndex / columns)]);
196
+ const positions = new InstancedBufferAttribute(new Float32Array(clouds.length * 3), 3);
197
+ const scales = new InstancedBufferAttribute(new Float32Array(clouds.length * 2), 2);
198
+ const cells = new InstancedBufferAttribute(new Float32Array(clouds.length * 2), 2);
199
+ const mask = texture(atlas, uv().add(instancedBufferAttribute(cells)).div(columns));
200
+ const place = instancedBufferAttribute(positions);
201
+ const direction = place.sub(cameraPosition).normalize();
202
+ const elevation = asin(clamp(direction.y, -1, 1)).mul(2 / Math.PI);
203
+ const sunCosine = direction.dot(sunDirection);
204
+ const toward = pow(max(sunCosine.mul(cloudFrontBackBlend).add(float(1).sub(cloudFrontBackBlend)), 0), 3);
205
+ const lit = mix(cloudLitBackColor, cloudLitColor, toward);
206
+ const shade = mix(cloudShadeBackColor, cloudShadeColor, toward);
207
+ const cloudColor = mix(shade, lit, mask.g).add(lit.mul(cloudCoverage.mul(COVERAGE_LIGHT_SHARE))).mul(float(1).add(cloudSunBrighten.mul(sunCosine.mul(.5).add(.5))));
208
+ const material = new SpriteNodeMaterial({
209
+ depthWrite: false,
210
+ transparent: true
211
+ });
212
+ material.positionNode = place;
213
+ material.scaleNode = instancedBufferAttribute(scales);
214
+ material.colorNode = cloudColor;
215
+ material.opacityNode = smoothstep(EDGE_FADE_START, EDGE_FADE_END, mask.r).mul(smoothstep(0, 1, saturate(elevation.add(BELOW_FADE_START).mul(BELOW_FADE_SCALE))));
216
+ material.alphaTest = DROPPED_COVERAGE;
217
+ const sprite = new Sprite(material);
218
+ sprite.center.set(.5, 0);
219
+ sprite.count = clouds.length;
220
+ sprite.frustumCulled = false;
221
+ const modelViewMatrix = new Matrix4();
222
+ sprite.onBeforeRender = (_renderer, _scene, camera) => {
223
+ modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse, sprite.matrixWorld);
224
+ for (const [instance, cloud] of orderByViewDepth(places, modelViewMatrix).entries()) {
225
+ positions.set(places[cloud] ?? [], instance * 3);
226
+ scales.set(cloudScales[cloud] ?? [], instance * 2);
227
+ cells.set(cloudCells[cloud] ?? [], instance * 2);
228
+ }
229
+ positions.needsUpdate = true;
230
+ scales.needsUpdate = true;
231
+ cells.needsUpdate = true;
232
+ };
233
+ return {
234
+ dispose: () => {
235
+ material.dispose();
236
+ },
237
+ places,
238
+ sprite
239
+ };
240
+ };
241
+ //#endregion
242
+ //#region src/atmosphere/createSkyGradientTexture.ts
243
+ const BYTE = 255;
244
+ const createSkyGradientTexture = ({ green, red }) => {
245
+ const width = Math.max(red.length, green.length, 1);
246
+ const data = new Uint8Array(width * 4);
247
+ for (let index = 0; index < width; index++) {
248
+ data[index * 4] = Math.round((red[index] ?? 0) * BYTE);
249
+ data[index * 4 + 1] = Math.round((green[index] ?? 0) * BYTE);
250
+ data[index * 4 + 3] = BYTE;
251
+ }
252
+ const texture = new DataTexture(data, width, 1, RGBAFormat, UnsignedByteType);
253
+ texture.magFilter = LinearFilter;
254
+ texture.minFilter = LinearFilter;
255
+ texture.wrapS = ClampToEdgeWrapping;
256
+ texture.wrapT = ClampToEdgeWrapping;
257
+ texture.needsUpdate = true;
258
+ return texture;
259
+ };
260
+ //#endregion
55
261
  //#region src/atmosphere/createSkyState.ts
56
262
  const createSkyState = () => ({
57
263
  cloudLitColor: new Color(),
@@ -73,13 +279,27 @@ const createSkyState = () => ({
73
279
  const createSkyUniforms = () => ({
74
280
  cloudCoverage: uniform(0),
75
281
  cloudDrift: uniform(new Vector2()),
282
+ cloudFrontBackBlend: uniform(1),
283
+ cloudLitBackColor: uniform(new Color()),
76
284
  cloudLitColor: uniform(new Color()),
285
+ cloudShadeBackColor: uniform(new Color()),
77
286
  cloudShadeColor: uniform(new Color()),
287
+ cloudSunBrighten: uniform(0),
288
+ frontBackBlend: uniform(DEFAULT_SKY_SHAPE.frontBackBlend),
289
+ haloColor: uniform(new Color(0, 0, 0)),
290
+ haloHeight: uniform(DEFAULT_SKY_SHAPE.haloHeight),
291
+ horizonBackColor: uniform(new Color()),
292
+ horizonBand: uniform(DEFAULT_SKY_SHAPE.horizonBand),
78
293
  horizonColor: uniform(new Color()),
79
294
  lightColor: uniform(new Color()),
80
295
  moonDirection: uniform(new Vector3(0, -1, 0)),
296
+ moonGlowColor: uniform(new Color(0, 0, 0)),
297
+ moonSize: uniform(DEFAULT_SKY_SHAPE.moonSize),
81
298
  starIntensity: uniform(0),
82
299
  sunDirection: uniform(new Vector3(0, 1, 0)),
300
+ sunHaloColor: uniform(new Color(0, 0, 0)),
301
+ sunHaloSize: uniform(DEFAULT_SKY_SHAPE.sunHaloSize),
302
+ zenithBackColor: uniform(new Color()),
83
303
  zenithColor: uniform(new Color())
84
304
  });
85
305
  //#endregion
@@ -108,6 +328,36 @@ const createSunLight = ({ cascadeCount, maxFar, shadowMapSize }) => {
108
328
  };
109
329
  };
110
330
  //#endregion
331
+ //#region src/random/createSeededRandom.ts
332
+ const createSeededRandom = (seed) => {
333
+ let state = seed;
334
+ return () => {
335
+ state = state + 1831565813 >>> 0;
336
+ let mixed = Math.imul(state ^ state >>> 15, 1 | state);
337
+ mixed ^= mixed + Math.imul(mixed ^ mixed >>> 7, 61 | mixed);
338
+ return ((mixed ^ mixed >>> 14) >>> 0) / 2 ** 32;
339
+ };
340
+ };
341
+ //#endregion
342
+ //#region src/atmosphere/placeCloudBand.ts
343
+ const placeCloudBand = ({ count, distanceRange: [near, far], heightRange: [low, high], seed, widthRange: [narrow, wide] }, spriteCount) => {
344
+ const random = createSeededRandom(seed);
345
+ return Array.from({ length: count }, () => {
346
+ const angle = random() * Math.PI * 2;
347
+ const distance = Math.sqrt(near ** 2 + random() * (far ** 2 - near ** 2));
348
+ const height = low + random() * (high - low);
349
+ return {
350
+ position: [
351
+ Math.cos(angle) * distance,
352
+ height,
353
+ Math.sin(angle) * distance
354
+ ],
355
+ spriteIndex: Math.floor(random() * spriteCount),
356
+ width: narrow + random() * (wide - narrow)
357
+ };
358
+ });
359
+ };
360
+ //#endregion
111
361
  //#region src/atmosphere/sampleSkyState.ts
112
362
  const lerp = (from, to, amount) => from + (to - from) * amount;
113
363
  const sampleSkyState = (skyKeyframes, minutes, tilt, skyState) => {
@@ -140,6 +390,28 @@ const advanceGameClock = (gameClock, deltaSeconds) => {
140
390
  gameClock.minutes = (gameClock.minutes + deltaSeconds * gameClock.minutesPerSecond) % MINUTES_PER_DAY;
141
391
  };
142
392
  //#endregion
393
+ //#region src/kits/architecture/createBoxesGeometry.ts
394
+ const createBoxesGeometry = (boxes) => {
395
+ const parts = boxes.map(([minX = 0, minY = 0, minZ = 0, maxX = 0, maxY = 0, maxZ = 0]) => new BoxGeometry(maxX - minX, maxY - minY, maxZ - minZ).translate((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2));
396
+ const geometry = mergeGeometries(parts) ?? new BufferGeometry();
397
+ for (const part of parts) part.dispose();
398
+ return geometry;
399
+ };
400
+ //#endregion
401
+ //#region src/kits/architecture/createLatheStackGeometry.ts
402
+ const createLatheStackGeometry = ({ isFaceted, radialSegments, sections }) => {
403
+ let footHeight = 0;
404
+ const parts = sections.map(({ bottomRadius, height, topRadius }) => {
405
+ const part = new CylinderGeometry(topRadius, bottomRadius, height, radialSegments).translate(0, footHeight + height / 2, 0);
406
+ footHeight += height;
407
+ return isFaceted ? part.toNonIndexed() : part;
408
+ });
409
+ const stackGeometry = mergeGeometries(parts) ?? new BufferGeometry();
410
+ for (const part of parts) part.dispose();
411
+ if (isFaceted) stackGeometry.computeVertexNormals();
412
+ return stackGeometry;
413
+ };
414
+ //#endregion
143
415
  //#region src/kits/statue/createStatueGeometry.ts
144
416
  const PLINTH_SIDES = 8;
145
417
  const FIGURE_SEGMENTS = 24;
@@ -179,17 +451,6 @@ const createStatueGeometry = () => {
179
451
  return statueGeometry;
180
452
  };
181
453
  //#endregion
182
- //#region src/random/createSeededRandom.ts
183
- const createSeededRandom = (seed) => {
184
- let state = seed;
185
- return () => {
186
- state = state + 1831565813 >>> 0;
187
- let mixed = Math.imul(state ^ state >>> 15, 1 | state);
188
- mixed ^= mixed + Math.imul(mixed ^ mixed >>> 7, 61 | mixed);
189
- return ((mixed ^ mixed >>> 14) >>> 0) / 2 ** 32;
190
- };
191
- };
192
- //#endregion
193
454
  //#region src/kits/tree/computeLeafCards.ts
194
455
  const VERTICES_PER_CARD = 4;
195
456
  const INDICES_PER_CARD = 6;
@@ -379,14 +640,18 @@ const createCausticsNode = ({ lightColor }, { causticStrength, deepDepth, level
379
640
  return lightColor.mul(veins.mul(fade).mul(causticStrength));
380
641
  };
381
642
  //#endregion
643
+ //#region src/renderer/constants.ts
644
+ const GENSHIN_TONE_MAPPING = NeutralToneMapping;
645
+ //#endregion
382
646
  //#region src/nodes/createGenshinRenderer.ts
383
647
  const createGenshinRenderer = (canvas) => {
648
+ const isWindows = window.navigator.userAgent.includes("Windows");
384
649
  const renderer = new WebGPURenderer({
385
650
  antialias: false,
386
651
  canvas,
387
- powerPreference: "high-performance"
652
+ ...isWindows ? {} : { powerPreference: "high-performance" }
388
653
  });
389
- renderer.toneMapping = NeutralToneMapping;
654
+ renderer.toneMapping = GENSHIN_TONE_MAPPING;
390
655
  return renderer;
391
656
  };
392
657
  //#endregion
@@ -402,7 +667,7 @@ const createRimNode = ({ rimColor, rimStrength, sunDirection }) => {
402
667
  //#region src/nodes/createToonMaterial.ts
403
668
  const createToonMaterial = ({ color, isOutlined = true, isVertexColors = false, lightUniforms, rampTexture }) => {
404
669
  const toonMaterial = new ToonNodeMaterial({
405
- color,
670
+ ...color === void 0 ? {} : { color },
406
671
  gradientMap: rampTexture,
407
672
  vertexColors: isVertexColors
408
673
  }, isOutlined);
@@ -449,12 +714,37 @@ const createLightUniforms = () => ({
449
714
  sunDirection: uniform(new Vector3(0, 1, 0))
450
715
  });
451
716
  //#endregion
717
+ //#region src/nodes/createSkyColorNode.ts
718
+ const LEAST_DIVISOR$1 = 1e-4;
719
+ const HALO_TOWARD_START = .3;
720
+ const HALO_TOWARD_SPAN = .7;
721
+ const HALO_SUN_HEIGHT_START = .2;
722
+ const HALO_SUN_HEIGHT_SPAN = .3;
723
+ const SUN_HALO_MIDDLE_SPREAD = .1;
724
+ const SUN_HALO_MIDDLE_WEIGHT = .12;
725
+ const SUN_HALO_OUTER_SPREAD = .01;
726
+ const SUN_HALO_OUTER_WEIGHT = .03;
727
+ const createSkyColorNode = ({ frontBackBlend, haloColor, haloHeight, horizonBackColor, horizonBand, horizonColor, sunDirection, sunHaloColor, sunHaloSize, zenithBackColor, zenithColor }, gradient, direction) => {
728
+ const height = direction.y;
729
+ const elevation = abs(asin(clamp(height, -1, 1)).mul(2 / Math.PI));
730
+ const sunCosine = direction.dot(sunDirection);
731
+ const toward = pow(max(sunCosine.mul(frontBackBlend).add(float(1).sub(frontBackBlend)), 0), 3);
732
+ const top = mix(zenithBackColor, zenithColor, toward);
733
+ const bottom = mix(horizonBackColor, horizonColor, toward);
734
+ const bottomShare = texture(gradient, vec2(elevation.div(max(horizonBand, LEAST_DIVISOR$1)), .5)).r;
735
+ const haloShare = texture(gradient, vec2(elevation.div(max(haloHeight, LEAST_DIVISOR$1)), .5)).g;
736
+ const sunSide = saturate(sunCosine.mul(.5).add(.5));
737
+ const towardSun = smoothstep(0, 1, max(sunSide.sub(HALO_TOWARD_START).div(HALO_TOWARD_SPAN), 0));
738
+ const sunUp = smoothstep(0, 1, saturate(abs(sunDirection.y).sub(HALO_SUN_HEIGHT_START).div(HALO_SUN_HEIGHT_SPAN)));
739
+ const halo = haloColor.mul(haloShare).mul(sunUp.mul(float(1).sub(towardSun)).add(towardSun));
740
+ const spread = sunHaloSize.mul(abs(height));
741
+ const sunHalo = min(pow(sunSide, spread), 1).add(min(pow(sunSide, spread.mul(SUN_HALO_MIDDLE_SPREAD)), 1).mul(SUN_HALO_MIDDLE_WEIGHT)).add(min(pow(sunSide, spread.mul(SUN_HALO_OUTER_SPREAD)), 1).mul(SUN_HALO_OUTER_WEIGHT)).mul(smoothstep(0, 1, max(sunSide.sub(.5).mul(2), 0)));
742
+ return mix(top, bottom, bottomShare).add(halo).add(sunHaloColor.mul(sunHalo));
743
+ };
744
+ //#endregion
452
745
  //#region src/nodes/createSkyNode.ts
453
- const HORIZON_BAND = .45;
454
746
  const SUN_DISC_COSINE = Math.cos(.035);
455
747
  const MOON_DISC_COSINE = Math.cos(.05);
456
- const SUN_GLOW_POWER = 12;
457
- const HORIZON_GLOW_POWER = 3;
458
748
  const SUN_DISC_BRIGHTNESS = 6;
459
749
  const CLOUD_SCALE = 1.6;
460
750
  const CLOUD_MIN_HEIGHT = .04;
@@ -462,26 +752,46 @@ const CLOUD_EDGE = .04;
462
752
  const CLOUD_SHADE_DEPTH = .18;
463
753
  const STAR_SCALE = 420;
464
754
  const STAR_THRESHOLD = .9975;
465
- const createSkyNode = ({ cloudCoverage, cloudDrift, cloudLitColor, cloudShadeColor, horizonColor, lightColor, moonDirection, starIntensity, sunDirection, zenithColor }) => Fn(() => {
466
- const direction = normalWorldGeometry.normalize();
467
- const height = direction.y;
468
- const gradient = mix(horizonColor, zenithColor, smoothstep(0, HORIZON_BAND, height));
469
- const sunCosine = direction.dot(sunDirection);
470
- const sunVisibility = smoothstep(-.1, .05, sunDirection.y);
471
- const sunGlow = pow(max(sunCosine, 0), SUN_GLOW_POWER).mul(sunVisibility);
472
- const horizonGlow = pow(max(sunCosine, 0), HORIZON_GLOW_POWER).mul(float(1).sub(smoothstep(0, .3, height.abs()))).mul(float(1).sub(smoothstep(0, .5, sunDirection.y))).mul(sunVisibility);
473
- const sunDisc = smoothstep(SUN_DISC_COSINE - 5e-4, SUN_DISC_COSINE, sunCosine).mul(sunVisibility);
474
- const moonDisc = smoothstep(MOON_DISC_COSINE - 5e-4, MOON_DISC_COSINE, direction.dot(moonDirection)).mul(smoothstep(-.05, .05, moonDirection.y));
475
- const stars = smoothstep(STAR_THRESHOLD, 1, mx_cell_noise_float(direction.mul(STAR_SCALE))).mul(starIntensity).mul(smoothstep(0, .1, height));
476
- const cloudPosition = direction.xz.div(max(height, CLOUD_MIN_HEIGHT)).mul(CLOUD_SCALE).add(cloudDrift);
477
- const cloudNoise = mx_fractal_noise_float(vec3(cloudPosition, 0), 4, 2, .5).mul(.5).add(.5);
478
- const cloudThreshold = float(1).sub(cloudCoverage);
479
- const cloudDensity = smoothstep(cloudThreshold, cloudThreshold.add(CLOUD_EDGE), cloudNoise).mul(smoothstep(0, .12, height));
480
- const cloudShade = smoothstep(cloudThreshold.add(CLOUD_SHADE_DEPTH), cloudThreshold.add(.22), cloudNoise);
481
- const cloudColor = mix(cloudLitColor, cloudShadeColor, cloudShade).add(lightColor.mul(sunGlow.mul(.6)));
482
- const sky = gradient.add(lightColor.mul(sunGlow.mul(.35).add(horizonGlow.mul(.45)))).add(vec3(stars)).add(vec3(.85, .88, 1).mul(moonDisc)).add(lightColor.mul(sunDisc.mul(SUN_DISC_BRIGHTNESS)));
483
- return mix(sky, cloudColor, cloudDensity);
484
- })();
755
+ const LEAST_DIVISOR = 1e-4;
756
+ const MOON_GLOW_SPREAD = .1;
757
+ const MOON_GLOW_POWER = 6;
758
+ const createSkyNode = (uniforms, gradient) => {
759
+ const { cloudCoverage, cloudDrift, cloudLitColor, cloudShadeColor, lightColor, moonDirection, moonGlowColor, moonSize, starIntensity, sunDirection } = uniforms;
760
+ return Fn(() => {
761
+ const direction = normalWorldGeometry.normalize();
762
+ const height = direction.y;
763
+ const sunCosine = direction.dot(sunDirection);
764
+ const gradientColor = createSkyColorNode(uniforms, gradient, direction);
765
+ const moonCosine = saturate(direction.dot(moonDirection));
766
+ const moonGlow = pow(max(moonCosine.sub(1).div(max(moonSize.mul(MOON_GLOW_SPREAD), LEAST_DIVISOR)).add(1), 0), MOON_GLOW_POWER);
767
+ const sunVisibility = smoothstep(-.1, .05, sunDirection.y);
768
+ const sunDisc = smoothstep(SUN_DISC_COSINE - 5e-4, SUN_DISC_COSINE, sunCosine).mul(sunVisibility);
769
+ const moonDisc = smoothstep(MOON_DISC_COSINE - 5e-4, MOON_DISC_COSINE, direction.dot(moonDirection)).mul(smoothstep(-.05, .05, moonDirection.y));
770
+ const stars = smoothstep(STAR_THRESHOLD, 1, mx_cell_noise_float(direction.mul(STAR_SCALE))).mul(starIntensity).mul(smoothstep(0, .1, height));
771
+ const cloudPosition = direction.xz.div(max(height, CLOUD_MIN_HEIGHT)).mul(CLOUD_SCALE).add(cloudDrift);
772
+ const cloudNoise = mx_fractal_noise_float(vec3(cloudPosition, 0), 4, 2, .5).mul(.5).add(.5);
773
+ const cloudThreshold = float(1).sub(cloudCoverage);
774
+ const cloudDensity = smoothstep(cloudThreshold, cloudThreshold.add(CLOUD_EDGE), cloudNoise).mul(smoothstep(0, .12, height));
775
+ const cloudShade = smoothstep(cloudThreshold.add(CLOUD_SHADE_DEPTH), cloudThreshold.add(.22), cloudNoise);
776
+ const cloudColor = mix(cloudLitColor, cloudShadeColor, cloudShade);
777
+ const sky = gradientColor.add(moonGlowColor.mul(moonGlow)).add(vec3(stars)).add(vec3(.85, .88, 1).mul(moonDisc)).add(lightColor.mul(sunDisc.mul(SUN_DISC_BRIGHTNESS)));
778
+ return mix(sky, cloudColor, cloudDensity);
779
+ })();
780
+ };
781
+ //#endregion
782
+ //#region src/nodes/createStoneMaterial.ts
783
+ const createStoneMaterial = ({ albedo, rimColor, rimPower, rimStrength, smoothness, specularColor }, glowNode = vec3(0)) => {
784
+ const material = new MeshPhysicalNodeMaterial({
785
+ color: new Color(albedo),
786
+ metalness: 0,
787
+ roughness: 1 - smoothness,
788
+ specularColor: new Color().fromArray(specularColor),
789
+ specularIntensity: 1
790
+ });
791
+ const fresnel = float(1).sub(normalView.dot(positionViewDirection).saturate()).pow(rimPower);
792
+ material.emissiveNode = color(new Color().fromArray(rimColor)).mul(fresnel.mul(rimStrength)).add(glowNode);
793
+ return material;
794
+ };
485
795
  //#endregion
486
796
  //#region src/nodes/createWaterMaterial.ts
487
797
  const RIPPLE_SCALE = .08;
@@ -616,6 +926,10 @@ const createFogUniforms = () => ({
616
926
  color: uniform(new Color()),
617
927
  density: uniform(0),
618
928
  heightFalloff: uniform(0),
929
+ scatterColor: uniform(new Color()),
930
+ scatterDirection: uniform(new Vector3(0, 1, 0)),
931
+ scatterPower: uniform(1),
932
+ scatterStrength: uniform(0),
619
933
  startDistance: uniform(0)
620
934
  });
621
935
  //#endregion
@@ -647,7 +961,7 @@ const createGradeLutTexture = (gradeOptions) => {
647
961
  //#endregion
648
962
  //#region src/post/createHeightFogNode.ts
649
963
  const MIN_RAY_CLIMB = 1e-6;
650
- const createHeightFogNode = (colorNode, depthNode, camera, { baseHeight, color, density, heightFalloff, startDistance }) => {
964
+ const createHeightFogNode = (colorNode, depthNode, camera, { baseHeight, color, density, heightFalloff, scatterColor, scatterDirection, scatterPower, scatterStrength, startDistance }) => {
651
965
  const cameraWorldMatrix = uniform(camera.matrixWorld);
652
966
  const cameraProjectionMatrixInverse = uniform(camera.projectionMatrixInverse);
653
967
  return Fn(() => {
@@ -668,7 +982,8 @@ const createHeightFogNode = (colorNode, depthNode, camera, { baseHeight, color,
668
982
  opticalDepth.assign(densityAtStart.mul(float(1).sub(exp(fogLength.mul(climb).negate()))).div(climb));
669
983
  });
670
984
  const opacity = float(1).sub(exp(opticalDepth.negate()));
671
- output.assign(vec4(mix(output.rgb, color, opacity), output.a));
985
+ const scatter = pow(max(ray.normalize().dot(scatterDirection), 0), scatterPower).mul(scatterStrength);
986
+ output.assign(vec4(mix(output.rgb, mix(color, scatterColor, scatter.min(1)), opacity), output.a));
672
987
  });
673
988
  return output;
674
989
  })();
@@ -770,6 +1085,14 @@ const QualityTierSettingsMap = {
770
1085
  }
771
1086
  };
772
1087
  //#endregion
1088
+ //#region src/renderer/getScreenDirection.ts
1089
+ const X_AXIS = new Vector3(1, 0, 0);
1090
+ const Y_AXIS = new Vector3(0, 1, 0);
1091
+ const getScreenDirection = ({ aspect, fov, pitch, yaw }, [x, y]) => {
1092
+ const halfHeight = Math.tan(MathUtils.degToRad(fov) / 2);
1093
+ return new Vector3((x * 2 - 1) * halfHeight * aspect, (1 - y * 2) * halfHeight, -1).normalize().applyAxisAngle(X_AXIS, pitch).applyAxisAngle(Y_AXIS, yaw);
1094
+ };
1095
+ //#endregion
773
1096
  //#region src/terrain/constants.ts
774
1097
  const TILE_INDEX_OFFSET = 2 ** 19;
775
1098
  const TILE_INDEX_SPAN = 2 ** 20;
@@ -1089,6 +1412,7 @@ const createGrassGeometry = (segmentCount, bladeCount) => {
1089
1412
  const { indices, positions } = computeGrassBlade(segmentCount);
1090
1413
  const grassGeometry = new InstancedBufferGeometry();
1091
1414
  grassGeometry.setAttribute("position", new BufferAttribute(positions, 3));
1415
+ grassGeometry.setAttribute("normal", new BufferAttribute(Float32Array.from({ length: positions.length }, (_, index) => index % 3 === 1 ? 1 : 0), 3));
1092
1416
  grassGeometry.setIndex(new BufferAttribute(indices, 1));
1093
1417
  grassGeometry.instanceCount = bladeCount;
1094
1418
  return grassGeometry;
@@ -1255,4 +1579,4 @@ const computeOutlineDistance = (outline, x, z) => {
1255
1579
  return isInside ? 0 : Math.sqrt(nearestSquared);
1256
1580
  };
1257
1581
  //#endregion
1258
- export { AntialiasingMode, GAME_MINUTES_PER_SECOND, MINUTES_PER_DAY, QualityTier, QualityTierSettingsMap, TERRAIN_LAYER, TILE_INDEX_OFFSET, TILE_INDEX_SPAN, ToonNodeMaterial, advanceGameClock, applySkyState, computeGradeLut, computeGrassBlade, computeLeafCards, computeOriginShift, computeOutlineDistance, computeRampValues, computeSunDirection, computeTerrainIndices, computeTerrainTile, computeTreeSkeleton, createCausticsNode, createFogUniforms, createGenshinRenderer, createGodraysLight, createGradeLutTexture, createGrassGeometry, createGrassMaterial, createGroundCapture, createHeightFogNode, createLeafMaterial, createLightUniforms, createPostPipeline, createPostUniforms, createRampTexture, createRimNode, createSeededRandom, createSimplexNoise, createSkyNode, createSkyState, createSkyUniforms, createStatueGeometry, createSunLight, createTerrainMaterial, createTerrainSelection, createTerrainTileGeometry, createTileStreamer, createToonMaterial, createTreeGeometry, createUnderwaterFogState, createWaterMaterial, createWaterUniforms, createWindNode, createWindUniforms, getParentTerrainTileKey, getTerrainTileColumn, getTerrainTileKey, getTerrainTileLevel, getTerrainTileRow, renderGroundCapture, resolveTerrainDraws, sampleSkyState, selectTerrainTiles, updateUnderwaterFog };
1582
+ export { AntialiasingMode, DEFAULT_SKY_GRADIENT, DEFAULT_SKY_SHAPE, GAME_MINUTES_PER_SECOND, GENSHIN_TONE_MAPPING, MINUTES_PER_DAY, QualityTier, QualityTierSettingsMap, TERRAIN_LAYER, TILE_INDEX_OFFSET, TILE_INDEX_SPAN, ToonNodeMaterial, addSmoothLoop, advanceGameClock, applySkyState, computeGradeLut, computeGrassBlade, computeLeafCards, computeOriginShift, computeOutlineDistance, computeRampValues, computeSunDirection, computeTerrainIndices, computeTerrainTile, computeTreeSkeleton, createBoxesGeometry, createCausticsNode, createCloudAtlasTexture, createCloudBandSprite, createFogUniforms, createGenshinRenderer, createGodraysLight, createGradeLutTexture, createGrassGeometry, createGrassMaterial, createGroundCapture, createHeightFogNode, createLatheStackGeometry, createLeafMaterial, createLightUniforms, createPostPipeline, createPostUniforms, createRampTexture, createRimNode, createSeededRandom, createSimplexNoise, createSkyColorNode, createSkyGradientTexture, createSkyNode, createSkyState, createSkyUniforms, createStatueGeometry, createStoneMaterial, createSunLight, createTerrainMaterial, createTerrainSelection, createTerrainTileGeometry, createTileStreamer, createToonMaterial, createTreeGeometry, createUnderwaterFogState, createWaterMaterial, createWaterUniforms, createWindNode, createWindUniforms, getCloudAtlasColumns, getParentTerrainTileKey, getScreenDirection, getTerrainTileColumn, getTerrainTileKey, getTerrainTileLevel, getTerrainTileRow, orderByViewDepth, placeCloudBand, renderGroundCapture, resolveTerrainDraws, sampleSkyState, selectTerrainTiles, toSceneColor, toneMapNeutral, updateUnderwaterFog };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genshin-engine",
3
- "version": "3.7.0",
3
+ "version": "3.9.0",
4
4
  "description": "An anime-style open-world engine for the browser on three.js WebGPU — toon materials, outlines, post-processing, streamed terrain and parametric world kits, as plain TypeScript and TSL modules.",
5
5
  "keywords": [
6
6
  "anime",
@@ -53,16 +53,16 @@
53
53
  },
54
54
  "devDependencies": {
55
55
  "@arethetypeswrong/core": "^0.18.5",
56
- "@esposter/configuration": "3.7.0",
57
- "@esposter/shared-node": "3.7.0",
58
- "@types/node": "^26.6.3",
56
+ "@esposter/configuration": "3.9.0",
57
+ "@esposter/shared-node": "3.9.0",
58
+ "@types/node": "^26.6.4",
59
59
  "@types/three": "^0.186.0",
60
- "publint": "^0.3.24",
60
+ "publint": "^0.3.25",
61
61
  "tsdown": "^0.23.0",
62
- "vitest": "^5.0.2"
62
+ "vitest": "^5.0.3"
63
63
  },
64
64
  "peerDependencies": {
65
65
  "three": "^0.186.1"
66
66
  },
67
- "gitHead": "7fd06688d06adf6dafff4bd869cd7a33d8028688"
67
+ "gitHead": "f9f6208c35e6cff4ed33910623c798cd88f09301"
68
68
  }