@needle-tools/engine 5.0.2 → 5.1.0-canary.525aa82
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 +24 -0
- package/README.md +6 -7
- package/SKILL.md +39 -21
- package/components.needle.json +1 -1
- package/dist/needle-engine.bundle-DPag02s9.min.js +1732 -0
- package/dist/needle-engine.bundle-IPMzQpe1.umd.cjs +1732 -0
- package/dist/{needle-engine.bundle-BoTyA-Le.js → needle-engine.bundle-qa_NEunk.js} +8881 -8148
- package/dist/needle-engine.d.ts +633 -61
- package/dist/needle-engine.js +576 -565
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{vendor-vHLk8sXu.js → vendor-CAcsI0eU.js} +116 -115
- package/dist/{vendor-CntUvmJu.umd.cjs → vendor-CEM38hLE.umd.cjs} +2 -2
- package/dist/{vendor-DPbfJJ4d.min.js → vendor-HRlxIBga.min.js} +2 -2
- package/lib/engine/api.d.ts +2 -0
- package/lib/engine/api.js +2 -0
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/engine_addressables.js +5 -1
- package/lib/engine/engine_addressables.js.map +1 -1
- package/lib/engine/engine_animation.d.ts +14 -7
- package/lib/engine/engine_animation.js +49 -9
- package/lib/engine/engine_animation.js.map +1 -1
- package/lib/engine/engine_components.js +33 -4
- package/lib/engine/engine_components.js.map +1 -1
- package/lib/engine/engine_context.d.ts +7 -2
- package/lib/engine/engine_context.js +10 -2
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_gameobject.d.ts +4 -0
- package/lib/engine/engine_gameobject.js.map +1 -1
- package/lib/engine/engine_init.js +4 -0
- package/lib/engine/engine_init.js.map +1 -1
- package/lib/engine/engine_input.js +4 -1
- package/lib/engine/engine_input.js.map +1 -1
- package/lib/engine/engine_materialpropertyblock.js +1 -20
- package/lib/engine/engine_materialpropertyblock.js.map +1 -1
- package/lib/engine/engine_networking.d.ts +11 -8
- package/lib/engine/engine_networking.js +43 -26
- package/lib/engine/engine_networking.js.map +1 -1
- package/lib/engine/engine_networking_instantiate.d.ts +100 -5
- package/lib/engine/engine_networking_instantiate.js +150 -16
- package/lib/engine/engine_networking_instantiate.js.map +1 -1
- package/lib/engine/engine_networking_prefabs.d.ts +59 -0
- package/lib/engine/engine_networking_prefabs.js +67 -0
- package/lib/engine/engine_networking_prefabs.js.map +1 -0
- package/lib/engine/engine_physics_rapier.d.ts +3 -0
- package/lib/engine/engine_physics_rapier.js +13 -9
- package/lib/engine/engine_physics_rapier.js.map +1 -1
- package/lib/engine/postprocessing/api.d.ts +2 -0
- package/lib/engine/postprocessing/api.js +2 -0
- package/lib/engine/postprocessing/api.js.map +1 -0
- package/lib/engine/postprocessing/index.d.ts +2 -0
- package/lib/engine/postprocessing/index.js +2 -0
- package/lib/engine/postprocessing/index.js.map +1 -0
- package/lib/engine/postprocessing/postprocessing.d.ts +83 -0
- package/lib/engine/postprocessing/postprocessing.js +280 -0
- package/lib/engine/postprocessing/postprocessing.js.map +1 -0
- package/lib/engine/postprocessing/types.d.ts +39 -0
- package/lib/engine/postprocessing/types.js +2 -0
- package/lib/engine/postprocessing/types.js.map +1 -0
- package/lib/engine/webcomponents/WebXRButtons.js +17 -3
- package/lib/engine/webcomponents/WebXRButtons.js.map +1 -1
- package/lib/engine/webcomponents/icons.js +3 -1
- package/lib/engine/webcomponents/icons.js.map +1 -1
- package/lib/engine/xr/NeedleXRSession.d.ts +1 -0
- package/lib/engine/xr/NeedleXRSession.js +43 -10
- package/lib/engine/xr/NeedleXRSession.js.map +1 -1
- package/lib/engine/xr/init.d.ts +4 -0
- package/lib/engine/xr/init.js +49 -0
- package/lib/engine/xr/init.js.map +1 -0
- package/lib/engine-components/AnimationUtils.d.ts +4 -1
- package/lib/engine-components/AnimationUtils.js +7 -19
- package/lib/engine-components/AnimationUtils.js.map +1 -1
- package/lib/engine-components/AnimatorController.d.ts +135 -2
- package/lib/engine-components/AnimatorController.js +216 -13
- package/lib/engine-components/AnimatorController.js.map +1 -1
- package/lib/engine-components/GroundProjection.d.ts +1 -0
- package/lib/engine-components/GroundProjection.js +184 -48
- package/lib/engine-components/GroundProjection.js.map +1 -1
- package/lib/engine-components/OrbitControls.d.ts +4 -0
- package/lib/engine-components/OrbitControls.js +5 -1
- package/lib/engine-components/OrbitControls.js.map +1 -1
- package/lib/engine-components/SeeThrough.d.ts +0 -2
- package/lib/engine-components/SeeThrough.js +0 -89
- package/lib/engine-components/SeeThrough.js.map +1 -1
- package/lib/engine-components/SyncedRoom.d.ts +4 -0
- package/lib/engine-components/SyncedRoom.js +23 -8
- package/lib/engine-components/SyncedRoom.js.map +1 -1
- package/lib/engine-components/SyncedTransform.js +5 -5
- package/lib/engine-components/SyncedTransform.js.map +1 -1
- package/lib/engine-components/Voip.d.ts +46 -0
- package/lib/engine-components/Voip.js +126 -2
- package/lib/engine-components/Voip.js.map +1 -1
- package/lib/engine-components/api.d.ts +1 -0
- package/lib/engine-components/api.js +1 -0
- package/lib/engine-components/api.js.map +1 -1
- package/lib/engine-components/codegen/components.d.ts +1 -0
- package/lib/engine-components/codegen/components.js +1 -0
- package/lib/engine-components/codegen/components.js.map +1 -1
- package/lib/engine-components/postprocessing/Effects/Tonemapping.d.ts +5 -2
- package/lib/engine-components/postprocessing/Effects/Tonemapping.js +11 -18
- package/lib/engine-components/postprocessing/Effects/Tonemapping.js.map +1 -1
- package/lib/engine-components/postprocessing/PostProcessingEffect.d.ts +3 -4
- package/lib/engine-components/postprocessing/PostProcessingEffect.js +6 -15
- package/lib/engine-components/postprocessing/PostProcessingEffect.js.map +1 -1
- package/lib/engine-components/postprocessing/PostProcessingHandler.d.ts +2 -1
- package/lib/engine-components/postprocessing/PostProcessingHandler.js.map +1 -1
- package/lib/engine-components/postprocessing/Volume.d.ts +18 -11
- package/lib/engine-components/postprocessing/Volume.js +61 -140
- package/lib/engine-components/postprocessing/Volume.js.map +1 -1
- package/lib/engine-components/postprocessing/index.d.ts +1 -0
- package/lib/engine-components/postprocessing/index.js +1 -0
- package/lib/engine-components/postprocessing/index.js.map +1 -1
- package/lib/engine-components/postprocessing/utils.d.ts +2 -0
- package/lib/engine-components/postprocessing/utils.js +2 -0
- package/lib/engine-components/postprocessing/utils.js.map +1 -1
- package/lib/engine-components/ui/Canvas.js +2 -2
- package/lib/engine-components/ui/Canvas.js.map +1 -1
- package/lib/engine-components/ui/Graphic.d.ts +3 -3
- package/lib/engine-components/ui/Graphic.js +6 -2
- package/lib/engine-components/ui/Graphic.js.map +1 -1
- package/lib/engine-components/ui/Text.d.ts +64 -11
- package/lib/engine-components/ui/Text.js +154 -45
- package/lib/engine-components/ui/Text.js.map +1 -1
- package/lib/engine-components/ui/index.d.ts +1 -0
- package/lib/engine-components/ui/index.js +1 -0
- package/lib/engine-components/ui/index.js.map +1 -1
- package/lib/engine-components-experimental/networking/PlayerSync.d.ts +25 -3
- package/lib/engine-components-experimental/networking/PlayerSync.js +60 -11
- package/lib/engine-components-experimental/networking/PlayerSync.js.map +1 -1
- package/package.json +6 -5
- package/plugins/vite/ai.d.ts +11 -10
- package/plugins/vite/ai.js +305 -31
- package/src/engine/api.ts +3 -0
- package/src/engine/engine_addressables.ts +4 -1
- package/src/engine/engine_animation.ts +47 -9
- package/src/engine/engine_components.ts +36 -7
- package/src/engine/engine_context.ts +11 -2
- package/src/engine/engine_gameobject.ts +5 -0
- package/src/engine/engine_init.ts +4 -0
- package/src/engine/engine_input.ts +2 -1
- package/src/engine/engine_materialpropertyblock.ts +1 -20
- package/src/engine/engine_networking.ts +46 -23
- package/src/engine/engine_networking_instantiate.ts +160 -18
- package/src/engine/engine_networking_prefabs.ts +80 -0
- package/src/engine/engine_physics_rapier.ts +14 -9
- package/src/engine/postprocessing/api.ts +2 -0
- package/src/engine/postprocessing/index.ts +2 -0
- package/src/engine/postprocessing/postprocessing.ts +322 -0
- package/src/engine/postprocessing/types.ts +43 -0
- package/src/engine/webcomponents/WebXRButtons.ts +21 -4
- package/src/engine/webcomponents/icons.ts +5 -3
- package/src/engine/xr/NeedleXRSession.ts +50 -15
- package/src/engine/xr/init.ts +56 -0
- package/src/engine-components/AnimationUtils.ts +7 -17
- package/src/engine-components/AnimatorController.ts +288 -18
- package/src/engine-components/GroundProjection.ts +226 -52
- package/src/engine-components/OrbitControls.ts +5 -1
- package/src/engine-components/SeeThrough.ts +0 -116
- package/src/engine-components/SyncedRoom.ts +28 -9
- package/src/engine-components/SyncedTransform.ts +5 -5
- package/src/engine-components/Voip.ts +129 -2
- package/src/engine-components/api.ts +1 -0
- package/src/engine-components/codegen/components.ts +1 -0
- package/src/engine-components/postprocessing/Effects/Tonemapping.ts +16 -24
- package/src/engine-components/postprocessing/PostProcessingEffect.ts +9 -16
- package/src/engine-components/postprocessing/PostProcessingHandler.ts +2 -1
- package/src/engine-components/postprocessing/Volume.ts +72 -163
- package/src/engine-components/postprocessing/index.ts +1 -0
- package/src/engine-components/postprocessing/utils.ts +2 -0
- package/src/engine-components/ui/Canvas.ts +2 -2
- package/src/engine-components/ui/Graphic.ts +7 -3
- package/src/engine-components/ui/Text.ts +170 -52
- package/src/engine-components/ui/index.ts +2 -1
- package/src/engine-components-experimental/networking/PlayerSync.ts +64 -11
- package/dist/needle-engine.bundle-B3ywqx5o.min.js +0 -1654
- package/dist/needle-engine.bundle-CzOPcOui.umd.cjs +0 -1654
|
@@ -9,27 +9,13 @@ import { Context } from "../engine/engine_setup.js";
|
|
|
9
9
|
import { isAnimationAction } from "../engine/engine_three_utils.js";
|
|
10
10
|
import { TypeStore } from "../engine/engine_typestore.js";
|
|
11
11
|
import { deepClone, getParam } from "../engine/engine_utils.js";
|
|
12
|
-
import type { AnimatorControllerModel, Condition, State, Transition } from "../engine/extensions/NEEDLE_animator_controller_model.js";
|
|
12
|
+
import type { AnimatorControllerModel, Condition, Parameter, State, Transition } from "../engine/extensions/NEEDLE_animator_controller_model.js";
|
|
13
13
|
import { AnimatorConditionMode, AnimatorControllerParameterType, AnimatorStateInfo, createMotion, StateMachineBehaviour } from "../engine/extensions/NEEDLE_animator_controller_model.js";
|
|
14
14
|
import type { Animator } from "./Animator.js";
|
|
15
15
|
|
|
16
16
|
const debug = getParam("debuganimatorcontroller");
|
|
17
17
|
const debugRootMotion = getParam("debugrootmotion");
|
|
18
18
|
|
|
19
|
-
/**
|
|
20
|
-
* Generates a hash code for a string
|
|
21
|
-
* @param str - The string to hash
|
|
22
|
-
* @returns A numeric hash value
|
|
23
|
-
*/
|
|
24
|
-
function stringToHash(str): number {
|
|
25
|
-
let hash = 0;
|
|
26
|
-
for (let i = 0; i < str.length; i++) {
|
|
27
|
-
const char = str.charCodeAt(i);
|
|
28
|
-
hash = ((hash << 5) - hash) + char;
|
|
29
|
-
hash = hash & hash;
|
|
30
|
-
}
|
|
31
|
-
return hash;
|
|
32
|
-
}
|
|
33
19
|
|
|
34
20
|
/**
|
|
35
21
|
* Configuration options for creating an AnimatorController
|
|
@@ -43,15 +29,275 @@ declare type CreateAnimatorControllerOptions = {
|
|
|
43
29
|
transitionDuration?: number,
|
|
44
30
|
}
|
|
45
31
|
|
|
46
|
-
|
|
32
|
+
|
|
33
|
+
// #region AnimatorControllerBuilder
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Configuration for an animation state in the builder
|
|
37
|
+
*/
|
|
38
|
+
export declare type StateOptions = {
|
|
39
|
+
/** The animation clip for this state */
|
|
40
|
+
clip: AnimationClip;
|
|
41
|
+
/** Whether the animation should loop (default: false) */
|
|
42
|
+
loop?: boolean;
|
|
43
|
+
/** Base speed multiplier (default: 1) */
|
|
44
|
+
speed?: number;
|
|
45
|
+
/** Name of a float parameter to multiply with speed */
|
|
46
|
+
speedParameter?: string;
|
|
47
|
+
/** Normalized cycle offset 0-1 (default: 0) */
|
|
48
|
+
cycleOffset?: number;
|
|
49
|
+
/** Name of a float parameter to use as cycle offset */
|
|
50
|
+
cycleOffsetParameter?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Configuration for a transition in the builder
|
|
55
|
+
*/
|
|
56
|
+
export declare type TransitionOptions = {
|
|
57
|
+
/** Duration of the crossfade in seconds (default: 0) */
|
|
58
|
+
duration?: number;
|
|
59
|
+
/** Normalized exit time 0-1 (default: 1). Only used when hasExitTime is true */
|
|
60
|
+
exitTime?: number;
|
|
61
|
+
/** Whether the transition waits for exitTime before transitioning (default: false) */
|
|
62
|
+
hasExitTime?: boolean;
|
|
63
|
+
/** Normalized offset into the destination state's animation (default: 0) */
|
|
64
|
+
offset?: number;
|
|
65
|
+
/** Whether duration is in seconds (true) or normalized (false) (default: false) */
|
|
66
|
+
hasFixedDuration?: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** String condition modes for the builder, mapped to {@link AnimatorConditionMode} */
|
|
70
|
+
export type ConditionMode = "if" | "ifNot" | "greater" | "less" | "equals" | "notEqual";
|
|
71
|
+
|
|
72
|
+
function conditionModeToEnum(mode: ConditionMode): AnimatorConditionMode {
|
|
73
|
+
switch (mode) {
|
|
74
|
+
case "if": return AnimatorConditionMode.If;
|
|
75
|
+
case "ifNot": return AnimatorConditionMode.IfNot;
|
|
76
|
+
case "greater": return AnimatorConditionMode.Greater;
|
|
77
|
+
case "less": return AnimatorConditionMode.Less;
|
|
78
|
+
case "equals": return AnimatorConditionMode.Equals;
|
|
79
|
+
case "notEqual": return AnimatorConditionMode.NotEqual;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
type BuilderTransition = {
|
|
84
|
+
to: string;
|
|
85
|
+
options: TransitionOptions;
|
|
86
|
+
conditions: Array<{ parameter: string; mode: ConditionMode; threshold: number }>;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
type BuilderState = {
|
|
90
|
+
name: string;
|
|
91
|
+
options: StateOptions;
|
|
92
|
+
transitions: BuilderTransition[];
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* A fluent builder for creating {@link AnimatorController} instances from code.
|
|
97
|
+
*
|
|
98
|
+
* Use {@link AnimatorController.build} to create a new builder.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* const controller = AnimatorController.build("CharacterController")
|
|
103
|
+
* .floatParameter("Speed", 0)
|
|
104
|
+
* .triggerParameter("Jump")
|
|
105
|
+
* .state("Idle", { clip: idleClip, loop: true })
|
|
106
|
+
* .state("Walk", { clip: walkClip, loop: true })
|
|
107
|
+
* .state("Jump", { clip: jumpClip })
|
|
108
|
+
* .transition("Idle", "Walk", { duration: 0.25 })
|
|
109
|
+
* .condition("Speed", "greater", 0.1)
|
|
110
|
+
* .transition("Walk", "Idle", { duration: 0.25 })
|
|
111
|
+
* .condition("Speed", "less", 0.1)
|
|
112
|
+
* .transition("*", "Jump", { duration: 0.1 })
|
|
113
|
+
* .condition("Jump", "if")
|
|
114
|
+
* .transition("Jump", "Idle", { hasExitTime: true, exitTime: 0.9, duration: 0.25 })
|
|
115
|
+
* .build();
|
|
116
|
+
* ```
|
|
117
|
+
*
|
|
118
|
+
* @category Animation and Sequencing
|
|
119
|
+
* @group Utilities
|
|
120
|
+
*/
|
|
121
|
+
export class AnimatorControllerBuilder {
|
|
122
|
+
private _name: string;
|
|
123
|
+
private _parameters: Parameter[] = [];
|
|
124
|
+
private _states: BuilderState[] = [];
|
|
125
|
+
private _anyStateTransitions: BuilderTransition[] = [];
|
|
126
|
+
private _defaultStateName: string | null = null;
|
|
127
|
+
private _lastTransition: BuilderTransition | null = null;
|
|
128
|
+
|
|
129
|
+
constructor(name?: string) {
|
|
130
|
+
this._name = name ?? "AnimatorController";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Adds a float parameter */
|
|
134
|
+
floatParameter(name: string, defaultValue: number = 0): this {
|
|
135
|
+
this._parameters.push({ name, hash: this._parameters.length, type: AnimatorControllerParameterType.Float, value: defaultValue });
|
|
136
|
+
return this;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Adds an integer parameter */
|
|
140
|
+
intParameter(name: string, defaultValue: number = 0): this {
|
|
141
|
+
this._parameters.push({ name, hash: this._parameters.length, type: AnimatorControllerParameterType.Int, value: defaultValue });
|
|
142
|
+
return this;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Adds a boolean parameter */
|
|
146
|
+
boolParameter(name: string, defaultValue: boolean = false): this {
|
|
147
|
+
this._parameters.push({ name, hash: this._parameters.length, type: AnimatorControllerParameterType.Bool, value: defaultValue });
|
|
148
|
+
return this;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Adds a trigger parameter */
|
|
152
|
+
triggerParameter(name: string): this {
|
|
153
|
+
this._parameters.push({ name, hash: this._parameters.length, type: AnimatorControllerParameterType.Trigger, value: false });
|
|
154
|
+
return this;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Adds a state to the controller. The first state added becomes the default state.
|
|
159
|
+
* @param name - Unique name for the state
|
|
160
|
+
* @param options - State configuration including clip, loop, speed
|
|
161
|
+
*/
|
|
162
|
+
state(name: string, options: StateOptions): this {
|
|
163
|
+
this._states.push({ name, options, transitions: [] });
|
|
164
|
+
return this;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Adds a transition between two states.
|
|
169
|
+
* Use `"*"` as the source to create a transition from any state.
|
|
170
|
+
* Chain `.condition()` calls after this to add conditions.
|
|
171
|
+
* @param from - Source state name, or `"*"` for any-state transition
|
|
172
|
+
* @param to - Destination state name
|
|
173
|
+
* @param options - Transition configuration
|
|
174
|
+
*/
|
|
175
|
+
transition(from: string, to: string, options?: TransitionOptions): this {
|
|
176
|
+
const t: BuilderTransition = { to, options: options ?? {}, conditions: [] };
|
|
177
|
+
if (from === "*") {
|
|
178
|
+
this._anyStateTransitions.push(t);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
const state = this._states.find(s => s.name === from);
|
|
182
|
+
if (!state) throw new Error(`AnimatorControllerBuilder: source state "${from}" not found. Add it with .state() first.`);
|
|
183
|
+
state.transitions.push(t);
|
|
184
|
+
}
|
|
185
|
+
this._lastTransition = t;
|
|
186
|
+
return this;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Adds a condition to the most recently added transition.
|
|
191
|
+
* Multiple conditions on the same transition are AND-ed together.
|
|
192
|
+
* @param parameter - Name of the parameter to evaluate
|
|
193
|
+
* @param mode - Condition mode: `"if"`, `"ifNot"`, `"greater"`, `"less"`, `"equals"`, `"notEqual"`
|
|
194
|
+
* @param threshold - Comparison threshold for numeric conditions (default: 0)
|
|
195
|
+
*/
|
|
196
|
+
condition(parameter: string, mode: ConditionMode, threshold: number = 0): this {
|
|
197
|
+
if (!this._lastTransition) throw new Error("AnimatorControllerBuilder: .condition() must be called after .transition()");
|
|
198
|
+
this._lastTransition.conditions.push({ parameter, mode, threshold });
|
|
199
|
+
return this;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Sets which state is the default/entry state.
|
|
204
|
+
* If not called, the first added state is used.
|
|
205
|
+
* @param name - Name of the state
|
|
206
|
+
*/
|
|
207
|
+
defaultState(name: string): this {
|
|
208
|
+
this._defaultStateName = name;
|
|
209
|
+
return this;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Builds and returns the {@link AnimatorController}.
|
|
214
|
+
* Resolves all state name references to indices.
|
|
215
|
+
*/
|
|
216
|
+
build(): AnimatorController {
|
|
217
|
+
const stateIndexMap = new Map<string, number>();
|
|
218
|
+
this._states.forEach((s, i) => stateIndexMap.set(s.name, i));
|
|
219
|
+
|
|
220
|
+
let defaultStateIndex = 0;
|
|
221
|
+
if (this._defaultStateName !== null) {
|
|
222
|
+
const idx = stateIndexMap.get(this._defaultStateName);
|
|
223
|
+
if (idx === undefined) throw new Error(`AnimatorControllerBuilder: default state "${this._defaultStateName}" not found`);
|
|
224
|
+
defaultStateIndex = idx;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const resolveTransition = (t: BuilderTransition): Transition => {
|
|
228
|
+
const destIndex = stateIndexMap.get(t.to);
|
|
229
|
+
if (destIndex === undefined) throw new Error(`AnimatorControllerBuilder: transition target "${t.to}" not found`);
|
|
230
|
+
return {
|
|
231
|
+
exitTime: t.options.exitTime ?? 1,
|
|
232
|
+
hasExitTime: t.options.hasExitTime ?? false,
|
|
233
|
+
duration: t.options.duration ?? 0,
|
|
234
|
+
offset: t.options.offset ?? 0,
|
|
235
|
+
hasFixedDuration: t.options.hasFixedDuration ?? false,
|
|
236
|
+
destinationState: destIndex,
|
|
237
|
+
conditions: t.conditions.map(c => ({
|
|
238
|
+
parameter: c.parameter,
|
|
239
|
+
mode: conditionModeToEnum(c.mode),
|
|
240
|
+
threshold: c.threshold,
|
|
241
|
+
})),
|
|
242
|
+
};
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const states: State[] = this._states.map((s, index) => {
|
|
246
|
+
const transitions: Transition[] = s.transitions.map(resolveTransition);
|
|
247
|
+
|
|
248
|
+
// Replicate any-state transitions onto every state (except self-targeting)
|
|
249
|
+
for (const anyT of this._anyStateTransitions) {
|
|
250
|
+
const destIndex = stateIndexMap.get(anyT.to);
|
|
251
|
+
if (destIndex === index) continue;
|
|
252
|
+
transitions.push(resolveTransition(anyT));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
name: s.name,
|
|
257
|
+
hash: index,
|
|
258
|
+
motion: {
|
|
259
|
+
name: s.options.clip.name,
|
|
260
|
+
clip: s.options.clip,
|
|
261
|
+
isLooping: s.options.loop ?? false,
|
|
262
|
+
},
|
|
263
|
+
transitions,
|
|
264
|
+
behaviours: [],
|
|
265
|
+
speed: s.options.speed,
|
|
266
|
+
speedParameter: s.options.speedParameter,
|
|
267
|
+
cycleOffset: s.options.cycleOffset,
|
|
268
|
+
cycleOffsetParameter: s.options.cycleOffsetParameter,
|
|
269
|
+
};
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const model: AnimatorControllerModel = {
|
|
273
|
+
name: this._name,
|
|
274
|
+
guid: new InstantiateIdProvider(Date.now()).generateUUID(),
|
|
275
|
+
parameters: this._parameters,
|
|
276
|
+
layers: [{
|
|
277
|
+
name: "Base Layer",
|
|
278
|
+
stateMachine: {
|
|
279
|
+
defaultState: defaultStateIndex,
|
|
280
|
+
states,
|
|
281
|
+
}
|
|
282
|
+
}],
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
return new AnimatorController(model);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// #endregion
|
|
289
|
+
|
|
290
|
+
// #region AnimatorController
|
|
291
|
+
/**
|
|
47
292
|
* Controls the playback of animations using a state machine architecture.
|
|
48
293
|
*
|
|
49
294
|
* The AnimatorController manages animation states, transitions between states,
|
|
50
295
|
* and parameters that affect those transitions. It is used by the {@link Animator}
|
|
51
296
|
* component to control animation behavior on 3D models.
|
|
52
297
|
*
|
|
53
|
-
* Use
|
|
54
|
-
*
|
|
298
|
+
* Use {@link AnimatorController.build} to fluently create a controller with parameters,
|
|
299
|
+
* states, transitions, and conditions. For simple sequential playback,
|
|
300
|
+
* use {@link AnimatorController.createFromClips}.
|
|
55
301
|
*
|
|
56
302
|
* @category Animation and Sequencing
|
|
57
303
|
* @group Utilities
|
|
@@ -119,6 +365,30 @@ export class AnimatorController {
|
|
|
119
365
|
return controller;
|
|
120
366
|
}
|
|
121
367
|
|
|
368
|
+
/**
|
|
369
|
+
* Creates a new {@link AnimatorControllerBuilder} for fluently constructing a controller with
|
|
370
|
+
* parameters, states, transitions, and conditions.
|
|
371
|
+
*
|
|
372
|
+
* @param name - Optional name for the controller
|
|
373
|
+
* @returns A new builder instance
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```ts
|
|
377
|
+
* const ctrl = AnimatorController.build("MyController")
|
|
378
|
+
* .floatParameter("Speed")
|
|
379
|
+
* .state("Idle", { clip: idleClip, loop: true })
|
|
380
|
+
* .state("Walk", { clip: walkClip, loop: true })
|
|
381
|
+
* .transition("Idle", "Walk", { duration: 0.25 })
|
|
382
|
+
* .condition("Speed", "greater", 0.1)
|
|
383
|
+
* .transition("Walk", "Idle", { duration: 0.25 })
|
|
384
|
+
* .condition("Speed", "less", 0.1)
|
|
385
|
+
* .build();
|
|
386
|
+
* ```
|
|
387
|
+
*/
|
|
388
|
+
static build(name?: string): AnimatorControllerBuilder {
|
|
389
|
+
return new AnimatorControllerBuilder(name);
|
|
390
|
+
}
|
|
391
|
+
|
|
122
392
|
/**
|
|
123
393
|
* Plays an animation state by name or hash.
|
|
124
394
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ShaderMaterial, Texture } from "three";
|
|
1
|
+
import { CubeUVReflectionMapping, ShaderMaterial, Texture } from "three";
|
|
2
2
|
import { GroundedSkybox as GroundProjection } from 'three/examples/jsm/objects/GroundedSkybox.js';
|
|
3
3
|
|
|
4
4
|
import { Gizmos } from "../engine/engine_gizmos.js";
|
|
@@ -12,6 +12,174 @@ import type { ContactShadows } from "./ContactShadows.js";
|
|
|
12
12
|
|
|
13
13
|
const debug = getParam("debuggroundprojection");
|
|
14
14
|
|
|
15
|
+
type GroundProjectionMaterial = GroundProjection["material"] & {
|
|
16
|
+
defines?: Record<string, string | number>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type GroundProjectionShaderUniforms = {
|
|
20
|
+
needleGroundProjectionBlurriness: { value: number };
|
|
21
|
+
needleGroundProjectionBlending: { value: number };
|
|
22
|
+
needleGroundProjectionAlphaFactor: { value: number };
|
|
23
|
+
needleGroundProjectionBackgroundIntensity: { value: number };
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const needleCubeUvMapVarying = /* glsl */`
|
|
27
|
+
#ifdef NEEDLE_USE_CUBE_UV_MAP
|
|
28
|
+
varying vec3 vNeedleGroundProjectionWorldDirection;
|
|
29
|
+
#endif
|
|
30
|
+
`;
|
|
31
|
+
|
|
32
|
+
const needleGroundProjectionFragmentPars = /* glsl */`
|
|
33
|
+
${needleCubeUvMapVarying}
|
|
34
|
+
uniform float needleGroundProjectionBlurriness;
|
|
35
|
+
uniform float needleGroundProjectionBlending;
|
|
36
|
+
uniform float needleGroundProjectionAlphaFactor;
|
|
37
|
+
uniform float needleGroundProjectionBackgroundIntensity;
|
|
38
|
+
|
|
39
|
+
float needleGroundProjectionSmoothstep(float edge0, float edge1, float x) {
|
|
40
|
+
float t = clamp((x - edge0) / max(edge1 - edge0, 0.000001), 0.0, 1.0);
|
|
41
|
+
return t * t * (3.0 - 2.0 * t);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
float needleGroundProjectionDistance() {
|
|
45
|
+
return length(vec2(0.0, vMapUv.y));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
float needleGroundProjectionBlurFactor(float needleGroundProjectionDistanceValue) {
|
|
49
|
+
return clamp(needleGroundProjectionSmoothstep(0.5, 1.0, needleGroundProjectionDistanceValue * 2.0), 0.0, 1.0);
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
const needleCubeUvMapFragment = /* glsl */`
|
|
54
|
+
#ifdef USE_MAP
|
|
55
|
+
|
|
56
|
+
float needleGroundProjectionDistanceValue = needleGroundProjectionDistance();
|
|
57
|
+
float needleGroundProjectionBlurFactorValue = needleGroundProjectionBlurFactor(needleGroundProjectionDistanceValue);
|
|
58
|
+
vec4 sampledDiffuseColor;
|
|
59
|
+
|
|
60
|
+
#ifdef NEEDLE_USE_CUBE_UV_MAP
|
|
61
|
+
sampledDiffuseColor = textureCubeUV(
|
|
62
|
+
map,
|
|
63
|
+
normalize( vNeedleGroundProjectionWorldDirection ),
|
|
64
|
+
needleGroundProjectionBlurriness * needleGroundProjectionBlurFactorValue
|
|
65
|
+
);
|
|
66
|
+
#else
|
|
67
|
+
#ifdef USE_MIPMAP_BIAS
|
|
68
|
+
sampledDiffuseColor = texture2D( map, vMapUv, mipmapBias );
|
|
69
|
+
#else
|
|
70
|
+
sampledDiffuseColor = texture2D( map, vMapUv );
|
|
71
|
+
#endif
|
|
72
|
+
#endif
|
|
73
|
+
|
|
74
|
+
#ifdef DECODE_VIDEO_TEXTURE
|
|
75
|
+
|
|
76
|
+
// use inline sRGB decode until browsers properly support SRGB8_ALPHA8 with video textures (#26516)
|
|
77
|
+
|
|
78
|
+
sampledDiffuseColor = vec4( mix( pow( sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), sampledDiffuseColor.rgb * 0.0773993808, vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) ), sampledDiffuseColor.w );
|
|
79
|
+
|
|
80
|
+
#endif
|
|
81
|
+
|
|
82
|
+
sampledDiffuseColor.rgb *= mix(1.0, needleGroundProjectionBackgroundIntensity, needleGroundProjectionBlurFactorValue);
|
|
83
|
+
diffuseColor *= sampledDiffuseColor;
|
|
84
|
+
|
|
85
|
+
#endif
|
|
86
|
+
`;
|
|
87
|
+
|
|
88
|
+
const needleGroundProjectionAlphaFragment = /* glsl */`
|
|
89
|
+
#ifdef USE_MAP
|
|
90
|
+
if (needleGroundProjectionBlending > 0.000001) {
|
|
91
|
+
float needleGroundProjectionBrightness = dot(diffuseColor.rgb, vec3(0.299, 0.587, 0.114));
|
|
92
|
+
float needleGroundProjectionStepFactor = needleGroundProjectionBlending - needleGroundProjectionBrightness * 0.1;
|
|
93
|
+
diffuseColor.a *= pow(
|
|
94
|
+
1.0 - needleGroundProjectionBlending * needleGroundProjectionSmoothstep(
|
|
95
|
+
0.35 * needleGroundProjectionStepFactor,
|
|
96
|
+
0.45 * needleGroundProjectionStepFactor,
|
|
97
|
+
needleGroundProjectionDistanceValue
|
|
98
|
+
),
|
|
99
|
+
5.0
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
#endif
|
|
103
|
+
diffuseColor.a *= needleGroundProjectionAlphaFactor;
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
function getCubeUvSize(texture: Texture) {
|
|
107
|
+
const imageHeight = texture.image?.height;
|
|
108
|
+
if (!imageHeight) return null;
|
|
109
|
+
|
|
110
|
+
const maxMip = Math.log2(imageHeight) - 2;
|
|
111
|
+
const texelHeight = 1 / imageHeight;
|
|
112
|
+
const texelWidth = 1 / (3 * Math.max(Math.pow(2, maxMip), 7 * 16));
|
|
113
|
+
return { texelWidth, texelHeight, maxMip };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getGroundProjectionShaderUniforms(material: GroundProjectionMaterial): GroundProjectionShaderUniforms {
|
|
117
|
+
const userData = material.userData as GroundProjectionMaterial["userData"] & {
|
|
118
|
+
needleGroundProjectionUniforms?: GroundProjectionShaderUniforms;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
return userData.needleGroundProjectionUniforms ??= {
|
|
122
|
+
needleGroundProjectionBlurriness: { value: 0 },
|
|
123
|
+
needleGroundProjectionBlending: { value: 0 },
|
|
124
|
+
needleGroundProjectionAlphaFactor: { value: 1 },
|
|
125
|
+
needleGroundProjectionBackgroundIntensity: { value: 1 }
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function configureGroundProjectionMaterial(material: GroundProjectionMaterial, texture: Texture) {
|
|
130
|
+
const projectionUniforms = getGroundProjectionShaderUniforms(material);
|
|
131
|
+
material.onBeforeCompile = shader => {
|
|
132
|
+
shader.uniforms.needleGroundProjectionBlurriness = projectionUniforms.needleGroundProjectionBlurriness;
|
|
133
|
+
shader.uniforms.needleGroundProjectionBlending = projectionUniforms.needleGroundProjectionBlending;
|
|
134
|
+
shader.uniforms.needleGroundProjectionAlphaFactor = projectionUniforms.needleGroundProjectionAlphaFactor;
|
|
135
|
+
shader.uniforms.needleGroundProjectionBackgroundIntensity = projectionUniforms.needleGroundProjectionBackgroundIntensity;
|
|
136
|
+
|
|
137
|
+
shader.vertexShader = shader.vertexShader
|
|
138
|
+
.replace("#include <uv_pars_vertex>", `#include <uv_pars_vertex>\n${needleCubeUvMapVarying}`)
|
|
139
|
+
.replace(
|
|
140
|
+
"#include <worldpos_vertex>",
|
|
141
|
+
`#include <worldpos_vertex>
|
|
142
|
+
#ifdef NEEDLE_USE_CUBE_UV_MAP
|
|
143
|
+
// GroundedSkybox mirrors geometry on Z, so undo that before deriving the sampling direction.
|
|
144
|
+
vNeedleGroundProjectionWorldDirection = transformDirection( vec3( position.x, position.y, -position.z ), modelMatrix );
|
|
145
|
+
#endif`
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
shader.fragmentShader = shader.fragmentShader
|
|
149
|
+
.replace(
|
|
150
|
+
"#include <map_pars_fragment>",
|
|
151
|
+
`#include <map_pars_fragment>
|
|
152
|
+
${needleGroundProjectionFragmentPars}
|
|
153
|
+
#include <cube_uv_reflection_fragment>`
|
|
154
|
+
)
|
|
155
|
+
.replace("#include <map_fragment>", needleCubeUvMapFragment)
|
|
156
|
+
.replace("#include <opaque_fragment>", `${needleGroundProjectionAlphaFragment}\n#include <opaque_fragment>`);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const defines = material.defines ??= {};
|
|
160
|
+
const prevDefineState = JSON.stringify(defines);
|
|
161
|
+
const cubeUvSize = texture.mapping === CubeUVReflectionMapping ? getCubeUvSize(texture) : null;
|
|
162
|
+
|
|
163
|
+
if (cubeUvSize) {
|
|
164
|
+
defines.NEEDLE_USE_CUBE_UV_MAP = 1;
|
|
165
|
+
defines.ENVMAP_TYPE_CUBE_UV = 1;
|
|
166
|
+
defines.CUBEUV_TEXEL_WIDTH = cubeUvSize.texelWidth;
|
|
167
|
+
defines.CUBEUV_TEXEL_HEIGHT = cubeUvSize.texelHeight;
|
|
168
|
+
defines.CUBEUV_MAX_MIP = `${cubeUvSize.maxMip}.0`;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
delete defines.NEEDLE_USE_CUBE_UV_MAP;
|
|
172
|
+
delete defines.ENVMAP_TYPE_CUBE_UV;
|
|
173
|
+
delete defines.CUBEUV_TEXEL_WIDTH;
|
|
174
|
+
delete defines.CUBEUV_TEXEL_HEIGHT;
|
|
175
|
+
delete defines.CUBEUV_MAX_MIP;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (prevDefineState !== JSON.stringify(defines)) {
|
|
179
|
+
material.needsUpdate = true;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
15
183
|
/**
|
|
16
184
|
* The [GroundProjectedEnv](https://engine.needle.tools/docs/api/GroundProjectedEnv) projects the environment map onto a virtual ground plane.
|
|
17
185
|
* Creates a realistic floor from 360° panoramas/HDRIs by deforming the skybox
|
|
@@ -147,12 +315,10 @@ export class GroundProjectedEnv extends Behaviour {
|
|
|
147
315
|
this._projection.rotation.copy(this.scene.backgroundRotation);
|
|
148
316
|
}
|
|
149
317
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
this.
|
|
153
|
-
|
|
154
|
-
else if (this._needsTextureUpdate && this.context.scene.background instanceof Texture) {
|
|
155
|
-
this.updateBlurriness(this.context.scene.background, this.context.scene.backgroundBlurriness);
|
|
318
|
+
if (this._projection && this.context.scene.background instanceof Texture) {
|
|
319
|
+
const blurriness = this.context.scene.backgroundBlurriness ?? 0;
|
|
320
|
+
const blurrinessChanged = this._lastBlurriness !== blurriness;
|
|
321
|
+
this.updateProjectionMaterial(this.context.scene.background, blurrinessChanged || this._needsTextureUpdate);
|
|
156
322
|
}
|
|
157
323
|
}
|
|
158
324
|
|
|
@@ -202,6 +368,7 @@ export class GroundProjectedEnv extends Behaviour {
|
|
|
202
368
|
|
|
203
369
|
try {
|
|
204
370
|
this._projection = new GroundProjection(backgroundTexture, this._height, this._radius, 64);
|
|
371
|
+
configureGroundProjectionMaterial(this._projection.material, backgroundTexture);
|
|
205
372
|
}
|
|
206
373
|
catch (e) {
|
|
207
374
|
console.error("Error creating three GroundProjection", e);
|
|
@@ -242,9 +409,7 @@ export class GroundProjectedEnv extends Behaviour {
|
|
|
242
409
|
this.env.height = this._height;
|
|
243
410
|
*/
|
|
244
411
|
|
|
245
|
-
|
|
246
|
-
this.updateBlurriness(backgroundTexture, this.context.scene.backgroundBlurriness);
|
|
247
|
-
}
|
|
412
|
+
this.updateProjectionMaterial(backgroundTexture, true);
|
|
248
413
|
|
|
249
414
|
this._lastBackground = backgroundTexture;
|
|
250
415
|
this._lastHeight = this._height;
|
|
@@ -254,24 +419,60 @@ export class GroundProjectedEnv extends Behaviour {
|
|
|
254
419
|
|
|
255
420
|
private _blurrynessShader: ShaderMaterial | null = null;
|
|
256
421
|
private _lastBlurriness: number = -1;
|
|
257
|
-
|
|
258
|
-
private
|
|
422
|
+
|
|
423
|
+
private updateProjectionMaterial(texture: Texture, forceTextureUpdate = false) {
|
|
259
424
|
if (!this._projection) {
|
|
260
425
|
return;
|
|
261
426
|
}
|
|
262
|
-
|
|
263
|
-
|
|
427
|
+
|
|
428
|
+
const blurriness = this.context.scene.backgroundBlurriness ?? 0;
|
|
429
|
+
const useCubeUvBlur = texture.mapping === CubeUVReflectionMapping;
|
|
430
|
+
|
|
431
|
+
let targetTexture = texture;
|
|
432
|
+
if (!useCubeUvBlur && blurriness > 0.001) {
|
|
433
|
+
const hasBlurredTextureAssigned = !!this._projection.material.map && this._projection.material.map !== texture;
|
|
434
|
+
if (forceTextureUpdate || !hasBlurredTextureAssigned) {
|
|
435
|
+
targetTexture = this.updateBlurriness(texture, blurriness);
|
|
436
|
+
}
|
|
437
|
+
else if (this._projection.material.map) {
|
|
438
|
+
targetTexture = this._projection.material.map;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (this._projection.material.map !== targetTexture) {
|
|
443
|
+
this._projection.material.map = targetTexture;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const appliedTexture = this._projection.material.map ?? texture;
|
|
447
|
+
appliedTexture.mapping = texture.mapping;
|
|
448
|
+
configureGroundProjectionMaterial(this._projection.material, appliedTexture);
|
|
449
|
+
|
|
450
|
+
const shaderUniforms = getGroundProjectionShaderUniforms(this._projection.material);
|
|
451
|
+
shaderUniforms.needleGroundProjectionBlurriness.value = useCubeUvBlur ? blurriness : 0;
|
|
452
|
+
shaderUniforms.needleGroundProjectionBackgroundIntensity.value = this.context.scene.backgroundIntensity ?? 1;
|
|
453
|
+
|
|
454
|
+
const wasTransparent = this._projection.material.transparent;
|
|
455
|
+
this._projection.material.transparent = (this.context.xr?.isAR === true && this.arBlending > 0.000001) ?? false;
|
|
456
|
+
shaderUniforms.needleGroundProjectionBlending.value = this._projection.material.transparent ? this.arBlending : 0;
|
|
457
|
+
shaderUniforms.needleGroundProjectionAlphaFactor.value = this.context.isInPassThrough ? 0.95 : 1;
|
|
458
|
+
|
|
459
|
+
if (wasTransparent !== this._projection.material.transparent) {
|
|
460
|
+
this._projection.material.needsUpdate = true;
|
|
264
461
|
}
|
|
265
462
|
|
|
463
|
+
this._projection.material.depthTest = true;
|
|
464
|
+
this._projection.material.depthWrite = false;
|
|
465
|
+
this._lastBlurriness = blurriness;
|
|
266
466
|
this._needsTextureUpdate = false;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
private updateBlurriness(texture: Texture, blurriness: number): Texture {
|
|
267
470
|
if (debug) console.log("Update Blurriness", blurriness);
|
|
268
471
|
this._blurrynessShader ??= new ShaderMaterial({
|
|
269
472
|
name: "GroundProjectionBlurriness",
|
|
270
473
|
uniforms: {
|
|
271
474
|
map: { value: texture },
|
|
272
|
-
blurriness: { value: blurriness }
|
|
273
|
-
blending: { value: 0 },
|
|
274
|
-
alphaFactor: { value: 1 }
|
|
475
|
+
blurriness: { value: blurriness }
|
|
275
476
|
},
|
|
276
477
|
vertexShader: blurVertexShader,
|
|
277
478
|
fragmentShader: blurFragmentShader
|
|
@@ -279,33 +480,10 @@ export class GroundProjectedEnv extends Behaviour {
|
|
|
279
480
|
this._blurrynessShader.depthWrite = false;
|
|
280
481
|
this._blurrynessShader.uniforms.map.value = texture;
|
|
281
482
|
this._blurrynessShader.uniforms.blurriness.value = blurriness;
|
|
282
|
-
this._lastBlurriness = blurriness;
|
|
283
483
|
texture.needsUpdate = true;
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
if (this._projection.material.transparent) {
|
|
288
|
-
this._blurrynessShader.uniforms.blending.value = this.arBlending;
|
|
289
|
-
}
|
|
290
|
-
else { this._blurrynessShader.uniforms.blending.value = 0; }
|
|
291
|
-
|
|
292
|
-
if (this.context.isInPassThrough) {
|
|
293
|
-
// Make the ground slightly transparent in passthrough mode
|
|
294
|
-
this._blurrynessShader.uniforms.alphaFactor.value = 0.95;
|
|
295
|
-
}
|
|
296
|
-
else {
|
|
297
|
-
this._blurrynessShader.uniforms.alphaFactor.value = 1;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// Make sure the material is updated if the transparency changed
|
|
301
|
-
if (wasTransparent !== this._projection.material.transparent) {
|
|
302
|
-
this._projection.material.needsUpdate = true;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// Update the texture
|
|
306
|
-
this._projection.material.map = Graphics.copyTexture(texture, this._blurrynessShader);
|
|
307
|
-
this._projection.material.depthTest = true;
|
|
308
|
-
this._projection.material.depthWrite = false;
|
|
484
|
+
const blurredTexture = Graphics.copyTexture(texture, this._blurrynessShader);
|
|
485
|
+
blurredTexture.mapping = texture.mapping;
|
|
486
|
+
return blurredTexture;
|
|
309
487
|
}
|
|
310
488
|
|
|
311
489
|
}
|
|
@@ -324,8 +502,6 @@ const blurVertexShader = `
|
|
|
324
502
|
const blurFragmentShader = `
|
|
325
503
|
uniform sampler2D map;
|
|
326
504
|
uniform float blurriness;
|
|
327
|
-
uniform float alphaFactor;
|
|
328
|
-
uniform float blending;
|
|
329
505
|
varying vec2 vUv;
|
|
330
506
|
|
|
331
507
|
const float PI = 3.14159265359;
|
|
@@ -356,6 +532,10 @@ const blurFragmentShader = `
|
|
|
356
532
|
vec4 color = vec4(0.0);
|
|
357
533
|
float totalWeight = 0.0;
|
|
358
534
|
int blurSize = int(60.0 * min(1.0, blurriness) * blurAmount); // Adjust blur size based on distance and blurriness
|
|
535
|
+
if (blurSize <= 0) {
|
|
536
|
+
gl_FragColor = texture2D(map, vUv);
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
359
539
|
float lodLevel = log2(float(blurSize)) * 0.5; // Compute LOD level
|
|
360
540
|
|
|
361
541
|
for (int x = -blurSize; x <= blurSize; x++) {
|
|
@@ -371,16 +551,10 @@ const blurFragmentShader = `
|
|
|
371
551
|
|
|
372
552
|
gl_FragColor = color;
|
|
373
553
|
|
|
374
|
-
float brightness = dot(gl_FragColor.rgb, vec3(0.299, 0.587, 0.114));
|
|
375
|
-
float stepFactor = blending - brightness * .1;
|
|
376
|
-
gl_FragColor.a = pow(1.0 - blending * customSmoothstep(0.35 * stepFactor, 0.45 * stepFactor, distance), 5.);
|
|
377
|
-
gl_FragColor.a *= alphaFactor;
|
|
378
|
-
// gl_FragColor.rgb = vec3(1.0);
|
|
379
|
-
|
|
380
554
|
// #include <tonemapping_fragment>
|
|
381
555
|
// #include <colorspace_fragment>
|
|
382
556
|
|
|
383
557
|
// Uncomment to visualize blur amount
|
|
384
558
|
// gl_FragColor = vec4(blurAmount, 0.0, 0.0, 1.0);
|
|
385
559
|
}
|
|
386
|
-
`;
|
|
560
|
+
`;
|