mirror of
https://github.com/yeicor-3d/yet-another-cad-viewer.git
synced 2025-12-19 22:24:17 +01:00
Initial commit
This commit is contained in:
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal 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
20
package.json
Normal 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
105
src/app.ts
Normal 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
5
src/index.css
Normal file
@@ -0,0 +1,5 @@
|
||||
body {
|
||||
background: black;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
12
src/index.html
Normal file
12
src/index.html
Normal 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
5
src/index.ts
Normal 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
49
src/orientation.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user