@@ -89,6 +89,7 @@
|
|
89
89
|
*/
|
90
90
|
export function getTempVector(vecOrX?: Vector3 | number | DOMPointReadOnly, y?: number, z?: number) {
|
91
91
|
const vec = _tempVecs.get();
|
92
|
+
vec.set(0, 0, 0); // initialize with default values
|
92
93
|
if (vecOrX instanceof Vector3) vec.copy(vecOrX);
|
93
94
|
else if (vecOrX instanceof DOMPointReadOnly) vec.set(vecOrX.x, vecOrX.y, vecOrX.z);
|
94
95
|
else {
|
@@ -108,6 +109,7 @@
|
|
108
109
|
*/
|
109
110
|
export function getTempQuaternion(value?: Quaternion | DOMPointReadOnly) {
|
110
111
|
const val = _tempQuats.get();
|
112
|
+
val.identity();
|
111
113
|
if (value instanceof Quaternion) val.copy(value);
|
112
114
|
else if (value instanceof DOMPointReadOnly) val.set(value.x, value.y, value.z, value.w);
|
113
115
|
return val;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Texture, Vector3 } from "three";
|
1
|
+
import { Material, MeshBasicMaterial, ShaderMaterial, Texture, Vector3 } from "three";
|
2
2
|
import { GroundedSkybox as GroundProjection } from 'three/examples/jsm/objects/GroundedSkybox.js';
|
3
3
|
|
4
4
|
import { serializable } from "../engine/engine_serialization_decorator.js";
|
@@ -88,6 +88,10 @@
|
|
88
88
|
if (this._projection && this.scene.backgroundRotation) {
|
89
89
|
this._projection.rotation.copy(this.scene.backgroundRotation);
|
90
90
|
}
|
91
|
+
const mat = this._projection?.material as unknown as ShaderMaterial;
|
92
|
+
if (mat?.uniforms?.blurriness && this.context.scene.backgroundBlurriness !== undefined && mat.uniforms.blurriness.value != this.context.scene.backgroundBlurriness) {
|
93
|
+
mat.uniforms.blurriness.value = this.context.scene.backgroundBlurriness;
|
94
|
+
}
|
91
95
|
}
|
92
96
|
|
93
97
|
private updateAndCreate() {
|
@@ -95,6 +99,9 @@
|
|
95
99
|
this._watcher?.apply();
|
96
100
|
}
|
97
101
|
|
102
|
+
/**
|
103
|
+
* Updates the ground projection. This is called automatically when the environment changes.
|
104
|
+
*/
|
98
105
|
updateProjection() {
|
99
106
|
if (!this.context.scene.environment || this.context.xr?.isPassThrough) {
|
100
107
|
this._projection?.removeFromParent();
|
@@ -107,6 +114,29 @@
|
|
107
114
|
this._projection?.removeFromParent();
|
108
115
|
this._projection = new GroundProjection(this.context.scene.environment, this._height, this.radius);
|
109
116
|
this._projection.position.y = this._height - offset;
|
117
|
+
const originalMaterial = this._projection.material as MeshBasicMaterial;
|
118
|
+
|
119
|
+
// const mat = this._projection.material;
|
120
|
+
const customShader = new ShaderMaterial({
|
121
|
+
uniforms: {
|
122
|
+
map: { value: this.context.scene.environment },
|
123
|
+
blurriness: { value: this.context.scene.backgroundBlurriness }
|
124
|
+
},
|
125
|
+
vertexShader: vertexShader,
|
126
|
+
fragmentShader: fragmentShader
|
127
|
+
});
|
128
|
+
customShader.depthWrite = false;
|
129
|
+
this._projection.material = customShader as unknown as MeshBasicMaterial;
|
130
|
+
// TODO: Blit the result of this shader once to a texture and use that texture for the projection
|
131
|
+
|
132
|
+
// setInterval(() => {
|
133
|
+
// if (this._projection?.material === originalMaterial) {
|
134
|
+
// this._projection.material = customShader as unknown as MeshBasicMaterial;
|
135
|
+
// }
|
136
|
+
// else {
|
137
|
+
// this._projection.material = originalMaterial;
|
138
|
+
// }
|
139
|
+
// }, 1000)
|
110
140
|
}
|
111
141
|
|
112
142
|
this._lastEnvironment = this.context.scene.environment;
|
@@ -120,11 +150,13 @@
|
|
120
150
|
this._projection.updateWorldMatrix(true, true);
|
121
151
|
const scenebounds = getBoundingBox(this.context.scene.children, [this._projection]);
|
122
152
|
let floor_y = scenebounds.min.y;
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
153
|
+
if (floor_y < Infinity) {
|
154
|
+
const wp = getWorldPosition(this._projection);
|
155
|
+
wp.x = scenebounds.min.x + (scenebounds.max.x - scenebounds.min.x) * .5;
|
156
|
+
wp.y = floor_y + this._height - offset;
|
157
|
+
wp.z = scenebounds.min.z + (scenebounds.max.z - scenebounds.min.z) * .5;
|
158
|
+
setWorldPosition(this._projection, wp);
|
159
|
+
}
|
128
160
|
}
|
129
161
|
|
130
162
|
/* TODO realtime adjustments aren't possible anymore with GroundedSkybox (mesh generation)
|
@@ -139,4 +171,71 @@
|
|
139
171
|
}
|
140
172
|
}
|
141
173
|
|
142
|
-
}
|
174
|
+
}
|
175
|
+
|
176
|
+
|
177
|
+
const vertexShader = `
|
178
|
+
varying vec2 vUv;
|
179
|
+
|
180
|
+
void main() {
|
181
|
+
vUv = uv;
|
182
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
183
|
+
}
|
184
|
+
`;
|
185
|
+
|
186
|
+
// Fragment Shader
|
187
|
+
const fragmentShader = `
|
188
|
+
uniform sampler2D map;
|
189
|
+
uniform float blurriness;
|
190
|
+
varying vec2 vUv;
|
191
|
+
|
192
|
+
const float PI = 3.14159265359;
|
193
|
+
|
194
|
+
// Gaussian function
|
195
|
+
float gaussian(float x, float sigma) {
|
196
|
+
return exp(-(x * x) / (2.0 * sigma * sigma)) / (sqrt(2.0 * PI) * sigma);
|
197
|
+
}
|
198
|
+
|
199
|
+
// Custom smoothstep function for desired falloff
|
200
|
+
float customSmoothstep(float edge0, float edge1, float x) {
|
201
|
+
float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
|
202
|
+
return t * t * (3.0 - 2.0 * t);
|
203
|
+
}
|
204
|
+
|
205
|
+
void main() {
|
206
|
+
vec2 center = vec2(0.0, 0.0);
|
207
|
+
vec2 pos = vUv;
|
208
|
+
pos.x = 0.0; // Only consider vertical distance
|
209
|
+
float distance = length(pos - center) * 2.0;
|
210
|
+
|
211
|
+
// Calculate blur amount based on custom falloff
|
212
|
+
float blurAmount = customSmoothstep(0.4, 1.0, distance);
|
213
|
+
blurAmount = clamp(blurAmount, 0.0, 1.0); // Ensure blur amount is within valid range
|
214
|
+
|
215
|
+
// Gaussian blur
|
216
|
+
vec2 pixelSize = 1.0 / vec2(textureSize(map, 0));
|
217
|
+
vec4 color = vec4(0.0);
|
218
|
+
float totalWeight = 0.0;
|
219
|
+
int blurSize = int(20.0 * min(1.0, blurriness) * blurAmount); // Adjust blur size based on distance and blurriness
|
220
|
+
float lodLevel = log2(float(blurSize)) * 0.5; // Compute LOD level
|
221
|
+
|
222
|
+
for (int x = -blurSize; x <= blurSize; x++) {
|
223
|
+
for (int y = -blurSize; y <= blurSize; y++) {
|
224
|
+
vec2 offset = vec2(float(x), float(y)) * pixelSize * blurAmount;
|
225
|
+
float weight = gaussian(length(vec2(float(x), float(y))), 1000.0 * blurAmount); // Use a fixed sigma value
|
226
|
+
color += textureLod(map, vUv + offset, lodLevel) * weight;
|
227
|
+
totalWeight += weight;
|
228
|
+
}
|
229
|
+
}
|
230
|
+
|
231
|
+
color = totalWeight > 0.0 ? color / totalWeight : texture2D(map, vUv);
|
232
|
+
|
233
|
+
gl_FragColor = color;
|
234
|
+
|
235
|
+
#include <tonemapping_fragment>
|
236
|
+
#include <colorspace_fragment>
|
237
|
+
|
238
|
+
// Uncomment to visualize blur amount
|
239
|
+
// gl_FragColor = vec4(blurAmount, 0.0, 0.0, 1.0);
|
240
|
+
}
|
241
|
+
`;
|
@@ -37,6 +37,12 @@
|
|
37
37
|
|
38
38
|
static observedAttributes = ["ar", "vr", "quicklook"];
|
39
39
|
|
40
|
+
constructor() {
|
41
|
+
super();
|
42
|
+
this.removeEventListener("click", this.#onclick);
|
43
|
+
this.addEventListener("click", this.#onclick);
|
44
|
+
}
|
45
|
+
|
40
46
|
attributeChangedCallback(_name: string, _oldValue: string, _newValue: string) {
|
41
47
|
this.#update()
|
42
48
|
}
|
@@ -147,20 +153,16 @@
|
|
147
153
|
|
148
154
|
#updateVisibility() {
|
149
155
|
if (this.#button) {
|
150
|
-
// if the user has set any display style don't override it
|
151
|
-
if (this.style.display?.length) {
|
152
|
-
return;
|
153
|
-
}
|
154
156
|
if (this.#button.style.display === "none") {
|
155
157
|
this.style.display = "none";
|
156
158
|
}
|
157
|
-
else {
|
159
|
+
else if (this.style.display === "none") {
|
158
160
|
this.style.display = "";
|
159
161
|
}
|
160
162
|
}
|
161
163
|
}
|
162
164
|
|
163
|
-
|
165
|
+
#onclick = (_ev: MouseEvent) => {
|
164
166
|
if (isDev) {
|
165
167
|
console.log("Needle Button clicked")
|
166
168
|
}
|