genshin-engine 3.7.0 → 3.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,8 +1,23 @@
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, ExtrudeGeometry, Frustum, HemisphereLight, InstancedBufferGeometry, OrthographicCamera, Scene, Texture, ToneMapping, Vector2, Vector3, Vector3Like } from "three";
2
+ import { MeshBasicNodeMaterial, MeshToonNodeMaterial, MeshToonNodeMaterialParameters, Node, RenderPipeline, RenderTarget, Renderer, SpriteNodeMaterial, 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 {
8
23
  cloudLitColor: Color;
@@ -20,6 +35,7 @@ export interface SkyKeyframe {
20
35
  //#endregion
21
36
  //#region src/atmosphere/SkyState.d.ts
22
37
  export interface SkyState extends Omit<SkyKeyframe, "minutes"> {
38
+ fogColor?: Color;
23
39
  lightDirection: Vector3;
24
40
  moonDirection: Vector3;
25
41
  sunDirection: Vector3;
@@ -31,6 +47,7 @@ export interface SkyUniforms {
31
47
  cloudDrift: UniformNode<"vec2", Vector2>;
32
48
  cloudLitColor: UniformNode<"color", Color>;
33
49
  cloudShadeColor: UniformNode<"color", Color>;
50
+ horizonBand: UniformNode<"float", number>;
34
51
  horizonColor: UniformNode<"color", Color>;
35
52
  lightColor: UniformNode<"color", Color>;
36
53
  moonDirection: UniformNode<"vec3", Vector3>;
@@ -53,6 +70,10 @@ export interface FogUniforms {
53
70
  color: UniformNode<"color", Color>;
54
71
  density: UniformNode<"float", number>;
55
72
  heightFalloff: UniformNode<"float", number>;
73
+ scatterColor: UniformNode<"color", Color>;
74
+ scatterDirection: UniformNode<"vec3", Vector3>;
75
+ scatterPower: UniformNode<"float", number>;
76
+ scatterStrength: UniformNode<"float", number>;
56
77
  startDistance: UniformNode<"float", number>;
57
78
  }
58
79
  //#endregion
@@ -96,6 +117,16 @@ export declare const applySkyState: (skyState: SkyState, { fogUniforms, godraysL
96
117
  //#region src/atmosphere/computeSunDirection.d.ts
97
118
  export declare const computeSunDirection: (minutes: number, tilt: number, sunDirection: Vector3) => Vector3;
98
119
  //#endregion
120
+ //#region src/atmosphere/createCloudAtlasTexture.d.ts
121
+ export declare const getCloudAtlasColumns: (spriteCount: number) => number;
122
+ export declare const createCloudAtlasTexture: (sprites: readonly CloudSprite[], cellSize: number) => CanvasTexture<OffscreenCanvas>;
123
+ //#endregion
124
+ //#region src/atmosphere/createCloudSpriteMaterial.d.ts
125
+ export declare const createCloudSpriteMaterial: (atlas: Texture, { index, spriteCount }: {
126
+ index: number;
127
+ spriteCount: number;
128
+ }, { cloudLitColor, cloudShadeColor }: Pick<SkyUniforms, "cloudLitColor" | "cloudShadeColor">) => SpriteNodeMaterial;
129
+ //#endregion
99
130
  //#region src/atmosphere/createSkyState.d.ts
100
131
  export declare const createSkyState: () => SkyState;
101
132
  //#endregion
@@ -105,6 +136,13 @@ export declare const createSkyUniforms: () => SkyUniforms;
105
136
  //#region src/atmosphere/createSunLight.d.ts
106
137
  export declare const createSunLight: ({ cascadeCount, maxFar, shadowMapSize }: SunLightOptions) => SunLight;
107
138
  //#endregion
139
+ //#region src/atmosphere/placeCloudBand.d.ts
140
+ export declare const placeCloudBand: ({ count, distanceRange: [near, far], heightRange: [low, high], seed, widthRange: [narrow, wide] }: CloudBandOptions, spriteCount: number) => {
141
+ position: [number, number, number];
142
+ spriteIndex: number;
143
+ width: number;
144
+ }[];
145
+ //#endregion
108
146
  //#region src/atmosphere/sampleSkyState.d.ts
109
147
  export declare const sampleSkyState: (skyKeyframes: readonly SkyKeyframe[], minutes: number, tilt: number, skyState: SkyState) => SkyState;
110
148
  //#endregion
@@ -121,6 +159,32 @@ export declare const advanceGameClock: (gameClock: GameClock, deltaSeconds: numb
121
159
  export declare const MINUTES_PER_DAY = 1440;
122
160
  export declare const GAME_MINUTES_PER_SECOND = 1;
123
161
  //#endregion
162
+ //#region src/kits/architecture/LatheSection.d.ts
163
+ export interface LatheSection {
164
+ bottomRadius: number;
165
+ height: number;
166
+ topRadius: number;
167
+ }
168
+ //#endregion
169
+ //#region src/kits/architecture/LatheStackOptions.d.ts
170
+ export interface LatheStackOptions {
171
+ isFaceted: boolean;
172
+ radialSegments: number;
173
+ sections: LatheSection[];
174
+ }
175
+ //#endregion
176
+ //#region src/kits/architecture/SilhouetteOptions.d.ts
177
+ export interface SilhouetteOptions {
178
+ contours: [number, number][][];
179
+ depth: [number, number];
180
+ }
181
+ //#endregion
182
+ //#region src/kits/architecture/createLatheStackGeometry.d.ts
183
+ export declare const createLatheStackGeometry: ({ isFaceted, radialSegments, sections }: LatheStackOptions) => BufferGeometry;
184
+ //#endregion
185
+ //#region src/kits/architecture/createSilhouetteGeometry.d.ts
186
+ export declare const createSilhouetteGeometry: ({ contours, depth: [front, back] }: SilhouetteOptions) => ExtrudeGeometry;
187
+ //#endregion
124
188
  //#region src/kits/statue/createStatueGeometry.d.ts
125
189
  export declare const createStatueGeometry: () => BufferGeometry;
126
190
  //#endregion
@@ -240,7 +304,7 @@ export declare const createLightUniforms: () => LightUniforms;
240
304
  export declare const createRimNode: ({ rimColor, rimStrength, sunDirection }: LightUniforms) => Node<"vec3">;
241
305
  //#endregion
242
306
  //#region src/nodes/createSkyNode.d.ts
243
- export declare const createSkyNode: ({ cloudCoverage, cloudDrift, cloudLitColor, cloudShadeColor, horizonColor, lightColor, moonDirection, starIntensity, sunDirection, zenithColor }: SkyUniforms) => Node<"vec3">;
307
+ export declare const createSkyNode: ({ cloudCoverage, cloudDrift, cloudLitColor, cloudShadeColor, horizonBand, horizonColor, lightColor, moonDirection, starIntensity, sunDirection, zenithColor }: SkyUniforms) => Node<"vec3">;
244
308
  //#endregion
245
309
  //#region src/nodes/createToonMaterial.d.ts
246
310
  export declare const createToonMaterial: ({ color, isOutlined, isVertexColors, lightUniforms, rampTexture }: ToonMaterialOptions) => ToonNodeMaterial;
@@ -312,7 +376,7 @@ export declare const createGodraysLight: (shadowMapSize: number, halfExtent: num
312
376
  export declare const createGradeLutTexture: (gradeOptions: GradeOptions) => Data3DTexture;
313
377
  //#endregion
314
378
  //#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">;
379
+ export declare const createHeightFogNode: (colorNode: Node<"vec4">, depthNode: TextureNode, camera: Camera, { baseHeight, color, density, heightFalloff, scatterColor, scatterDirection, scatterPower, scatterStrength, startDistance }: FogUniforms) => Node<"vec4">;
316
380
  //#endregion
317
381
  //#region src/post/createPostPipeline.d.ts
318
382
  export declare const createPostPipeline: ({ camera, fogUniforms, godraysLight, gradeLutTexture, postUniforms: { godraysColor, gradeIntensity, outlineColor, outlineFadeDistance, outlineThickness }, qualityTierSettings: { antialiasingMode, godraysStepCount, isBloomEnabled }, renderer, scene }: PostPipelineOptions) => PostPipeline;
@@ -320,6 +384,9 @@ export declare const createPostPipeline: ({ camera, fogUniforms, godraysLight, g
320
384
  //#region src/post/createPostUniforms.d.ts
321
385
  export declare const createPostUniforms: () => PostUniforms;
322
386
  //#endregion
387
+ //#region src/post/toSceneColor.d.ts
388
+ export declare const toSceneColor: (displayColor: Color) => Color;
389
+ //#endregion
323
390
  //#region src/random/createSeededRandom.d.ts
324
391
  export declare const createSeededRandom: (seed: number) => (() => number);
325
392
  //#endregion
@@ -333,6 +400,44 @@ export declare enum QualityTier {
333
400
  //#region src/renderer/QualityTierSettingsMap.d.ts
334
401
  export declare const QualityTierSettingsMap: Readonly<Record<QualityTier, QualityTierSettings>>;
335
402
  //#endregion
403
+ //#region src/renderer/constants.d.ts
404
+ export declare const GENSHIN_TONE_MAPPING: ToneMapping;
405
+ //#endregion
406
+ //#region src/renderer/getScreenDirection.d.ts
407
+ export declare const getScreenDirection: ({ aspect, fov, pitch, yaw }: {
408
+ aspect: number;
409
+ fov: number;
410
+ pitch: number;
411
+ yaw: number;
412
+ }, [x, y]: [number, number]) => Vector3;
413
+ //#endregion
414
+ //#region src/scene/SceneMaterial.d.ts
415
+ export interface SceneMaterial {
416
+ colors: Record<string, [number, number, number, number]>;
417
+ floats: Record<string, number>;
418
+ name: string;
419
+ textures: Record<string, {
420
+ name: string;
421
+ offset: [number, number];
422
+ scale: [number, number];
423
+ }>;
424
+ }
425
+ //#endregion
426
+ //#region src/scene/ScenePlacement.d.ts
427
+ export interface ScenePlacement {
428
+ materials: string[];
429
+ mesh: string;
430
+ position: [number, number, number];
431
+ rotation: [number, number, number, number];
432
+ scale: [number, number, number];
433
+ }
434
+ //#endregion
435
+ //#region src/scene/SceneLayout.d.ts
436
+ export interface SceneLayout {
437
+ materials: Record<string, SceneMaterial>;
438
+ placements: ScenePlacement[];
439
+ }
440
+ //#endregion
336
441
  //#region src/terrain/TerrainSelection.d.ts
337
442
  export interface TerrainSelection {
338
443
  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";
1
+ import { Box3, BufferAttribute, BufferGeometry, CanvasTexture, Color, CylinderGeometry, Data3DTexture, DataTexture, DirectionalLight, DoubleSide, ExtrudeGeometry, HalfFloatType, InstancedBufferGeometry, LatheGeometry, LinearFilter, MathUtils, NeutralToneMapping, NoColorSpace, OrthographicCamera, Quaternion, RedFormat, ShapePath, Vector2, Vector3 } from "three";
2
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";
3
+ import { MeshBasicNodeMaterial, 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,6 +11,49 @@ 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/post/toSceneColor.ts
15
+ const TOE_END = .08;
16
+ const TOE_OFFSET = .04;
17
+ const COMPRESSION_START = .76;
18
+ const DESATURATION = .15;
19
+ const INVERSE_STEP_COUNT = 16;
20
+ const SLOPE_NUDGE = 1e-4;
21
+ const SCENE_CHANNEL_LIMIT = 1.5;
22
+ const toneMap = ([red, green, blue]) => {
23
+ const lowest = Math.min(red, green, blue);
24
+ const offset = lowest < TOE_END ? lowest - 6.25 * lowest * lowest : TOE_OFFSET;
25
+ const offsetColor = [
26
+ red - offset,
27
+ green - offset,
28
+ blue - offset
29
+ ];
30
+ const peak = Math.max(...offsetColor);
31
+ if (peak < COMPRESSION_START) return offsetColor;
32
+ const headroom = .24;
33
+ const newPeak = 1 - headroom * headroom / (peak + headroom - COMPRESSION_START);
34
+ const whiteShare = 1 - 1 / (DESATURATION * (peak - newPeak) + 1);
35
+ return offsetColor.map((channel) => channel * newPeak / peak + (newPeak - channel * newPeak / peak) * whiteShare);
36
+ };
37
+ const toSceneColor = (displayColor) => {
38
+ const target = [
39
+ displayColor.r,
40
+ displayColor.g,
41
+ displayColor.b
42
+ ];
43
+ let input = [...target];
44
+ for (let step = 0; step < INVERSE_STEP_COUNT; step++) {
45
+ const current = input;
46
+ const output = toneMap(current);
47
+ input = current.map((channel, index) => {
48
+ const nudged = [...current];
49
+ nudged[index] = channel + SLOPE_NUDGE;
50
+ const slope = ((toneMap(nudged)[index] ?? 0) - (output[index] ?? 0)) / SLOPE_NUDGE;
51
+ return Math.min(Math.max(channel + ((target[index] ?? 0) - (output[index] ?? 0)) / slope, 0), SCENE_CHANNEL_LIMIT);
52
+ });
53
+ }
54
+ return new Color(...input);
55
+ };
56
+ //#endregion
14
57
  //#region src/atmosphere/applySkyState.ts
15
58
  const GODRAYS_REDRAW_COSINE = Math.cos(.6 * Math.PI / 180);
16
59
  const godraysDirection = new Vector3();
@@ -25,16 +68,18 @@ const applySkyState = (skyState, { fogUniforms, godraysLight, hemisphere, light,
25
68
  lightUniforms.lightColor.value.copy(lightColor);
26
69
  lightUniforms.sunDirection.value.copy(lightDirection);
27
70
  lightUniforms.rimColor.value.copy(horizonColor);
28
- fogUniforms.color.value.copy(horizonColor);
71
+ fogUniforms.color.value.copy(toSceneColor(skyState.fogColor ?? horizonColor));
72
+ fogUniforms.scatterColor.value.copy(toSceneColor(lightColor));
73
+ fogUniforms.scatterDirection.value.copy(lightDirection);
29
74
  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);
75
+ skyUniforms.cloudLitColor.value.copy(toSceneColor(skyState.cloudLitColor));
76
+ skyUniforms.cloudShadeColor.value.copy(toSceneColor(skyState.cloudShadeColor));
77
+ skyUniforms.horizonColor.value.copy(toSceneColor(horizonColor));
33
78
  skyUniforms.lightColor.value.copy(lightColor);
34
79
  skyUniforms.moonDirection.value.copy(skyState.moonDirection);
35
80
  skyUniforms.starIntensity.value = skyState.starIntensity;
36
81
  skyUniforms.sunDirection.value.copy(skyState.sunDirection);
37
- skyUniforms.zenithColor.value.copy(skyState.zenithColor);
82
+ skyUniforms.zenithColor.value.copy(toSceneColor(skyState.zenithColor));
38
83
  godraysDirection.subVectors(godraysLight.position, godraysLight.target.position).normalize();
39
84
  if (godraysDirection.dot(lightDirection) >= GODRAYS_REDRAW_COSINE) return;
40
85
  godraysLight.position.copy(godraysLight.target.position).addScaledVector(lightDirection, lightDistance);
@@ -52,6 +97,58 @@ const computeSunDirection = (minutes, tilt, sunDirection) => {
52
97
  return sunDirection.set(Math.sin(hourAngle), height * Math.cos(tilt), height * Math.sin(tilt));
53
98
  };
54
99
  //#endregion
100
+ //#region src/atmosphere/createCloudAtlasTexture.ts
101
+ const getCloudAtlasColumns = (spriteCount) => Math.ceil(Math.sqrt(spriteCount));
102
+ const createCloudAtlasTexture = (sprites, cellSize) => {
103
+ const columns = getCloudAtlasColumns(sprites.length);
104
+ const canvas = new OffscreenCanvas(columns * cellSize, columns * cellSize);
105
+ const context = canvas.getContext("2d");
106
+ if (context) {
107
+ context.globalCompositeOperation = "lighter";
108
+ const fill = (loops, column, row, style) => {
109
+ const path = new Path2D();
110
+ for (const [first, ...rest] of loops) {
111
+ if (!first) continue;
112
+ const toCanvas = ([x, y]) => [(column + x) * cellSize, (row + 1 - y) * cellSize];
113
+ path.moveTo(...toCanvas(first));
114
+ for (const point of rest) path.lineTo(...toCanvas(point));
115
+ path.closePath();
116
+ }
117
+ context.fillStyle = style;
118
+ context.fill(path, "evenodd");
119
+ };
120
+ for (const [index, { lit, outline }] of sprites.entries()) {
121
+ const column = index % columns;
122
+ const row = Math.floor(index / columns);
123
+ fill(outline, column, row, "#f00");
124
+ fill(lit, column, row, "#0f0");
125
+ }
126
+ }
127
+ const texture = new CanvasTexture(canvas);
128
+ texture.colorSpace = NoColorSpace;
129
+ texture.magFilter = LinearFilter;
130
+ texture.minFilter = LinearFilter;
131
+ texture.generateMipmaps = false;
132
+ return texture;
133
+ };
134
+ //#endregion
135
+ //#region src/atmosphere/createCloudSpriteMaterial.ts
136
+ const createCloudSpriteMaterial = (atlas, { index, spriteCount }, { cloudLitColor, cloudShadeColor }) => {
137
+ const columns = getCloudAtlasColumns(spriteCount);
138
+ const column = index % columns;
139
+ const row = Math.floor(index / columns);
140
+ const cellUv = uv().add(vec2(column, columns - 1 - row)).div(columns);
141
+ const mask = texture(atlas, cellUv);
142
+ const material = new SpriteNodeMaterial({
143
+ depthWrite: false,
144
+ transparent: true
145
+ });
146
+ material.colorNode = mix(cloudShadeColor, cloudLitColor, mask.g);
147
+ material.opacityNode = mask.r;
148
+ material.alphaTest = .5;
149
+ return material;
150
+ };
151
+ //#endregion
55
152
  //#region src/atmosphere/createSkyState.ts
56
153
  const createSkyState = () => ({
57
154
  cloudLitColor: new Color(),
@@ -75,6 +172,7 @@ const createSkyUniforms = () => ({
75
172
  cloudDrift: uniform(new Vector2()),
76
173
  cloudLitColor: uniform(new Color()),
77
174
  cloudShadeColor: uniform(new Color()),
175
+ horizonBand: uniform(.45),
78
176
  horizonColor: uniform(new Color()),
79
177
  lightColor: uniform(new Color()),
80
178
  moonDirection: uniform(new Vector3(0, -1, 0)),
@@ -108,6 +206,36 @@ const createSunLight = ({ cascadeCount, maxFar, shadowMapSize }) => {
108
206
  };
109
207
  };
110
208
  //#endregion
209
+ //#region src/random/createSeededRandom.ts
210
+ const createSeededRandom = (seed) => {
211
+ let state = seed;
212
+ return () => {
213
+ state = state + 1831565813 >>> 0;
214
+ let mixed = Math.imul(state ^ state >>> 15, 1 | state);
215
+ mixed ^= mixed + Math.imul(mixed ^ mixed >>> 7, 61 | mixed);
216
+ return ((mixed ^ mixed >>> 14) >>> 0) / 2 ** 32;
217
+ };
218
+ };
219
+ //#endregion
220
+ //#region src/atmosphere/placeCloudBand.ts
221
+ const placeCloudBand = ({ count, distanceRange: [near, far], heightRange: [low, high], seed, widthRange: [narrow, wide] }, spriteCount) => {
222
+ const random = createSeededRandom(seed);
223
+ return Array.from({ length: count }, () => {
224
+ const angle = random() * Math.PI * 2;
225
+ const distance = Math.sqrt(near ** 2 + random() * (far ** 2 - near ** 2));
226
+ const height = low + random() * (high - low);
227
+ return {
228
+ position: [
229
+ Math.cos(angle) * distance,
230
+ height,
231
+ Math.sin(angle) * distance
232
+ ],
233
+ spriteIndex: Math.floor(random() * spriteCount),
234
+ width: narrow + random() * (wide - narrow)
235
+ };
236
+ });
237
+ };
238
+ //#endregion
111
239
  //#region src/atmosphere/sampleSkyState.ts
112
240
  const lerp = (from, to, amount) => from + (to - from) * amount;
113
241
  const sampleSkyState = (skyKeyframes, minutes, tilt, skyState) => {
@@ -140,6 +268,34 @@ const advanceGameClock = (gameClock, deltaSeconds) => {
140
268
  gameClock.minutes = (gameClock.minutes + deltaSeconds * gameClock.minutesPerSecond) % MINUTES_PER_DAY;
141
269
  };
142
270
  //#endregion
271
+ //#region src/kits/architecture/createLatheStackGeometry.ts
272
+ const createLatheStackGeometry = ({ isFaceted, radialSegments, sections }) => {
273
+ let footHeight = 0;
274
+ const parts = sections.map(({ bottomRadius, height, topRadius }) => {
275
+ const part = new CylinderGeometry(topRadius, bottomRadius, height, radialSegments).translate(0, footHeight + height / 2, 0);
276
+ footHeight += height;
277
+ return isFaceted ? part.toNonIndexed() : part;
278
+ });
279
+ const stackGeometry = mergeGeometries(parts) ?? new BufferGeometry();
280
+ for (const part of parts) part.dispose();
281
+ if (isFaceted) stackGeometry.computeVertexNormals();
282
+ return stackGeometry;
283
+ };
284
+ //#endregion
285
+ //#region src/kits/architecture/createSilhouetteGeometry.ts
286
+ const createSilhouetteGeometry = ({ contours, depth: [front, back] }) => {
287
+ const shapePath = new ShapePath();
288
+ for (const [first, ...rest] of contours) {
289
+ if (!first) continue;
290
+ shapePath.moveTo(...first);
291
+ for (const point of rest) shapePath.lineTo(...point);
292
+ }
293
+ return new ExtrudeGeometry(shapePath.toShapes(), {
294
+ bevelEnabled: false,
295
+ depth: back - front
296
+ }).translate(0, 0, front);
297
+ };
298
+ //#endregion
143
299
  //#region src/kits/statue/createStatueGeometry.ts
144
300
  const PLINTH_SIDES = 8;
145
301
  const FIGURE_SEGMENTS = 24;
@@ -179,17 +335,6 @@ const createStatueGeometry = () => {
179
335
  return statueGeometry;
180
336
  };
181
337
  //#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
338
  //#region src/kits/tree/computeLeafCards.ts
194
339
  const VERTICES_PER_CARD = 4;
195
340
  const INDICES_PER_CARD = 6;
@@ -379,14 +524,18 @@ const createCausticsNode = ({ lightColor }, { causticStrength, deepDepth, level
379
524
  return lightColor.mul(veins.mul(fade).mul(causticStrength));
380
525
  };
381
526
  //#endregion
527
+ //#region src/renderer/constants.ts
528
+ const GENSHIN_TONE_MAPPING = NeutralToneMapping;
529
+ //#endregion
382
530
  //#region src/nodes/createGenshinRenderer.ts
383
531
  const createGenshinRenderer = (canvas) => {
532
+ const isWindows = window.navigator.userAgent.includes("Windows");
384
533
  const renderer = new WebGPURenderer({
385
534
  antialias: false,
386
535
  canvas,
387
- powerPreference: "high-performance"
536
+ ...isWindows ? {} : { powerPreference: "high-performance" }
388
537
  });
389
- renderer.toneMapping = NeutralToneMapping;
538
+ renderer.toneMapping = GENSHIN_TONE_MAPPING;
390
539
  return renderer;
391
540
  };
392
541
  //#endregion
@@ -402,7 +551,7 @@ const createRimNode = ({ rimColor, rimStrength, sunDirection }) => {
402
551
  //#region src/nodes/createToonMaterial.ts
403
552
  const createToonMaterial = ({ color, isOutlined = true, isVertexColors = false, lightUniforms, rampTexture }) => {
404
553
  const toonMaterial = new ToonNodeMaterial({
405
- color,
554
+ ...color === void 0 ? {} : { color },
406
555
  gradientMap: rampTexture,
407
556
  vertexColors: isVertexColors
408
557
  }, isOutlined);
@@ -450,7 +599,6 @@ const createLightUniforms = () => ({
450
599
  });
451
600
  //#endregion
452
601
  //#region src/nodes/createSkyNode.ts
453
- const HORIZON_BAND = .45;
454
602
  const SUN_DISC_COSINE = Math.cos(.035);
455
603
  const MOON_DISC_COSINE = Math.cos(.05);
456
604
  const SUN_GLOW_POWER = 12;
@@ -462,10 +610,10 @@ const CLOUD_EDGE = .04;
462
610
  const CLOUD_SHADE_DEPTH = .18;
463
611
  const STAR_SCALE = 420;
464
612
  const STAR_THRESHOLD = .9975;
465
- const createSkyNode = ({ cloudCoverage, cloudDrift, cloudLitColor, cloudShadeColor, horizonColor, lightColor, moonDirection, starIntensity, sunDirection, zenithColor }) => Fn(() => {
613
+ const createSkyNode = ({ cloudCoverage, cloudDrift, cloudLitColor, cloudShadeColor, horizonBand, horizonColor, lightColor, moonDirection, starIntensity, sunDirection, zenithColor }) => Fn(() => {
466
614
  const direction = normalWorldGeometry.normalize();
467
615
  const height = direction.y;
468
- const gradient = mix(horizonColor, zenithColor, smoothstep(0, HORIZON_BAND, height));
616
+ const gradient = mix(horizonColor, zenithColor, smoothstep(0, horizonBand, height));
469
617
  const sunCosine = direction.dot(sunDirection);
470
618
  const sunVisibility = smoothstep(-.1, .05, sunDirection.y);
471
619
  const sunGlow = pow(max(sunCosine, 0), SUN_GLOW_POWER).mul(sunVisibility);
@@ -616,6 +764,10 @@ const createFogUniforms = () => ({
616
764
  color: uniform(new Color()),
617
765
  density: uniform(0),
618
766
  heightFalloff: uniform(0),
767
+ scatterColor: uniform(new Color()),
768
+ scatterDirection: uniform(new Vector3(0, 1, 0)),
769
+ scatterPower: uniform(1),
770
+ scatterStrength: uniform(0),
619
771
  startDistance: uniform(0)
620
772
  });
621
773
  //#endregion
@@ -647,7 +799,7 @@ const createGradeLutTexture = (gradeOptions) => {
647
799
  //#endregion
648
800
  //#region src/post/createHeightFogNode.ts
649
801
  const MIN_RAY_CLIMB = 1e-6;
650
- const createHeightFogNode = (colorNode, depthNode, camera, { baseHeight, color, density, heightFalloff, startDistance }) => {
802
+ const createHeightFogNode = (colorNode, depthNode, camera, { baseHeight, color, density, heightFalloff, scatterColor, scatterDirection, scatterPower, scatterStrength, startDistance }) => {
651
803
  const cameraWorldMatrix = uniform(camera.matrixWorld);
652
804
  const cameraProjectionMatrixInverse = uniform(camera.projectionMatrixInverse);
653
805
  return Fn(() => {
@@ -668,7 +820,8 @@ const createHeightFogNode = (colorNode, depthNode, camera, { baseHeight, color,
668
820
  opticalDepth.assign(densityAtStart.mul(float(1).sub(exp(fogLength.mul(climb).negate()))).div(climb));
669
821
  });
670
822
  const opacity = float(1).sub(exp(opticalDepth.negate()));
671
- output.assign(vec4(mix(output.rgb, color, opacity), output.a));
823
+ const scatter = pow(max(ray.normalize().dot(scatterDirection), 0), scatterPower).mul(scatterStrength);
824
+ output.assign(vec4(mix(output.rgb, mix(color, scatterColor, scatter.min(1)), opacity), output.a));
672
825
  });
673
826
  return output;
674
827
  })();
@@ -770,6 +923,14 @@ const QualityTierSettingsMap = {
770
923
  }
771
924
  };
772
925
  //#endregion
926
+ //#region src/renderer/getScreenDirection.ts
927
+ const X_AXIS = new Vector3(1, 0, 0);
928
+ const Y_AXIS = new Vector3(0, 1, 0);
929
+ const getScreenDirection = ({ aspect, fov, pitch, yaw }, [x, y]) => {
930
+ const halfHeight = Math.tan(MathUtils.degToRad(fov) / 2);
931
+ return new Vector3((x * 2 - 1) * halfHeight * aspect, (1 - y * 2) * halfHeight, -1).normalize().applyAxisAngle(X_AXIS, pitch).applyAxisAngle(Y_AXIS, yaw);
932
+ };
933
+ //#endregion
773
934
  //#region src/terrain/constants.ts
774
935
  const TILE_INDEX_OFFSET = 2 ** 19;
775
936
  const TILE_INDEX_SPAN = 2 ** 20;
@@ -1089,6 +1250,7 @@ const createGrassGeometry = (segmentCount, bladeCount) => {
1089
1250
  const { indices, positions } = computeGrassBlade(segmentCount);
1090
1251
  const grassGeometry = new InstancedBufferGeometry();
1091
1252
  grassGeometry.setAttribute("position", new BufferAttribute(positions, 3));
1253
+ grassGeometry.setAttribute("normal", new BufferAttribute(Float32Array.from({ length: positions.length }, (_, index) => index % 3 === 1 ? 1 : 0), 3));
1092
1254
  grassGeometry.setIndex(new BufferAttribute(indices, 1));
1093
1255
  grassGeometry.instanceCount = bladeCount;
1094
1256
  return grassGeometry;
@@ -1255,4 +1417,4 @@ const computeOutlineDistance = (outline, x, z) => {
1255
1417
  return isInside ? 0 : Math.sqrt(nearestSquared);
1256
1418
  };
1257
1419
  //#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 };
1420
+ export { AntialiasingMode, GAME_MINUTES_PER_SECOND, GENSHIN_TONE_MAPPING, 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, createCloudAtlasTexture, createCloudSpriteMaterial, createFogUniforms, createGenshinRenderer, createGodraysLight, createGradeLutTexture, createGrassGeometry, createGrassMaterial, createGroundCapture, createHeightFogNode, createLatheStackGeometry, createLeafMaterial, createLightUniforms, createPostPipeline, createPostUniforms, createRampTexture, createRimNode, createSeededRandom, createSilhouetteGeometry, createSimplexNoise, createSkyNode, createSkyState, createSkyUniforms, createStatueGeometry, createSunLight, createTerrainMaterial, createTerrainSelection, createTerrainTileGeometry, createTileStreamer, createToonMaterial, createTreeGeometry, createUnderwaterFogState, createWaterMaterial, createWaterUniforms, createWindNode, createWindUniforms, getCloudAtlasColumns, getParentTerrainTileKey, getScreenDirection, getTerrainTileColumn, getTerrainTileKey, getTerrainTileLevel, getTerrainTileRow, placeCloudBand, renderGroundCapture, resolveTerrainDraws, sampleSkyState, selectTerrainTiles, toSceneColor, updateUnderwaterFog };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genshin-engine",
3
- "version": "3.7.0",
3
+ "version": "3.8.1",
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",
56
+ "@esposter/configuration": "3.8.1",
57
+ "@esposter/shared-node": "3.8.1",
58
58
  "@types/node": "^26.6.3",
59
59
  "@types/three": "^0.186.0",
60
60
  "publint": "^0.3.24",
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": "1cc0b24081a5fd56ec0c57b784f978f1ad0df0f4"
68
68
  }