Initial commit

This commit is contained in:
Yeicor
2024-01-28 21:13:03 +01:00
commit ff629bc006
8 changed files with 1879 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
# Include your project-specific ignores in this file
# Read about how to use .gitignore: https://help.github.com/articles/ignoring-files
# Useful .gitignore templates: https://github.com/github/gitignore
/node_modules/
/dist/
/.cache/
/.parcel-cache/
/.idea/

20
package.json Normal file
View File

@@ -0,0 +1,20 @@
{
"name": "yet-another-cad-viewer",
"version": "0.0.1",
"description": "",
"license": "MIT",
"author": "Yeicor",
"scripts": {
"start": "parcel src/index.html",
"build": "parcel build src/index.html"
},
"dependencies": {
"stats.js": "^0.17.0",
"three": "^0.160.1",
"three-orientation-gizmo": "https://github.com/jrj2211/three-orientation-gizmo"
},
"devDependencies": {
"@types/three": "^0.160.0",
"parcel": "^2.11.0"
}
}

105
src/app.ts Normal file
View File

@@ -0,0 +1,105 @@
import * as THREE from "three";
import {Box3, Matrix4, Vector3} from "three";
import {OrbitControls} from "three/examples/jsm/controls/OrbitControls";
import {GLTFLoader} from "three/examples/jsm/loaders/GLTFLoader";
import {OrientationGizmo} from "./orientation";
import * as Stats from "stats.js";
export class App {
renderer = new THREE.WebGLRenderer({antialias: true});
//camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 1000);
camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0.01, 1000);
private controls = new OrbitControls(this.camera, this.renderer.domElement);
// CAD has Z up, so rotate the scene to match
scene = new THREE.Scene();
private helperGroup = new THREE.Group();
private modelGroup = new THREE.Group();
loader = new GLTFLoader();
private gizmo = new OrientationGizmo(this.camera, this.controls);
private stats = new Stats();
install() {
// Prepare camera and scene
//this.setupSceneHelpers(new THREE.Box3().setFromCenterAndSize(new THREE.Vector3(), new THREE.Vector3(10, 10, 10)));
// this.helperGroup.setRotationFromMatrix(this.threeToCad)
this.scene.add(this.helperGroup);
// this.modelGroup.setRotationFromMatrix(this.threeToCad)
this.scene.add(this.modelGroup);
// Set up renderer
document.body.appendChild(this.renderer.domElement);
this.renderer.setAnimationLoop(this._loop.bind(this));
// On window resize, also resize the renderer
let onResize = () => {
this.renderer.setSize(window.innerWidth, window.innerHeight);
if (this.camera instanceof THREE.PerspectiveCamera) {
this.camera.aspect = window.innerWidth / window.innerHeight;
} else {
const aspect = window.innerWidth / window.innerHeight;
const frustumSize = 2
this.camera.left = - frustumSize * aspect / 2;
this.camera.right = frustumSize * aspect / 2;
this.camera.top = frustumSize / 2;
this.camera.bottom = - frustumSize / 2;
}
this.camera.updateProjectionMatrix();
};
window.addEventListener('resize', onResize);
onResize()
// Misc installation
this.gizmo.install();
document.body.appendChild(this.stats.dom)
this.stats.dom.style.left = '';
this.stats.dom.style.right = '0px';
this.stats.dom.style.top = '120px';
this.stats.showPanel(1); // 0: fps, 1: ms, 2: mb, 3+: custom
}
private setupSceneHelpers(bb: Box3) { // The bounding box in three.js coordinates
this.helperGroup.clear();
let center = bb.getCenter(new THREE.Vector3());
this.helperGroup.applyMatrix4(new Matrix4().makeTranslation(center))
let size = bb.getSize(new THREE.Vector3());
console.log(center, size)
this.controls.target.set(center.x, center.y, center.z);
this.camera.position.set(center.x, center.y, center.z);
this.camera.position.x += size.x * 0.75;
this.camera.position.y += size.y * 0.5;
this.camera.position.z += size.z;
this.controls.update()
this.helperGroup.add(new THREE.HemisphereLight(0xffffff, 0x444444))
let gridXZ = new THREE.GridHelper(1, 10);
gridXZ.applyMatrix4(new Matrix4().makeTranslation(new Vector3(0, -size.y / 2, 0)))
gridXZ.scale.set(size.x, 1, size.z)
this.helperGroup.add(gridXZ)
let gridXY = new THREE.GridHelper(1, 10);
gridXY.applyMatrix4(new Matrix4().makeRotationX(Math.PI / 2))
gridXY.applyMatrix4(new Matrix4().makeTranslation(new Vector3(0, 0, -size.z / 2)))
gridXY.scale.set(size.x, 1, size.y)
this.helperGroup.add(gridXY)
let gridYZ = new THREE.GridHelper(1, 10);
gridYZ.applyMatrix4(new Matrix4().makeRotationZ(Math.PI / 2))
gridYZ.applyMatrix4(new Matrix4().makeTranslation(new Vector3(-size.x / 2, 0, 0)))
// noinspection JSSuspiciousNameCombination
gridYZ.scale.set(size.y, 1, size.z)
this.helperGroup.add(gridYZ)
let axes = new THREE.AxesHelper(size.length() / 4);
axes.applyMatrix4(new THREE.Matrix4().makeRotationX(-Math.PI / 2)) // Y-up to Z-up (reference-only)
this.helperGroup.add(axes)
}
addModel(url: string) {
this.loader.loadAsync(url, console.log).then((model) => {
this.modelGroup.add(model.scene)
this.setupSceneHelpers(new THREE.Box3().setFromObject(model.scene));
});
}
_loop(time) {
this.stats.begin();
this.controls.update();
this.gizmo.update();
this.renderer.render(this.scene, this.camera);
this.stats.end();
this.stats.update();
}
}

