mirror of
https://github.com/yeicor-3d/yet-another-cad-viewer.git
synced 2025-12-20 06:27:04 +01:00
start working on clipping planes
This commit is contained in:
@@ -2,19 +2,31 @@
|
|||||||
import {
|
import {
|
||||||
VBtn,
|
VBtn,
|
||||||
VBtnToggle,
|
VBtnToggle,
|
||||||
|
VCheckboxBtn,
|
||||||
|
VDivider,
|
||||||
VExpansionPanel,
|
VExpansionPanel,
|
||||||
VExpansionPanelText,
|
VExpansionPanelText,
|
||||||
VExpansionPanelTitle,
|
VExpansionPanelTitle,
|
||||||
VSlider,
|
VSlider,
|
||||||
VSpacer,
|
VSpacer,
|
||||||
VTooltip
|
VTooltip,
|
||||||
} from "vuetify/lib/components";
|
} from "vuetify/lib/components";
|
||||||
import {extrasNameKey} from "../misc/gltf";
|
import {extrasNameKey} from "../misc/gltf";
|
||||||
import {Document, Mesh} from "@gltf-transform/core";
|
import {Document, Mesh} from "@gltf-transform/core";
|
||||||
import {watch} from "vue";
|
import {ref, watch, inject, ShallowRef} from "vue";
|
||||||
import type ModelViewerWrapper from "../viewer/ModelViewerWrapper.vue";
|
import type ModelViewerWrapper from "../viewer/ModelViewerWrapper.vue";
|
||||||
import {mdiCircleOpacity, mdiDelete, mdiRectangle, mdiRectangleOutline, mdiVectorRectangle} from '@mdi/js'
|
import {
|
||||||
import SvgIcon from '@jamescoyle/vue-icon/lib/svg-icon.vue';
|
mdiCircleOpacity,
|
||||||
|
mdiCube,
|
||||||
|
mdiDelete,
|
||||||
|
mdiRectangle,
|
||||||
|
mdiRectangleOutline,
|
||||||
|
mdiSwapHorizontal,
|
||||||
|
mdiVectorRectangle
|
||||||
|
} from '@mdi/js'
|
||||||
|
import SvgIcon from '@jamescoyle/vue-icon';
|
||||||
|
import {Box3, Plane, Vector3} from "three";
|
||||||
|
import {SceneMgr} from "../misc/scene";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
meshes: Array<Mesh>,
|
meshes: Array<Mesh>,
|
||||||
@@ -28,7 +40,12 @@ let modelName = props.meshes[0].getExtras()[extrasNameKey] // + " blah blah blah
|
|||||||
// Reactive properties
|
// Reactive properties
|
||||||
const enabledFeatures = defineModel<Array<number>>("enabledFeatures", {default: [0, 1, 2]});
|
const enabledFeatures = defineModel<Array<number>>("enabledFeatures", {default: [0, 1, 2]});
|
||||||
const opacity = defineModel<number>("opacity", {default: 1});
|
const opacity = defineModel<number>("opacity", {default: 1});
|
||||||
// TODO: Clipping planes (+ stencil!)
|
const clipPlaneX = ref(0);
|
||||||
|
const clipPlaneSwappedX = ref(false);
|
||||||
|
const clipPlaneY = ref(0);
|
||||||
|
const clipPlaneSwappedY = ref(false);
|
||||||
|
const clipPlaneZ = ref(0);
|
||||||
|
const clipPlaneSwappedZ = ref(false);
|
||||||
|
|
||||||
// Count the number of faces, edges and vertices
|
// Count the number of faces, edges and vertices
|
||||||
let faceCount = props.meshes.map((m) => m.listPrimitives().filter(p => p.getMode() === WebGL2RenderingContext.TRIANGLES).length).reduce((a, b) => a + b, 0)
|
let faceCount = props.meshes.map((m) => m.listPrimitives().filter(p => p.getMode() === WebGL2RenderingContext.TRIANGLES).length).reduce((a, b) => a + b, 0)
|
||||||
@@ -89,6 +106,59 @@ function onOpacityChange(newOpacity: number) {
|
|||||||
|
|
||||||
watch(opacity, onOpacityChange);
|
watch(opacity, onOpacityChange);
|
||||||
|
|
||||||
|
let document: ShallowRef<Document> = inject('document');
|
||||||
|
function onClipPlanesChange() {
|
||||||
|
let scene = props.viewer?.scene;
|
||||||
|
let sceneModel = (scene as any)?._model;
|
||||||
|
if (!scene || !sceneModel) return;
|
||||||
|
console.log('Clip planes may have changed', clipPlaneX.value, clipPlaneY.value, clipPlaneZ.value, clipPlaneSwappedX.value, clipPlaneSwappedY.value, clipPlaneSwappedZ.value)
|
||||||
|
let enabled = !clipPlaneSwappedX.value && clipPlaneX.value > 0 ||
|
||||||
|
!clipPlaneSwappedY.value && clipPlaneY.value > 0 ||
|
||||||
|
!clipPlaneSwappedZ.value && clipPlaneZ.value > 0 ||
|
||||||
|
clipPlaneSwappedX.value && clipPlaneX.value < 1 ||
|
||||||
|
clipPlaneSwappedY.value && clipPlaneY.value < 1 ||
|
||||||
|
clipPlaneSwappedZ.value && clipPlaneZ.value < 1;
|
||||||
|
let bbox: Box3
|
||||||
|
if (props.viewer?.renderer && enabled) { // Global value for all models, once set it cannot be unset
|
||||||
|
props.viewer.renderer.alpha = true;
|
||||||
|
props.viewer.renderer.localClippingEnabled = enabled;
|
||||||
|
console.log('Local clipping enabled', props.viewer.renderer)
|
||||||
|
bbox = SceneMgr.getBoundingBox(document);
|
||||||
|
}
|
||||||
|
sceneModel.traverse((child) => {
|
||||||
|
if (child.userData[extrasNameKey] === modelName) {
|
||||||
|
if (child.material) {
|
||||||
|
if (bbox) {
|
||||||
|
let planes = [
|
||||||
|
new Plane(new Vector3(1, 0, 0), bbox.min.x + clipPlaneX.value * (bbox.max.x - bbox.min.x)),
|
||||||
|
new Plane(new Vector3(0, 1, 0), bbox.min.y + clipPlaneY.value * (bbox.max.y - bbox.min.y)),
|
||||||
|
new Plane(new Vector3(0, 0, 1), bbox.min.z + clipPlaneZ.value * (bbox.max.z - bbox.min.z)),
|
||||||
|
];
|
||||||
|
if (clipPlaneSwappedX.value) planes[0].negate();
|
||||||
|
if (clipPlaneSwappedY.value) planes[1].negate();
|
||||||
|
if (clipPlaneSwappedZ.value) planes[2].negate();
|
||||||
|
if (modelName == 'fox') console.log('Clipping planes', planes, child.material)
|
||||||
|
// FIXME: Not working...
|
||||||
|
child.material.clippingPlanes = planes;
|
||||||
|
// props.viewer.renderer.clippingPlanes = planes;
|
||||||
|
// child.material.clipShadows = false;
|
||||||
|
// child.material.clipIntersection = false;
|
||||||
|
// child.material.alphaToCoverage = true;
|
||||||
|
// child.material.needsUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
scene.queueRender()
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(clipPlaneX, onClipPlanesChange);
|
||||||
|
watch(clipPlaneY, onClipPlanesChange);
|
||||||
|
watch(clipPlaneZ, onClipPlanesChange);
|
||||||
|
watch(clipPlaneSwappedX, onClipPlanesChange);
|
||||||
|
watch(clipPlaneSwappedY, onClipPlanesChange);
|
||||||
|
watch(clipPlaneSwappedZ, onClipPlanesChange);
|
||||||
|
|
||||||
function onModelLoad() {
|
function onModelLoad() {
|
||||||
let scene = props.viewer?.scene;
|
let scene = props.viewer?.scene;
|
||||||
let sceneModel = (scene as any)?._model;
|
let sceneModel = (scene as any)?._model;
|
||||||
@@ -115,6 +185,8 @@ function onModelLoad() {
|
|||||||
onEnabledFeaturesChange(enabledFeatures.value)
|
onEnabledFeaturesChange(enabledFeatures.value)
|
||||||
// Opacity may have been reset after a reload
|
// Opacity may have been reset after a reload
|
||||||
onOpacityChange(opacity.value)
|
onOpacityChange(opacity.value)
|
||||||
|
// Clip planes may have been reset after a reload
|
||||||
|
onClipPlanesChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
// props.viewer.elem may not yet be available, so we need to wait for it
|
// props.viewer.elem may not yet be available, so we need to wait for it
|
||||||
@@ -151,6 +223,49 @@ props.viewer.onElemReady((elem) => elem.addEventListener('load', onModelLoad))
|
|||||||
<svg-icon type="mdi" :path="mdiCircleOpacity"></svg-icon>
|
<svg-icon type="mdi" :path="mdiCircleOpacity"></svg-icon>
|
||||||
</template>
|
</template>
|
||||||
</v-slider>
|
</v-slider>
|
||||||
|
<v-divider></v-divider>
|
||||||
|
<v-slider v-model="clipPlaneX" hide-details min="0" max="1">
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<svg-icon type="mdi" :path="mdiCube" :rotate="120"></svg-icon>
|
||||||
|
X
|
||||||
|
</template>
|
||||||
|
<template v-slot:append>
|
||||||
|
<v-checkbox-btn trueIcon="mdi-checkbox-marked-outline" falseIcon="mdi-checkbox-blank-outline"
|
||||||
|
v-model="clipPlaneSwappedX">
|
||||||
|
<template v-slot:label>
|
||||||
|
<svg-icon type="mdi" :path="mdiSwapHorizontal"></svg-icon>
|
||||||
|
</template>
|
||||||
|
</v-checkbox-btn>
|
||||||
|
</template>
|
||||||
|
</v-slider>
|
||||||
|
<v-slider v-model="clipPlaneY" hide-details min="0" max="1">
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<svg-icon type="mdi" :path="mdiCube" :rotate="-120"></svg-icon>
|
||||||
|
Y
|
||||||
|
</template>
|
||||||
|
<template v-slot:append>
|
||||||
|
<v-checkbox-btn trueIcon="mdi-checkbox-marked-outline" falseIcon="mdi-checkbox-blank-outline"
|
||||||
|
v-model="clipPlaneSwappedY">
|
||||||
|
<template v-slot:label>
|
||||||
|
<svg-icon type="mdi" :path="mdiSwapHorizontal"></svg-icon>
|
||||||
|
</template>
|
||||||
|
</v-checkbox-btn>
|
||||||
|
</template>
|
||||||
|
</v-slider>
|
||||||
|
<v-slider v-model="clipPlaneZ" hide-details min="0" max="1">
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<svg-icon type="mdi" :path="mdiCube"></svg-icon>
|
||||||
|
Z
|
||||||
|
</template>
|
||||||
|
<template v-slot:append>
|
||||||
|
<v-checkbox-btn trueIcon="mdi-checkbox-marked-outline" falseIcon="mdi-checkbox-blank-outline"
|
||||||
|
v-model="clipPlaneSwappedZ">
|
||||||
|
<template v-slot:label>
|
||||||
|
<svg-icon type="mdi" :path="mdiSwapHorizontal"></svg-icon>
|
||||||
|
</template>
|
||||||
|
</v-checkbox-btn>
|
||||||
|
</template>
|
||||||
|
</v-slider>
|
||||||
</v-expansion-panel-text>
|
</v-expansion-panel-text>
|
||||||
</v-expansion-panel>
|
</v-expansion-panel>
|
||||||
</template>
|
</template>
|
||||||
@@ -199,4 +314,12 @@ props.viewer.onElemReady((elem) => elem.addEventListener('load', onModelLoad))
|
|||||||
.hide-this-icon {
|
.hide-this-icon {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mdi-checkbox-blank-outline { /* HACK: mdi is not fully imported, only required icons... */
|
||||||
|
background-image: url('data:image/svg+xml;charset=UTF-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white"><path d="M19,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3M19,5V19H5V5H19Z"/></svg>');
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdi-checkbox-marked-outline { /* HACK: mdi is not fully imported, only required icons... */
|
||||||
|
background-image: url('data:image/svg+xml;charset=UTF-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white"><path d="M19,19H5V5H15V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V11H19M7.91,10.08L6.5,11.5L11,16L21,6L19.59,4.58L11,13.17L7.91,10.08Z"/></svg>');
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user