Checkpoint web parity through Chromium input tasks
This commit is contained in:
@@ -7,6 +7,8 @@ import { cloneMeshGeometryBuffers } from "../../../protocol/mesh-geometry-delta"
|
||||
import { nonMeshChunkTransferables } from "../../../protocol/nonmesh-binary";
|
||||
import type { OffscreenViewportRequest, OffscreenViewportResponse } from "./offscreen-viewport-protocol";
|
||||
import { PBR_PROFILE, PBR_SHADOW_PROFILE, PBR_TONE_MAPPING } from "./pbr";
|
||||
import { resolveViewportPixelMetrics, viewportNDC } from "../../../protocol/viewport-dpr";
|
||||
import { observePointerEvent } from "../../../protocol/pointer-contract";
|
||||
import type { NonMeshElementKind } from "./nonmesh";
|
||||
import type { GreasePencilPointPreview, GreasePencilPointRef } from "./grease-pencil";
|
||||
import type { CurveGizmoFrameIR, CurveGizmoHandleIR } from "../../../protocol/nonmesh-interaction";
|
||||
@@ -118,7 +120,7 @@ export class OffscreenViewportRenderer implements ViewportBackend {
|
||||
canvas: offscreen,
|
||||
width: Math.max(1, canvas.clientWidth),
|
||||
height: Math.max(1, canvas.clientHeight),
|
||||
pixelRatio: Math.min(window.devicePixelRatio || 1, 2),
|
||||
pixelRatio: resolveViewportPixelMetrics(1, 1, window.devicePixelRatio).pixelRatio,
|
||||
};
|
||||
this.worker.postMessage(request, [offscreen]);
|
||||
this.resizeObserver = new ResizeObserver(() => this.resize());
|
||||
@@ -237,15 +239,21 @@ export class OffscreenViewportRenderer implements ViewportBackend {
|
||||
}
|
||||
|
||||
private resize(): void {
|
||||
const metrics = resolveViewportPixelMetrics(Math.max(1, this.canvas.clientWidth), Math.max(1, this.canvas.clientHeight), window.devicePixelRatio);
|
||||
this.canvas.dataset.viewportCssSize = `${metrics.cssWidth}x${metrics.cssHeight}`;
|
||||
this.canvas.dataset.viewportBackingSize = `${metrics.backingWidth}x${metrics.backingHeight}`;
|
||||
this.canvas.dataset.viewportPixelRatio = String(metrics.pixelRatio);
|
||||
this.worker.postMessage({
|
||||
type: "resize",
|
||||
width: Math.max(1, this.canvas.clientWidth),
|
||||
height: Math.max(1, this.canvas.clientHeight),
|
||||
pixelRatio: Math.min(window.devicePixelRatio || 1, 2),
|
||||
width: metrics.cssWidth,
|
||||
height: metrics.cssHeight,
|
||||
pixelRatio: metrics.pixelRatio,
|
||||
} satisfies OffscreenViewportRequest);
|
||||
}
|
||||
|
||||
private pointerDown = (event: PointerEvent): void => {
|
||||
try { this.canvas.dataset.lastPointer = JSON.stringify(observePointerEvent(event)); }
|
||||
catch { this.canvas.dataset.lastPointer = "BLOCKED"; }
|
||||
this.pointer = { id: event.pointerId, x: event.clientX, y: event.clientY, moved: false };
|
||||
try {
|
||||
this.canvas.setPointerCapture(event.pointerId);
|
||||
@@ -266,12 +274,13 @@ export class OffscreenViewportRenderer implements ViewportBackend {
|
||||
};
|
||||
|
||||
private pointerUp = (event: PointerEvent): void => {
|
||||
try { this.canvas.dataset.lastPointer = JSON.stringify(observePointerEvent(event)); }
|
||||
catch { this.canvas.dataset.lastPointer = "BLOCKED"; }
|
||||
if (!this.pointer || this.pointer.id !== event.pointerId) return;
|
||||
if (!this.pointer.moved) {
|
||||
const bounds = this.canvas.getBoundingClientRect();
|
||||
const x = ((event.clientX - bounds.left) / Math.max(1, bounds.width)) * 2 - 1;
|
||||
const y = -((event.clientY - bounds.top) / Math.max(1, bounds.height)) * 2 + 1;
|
||||
this.worker.postMessage({ type: "pick", x, y, additive: event.shiftKey || event.ctrlKey || event.metaKey, baseSelectionRevision: this.greasePencilSelectionRevision } satisfies OffscreenViewportRequest);
|
||||
const ndc = viewportNDC(event.clientX, event.clientY, bounds);
|
||||
this.worker.postMessage({ type: "pick", x: ndc.x, y: ndc.y, additive: event.shiftKey || event.ctrlKey || event.metaKey, baseSelectionRevision: this.greasePencilSelectionRevision } satisfies OffscreenViewportRequest);
|
||||
}
|
||||
this.pointer = null;
|
||||
};
|
||||
|
||||
@@ -69,6 +69,8 @@ import {
|
||||
import { VIEWPORT_DEFAULT_ORBIT, VIEWPORT_ORBIT_MAX_DISTANCE, VIEWPORT_ORBIT_MIN_DISTANCE, VIEWPORT_ORBIT_ROTATE_SENSITIVITY, VIEWPORT_ORBIT_ZOOM_SENSITIVITY, orbitPosition, orbitStateFromPosition } from "../../../protocol/viewport-camera";
|
||||
import { validatePaintDepthVisibilityRequest, type PaintDepthVisibilityRequestIR, type PaintDepthVisibilityResultIR } from "../../../protocol/paint-depth-visibility";
|
||||
import { samplePaintDepthVisibilityGPU } from "./paint-depth-visibility";
|
||||
import { resolveViewportPixelMetrics, viewportNDC } from "../../../protocol/viewport-dpr";
|
||||
import { observePointerEvent } from "../../../protocol/pointer-contract";
|
||||
|
||||
export function collectMeshInstanceGroups(snapshot: SceneSnapshotIR, minimumSize = 2): Map<string, string[]> {
|
||||
const groups = new Map<string, string[]>();
|
||||
@@ -137,7 +139,7 @@ export class ViewportRenderer {
|
||||
this.raycaster.params.Points.threshold = 0.14;
|
||||
this.renderer = new WebGLRenderer({ canvas, antialias: true, alpha: false, preserveDrawingBuffer: true });
|
||||
configurePBRRenderer(this.renderer);
|
||||
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
|
||||
this.renderer.setPixelRatio(resolveViewportPixelMetrics(1, 1, window.devicePixelRatio).pixelRatio);
|
||||
this.renderer.setClearColor(new Color("#25272b"));
|
||||
this.canvas.dataset.rendererBackend = "webgl-pbr";
|
||||
this.canvas.dataset.pbrProfile = PBR_PROFILE;
|
||||
@@ -170,6 +172,8 @@ export class ViewportRenderer {
|
||||
this.resizeObserver = new ResizeObserver(() => this.resize());
|
||||
this.resizeObserver.observe(canvas);
|
||||
this.canvas.addEventListener("click", this.handleClick);
|
||||
this.canvas.addEventListener("pointerdown", this.handlePointerObservation);
|
||||
this.canvas.addEventListener("pointercancel", this.handlePointerObservation);
|
||||
this.canvas.addEventListener("webglcontextlost", this.handleContextLost);
|
||||
this.canvas.addEventListener("webglcontextrestored", this.handleContextRestored);
|
||||
this.resize();
|
||||
@@ -793,10 +797,14 @@ export class ViewportRenderer {
|
||||
private resize(): void {
|
||||
const width = Math.max(1, this.canvas.clientWidth);
|
||||
const height = Math.max(1, this.canvas.clientHeight);
|
||||
const metrics = resolveViewportPixelMetrics(width, height, window.devicePixelRatio);
|
||||
this.camera.aspect = width / height;
|
||||
this.camera.updateProjectionMatrix();
|
||||
this.controls.rotateSpeed = VIEWPORT_ORBIT_ROTATE_SENSITIVITY * height / (2 * Math.PI);
|
||||
this.renderer.setSize(width, height, false);
|
||||
this.canvas.dataset.viewportCssSize = `${metrics.cssWidth}x${metrics.cssHeight}`;
|
||||
this.canvas.dataset.viewportBackingSize = `${metrics.backingWidth}x${metrics.backingHeight}`;
|
||||
this.canvas.dataset.viewportPixelRatio = String(metrics.pixelRatio);
|
||||
this.publishCameraState();
|
||||
}
|
||||
|
||||
@@ -810,12 +818,14 @@ export class ViewportRenderer {
|
||||
}
|
||||
|
||||
private handleClick = (event: MouseEvent): void => {
|
||||
if ("pointerType" in event) {
|
||||
try { this.canvas.dataset.lastPointer = JSON.stringify(observePointerEvent(event as MouseEvent & { pointerType?: string; pointerId?: number; pressure?: number; tiltX?: number; tiltY?: number; buttons?: number; type?: string })); }
|
||||
catch { this.canvas.dataset.lastPointer = "BLOCKED"; }
|
||||
}
|
||||
const bounds = this.canvas.getBoundingClientRect();
|
||||
if (bounds.width <= 0 || bounds.height <= 0) return;
|
||||
this.pointer.set(
|
||||
((event.clientX - bounds.left) / bounds.width) * 2 - 1,
|
||||
-((event.clientY - bounds.top) / bounds.height) * 2 + 1,
|
||||
);
|
||||
const ndc = viewportNDC(event.clientX, event.clientY, bounds);
|
||||
this.pointer.set(ndc.x, ndc.y);
|
||||
this.raycaster.setFromCamera(this.pointer, this.camera);
|
||||
const hits = this.raycaster.intersectObjects(this.importedRoot.children, true);
|
||||
const greasePencilHit = this.editMode
|
||||
@@ -886,6 +896,11 @@ export class ViewportRenderer {
|
||||
if (typeof objectId === "string") this.onSelect?.(objectId, additive);
|
||||
};
|
||||
|
||||
private handlePointerObservation = (event: PointerEvent): void => {
|
||||
try { this.canvas.dataset.lastPointer = JSON.stringify(observePointerEvent(event)); }
|
||||
catch { this.canvas.dataset.lastPointer = "BLOCKED"; }
|
||||
};
|
||||
|
||||
private renderLoop = (): void => {
|
||||
if (this.disposed) return;
|
||||
if (!this.contextLost) {
|
||||
@@ -912,7 +927,7 @@ export class ViewportRenderer {
|
||||
this.contextLost = false;
|
||||
this.canvas.dataset.deviceStatus = "restoring";
|
||||
configurePBRRenderer(this.renderer);
|
||||
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
|
||||
this.renderer.setPixelRatio(resolveViewportPixelMetrics(1, 1, window.devicePixelRatio).pixelRatio);
|
||||
this.renderer.setClearColor(new Color("#25272b"));
|
||||
this.resize();
|
||||
this.volumeRenderCache.clear();
|
||||
@@ -949,6 +964,8 @@ export class ViewportRenderer {
|
||||
window.cancelAnimationFrame(this.animationFrame);
|
||||
this.resizeObserver.disconnect();
|
||||
this.canvas.removeEventListener("click", this.handleClick);
|
||||
this.canvas.removeEventListener("pointerdown", this.handlePointerObservation);
|
||||
this.canvas.removeEventListener("pointercancel", this.handlePointerObservation);
|
||||
this.canvas.removeEventListener("webglcontextlost", this.handleContextLost);
|
||||
this.canvas.removeEventListener("webglcontextrestored", this.handleContextRestored);
|
||||
this.controls.dispose();
|
||||
|
||||
Reference in New Issue
Block a user