mirror of
https://github.com/yeicor-3d/yet-another-cad-viewer.git
synced 2025-12-19 22:24:17 +01:00
render inside of meshes differently without needing to mess with the stencil buffer
This commit is contained in:
@@ -25,8 +25,7 @@ import {
|
|||||||
mdiVectorRectangle
|
mdiVectorRectangle
|
||||||
} from '@mdi/js'
|
} from '@mdi/js'
|
||||||
import SvgIcon from '@jamescoyle/vue-icon';
|
import SvgIcon from '@jamescoyle/vue-icon';
|
||||||
import type {Box3} from "three";
|
import {BackSide, Color, FrontSide, Mesh as TMesh, Plane, Vector3} from "three";
|
||||||
import {Plane, Vector3} from "three";
|
|
||||||
import {SceneMgr} from "../misc/scene";
|
import {SceneMgr} from "../misc/scene";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -76,6 +75,7 @@ function onEnabledFeaturesChange(newEnabledFeatures: Array<number>) {
|
|||||||
let visible = newEnabledFeatures.includes(childIsFace ? 0 : childIsEdge ? 1 : childIsVertex ? 2 : -1);
|
let visible = newEnabledFeatures.includes(childIsFace ? 0 : childIsEdge ? 1 : childIsVertex ? 2 : -1);
|
||||||
if (child.visible !== visible) {
|
if (child.visible !== visible) {
|
||||||
child.visible = visible;
|
child.visible = visible;
|
||||||
|
if (child.userData.backChild) child.userData.backChild.visible = visible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,45 +110,41 @@ watch(opacity, onOpacityChange);
|
|||||||
let document: ShallowRef<Document> = inject('document');
|
let document: ShallowRef<Document> = inject('document');
|
||||||
|
|
||||||
function onClipPlanesChange() {
|
function onClipPlanesChange() {
|
||||||
console.log('Clip planes may have changed', clipPlaneX.value, clipPlaneY.value, clipPlaneZ.value, clipPlaneSwappedX.value, clipPlaneSwappedY.value, clipPlaneSwappedZ.value)
|
|
||||||
let scene = props.viewer?.scene;
|
let scene = props.viewer?.scene;
|
||||||
let sceneModel = (scene as any)?._model;
|
let sceneModel = (scene as any)?._model;
|
||||||
if (!scene || !sceneModel) return;
|
if (!scene || !sceneModel) return;
|
||||||
let enabled = clipPlaneSwappedX.value && clipPlaneX.value > 0 ||
|
let enabledX = clipPlaneX.value < 1 && !clipPlaneSwappedX.value || clipPlaneX.value > 0 && clipPlaneSwappedX.value;
|
||||||
clipPlaneSwappedY.value && clipPlaneY.value > 0 ||
|
let enabledY = clipPlaneY.value < 1 && !clipPlaneSwappedY.value || clipPlaneY.value > 0 && clipPlaneSwappedY.value;
|
||||||
clipPlaneSwappedZ.value && clipPlaneZ.value > 0 ||
|
let enabledZ = clipPlaneZ.value < 1 && !clipPlaneSwappedZ.value || clipPlaneZ.value > 0 && clipPlaneSwappedZ.value;
|
||||||
!clipPlaneSwappedX.value && clipPlaneX.value < 1 ||
|
// let enabled = [enabledX, enabledY, enabledZ];
|
||||||
!clipPlaneSwappedY.value && clipPlaneY.value < 1 ||
|
if (props.viewer?.renderer && (enabledX || enabledY || enabledZ)) {
|
||||||
!clipPlaneSwappedZ.value && clipPlaneZ.value < 1;
|
// Global value for all models, once set it cannot be unset (unknown for other models...)
|
||||||
if (props.viewer?.renderer && enabled) { // Global value for all models, once set it cannot be unset
|
props.viewer.renderer.threeRenderer.localClippingEnabled = true;
|
||||||
props.viewer.renderer.threeRenderer.localClippingEnabled = enabled;
|
|
||||||
}
|
}
|
||||||
let bbox = SceneMgr.getBoundingBox(document);
|
let bbox = SceneMgr.getBoundingBox(document);
|
||||||
// Due to model-viewer's camera manipulation, the bounding box
|
// Due to model-viewer's camera manipulation, the bounding box
|
||||||
bbox.translate(scene.getTarget().multiplyScalar(-1));
|
bbox.applyMatrix4(sceneModel.matrixWorld);
|
||||||
console.log('Bounding box', bbox)
|
|
||||||
sceneModel.traverse((child) => {
|
sceneModel.traverse((child) => {
|
||||||
if (child.userData[extrasNameKey] === modelName) {
|
if (child.userData[extrasNameKey] === modelName) {
|
||||||
if (child.material) {
|
if (child.material) {
|
||||||
if (bbox) {
|
if (bbox) {
|
||||||
|
let offsetX = bbox.min.x + clipPlaneX.value * (bbox.max.x - bbox.min.x);
|
||||||
|
let offsetY = bbox.min.z + clipPlaneY.value * (bbox.max.z - bbox.min.z);
|
||||||
|
let offsetZ = bbox.min.y + clipPlaneZ.value * (bbox.max.y - bbox.min.y);
|
||||||
let planes = [
|
let planes = [
|
||||||
new Plane(new Vector3(-1, 0, 0), bbox.min.x + clipPlaneX.value * (bbox.max.x - bbox.min.x)),
|
new Plane(new Vector3(-1, 0, 0), offsetX),
|
||||||
new Plane(new Vector3(0, 0, 1), bbox.min.z + clipPlaneY.value * (bbox.max.z - bbox.min.z)),
|
new Plane(new Vector3(0, 0, 1), offsetY),
|
||||||
new Plane(new Vector3(0, -1, 0), bbox.min.y + clipPlaneZ.value * (bbox.max.y - bbox.min.y)),
|
new Plane(new Vector3(0, -1, 0), offsetZ),
|
||||||
];
|
];
|
||||||
if (clipPlaneSwappedX.value) planes[0].negate();
|
if (clipPlaneSwappedX.value) planes[0].negate();
|
||||||
if (clipPlaneSwappedY.value) planes[1].negate();
|
if (clipPlaneSwappedY.value) planes[1].negate();
|
||||||
if (clipPlaneSwappedZ.value) planes[2].negate();
|
if (clipPlaneSwappedZ.value) planes[2].negate();
|
||||||
|
if (!enabledZ) planes.pop();
|
||||||
|
if (!enabledY) planes.splice(1, 1);
|
||||||
|
if (!enabledX) planes.shift();
|
||||||
if (modelName == 'fox') console.log('Clipping planes', planes, child.material)
|
if (modelName == 'fox') console.log('Clipping planes', planes, child.material)
|
||||||
child.material.clippingPlanes = planes;
|
child.material.clippingPlanes = planes;
|
||||||
if (child.userData.backChild) {
|
if (child.userData.backChild) child.userData.backChild.material.clippingPlanes = planes;
|
||||||
child.userData.backChild.material.clippingPlanes = planes;
|
|
||||||
}
|
|
||||||
// props.viewer.renderer.clippingPlanes = planes;
|
|
||||||
// child.material.clipShadows = false;
|
|
||||||
// child.material.clipIntersection = false;
|
|
||||||
// child.material.alphaToCoverage = true;
|
|
||||||
// child.material.needsUpdate = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,36 +168,26 @@ function onModelLoad() {
|
|||||||
// Iterate all primitives of the mesh and set their visibility based on the enabled features
|
// Iterate all primitives of the mesh and set their visibility based on the enabled features
|
||||||
// Use the scene graph instead of the document to avoid reloading the same model, at the cost
|
// 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
|
// of not actually removing the primitives from the scene graph
|
||||||
let stencilBackChildrenToAdd = [];
|
let childrenToAdd = [];
|
||||||
let stencilRenderOrder = 1;
|
|
||||||
sceneModel.traverse((child) => {
|
sceneModel.traverse((child) => {
|
||||||
if (child.userData[extrasNameKey] === modelName) {
|
if (child.userData[extrasNameKey] === modelName) {
|
||||||
if (child.type == 'Mesh' || child.type == 'SkinnedMesh') {
|
if (child.type == 'Mesh' || child.type == 'SkinnedMesh') {
|
||||||
// Use the stencil buffer to fill the inside (for nice clipping)
|
// We could implement cutting planes using the stencil buffer:
|
||||||
// This requires a new material, so we need to clone the existing one
|
|
||||||
// https://threejs.org/examples/?q=clipping#webgl_clipping_stencil
|
// https://threejs.org/examples/?q=clipping#webgl_clipping_stencil
|
||||||
// child.material.side = FrontSide;
|
// But this is buggy for lots of models, so instead we just draw
|
||||||
// child.material.stencilFunc = AlwaysStencilFunc;
|
// back faces with a different material.
|
||||||
// child.material.stencilFail = DecrementWrapStencilOp;
|
child.material.side = FrontSide;
|
||||||
// child.material.stencilZFail = DecrementWrapStencilOp;
|
|
||||||
// child.material.stencilZPass = DecrementWrapStencilOp;
|
if (modelName !== "__helpers") {
|
||||||
// child.renderOrder = stencilRenderOrder;
|
// The back of the material only writes to the stencil buffer the areas
|
||||||
//
|
// that should be covered by the plane, but does not render anything
|
||||||
// let backMaterial = child.material.clone();
|
let backMaterial = child.material.clone();
|
||||||
// backMaterial.side = BackSide;
|
backMaterial.side = BackSide;
|
||||||
// backMaterial.depthWrite = false;
|
backMaterial.color = new Color(0.25, 0.25, 0.25)
|
||||||
// backMaterial.depthTest = false;
|
let backChild = new TMesh(child.geometry, backMaterial);
|
||||||
// backMaterial.colorWrite = false;
|
child.userData.backChild = backChild;
|
||||||
// backMaterial.stencilWrite = true;
|
childrenToAdd.push(backChild);
|
||||||
// backMaterial.stencilFail = IncrementWrapStencilOp;
|
}
|
||||||
// backMaterial.stencilZFail = IncrementWrapStencilOp;
|
|
||||||
// backMaterial.stencilZPass = IncrementWrapStencilOp;
|
|
||||||
// let backChild = new TMesh(child.geometry, backMaterial);
|
|
||||||
// backChild.renderOrder = stencilRenderOrder;
|
|
||||||
//
|
|
||||||
// stencilBackChildrenToAdd.push(backChild);
|
|
||||||
// stencilRenderOrder++;
|
|
||||||
// child.userData.backChild = backChild;
|
|
||||||
}
|
}
|
||||||
// if (child.type == 'Line' || child.type == 'LineSegments') {
|
// if (child.type == 'Line' || child.type == 'LineSegments') {
|
||||||
// child.material.linewidth = 3; // Not supported in WebGL2
|
// child.material.linewidth = 3; // Not supported in WebGL2
|
||||||
@@ -213,7 +199,7 @@ function onModelLoad() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
stencilBackChildrenToAdd.forEach((child) => sceneModel.add(child));
|
childrenToAdd.forEach((child) => sceneModel.add(child));
|
||||||
scene.queueRender()
|
scene.queueRender()
|
||||||
|
|
||||||
// Furthermore...
|
// Furthermore...
|
||||||
|
|||||||
Reference in New Issue
Block a user