mirror of
https://github.com/yeicor-3d/yet-another-cad-viewer.git
synced 2025-12-19 22:24:17 +01:00
add support for axes
This commit is contained in:
62
src/misc/helpers.ts
Normal file
62
src/misc/helpers.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import {Document, TypedArray} from '@gltf-transform/core'
|
||||
import {Matrix4, Vector3} from 'three'
|
||||
|
||||
|
||||
/** Exports the colors used for the axes, primary and secondary. They match the orientation gizmo. */
|
||||
export const AxesColors = {
|
||||
x: [[247, 60, 60], [148, 36, 36]],
|
||||
z: [[108, 203, 38], [65, 122, 23]],
|
||||
y: [[23, 140, 240], [14, 84, 144]]
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Axes helper as a GLTF model, useful for debugging positions and orientations.
|
||||
*/
|
||||
export function newAxes(doc: Document, size: Vector3, transform: Matrix4) {
|
||||
const buffer = doc.createBuffer()
|
||||
const positions = doc.createAccessor('axesPosition')
|
||||
.setArray(new Float32Array([
|
||||
0, 0, 0,
|
||||
size.x, 0, 0,
|
||||
0, 0, 0,
|
||||
0, size.y, 0,
|
||||
0, 0, 0,
|
||||
0, 0, -size.z,
|
||||
]) as TypedArray)
|
||||
.setType('VEC3')
|
||||
.setBuffer(buffer)
|
||||
const indices = doc.createAccessor('axesIndices')
|
||||
.setArray(new Uint32Array([0, 1, 2, 3, 4, 5]) as TypedArray)
|
||||
.setType('SCALAR')
|
||||
.setBuffer(buffer)
|
||||
const colors = doc.createAccessor('axesColor')
|
||||
.setArray(new Float32Array([
|
||||
...(AxesColors.x[0]), ...(AxesColors.x[1]),
|
||||
...(AxesColors.y[0]), ...(AxesColors.y[1]),
|
||||
...(AxesColors.z[0]), ...(AxesColors.z[1]),
|
||||
].map(x => x / 255.0)) as TypedArray)
|
||||
.setType('VEC3')
|
||||
.setBuffer(buffer)
|
||||
const material = doc.createMaterial('axesMaterial')
|
||||
.setAlphaMode('OPAQUE')
|
||||
const geometry = doc.createPrimitive()
|
||||
.setIndices(indices)
|
||||
.setAttribute('POSITION', positions)
|
||||
.setAttribute('COLOR_0', colors)
|
||||
.setMode(WebGL2RenderingContext.LINES)
|
||||
.setMaterial(material)
|
||||
const mesh = doc.createMesh('axes').addPrimitive(geometry)
|
||||
const node = doc.createNode('axes').setMesh(mesh).setMatrix(transform.elements as any)
|
||||
doc.createScene('axesScene').addChild(node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Grid helper as a GLTF model, useful for debugging sizes with an OrthographicCamera.
|
||||
*
|
||||
* The grid is built as a box of triangles (representing lines) looking to the inside of the box.
|
||||
* This ensures that only the back of the grid is always visible, regardless of the camera position.
|
||||
*/
|
||||
export function newGrid(doc: Document, size: Vector3, transform: Matrix4 = new Matrix4(), divisions = 10) {
|
||||
const buffer = doc.createBuffer();
|
||||
// TODO: implement grid
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import {Ref, ShallowRef} from 'vue';
|
||||
import {Document} from '@gltf-transform/core';
|
||||
import {mergeFinalize, mergePartial, removeModel, toBuffer} from "./gltf";
|
||||
import {newAxes} from "./helpers";
|
||||
import { Matrix4, Vector3 } from 'three';
|
||||
|
||||
/** This class helps manage SceneManagerData. All methods are static to support reactivity... */
|
||||
export class SceneMgr {
|
||||
@@ -15,6 +17,16 @@ export class SceneMgr {
|
||||
await this.showCurrentDoc(sceneUrl, document);
|
||||
|
||||
console.log("Model", name, "loaded in", performance.now() - loadStart, "ms");
|
||||
|
||||
if (name !== "__helpers") {
|
||||
// Add a helper axes to the scene
|
||||
let helpersDoc = new Document();
|
||||
// TODO: Get bounding box of the model and use it to set the size of the helpers
|
||||
newAxes(helpersDoc, new Vector3(10, 10, 10), new Matrix4());
|
||||
let helpersUrl = URL.createObjectURL(new Blob([await toBuffer(helpersDoc)]));
|
||||
await SceneMgr.loadModel(sceneUrl, document, "__helpers", helpersUrl);
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import {watch} from "vue";
|
||||
import type ModelViewerWrapper from "../viewer/ModelViewerWrapper.vue";
|
||||
import {mdiCircleOpacity, mdiDelete, mdiRectangle, mdiRectangleOutline, mdiVectorRectangle} from '@mdi/js'
|
||||
import SvgIcon from '@jamescoyle/vue-icon/lib/svg-icon.vue';
|
||||
import type {WebGLProgramParametersWithUniforms, WebGLRenderer} from "three";
|
||||
|
||||
const props = defineProps<{ mesh: Mesh, viewer: InstanceType<typeof ModelViewerWrapper> | null, document: Document }>();
|
||||
const emit = defineEmits<{ remove: [] }>()
|
||||
@@ -23,7 +22,7 @@ const emit = defineEmits<{ remove: [] }>()
|
||||
let modelName = props.mesh.getExtras()[extrasNameKey] // + " blah blah blah blah blag blah blah blah"
|
||||
|
||||
let faceCount = props.mesh.listPrimitives().filter(p => p.getMode() === WebGL2RenderingContext.TRIANGLES).length
|
||||
let edgeCount = props.mesh.listPrimitives().filter(p => p.getMode() === WebGL2RenderingContext.LINE_STRIP).length
|
||||
let edgeCount = props.mesh.listPrimitives().filter(p => p.getMode() in [WebGL2RenderingContext.LINE_STRIP, WebGL2RenderingContext.LINES]).length
|
||||
let vertexCount = props.mesh.listPrimitives().filter(p => p.getMode() === WebGL2RenderingContext.POINTS).length
|
||||
|
||||
const enabledFeatures = defineModel<Array<number>>("enabledFeatures", {default: [0, 1, 2]});
|
||||
@@ -40,7 +39,7 @@ function onEnabledFeaturesChange(newEnabledFeatures: Array<number>) {
|
||||
sceneModel.traverse((child) => {
|
||||
if (child.userData[extrasNameKey] === modelName) {
|
||||
let childIsFace = child.type == 'Mesh' || child.type == 'SkinnedMesh'
|
||||
let childIsEdge = child.type == 'Line'
|
||||
let childIsEdge = child.type == 'Line' || child.type == 'LineSegments'
|
||||
let childIsVertex = child.type == 'Points'
|
||||
if (childIsFace || childIsEdge || childIsVertex) {
|
||||
let visible = newEnabledFeatures.includes(childIsFace ? 0 : childIsEdge ? 1 : childIsVertex ? 2 : -1);
|
||||
@@ -62,7 +61,7 @@ function onOpacityChange(newOpacity: number) {
|
||||
// Iterate all primitives of the mesh and set their opacity based on the enabled features
|
||||
// Use the scene graph instead of the document to avoid reloading the same model, at the cost
|
||||
// of not actually removing the primitives from the scene graph
|
||||
console.log('Opacity may have changed', newOpacity)
|
||||
// console.log('Opacity may have changed', newOpacity)
|
||||
sceneModel.traverse((child) => {
|
||||
if (child.userData[extrasNameKey] === modelName) {
|
||||
if (child.material && child.material.opacity !== newOpacity) {
|
||||
@@ -86,7 +85,7 @@ function onModelLoad() {
|
||||
// of not actually removing the primitives from the scene graph
|
||||
sceneModel.traverse((child) => {
|
||||
if (child.userData[extrasNameKey] === modelName) {
|
||||
// if (child.type == 'Line') {
|
||||
// if (child.type == 'Line' || child.type == 'LineSegments') {
|
||||
// child.material.linewidth = 3; // Not supported in WebGL2
|
||||
// If wide lines are really needed, we need https://threejs.org/examples/?q=line#webgl_lines_fat
|
||||
// }
|
||||
|
||||
@@ -19,7 +19,7 @@ function createGizmo(expectedParent: HTMLElement, scene: ModelScene): HTMLElemen
|
||||
bubbleSizeSeconday: expectedParent.clientWidth / 14,
|
||||
fontSize: (expectedParent.clientWidth / 10) + "px"
|
||||
});
|
||||
// HACK: Swap axes to match A-Frame
|
||||
// HACK: Swap axes to fake the CAD orientation
|
||||
for (let swap of [["y", "-z"], ["z", "-y"], ["z", "-z"]]) {
|
||||
let indexA = gizmo.bubbles.findIndex((bubble) => bubble.axis == swap[0])
|
||||
let indexB = gizmo.bubbles.findIndex((bubble) => bubble.axis == swap[1])
|
||||
|
||||
Reference in New Issue
Block a user