Skip to content

Commit c9b7b2d

Browse files
initial editor
1 parent d723d79 commit c9b7b2d

File tree

3 files changed

+132
-31
lines changed

3 files changed

+132
-31
lines changed

demo/index.html

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,7 @@
446446
'cameraUp': cameraUpArray,
447447
'initialCameraPosition': cameraPositionArray,
448448
'initialCameraLookAt': cameraLookAtArray,
449+
'frameloop': 'demand',
449450
};
450451
const sceneOptions = {
451452
'halfPrecisionCovariancesOnGPU': true,
@@ -462,12 +463,15 @@
462463
document.getElementById("demo-content").style.display = 'none';
463464
document.body.style.backgroundColor = "#000000";
464465
history.pushState("ViewSplat", null);
465-
let viewer;
466+
467+
const viewer = new GaussianSplats3D.Viewer(viewerOptions);
468+
469+
let editor;
466470
if (editMode) {
467-
viewer = new GaussianSplats3D.Editor(viewerOptions);
468-
} else {
469-
viewer = new GaussianSplats3D.Viewer(viewerOptions);
471+
const editorOptions = {};
472+
editor = new GaussianSplats3D.Editor(viewer, editorOptions);
470473
}
474+
471475
viewer.loadSplatBuffer(splatBuffer, sceneOptions)
472476
.then(() => {
473477
viewer.start();

src/Editor.js

Lines changed: 113 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,127 @@
1-
import { Viewer } from './Viewer.js';
1+
import * as THREE from 'three';
2+
import { getCurrentTime } from './Util.js';
23

3-
export class Editor extends Viewer {
4-
constructor(params = {}) {
5-
super(params);
4+
export class Editor {
5+
constructor(viewer, options = {}) {
6+
this.viewer = viewer;
67

78
this.editPanel = null;
8-
this.editPanelCells = {};
99

10-
this.initializedEditor = false;
11-
this.initEditor();
10+
this.pointerUpHandlerPlaneFinder = this.onPointerUpPlaneFinder.bind(this);
11+
12+
this.initialized = false;
13+
this.init();
1214
}
1315

14-
initEditor() {
15-
if (this.initializedEditor) return;
16+
init() {
17+
if (this.initialized) return;
18+
19+
this.setupEditPanel();
20+
21+
this.initialized = true;
22+
}
23+
24+
setupEditPanel() {
25+
this.editPanel = document.createElement('div');
26+
this.editPanel.style.position = 'absolute';
27+
this.editPanel.style.padding = '10px';
28+
this.editPanel.style.backgroundColor = '#cccccc';
29+
this.editPanel.style.border = '#aaaaaa 1px solid';
30+
this.editPanel.style.zIndex = 90;
31+
this.editPanel.style.width = '375px';
32+
this.editPanel.style.fontFamily = 'arial';
33+
this.editPanel.style.fontSize = '10pt';
34+
this.editPanel.style.textAlign = 'left';
35+
36+
37+
const editTable = document.createElement('div');
38+
editTable.style.width = '100%';
1639

17-
if (this.useBuiltInControls) {
18-
this.rootElement.addEventListener('pointerup', this.onMouseUpEdit.bind(this), false);
40+
41+
// Plane Finder
42+
const planeFinder = document.createElement('div');
43+
planeFinder.style.width = '100%';
44+
planeFinder.style.display = 'flex';
45+
planeFinder.style.flexDirection = 'row';
46+
planeFinder.style.justifyContent = 'space-between';
47+
48+
const planeFinderLabel = document.createElement('p');
49+
planeFinderLabel.id = 'planeFinderLabel';
50+
planeFinderLabel.innerHTML =
51+
`Up: ${this.viewer.camera.up.x.toFixed(3)}, ${this.viewer.camera.up.y.toFixed(3)}, ${this.viewer.camera.up.z.toFixed(3)}`;
52+
53+
const planeFinderButton = document.createElement('button');
54+
planeFinderButton.innerHTML = 'Find ground plane';
55+
planeFinderButton.addEventListener('click', () => {
56+
this.setupPlaneFinder();
57+
});
58+
59+
planeFinder.appendChild(planeFinderLabel);
60+
planeFinder.appendChild(planeFinderButton);
61+
62+
editTable.appendChild(planeFinder);
63+
64+
this.editPanel.appendChild(editTable);
65+
this.editPanel.style.display = 'block';
66+
this.viewer.renderer.domElement.parentElement.prepend(this.editPanel);
67+
}
68+
69+
setupPlaneFinder() {
70+
if (this.viewer.useBuiltInControls) {
71+
this.viewer.rootElement.removeEventListener('pointerup', this.viewer.pointerUpHandler);
72+
this.viewer.rootElement.addEventListener('pointerup', this.pointerUpHandlerPlaneFinder);
1973
}
74+
}
2075

21-
this.initializedEditor = true;
76+
teardownPlaneFinder() {
77+
if (this.viewer.useBuiltInControls) {
78+
this.viewer.rootElement.removeEventListener('pointerup', this.pointerUpHandlerPlaneFinder);
79+
this.viewer.rootElement.addEventListener('pointerup', this.viewer.pointerUpHandler);
80+
}
2281
}
2382

24-
onMouseUpEdit = function() {
83+
onPointerUpPlaneFinder = function() {
84+
const renderDimensions = new THREE.Vector2();
85+
const clickOffset = new THREE.Vector2();
86+
// const toNewFocalPoint = new THREE.Vector3();
87+
const outHits = [];
88+
let points = [];
89+
2590
return function(mouse) {
26-
console.log('Editor.onMouseUpEdit', mouse);
27-
};
28-
};
91+
clickOffset.copy(this.viewer.mousePosition).sub(this.viewer.mouseDownPosition);
92+
const mouseUpTime = getCurrentTime();
93+
const wasClick = mouseUpTime - this.viewer.mouseDownTime < 0.5 && clickOffset.length() < 2;
94+
95+
if (!this.transitioningCameraTarget && wasClick) {
96+
this.viewer.getRenderDimensions(renderDimensions);
97+
outHits.length = 0;
98+
this.viewer.raycaster.setFromCameraAndScreenPosition(this.viewer.camera, this.viewer.mousePosition, renderDimensions);
99+
this.viewer.mousePosition.set(mouse.offsetX, mouse.offsetY);
100+
this.viewer.raycaster.intersectSplatMesh(this.viewer.splatMesh, outHits);
101+
102+
if (outHits.length > 0) {
103+
const intersectionPoint = outHits[0].origin;
29104

30-
// onMouseUp = function() {
31-
// return function(mouse) {
32-
// console.log('Editor.onMouseUp', mouse);
33-
// };
34-
// }();
105+
points.push(intersectionPoint);
106+
107+
if (points.length === 3) {
108+
const plane = new THREE.Plane();
109+
110+
plane.setFromCoplanarPoints(points[0], points[1], points[2]);
111+
112+
this.viewer.camera.up = plane.normal;
113+
this.viewer.invalidate();
114+
115+
// Update label
116+
const planeFinderLabel = document.getElementById('planeFinderLabel');
117+
planeFinderLabel.innerHTML =
118+
`${this.viewer.camera.up.x.toFixed(3)},${this.viewer.camera.up.y.toFixed(3)},${this.viewer.camera.up.z.toFixed(3)}`;
119+
120+
points = [];
121+
this.teardownPlaneFinder();
122+
}
123+
}
124+
}
125+
};
126+
}();
35127
}

src/Viewer.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export class Viewer {
2222
if (!params.initialCameraLookAt) params.initialCameraLookAt = [0, 0, 0];
2323
if (params.selfDrivenMode === undefined) params.selfDrivenMode = true;
2424
if (params.useBuiltInControls === undefined) params.useBuiltInControls = true;
25+
if (!params.frameloop) params.frameloop = 'always';
2526

2627
this.rootElement = params.rootElement;
2728
this.usingExternalCamera = params.camera ? true : false;
@@ -76,11 +77,13 @@ export class Viewer {
7677
this.mouseDownPosition = new THREE.Vector2();
7778
this.mouseDownTime = null;
7879

80+
this.pointerUpHandler = this.onMouseUp.bind(this);
81+
7982
this.loadingSpinner = new LoadingSpinner();
8083
this.loadingSpinner.hide();
8184

82-
this.frameloop = 'demand'; // 'demand' | 'always'
8385
this.viewerNeedsUpdate = true;
86+
this.frameloop = params.frameloop; // 'demand' | 'always'
8487

8588
this.initialized = false;
8689
this.init();
@@ -92,8 +95,8 @@ export class Viewer {
9295

9396
if (!this.rootElement && !this.usingExternalRenderer) {
9497
this.rootElement = document.createElement('div');
95-
this.rootElement.style.width = '100%';
96-
this.rootElement.style.height = '100%';
98+
this.rootElement.style.width = '100vw';
99+
this.rootElement.style.height = '100vh';
97100
document.body.appendChild(this.rootElement);
98101
}
99102

@@ -132,10 +135,11 @@ export class Viewer {
132135
this.controls.minPolarAngle = 0.1;
133136
this.controls.enableDamping = true;
134137
this.controls.dampingFactor = 0.25;
138+
this.controls.zoomToCursor = true;
135139
this.controls.target.copy(this.initialCameraLookAt);
136140
this.rootElement.addEventListener('pointermove', this.onMouseMove.bind(this), false);
137141
this.rootElement.addEventListener('pointerdown', this.onMouseDown.bind(this), false);
138-
this.rootElement.addEventListener('pointerup', this.onMouseUp.bind(this), false);
142+
this.rootElement.addEventListener('pointerup', this.pointerUpHandler, false);
139143
this.controls.addEventListener('change', this.onControlsChange.bind(this), false);
140144
window.addEventListener('keydown', this.onKeyDown.bind(this), false);
141145
}
@@ -177,7 +181,6 @@ export class Viewer {
177181
break;
178182
case 'KeyC':
179183
this.showMeshCursor = !this.showMeshCursor;
180-
this.invalidate();
181184
break;
182185
case 'KeyP':
183186
this.showControlPlane = !this.showControlPlane;
@@ -191,6 +194,8 @@ export class Viewer {
191194
}
192195
break;
193196
}
197+
198+
this.invalidate();
194199
};
195200

196201
}();
@@ -236,7 +241,6 @@ export class Viewer {
236241
}
237242
}
238243
};
239-
240244
}();
241245

242246
onControlsChange() {
@@ -259,6 +263,7 @@ export class Viewer {
259263
setupInfoPanel() {
260264
this.infoPanel = document.createElement('div');
261265
this.infoPanel.style.position = 'absolute';
266+
this.infoPanel.style.right = '0px';
262267
this.infoPanel.style.padding = '10px';
263268
this.infoPanel.style.backgroundColor = '#cccccc';
264269
this.infoPanel.style.border = '#aaaaaa 1px solid';

0 commit comments

Comments
 (0)