Skip to content

Commit a92e5f4

Browse files
initial editor
1 parent 701ee0d commit a92e5f4

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
@@ -435,6 +435,7 @@
435435
'cameraUp': cameraUpArray,
436436
'initialCameraPosition': cameraPositionArray,
437437
'initialCameraLookAt': cameraLookAtArray,
438+
'frameloop': 'demand',
438439
};
439440
const sceneOptions = {
440441
'halfPrecisionCovariancesOnGPU': true,
@@ -451,12 +452,15 @@
451452
document.getElementById("demo-content").style.display = 'none';
452453
document.body.style.backgroundColor = "#000000";
453454
history.pushState("ViewSplat", null);
454-
let viewer;
455+
456+
const viewer = new GaussianSplats3D.Viewer(viewerOptions);
457+
458+
let editor;
455459
if (editMode) {
456-
viewer = new GaussianSplats3D.Editor(viewerOptions);
457-
} else {
458-
viewer = new GaussianSplats3D.Viewer(viewerOptions);
460+
const editorOptions = {};
461+
editor = new GaussianSplats3D.Editor(viewer, editorOptions);
459462
}
463+
460464
viewer.loadSplatBuffer(splatBuffer, sceneOptions)
461465
.then(() => {
462466
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;
@@ -77,11 +78,13 @@ export class Viewer {
7778
this.mouseDownPosition = new THREE.Vector2();
7879
this.mouseDownTime = null;
7980

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

83-
this.frameloop = 'demand'; // 'demand' | 'always'
8486
this.viewerNeedsUpdate = true;
87+
this.frameloop = params.frameloop; // 'demand' | 'always'
8588

8689
this.initialized = false;
8790
this.init();
@@ -93,8 +96,8 @@ export class Viewer {
9396

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

@@ -135,10 +138,11 @@ export class Viewer {
135138
this.controls.minPolarAngle = 0.1;
136139
this.controls.enableDamping = true;
137140
this.controls.dampingFactor = 0.25;
141+
this.controls.zoomToCursor = true;
138142
this.controls.target.copy(this.initialCameraLookAt);
139143
this.rootElement.addEventListener('pointermove', this.onMouseMove.bind(this), false);
140144
this.rootElement.addEventListener('pointerdown', this.onMouseDown.bind(this), false);
141-
this.rootElement.addEventListener('pointerup', this.onMouseUp.bind(this), false);
145+
this.rootElement.addEventListener('pointerup', this.pointerUpHandler, false);
142146
this.controls.addEventListener('change', this.onControlsChange.bind(this), false);
143147
window.addEventListener('keydown', this.onKeyDown.bind(this), false);
144148
}
@@ -178,7 +182,6 @@ export class Viewer {
178182
break;
179183
case 'KeyC':
180184
this.showMeshCursor = !this.showMeshCursor;
181-
this.invalidate();
182185
break;
183186
case 'KeyP':
184187
this.showControlPlane = !this.showControlPlane;
@@ -192,6 +195,8 @@ export class Viewer {
192195
}
193196
break;
194197
}
198+
199+
this.invalidate();
195200
};
196201

197202
}();
@@ -237,7 +242,6 @@ export class Viewer {
237242
}
238243
}
239244
};
240-
241245
}();
242246

243247
onControlsChange() {
@@ -260,6 +264,7 @@ export class Viewer {
260264
setupInfoPanel() {
261265
this.infoPanel = document.createElement('div');
262266
this.infoPanel.style.position = 'absolute';
267+
this.infoPanel.style.right = '0px';
263268
this.infoPanel.style.padding = '10px';
264269
this.infoPanel.style.backgroundColor = '#cccccc';
265270
this.infoPanel.style.border = '#aaaaaa 1px solid';

0 commit comments

Comments
 (0)