5
src/index.css Normal file
View File

@@ -0,0 +1,5 @@
body {
background: black;
margin: 0;
overflow: hidden;
}

12
src/index.html Normal file
View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Yet Another CAD Viewer</title>
<meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no' />
<link rel="stylesheet" type="text/css" href="./index.css">
<script type="module" src="./index.ts"></script>
</head>
<body>
</body>
</html>

5
src/index.ts Normal file
View File

@@ -0,0 +1,5 @@
import {App} from "./app";
const app = new App()
app.install();
app.addModel(`https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Duck/glTF-Binary/Duck.glb`)

49
src/orientation.ts Normal file
View File

@@ -0,0 +1,49 @@
import {Camera} from "three";
import * as OrientationGizmoRaw from "three-orientation-gizmo/src/OrientationGizmo";
import THREE = require("three");
import {OrbitControls} from "three/examples/jsm/controls/OrbitControls";
window.THREE = THREE // HACK: Required for the gizmo to work
export class OrientationGizmo {
element: OrientationGizmoRaw
constructor(camera: Camera, controls: OrbitControls) {
this.element = new OrientationGizmoRaw(camera, {
size: 120,
bubbleSizePrimary: 12,
bubbleSizeSeconday: 10,
fontSize: "14px"
});
// Place in the top right corner
this.element.style.position = "absolute";
this.element.style.top = "0px";
this.element.style.right = "0px";
this.element.style.zIndex = "1000";
// HACK: Swap axes to match A-Frame
for (let swap of [["y", "-z"], ["z", "-y"], ["z", "-z"]]) {
let indexA = this.element.bubbles.findIndex((bubble) => bubble.axis == swap[0])
let indexB = this.element.bubbles.findIndex((bubble) => bubble.axis == swap[1])
let dirA = this.element.bubbles[indexA].direction.clone();
let dirB = this.element.bubbles[indexB].direction.clone();
this.element.bubbles[indexA].direction.copy(dirB);
this.element.bubbles[indexB].direction.copy(dirA);
}
// Append and listen for events
this.element.onAxisSelected = (axis) => {
let magnitude = camera.position.clone().sub(controls.target).length()
let direction = new THREE.Vector3(axis.direction.x, axis.direction.y, axis.direction.z);
direction.normalize();
console.log(controls.target, direction, magnitude)
camera.position.copy(controls.target.clone().add(direction.multiplyScalar(magnitude)));
}
}
install() {
document.body.appendChild(this.element);
}
update() {
this.element.update();
}
}

1675
yarn.lock Normal file

File diff suppressed because it is too large Load Diff