add support for axes

This commit is contained in:
Yeicor
2024-02-25 12:15:26 +01:00
parent f22e19fb3d
commit 878367638f
4 changed files with 79 additions and 6 deletions

62
src/misc/helpers.ts Normal file
View 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
}

View File

@@ -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;
}

View File

@@ -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
// }

View File

@@ -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])