@@ -11,6 +11,7 @@
|
|
11
11
|
import * as ThreeMeshUI from 'three-mesh-ui'
|
12
12
|
import { getParam } from "../../engine/engine_utils";
|
13
13
|
import { LayoutGroup } from "./Layout";
|
14
|
+
import { Mathf } from "../../engine/engine_math";
|
14
15
|
|
15
16
|
export enum RenderMode {
|
16
17
|
ScreenSpaceOverlay = 0,
|
@@ -127,6 +128,9 @@
|
|
127
128
|
awake() {
|
128
129
|
//@ts-ignore
|
129
130
|
this.shadowComponent = this.gameObject;
|
131
|
+
this.previousParent = this.gameObject.parent;
|
132
|
+
if (debugLayout)
|
133
|
+
console.log("Canvas.Awake()", this.previousParent?.name + "/" + this.gameObject.name)
|
130
134
|
super.awake();
|
131
135
|
}
|
132
136
|
|
@@ -137,7 +141,8 @@
|
|
137
141
|
onEnable() {
|
138
142
|
super.onEnable();
|
139
143
|
this._updateRenderSettingsRoutine = undefined;
|
140
|
-
this.
|
144
|
+
this._lastMatrixWorld = new Matrix4();
|
145
|
+
this.onUpdateRenderMode();
|
141
146
|
document.addEventListener("resize", this._boundRenderSettingsChanged);
|
142
147
|
// We want to run AFTER all regular onBeforeRender callbacks
|
143
148
|
this.context.pre_render_callbacks.push(this.onBeforeRenderRoutine);
|
@@ -185,9 +190,11 @@
|
|
185
190
|
}
|
186
191
|
|
187
192
|
onBeforeRenderRoutine = () => {
|
193
|
+
this.previousParent = this.gameObject.parent;
|
194
|
+
// console.log(this.previousParent?.name + "/" + this.gameObject.name);
|
195
|
+
|
188
196
|
if (this.renderOnTop) {
|
189
197
|
// This is just a test but in reality it should be combined with all world canvases with render on top in one render pass
|
190
|
-
this.previousParent = this.gameObject.parent;
|
191
198
|
this.gameObject.removeFromParent();
|
192
199
|
}
|
193
200
|
else {
|
@@ -201,8 +208,13 @@
|
|
201
208
|
}
|
202
209
|
|
203
210
|
onAfterRenderRoutine = () => {
|
204
|
-
if (this.
|
205
|
-
|
211
|
+
if ((this.screenspace || this.renderMode) && this.previousParent && this.context.mainCamera) {
|
212
|
+
if (this.screenspace) {
|
213
|
+
const camObj = this.context.mainCamera;
|
214
|
+
camObj?.add(this.gameObject);
|
215
|
+
} else {
|
216
|
+
this.previousParent.add(this.gameObject);
|
217
|
+
}
|
206
218
|
this.context.renderer.autoClear = false;
|
207
219
|
this.context.renderer.clearDepth();
|
208
220
|
this.onUpdateRenderMode(true);
|
@@ -212,6 +224,7 @@
|
|
212
224
|
EventSystem.ensureUpdateMeshUI(ThreeMeshUI, this.context);
|
213
225
|
this.context.renderer.render(this.gameObject, this.context.mainCamera);
|
214
226
|
this.context.renderer.autoClear = true;
|
227
|
+
this.previousParent.add(this.gameObject);
|
215
228
|
}
|
216
229
|
this._lastMatrixWorld?.copy(this.gameObject.matrixWorld);
|
217
230
|
}
|
@@ -278,7 +291,10 @@
|
|
278
291
|
}
|
279
292
|
this._activeRenderMode = this._renderMode;
|
280
293
|
let camera = this.context.mainCameraComponent;
|
281
|
-
let planeDistance: number =
|
294
|
+
let planeDistance: number = 10;
|
295
|
+
if (camera && camera.nearClipPlane > 0 && camera.farClipPlane > 0) {
|
296
|
+
planeDistance = Mathf.lerp(camera.nearClipPlane, camera.farClipPlane, .5);
|
297
|
+
}
|
282
298
|
if (this._renderMode === RenderMode.ScreenSpaceCamera) {
|
283
299
|
if (this.worldCamera)
|
284
300
|
camera = this.worldCamera as Camera;
|
@@ -295,15 +311,12 @@
|
|
295
311
|
// showBalloonWarning("Screenspace Canvas is not supported yet. Please use worldspace");
|
296
312
|
if (!camera) return;
|
297
313
|
|
298
|
-
const canvas = this.gameObject;
|
299
|
-
const camObj = camera.gameObject;
|
300
|
-
camObj?.add(canvas);
|
301
314
|
// we move the plane SLIGHTLY closer to be sure not to cull the canvas
|
302
|
-
const plane = planeDistance
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
315
|
+
const plane = planeDistance + .01;
|
316
|
+
this.gameObject.position.x = 0;
|
317
|
+
this.gameObject.position.y = 0;
|
318
|
+
this.gameObject.position.z = -plane;
|
319
|
+
this.gameObject.quaternion.identity();
|
307
320
|
|
308
321
|
const rect = this.gameObject.getComponent(RectTransform)!;
|
309
322
|
let hasChanged = false;
|
@@ -316,10 +329,10 @@
|
|
316
329
|
|
317
330
|
const vFOV = camera.fieldOfView! * Math.PI / 180;
|
318
331
|
const h = 2 * Math.tan(vFOV / 2) * Math.abs(plane);
|
319
|
-
|
320
|
-
|
332
|
+
this.gameObject.scale.x = h / this.context.domHeight;
|
333
|
+
this.gameObject.scale.y = h / this.context.domHeight;
|
321
334
|
// Set scale.z, otherwise small offsets in screenspace mode have different visual results based on export scale and other settings
|
322
|
-
|
335
|
+
this.gameObject.scale.z = .01;
|
323
336
|
|
324
337
|
if (hasChanged) {
|
325
338
|
rect.sizeDelta.x = this.context.domWidth;
|
@@ -80,7 +80,7 @@
|
|
80
80
|
ImmersiveAR = "immersive-ar",
|
81
81
|
}
|
82
82
|
|
83
|
-
export declare type
|
83
|
+
export declare type OnRenderCallback = (renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, material: Material, group: Group) => void
|
84
84
|
|
85
85
|
|
86
86
|
export function registerComponent(script: IComponent, context?: Context) {
|
@@ -440,33 +440,57 @@
|
|
440
440
|
}
|
441
441
|
}
|
442
442
|
|
443
|
-
private _onBeforeRenderListeners: { [key: string]: OnBeforeRenderCallback[] } = {};
|
444
443
|
|
444
|
+
|
445
|
+
private _onBeforeRenderListeners = new Map<string, OnRenderCallback[]>();
|
446
|
+
private _onAfterRenderListeners = new Map<string, OnRenderCallback[]>();
|
447
|
+
|
445
448
|
/** use this to subscribe to onBeforeRender events on threejs objects */
|
446
|
-
addBeforeRenderListener(target: Object3D, callback:
|
447
|
-
if (!this._onBeforeRenderListeners
|
448
|
-
this._onBeforeRenderListeners
|
449
|
-
|
450
|
-
const arr = this._onBeforeRenderListeners[target.uuid];
|
451
|
-
if (!arr) return;
|
452
|
-
for (let i = 0; i < arr.length; i++) {
|
453
|
-
const fn = arr[i];
|
454
|
-
fn(renderer, scene, camera, geometry, material, group);
|
455
|
-
}
|
456
|
-
}
|
457
|
-
target.onBeforeRender = onBeforeRenderCallback as any;
|
449
|
+
addBeforeRenderListener(target: Object3D, callback: OnRenderCallback) {
|
450
|
+
if (!this._onBeforeRenderListeners.has(target.uuid)) {
|
451
|
+
this._onBeforeRenderListeners.set(target.uuid, []);
|
452
|
+
target.onBeforeRender = this._createRenderCallbackWrapper(target, this._onBeforeRenderListeners);
|
458
453
|
}
|
459
|
-
this._onBeforeRenderListeners
|
454
|
+
this._onBeforeRenderListeners.get(target.uuid)?.push(callback);
|
460
455
|
}
|
456
|
+
removeBeforeRenderListener(target: Object3D, callback: OnRenderCallback) {
|
457
|
+
if (this._onBeforeRenderListeners.has(target.uuid)) {
|
458
|
+
const arr = this._onBeforeRenderListeners.get(target.uuid)!;
|
459
|
+
const idx = arr.indexOf(callback);
|
460
|
+
if (idx >= 0) arr.splice(idx, 1);
|
461
|
+
}
|
462
|
+
}
|
461
463
|
|
462
|
-
|
463
|
-
|
464
|
-
|
464
|
+
/** use this to subscribe to onAfterRender events on threejs objects */
|
465
|
+
addAfterRenderListener(target: Object3D, callback: OnRenderCallback) {
|
466
|
+
if (!this._onAfterRenderListeners.has(target.uuid)) {
|
467
|
+
this._onAfterRenderListeners.set(target.uuid, []);
|
468
|
+
target.onAfterRender = this._createRenderCallbackWrapper(target, this._onAfterRenderListeners);
|
469
|
+
}
|
470
|
+
this._onAfterRenderListeners.get(target.uuid)?.push(callback);
|
471
|
+
}
|
472
|
+
removeAfterRenderListener(target: Object3D, callback: OnRenderCallback) {
|
473
|
+
if (this._onAfterRenderListeners.has(target.uuid)) {
|
474
|
+
const arr = this._onAfterRenderListeners.get(target.uuid)!;
|
465
475
|
const idx = arr.indexOf(callback);
|
466
476
|
if (idx >= 0) arr.splice(idx, 1);
|
467
477
|
}
|
468
478
|
}
|
469
479
|
|
480
|
+
|
481
|
+
private _createRenderCallbackWrapper(target: Object3D, array: Map<string, OnRenderCallback[]>): OnRenderCallback {
|
482
|
+
return (renderer, scene, camera, geometry, material, group) => {
|
483
|
+
const arr = array.get(target.uuid);
|
484
|
+
if (!arr) return;
|
485
|
+
for (let i = 0; i < arr.length; i++) {
|
486
|
+
const fn = arr[i];
|
487
|
+
fn(renderer, scene, camera, geometry, material, group);
|
488
|
+
}
|
489
|
+
}
|
490
|
+
}
|
491
|
+
|
492
|
+
|
493
|
+
|
470
494
|
private _requireDepthTexture: boolean = false;
|
471
495
|
private _requireColorTexture: boolean = false;
|
472
496
|
private _renderTarget?: WebGLRenderTarget;
|
@@ -584,7 +608,7 @@
|
|
584
608
|
else {
|
585
609
|
ContextRegistry.dispatchCallback(ContextEvent.MissingCamera, this);
|
586
610
|
if (!this.mainCamera && !this.isManagedExternally)
|
587
|
-
console.
|
611
|
+
console.warn("Missing camera in main scene", this);
|
588
612
|
}
|
589
613
|
}
|
590
614
|
|
@@ -164,7 +164,8 @@
|
|
164
164
|
actualWidth -= this.padding.horizontal;
|
165
165
|
actualHeight -= this.padding.vertical;
|
166
166
|
|
167
|
-
//
|
167
|
+
// if (rect.name === "Title")
|
168
|
+
// console.log(rect.name, "width=" + totalWidth + ", height=" + totalHeight, rect.anchoredPosition.x)
|
168
169
|
|
169
170
|
const paddingAxis = axis === Axis.Horizontal ? this.padding.horizontal : this.padding.vertical;
|
170
171
|
const isHorizontal = axis === Axis.Horizontal;
|
@@ -243,7 +244,7 @@
|
|
243
244
|
}
|
244
245
|
|
245
246
|
// Apply layout
|
246
|
-
let k =
|
247
|
+
let k = 1;
|
247
248
|
for (let i = 0; i < this.gameObject.children.length; i++) {
|
248
249
|
const ch = this.gameObject.children[i];
|
249
250
|
const rt = GameObject.getComponent(ch, RectTransform);
|
@@ -269,10 +270,14 @@
|
|
269
270
|
let halfSize = size * .5;
|
270
271
|
start += halfSize;
|
271
272
|
|
273
|
+
// TODO: this isnt correct yet!
|
272
274
|
if (forceExpandSize) {
|
273
|
-
|
274
|
-
|
275
|
-
|
275
|
+
// this is the center of the cell
|
276
|
+
let preferredStart = sizePerChild * k - sizePerChild * .5;
|
277
|
+
if (preferredStart > start) {
|
278
|
+
start = preferredStart - sizePerChild * .5 + size + this.padding.left;
|
279
|
+
start -= halfSize;
|
280
|
+
}
|
276
281
|
}
|
277
282
|
|
278
283
|
let value = start;
|
@@ -31,7 +31,7 @@
|
|
31
31
|
|
32
32
|
export class RectTransform extends BaseUIComponent implements IRectTransform, IRectTransformChangedReceiver {
|
33
33
|
|
34
|
-
offset: number =
|
34
|
+
offset: number = .01;
|
35
35
|
|
36
36
|
// @serializable(Object3D)
|
37
37
|
// root? : Object3D;
|
@@ -103,7 +103,8 @@
|
|
103
103
|
awake() {
|
104
104
|
super.awake();
|
105
105
|
this.lastMatrix = new Matrix4();
|
106
|
-
this.rectBlock = new Object3D()
|
106
|
+
this.rectBlock = new Object3D();
|
107
|
+
// Is this legacy? Not sure if this is still needed
|
107
108
|
this.rectBlock.position.z = .1;
|
108
109
|
this.rectBlock.name = this.name;
|
109
110
|
|
@@ -116,6 +117,8 @@
|
|
116
117
|
onChange(this, "_anchoredPosition", () => { this.markDirty(); });
|
117
118
|
onChange(this, "sizeDelta", () => { this.markDirty(); });
|
118
119
|
onChange(this, "pivot", () => { this.markDirty(); });
|
120
|
+
onChange(this, "anchorMin", () => { this.markDirty(); });
|
121
|
+
onChange(this, "anchorMax", () => { this.markDirty(); });
|
119
122
|
|
120
123
|
// When exported with an anchored position offset we remove it here
|
121
124
|
// because it would otherwise be applied twice when the anchoring is animated
|
@@ -191,9 +194,10 @@
|
|
191
194
|
|
192
195
|
const uiobject = this.shadowComponent;
|
193
196
|
if (!uiobject) return;
|
194
|
-
if (
|
195
|
-
|
196
|
-
|
197
|
+
if (this.gameObject.parent)
|
198
|
+
this._parentRectTransform = GameObject.getComponentInParent(this.gameObject.parent, RectTransform) as RectTransform;
|
199
|
+
else
|
200
|
+
this._parentRectTransform = undefined;
|
197
201
|
this._transformNeedsUpdate = false;
|
198
202
|
this.lastMatrix.copy(this.gameObject.matrix);
|
199
203
|
|
@@ -229,7 +233,7 @@
|
|
229
233
|
else {
|
230
234
|
// We have to rotate the canvas when it's in worldspace
|
231
235
|
const canvas = this.Root as any as ICanvas;
|
232
|
-
if (
|
236
|
+
if (!canvas.screenspace) uiobject.rotation.y = Math.PI;
|
233
237
|
}
|
234
238
|
|
235
239
|
// iterate other components on this object that might need to know about the transform change
|
@@ -129,17 +129,28 @@
|
|
129
129
|
// otherwise some renderers outside of the probe will be affected or vice versa
|
130
130
|
for (let i = 0; i < _rend.sharedMaterials.length; i++) {
|
131
131
|
const material = _rend.sharedMaterials[i];
|
132
|
-
if (!material)
|
133
|
-
if (material["envMap"] === undefined) continue;
|
134
|
-
if (material["envMap"] === this.texture) {
|
132
|
+
if (!material) {
|
135
133
|
continue;
|
136
134
|
}
|
135
|
+
if (material["envMap"] === undefined) {
|
136
|
+
continue;
|
137
|
+
}
|
138
|
+
|
137
139
|
let cached = rendererCache[i];
|
138
140
|
|
139
141
|
// make sure we have the currently assigned material cached (and an up to date clone of that)
|
140
142
|
// TODO: this is causing problems with progressive textures sometimes (depending on the order) when the version changes and we re-create materials over and over. We might want to just set the material envmap instead of making a clone
|
141
|
-
|
142
|
-
|
143
|
+
let isCachedInstance = material === cached?.copy;
|
144
|
+
let hasChanged = !cached || cached.material.uuid !== material.uuid || cached.copy.version !== material.version;
|
145
|
+
if (!isCachedInstance && hasChanged) {
|
146
|
+
if (debug) {
|
147
|
+
let reason = "";
|
148
|
+
if (!cached) reason = "not cached";
|
149
|
+
else if (cached.material !== material) reason = "reference changed; cached instance?: " + isCachedInstance;
|
150
|
+
else if (cached.copy.version !== material.version) reason = "version changed";
|
151
|
+
console.warn("Cloning material", material.name, material.version, "Reason:", reason, "\n", material.uuid, "\n", cached?.copy.uuid, "\n", _rend.name);
|
152
|
+
}
|
153
|
+
|
143
154
|
const clone = material.clone();
|
144
155
|
clone.version = material.version;
|
145
156
|
|
@@ -158,8 +169,7 @@
|
|
158
169
|
clone[$reflectionProbeKey] = this;
|
159
170
|
clone[$originalMaterial] = material;
|
160
171
|
|
161
|
-
if (debug)
|
162
|
-
console.log("Set reflection", _rend.name, _rend.guid);
|
172
|
+
if (debug) console.log("Set reflection", _rend.name, _rend.guid);
|
163
173
|
}
|
164
174
|
|
165
175
|
/** this is the material that we copied and that has the reflection probe */
|
@@ -605,15 +605,15 @@
|
|
605
605
|
}
|
606
606
|
}
|
607
607
|
|
608
|
+
|
609
|
+
if (this.reflectionProbeUsage !== ReflectionProbeUsage.Off && this._reflectionProbe) {
|
610
|
+
this._reflectionProbe.onSet(this);
|
611
|
+
}
|
612
|
+
|
608
613
|
}
|
609
|
-
|
610
614
|
onBeforeRenderThree(_renderer, _scene, _camera, _geometry, material, _group) {
|
611
615
|
|
612
616
|
this.loadProgressiveTextures(material);
|
613
|
-
// TODO: we might want to just set the material.envMap for one material during rendering instead of cloning the material
|
614
|
-
if (this.reflectionProbeUsage !== ReflectionProbeUsage.Off && this._reflectionProbe) {
|
615
|
-
this._reflectionProbe.onSet(this);
|
616
|
-
}
|
617
617
|
|
618
618
|
if (material.envMapIntensity !== undefined) {
|
619
619
|
const factor = this.hasLightmap ? Math.PI : 1;
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import { Behaviour, GameObject } from "./Component";
|
2
2
|
import * as THREE from "three";
|
3
3
|
import { Texture } from "three";
|
4
|
-
import { Context,
|
4
|
+
import { Context, OnRenderCallback } from "../engine/engine_setup";
|
5
5
|
|
6
6
|
// this component is automatically added by the Renderer if the object has lightmap uvs AND we have a lightmap
|
7
7
|
// for multimaterial objects GLTF exports a "Group" with the renderer component
|
@@ -17,7 +17,6 @@
|
|
17
17
|
Camera,
|
18
18
|
Color,
|
19
19
|
MeshStandardMaterial,
|
20
|
-
LinearEncoding,
|
21
20
|
sRGBEncoding,
|
22
21
|
MeshPhysicalMaterial,
|
23
22
|
} from 'three';
|
@@ -1109,9 +1108,6 @@
|
|
1109
1108
|
const textureTransformInput = `</Materials/Material_${material.id}/${uvReader}.outputs:result>`;
|
1110
1109
|
const textureTransformOutput = `</Materials/Material_${material.id}/Transform2d_${mapType}.outputs:result>`;
|
1111
1110
|
|
1112
|
-
const rawTextureExtra = `(
|
1113
|
-
colorSpace = "Raw"
|
1114
|
-
)`;
|
1115
1111
|
const needsTextureScale = mapType !== 'normal' && (color && (color.r !== 1 || color.g !== 1 || color.b !== 1 || opacity !== 1)) || false;
|
1116
1112
|
const needsNormalScaleAndBias = mapType === 'normal';
|
1117
1113
|
const normalScaleValueString = (material.normalScale ? material.normalScale.x * 2 : 2).toFixed( PRECISION );
|
@@ -1133,7 +1129,8 @@
|
|
1133
1129
|
def Shader "Texture_${texture.id}_${mapType}"
|
1134
1130
|
{
|
1135
1131
|
uniform token info:id = "UsdUVTexture"
|
1136
|
-
asset inputs:file = @textures/Texture_${id}.${isRGBA ? 'png' : 'jpg'}@
|
1132
|
+
asset inputs:file = @textures/Texture_${id}.${isRGBA ? 'png' : 'jpg'}@
|
1133
|
+
token inputs:sourceColorSpace = "${ texture.colorSpace === 'srgb' ? 'sRGB' : 'raw' }"
|
1137
1134
|
float2 inputs:st.connect = ${needsTextureTransform ? textureTransformOutput : textureTransformInput}
|
1138
1135
|
${needsTextureScale ? `
|
1139
1136
|
float4 inputs:scale = (${color ? color.r + ', ' + color.g + ', ' + color.b : '1, 1, 1'}, ${opacity ? opacity : '1'})
|