Skip to content

Commit d69c62b

Browse files
Update 3D rendering logic (#353)
* Update makeShapeFromMesh * Move normal computation to three * Fix loading animation * Disable edges rendering for STL file * Fix worker busy signal * Update Playwright Snapshots * Remove comments --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 54f0ca4 commit d69c62b

File tree

10 files changed

+190
-103
lines changed

10 files changed

+190
-103
lines changed

packages/base/src/3dview/helpers.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import { LineSegments2 } from 'three/examples/jsm/lines/LineSegments2.js';
1111

1212
import { getCSSVariableColor } from '../tools';
1313

14-
export const DEFAULT_LINEWIDTH = 4;
15-
export const SELECTED_LINEWIDTH = 12;
14+
export const DEFAULT_LINEWIDTH = 2;
15+
export const SELECTED_LINEWIDTH = 6;
1616

1717
// Apply the BVH extension
1818

@@ -122,29 +122,30 @@ export function buildShape(options: {
122122
const { faceList, edgeList, jcObject } = data;
123123

124124
const vertices: Array<number> = [];
125-
const normals: Array<number> = [];
126125
const triangles: Array<number> = [];
127126

128127
let vInd = 0;
129128
if (faceList.length === 0 && edgeList.length === 0) {
130129
return null;
131130
}
132-
faceList.forEach(face => {
131+
for (const face of faceList) {
133132
// Copy Vertices into three.js Vector3 List
134-
vertices.push(...face.vertexCoord);
135-
normals.push(...face.normalCoord);
136-
133+
const vertexCoorLength = face.vertexCoord.length;
134+
for (let ii = 0; ii < vertexCoorLength; ii++) {
135+
vertices.push(face.vertexCoord[ii]);
136+
}
137137
// Sort Triangles into a three.js Face List
138-
for (let i = 0; i < face.triIndexes.length; i += 3) {
138+
const triIndexesLength = face.triIndexes.length;
139+
for (let i = 0; i < triIndexesLength; i += 3) {
139140
triangles.push(
140141
face.triIndexes[i + 0] + vInd,
141142
face.triIndexes[i + 1] + vInd,
142143
face.triIndexes[i + 2] + vInd
143144
);
144145
}
145146

146-
vInd += face.vertexCoord.length / 3;
147-
});
147+
vInd += vertexCoorLength / 3;
148+
}
148149

149150
let color = DEFAULT_MESH_COLOR;
150151
let visible = jcObject.visible;
@@ -167,20 +168,20 @@ export function buildShape(options: {
167168
// it's too bad Three.js does not easily allow setting uniforms independently per-mesh
168169
const material = new THREE.MeshPhongMaterial({
169170
color,
170-
side: THREE.DoubleSide,
171171
wireframe: false,
172172
flatShading: false,
173173
clippingPlanes,
174174
shininess: 0
175175
});
176176

177177
const geometry = new THREE.BufferGeometry();
178+
178179
geometry.setIndex(triangles);
179180
geometry.setAttribute(
180181
'position',
181182
new THREE.Float32BufferAttribute(vertices, 3)
182183
);
183-
geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));
184+
geometry.computeVertexNormals();
184185
geometry.computeBoundingBox();
185186
if (vertices.length > 0) {
186187
geometry.computeBoundsTree();
@@ -231,7 +232,7 @@ export function buildShape(options: {
231232

232233
let edgeIdx = 0;
233234
const edgesMeshes: LineSegments2[] = [];
234-
edgeList.forEach(edge => {
235+
for (const edge of edgeList) {
235236
const edgeMaterial = new LineMaterial({
236237
linewidth: DEFAULT_LINEWIDTH,
237238
// @ts-ignore Missing typing in ThreeJS
@@ -242,7 +243,6 @@ export function buildShape(options: {
242243
polygonOffsetFactor: -5,
243244
polygonOffsetUnits: -5
244245
});
245-
246246
const edgeGeometry = new LineGeometry();
247247
edgeGeometry.setPositions(edge.vertexCoord);
248248
const edgesMesh = new LineSegments2(edgeGeometry, edgeMaterial);
@@ -254,10 +254,10 @@ export function buildShape(options: {
254254
};
255255

256256
edgesMeshes.push(edgesMesh);
257-
257+
meshGroup.add(edgesMesh);
258258
edgeIdx++;
259-
});
260-
meshGroup.add(...edgesMeshes);
259+
}
260+
261261
meshGroup.add(mainMesh);
262262

263263
return { meshGroup, mainMesh, edgesMeshes };

packages/base/src/3dview/mainview.tsx

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ import {
5050
} from './helpers';
5151
import { MainViewModel } from './mainviewmodel';
5252
import { Spinner } from './spinner';
53-
5453
interface IProps {
5554
viewModel: MainViewModel;
5655
}
@@ -92,6 +91,7 @@ export class MainView extends React.Component<IProps, IStates> {
9291
this
9392
);
9493
this._mainViewModel.renderSignal.connect(this._requestRender, this);
94+
this._mainViewModel.workerBusy.connect(this._workerBusyHandler, this);
9595

9696
// @ts-ignore Missing ThreeJS typing
9797
this._raycaster.params.Line2 = {};
@@ -145,6 +145,7 @@ export class MainView extends React.Component<IProps, IStates> {
145145
);
146146

147147
this._mainViewModel.renderSignal.disconnect(this._requestRender, this);
148+
this._mainViewModel.workerBusy.disconnect(this._workerBusyHandler, this);
148149
this._mainViewModel.dispose();
149150
}
150151

@@ -570,6 +571,9 @@ export class MainView extends React.Component<IProps, IStates> {
570571
if (this._explodedViewLinesHelperGroup !== null) {
571572
this._scene.remove(this._explodedViewLinesHelperGroup);
572573
}
574+
if (this._clippingPlaneMesh !== null) {
575+
this._scene.remove(this._clippingPlaneMesh);
576+
}
573577

574578
const guidata = this._model.sharedModel.getOption('guidata');
575579
const selectedNames = this._selectedMeshes.map(sel => sel.name);
@@ -641,7 +645,10 @@ export class MainView extends React.Component<IProps, IStates> {
641645

642646
this._scene.add(this._clippingPlaneMesh);
643647
this._scene.add(this._meshGroup);
644-
648+
if (this._loadingTimeout) {
649+
clearTimeout(this._loadingTimeout);
650+
this._loadingTimeout = null;
651+
}
645652
this.setState(old => ({ ...old, loading: false }));
646653
};
647654

@@ -719,6 +726,19 @@ export class MainView extends React.Component<IProps, IStates> {
719726
this._updateRefLength(true);
720727
}
721728

729+
private _workerBusyHandler(_: MainViewModel, busy: boolean) {
730+
if (this._loadingTimeout) {
731+
clearTimeout(this._loadingTimeout);
732+
}
733+
if (busy) {
734+
this._loadingTimeout = setTimeout(() => {
735+
// Do not show loading animation for the first 250
736+
this.setState(old => ({ ...old, loading: true }));
737+
}, 250);
738+
} else {
739+
this.setState(old => ({ ...old, loading: false }));
740+
}
741+
}
722742
private async _requestRender(
723743
sender: MainViewModel,
724744
renderData: {
@@ -728,9 +748,8 @@ export class MainView extends React.Component<IProps, IStates> {
728748
}
729749
) {
730750
const { shapes, postShapes, postResult } = renderData;
731-
732751
if (shapes !== null && shapes !== undefined) {
733-
this._shapeToMesh(renderData.shapes);
752+
this._shapeToMesh(shapes);
734753
const options = {
735754
binary: true,
736755
onlyVisible: false
@@ -811,7 +830,9 @@ export class MainView extends React.Component<IProps, IStates> {
811830
private _updateSelected(selection: { [key: string]: ISelection }) {
812831
// Reset original color for old selection
813832
for (const selectedMesh of this._selectedMeshes) {
814-
let originalColor = DEFAULT_MESH_COLOR;
833+
let originalColor = selectedMesh.name.startsWith('edge-')
834+
? DEFAULT_EDGE_COLOR
835+
: DEFAULT_MESH_COLOR;
815836
const guidata = this._model.sharedModel.getOption('guidata');
816837
if (
817838
guidata &&
@@ -1309,4 +1330,5 @@ export class MainView extends React.Component<IProps, IStates> {
13091330
private _collaboratorPointers: IDict<IPointer>;
13101331
private _pointerGeometry: THREE.SphereGeometry;
13111332
private _contextMenu: ContextMenu;
1333+
private _loadingTimeout: ReturnType<typeof setTimeout> | null;
13121334
}

packages/base/src/3dview/mainviewmodel.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ export class MainViewModel implements IDisposable {
4545
> {
4646
return this._renderSignal;
4747
}
48+
49+
get workerBusy(): ISignal<this, boolean> {
50+
return this._workerBusy;
51+
}
4852
get jcadModel() {
4953
return this._jcadModel;
5054
}
@@ -87,7 +91,6 @@ export class MainViewModel implements IDisposable {
8791
switch (msg.action) {
8892
case MainAction.DISPLAY_SHAPE: {
8993
const { result, postResult } = msg.payload;
90-
9194
const rawPostResult: IDict<IPostOperatorInput> = {};
9295
const threejsPostResult: IDict<IPostOperatorInput> = {};
9396

@@ -119,6 +122,7 @@ export class MainViewModel implements IDisposable {
119122
});
120123
this.sendRawGeometryToWorker(rawPostResult);
121124
}
125+
this._workerBusy.emit(false);
122126

123127
break;
124128
}
@@ -127,7 +131,7 @@ export class MainViewModel implements IDisposable {
127131
return;
128132
}
129133
const content = this._jcadModel.getContent();
130-
134+
this._workerBusy.emit(true);
131135
this._postMessage({
132136
action: WorkerAction.LOAD_FILE,
133137
payload: {
@@ -204,10 +208,12 @@ export class MainViewModel implements IDisposable {
204208
): Promise<void> {
205209
if (change.objectChange) {
206210
await this._worker.ready;
211+
const content = this._jcadModel.getContent();
212+
this._workerBusy.emit(true);
207213
this._postMessage({
208214
action: WorkerAction.LOAD_FILE,
209215
payload: {
210-
content: this._jcadModel.getContent()
216+
content
211217
}
212218
});
213219
}
@@ -228,6 +234,7 @@ export class MainViewModel implements IDisposable {
228234
postResult?: IDict<IPostOperatorInput>;
229235
}
230236
>(this);
237+
private _workerBusy = new Signal<this, boolean>(this);
231238
private _isDisposed = false;
232239
}
233240

0 commit comments

Comments
 (0)