From fd44fea8cd53098f46e6664494f4e75d7aec502e Mon Sep 17 00:00:00 2001 From: memelotsqui Date: Thu, 23 Nov 2023 11:02:14 -0600 Subject: [PATCH 1/6] clone base geometry instead of using it --- src/library/cull-mesh.js | 50 +++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/src/library/cull-mesh.js b/src/library/cull-mesh.js index 4642f44c..173137ee 100644 --- a/src/library/cull-mesh.js +++ b/src/library/cull-mesh.js @@ -1,4 +1,5 @@ -import { BufferAttribute, BackSide, FrontSide, Raycaster, Vector3, Color, BufferGeometry,LineBasicMaterial,Line, MeshBasicMaterial } from "three"; +import { Mesh, BufferAttribute, BackSide, FrontSide, Raycaster, Vector3, Color, BufferGeometry,LineBasicMaterial,Line, MeshBasicMaterial } from "three"; +import { computeBoundsTree, disposeBoundsTree, acceleratedRaycast, SAH } from 'three-mesh-bvh'; let origin = new Vector3(); let direction = new Vector3(); @@ -16,6 +17,32 @@ const backMat = new MeshBasicMaterial({side:BackSide}) let mainScene; +const createCloneCullMesh = (mesh) => { + // clone mesh + const clonedGeometry = mesh.geometry.clone(); + const clonedMaterial = mesh.material.clone(); + + // vrm0 mesh rotation + // if (mesh.userData.isVRM0){ + // const positions = clonedGeometry.attributes.position; + // for (let i = 0; i < positions.array.length; i += 3) { + // positions.array[i] = -positions.array[i]; // Flip x-coordinate + // positions.array[i + 2] = -positions.array[i + 2]; // Flip z-coordinate + // } + + // } + + // bvh calculation + + const clonedMesh = new Mesh(clonedGeometry, clonedMaterial); + + // mainScene.add(clonedMesh); + clonedMesh.geometry.computeBoundsTree({strategy:SAH}); + //mainScene.add(clonedMesh); + console.log("created") + return clonedMesh; +} + export const CullHiddenFaces = async(meshes) => { // make a 2 dimensional array that will hold the layers const meshData = []; @@ -40,16 +67,27 @@ export const CullHiddenFaces = async(meshes) => { meshData[mesh.userData.cullLayer] = {origMeshes:[], posMeshes:[], negMeshes:[], scaleMeshes:[], positionMeshes:[]} } - mesh.getWorldScale(worldScale); - mesh.getWorldPosition(worldPosition); + + // mesh.getWorldScale(worldScale); + // mesh.getWorldPosition(worldPosition); + worldScale.set(1,1,1) + worldPosition.set(0,0,0) meshData[mesh.userData.cullLayer].scaleMeshes.push(worldScale); meshData[mesh.userData.cullLayer].positionMeshes.push(worldPosition); + + if (mesh.userData.cullingCloneP == null){ + const cullClone = createCloneCullMesh(mesh); + mesh.userData.cullingCloneP = cullClone.clone(); + mesh.userData.cullingCloneN = cullClone.clone(); + } + // clone the mesh to only detect collisions in front faces - const cloneP = mesh.clone() + const cloneP = mesh.userData.cullingCloneP; + const cloneN = mesh.userData.cullingCloneN + cloneP.material = frontMat; - const cloneN = mesh.clone() cloneN.userData.cancelMesh = cloneP; cloneN.material = backMat; cloneP.userData.maxCullDistance = cloneN.userData.maxCullDistance = mesh.userData.maxCullDistance; @@ -89,7 +127,7 @@ export const CullHiddenFaces = async(meshes) => { const faceNormals = mesh.geometry.userData.faceNormals; geomsIndices.push({ geom: mesh.geometry, - index: getIndexBuffer(meshPosition,meshScale, index,vertexData,normalsData, faceNormals, hitArr,mesh.userData.cullDistance/*,i === 0*/) + index: getIndexBuffer(meshPosition,meshScale, index,vertexData,normalsData, faceNormals, hitArr,mesh.userData.cullDistance,/*i === 0*/) }) } } From 3053237319e4c03098721880a0bd7ab7af987b73 Mon Sep 17 00:00:00 2001 From: memelotsqui Date: Thu, 23 Nov 2023 11:02:36 -0600 Subject: [PATCH 2/6] remove bvh calculations in selector --- src/components/Selector.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Selector.jsx b/src/components/Selector.jsx index 041d1c3f..0a120a77 100644 --- a/src/components/Selector.jsx +++ b/src/components/Selector.jsx @@ -623,8 +623,8 @@ export default function Selector({confirmDialog, uploadVRMURL, templateInfo, ani if (cullingIgnore.indexOf(child.name) === -1) cullingMeshes.push(child) - if (child.geometry.boundsTree == null) - child.geometry.computeBoundsTree({strategy:SAH}); + // if (child.geometry.boundsTree == null) + // child.geometry.computeBoundsTree({strategy:SAH}); createFaceNormals(child.geometry) if (child.isSkinnedMesh) { From 51a9d6b40bbf2b12420ec5339fb339b4bbdf0cae Mon Sep 17 00:00:00 2001 From: memelotsqui Date: Thu, 23 Nov 2023 12:21:12 -0600 Subject: [PATCH 3/6] moodify non vrm geometry to work with cull faces --- src/library/cull-mesh.js | 45 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/library/cull-mesh.js b/src/library/cull-mesh.js index 173137ee..0450b67f 100644 --- a/src/library/cull-mesh.js +++ b/src/library/cull-mesh.js @@ -23,23 +23,19 @@ const createCloneCullMesh = (mesh) => { const clonedMaterial = mesh.material.clone(); // vrm0 mesh rotation - // if (mesh.userData.isVRM0){ - // const positions = clonedGeometry.attributes.position; - // for (let i = 0; i < positions.array.length; i += 3) { - // positions.array[i] = -positions.array[i]; // Flip x-coordinate - // positions.array[i + 2] = -positions.array[i + 2]; // Flip z-coordinate - // } - - // } - - // bvh calculation + if (!mesh.userData.isVRM0){ + const positions = clonedGeometry.attributes.position; + for (let i = 0; i < positions.array.length; i += 3) { + positions.array[i] = -positions.array[i]; // Flip x-coordinate + positions.array[i + 2] = -positions.array[i + 2]; // Flip z-coordinate + } + positions.needsUpdate = true; + } const clonedMesh = new Mesh(clonedGeometry, clonedMaterial); - // mainScene.add(clonedMesh); + // bvh calculation clonedMesh.geometry.computeBoundsTree({strategy:SAH}); - //mainScene.add(clonedMesh); - console.log("created") return clonedMesh; } @@ -64,7 +60,7 @@ export const CullHiddenFaces = async(meshes) => { // if it hasnt been previously created an array in this index value, create it if (meshData[mesh.userData.cullLayer] == null){ - meshData[mesh.userData.cullLayer] = {origMeshes:[], posMeshes:[], negMeshes:[], scaleMeshes:[], positionMeshes:[]} + meshData[mesh.userData.cullLayer] = {origMeshes:[], cloneMeshes:[], posMeshes:[], negMeshes:[], scaleMeshes:[], positionMeshes:[]} } @@ -77,13 +73,14 @@ export const CullHiddenFaces = async(meshes) => { - if (mesh.userData.cullingCloneP == null){ - const cullClone = createCloneCullMesh(mesh); - mesh.userData.cullingCloneP = cullClone.clone(); - mesh.userData.cullingCloneN = cullClone.clone(); + if (mesh.userData.cullingClone == null){ + mesh.userData.cullingClone = createCloneCullMesh(mesh); + mesh.userData.cullingCloneP = mesh.userData.cullingClone.clone(); + mesh.userData.cullingCloneN = mesh.userData.cullingClone.clone(); } - + // clone the mesh to only detect collisions in front faces + const clone = mesh.userData.cullingClone; const cloneP = mesh.userData.cullingCloneP; const cloneN = mesh.userData.cullingCloneN @@ -93,6 +90,7 @@ export const CullHiddenFaces = async(meshes) => { cloneP.userData.maxCullDistance = cloneN.userData.maxCullDistance = mesh.userData.maxCullDistance; meshData[mesh.userData.cullLayer].origMeshes.push(mesh) + meshData[mesh.userData.cullLayer].cloneMeshes.push(clone) meshData[mesh.userData.cullLayer].posMeshes.push(cloneP) meshData[mesh.userData.cullLayer].negMeshes.push(cloneN) @@ -119,15 +117,16 @@ export const CullHiddenFaces = async(meshes) => { for (let k = 0; k < meshData[i].origMeshes.length; k++){ const mesh = meshData[i].origMeshes[k]; + const cloneMesh = meshData[i].cloneMeshes[k]; const meshScale = meshData[i].scaleMeshes[k]; const meshPosition = meshData[i].positionMeshes[k]; const index = mesh.userData.origIndexBuffer.array; - const vertexData = mesh.geometry.attributes.position.array; - const normalsData = mesh.geometry.attributes.normal.array; - const faceNormals = mesh.geometry.userData.faceNormals; + const vertexData = cloneMesh.geometry.attributes.position.array; + const normalsData = cloneMesh.geometry.attributes.normal.array; + const faceNormals = cloneMesh.geometry.userData.faceNormals; geomsIndices.push({ geom: mesh.geometry, - index: getIndexBuffer(meshPosition,meshScale, index,vertexData,normalsData, faceNormals, hitArr,mesh.userData.cullDistance,/*i === 0*/) + index: getIndexBuffer(meshPosition,meshScale, index,vertexData,normalsData, faceNormals, hitArr,mesh.userData.cullDistance/*,i === 0*/) }) } } From 2ce31de1fab7fd92e1094f8bbe4c03d67d7fcea5 Mon Sep 17 00:00:00 2001 From: memelotsqui Date: Thu, 23 Nov 2023 12:28:22 -0600 Subject: [PATCH 4/6] set by default click to add faces --- src/components/Scene.jsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/components/Scene.jsx b/src/components/Scene.jsx index f13d21b9..f7e90b65 100644 --- a/src/components/Scene.jsx +++ b/src/components/Scene.jsx @@ -128,10 +128,6 @@ export default function Scene({sceneModel, lookatManager}) { const setOriginalInidicesAndColliders = () => { avatarModel.traverse((child)=>{ if (child.isMesh) { - if (child.userData.lastBoundsTree){ - child.userData.lastBoundsTree = child.geometry.boundsTree; - child.geometry.disposeBoundsTree(); - } if (child.userData.origIndexBuffer){ child.userData.clippedIndexGeometry = child.geometry.index.clone(); child.geometry.setIndex(child.userData.origIndexBuffer); @@ -145,7 +141,6 @@ export default function Scene({sceneModel, lookatManager}) { if (child.isMesh) { if (child.userData.origIndexBuffer){ child.geometry.setIndex(child.userData.clippedIndexGeometry); - child.geometry.boundsTree = child.userData.lastBoundsTree; } } }) @@ -193,7 +188,7 @@ export default function Scene({sceneModel, lookatManager}) { const isCtrlPressed = event.ctrlKey; - const displayCullFaces = local["traitInformation_display_cull"] == null ? false : local["traitInformation_display_cull"]; + const displayCullFaces = true;//local["traitInformation_display_cull"] == null ? false : local["traitInformation_display_cull"]; if (displayCullFaces){ setOriginalInidicesAndColliders(); From deccb04f58d9058a4e4e70e452f4950d9c112704 Mon Sep 17 00:00:00 2001 From: memelotsqui Date: Thu, 23 Nov 2023 12:28:54 -0600 Subject: [PATCH 5/6] remove option to select cullfaces on click --- src/components/TraitInformation.jsx | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/components/TraitInformation.jsx b/src/components/TraitInformation.jsx index f87ac927..9238bf37 100644 --- a/src/components/TraitInformation.jsx +++ b/src/components/TraitInformation.jsx @@ -17,7 +17,6 @@ export default function TraitInformation({animationManager, lookatManager}){ const [cullInDistance, setCullInDistance] = useState(0); const [cullLayer, setCullLayer] = useState(0); const [animationName, setAnimationName] = useState(animationManager.getCurrentAnimationName()); - const [displayCullFaces, setDisplayCullFaces] = useState(local["traitInformation_display_cull"] == null ? false : local["traitInformation_display_cull"]); const [hasMouseLook, setHasMouseLook] = useState(lookatManager.userActivated); useEffect(() => { @@ -74,12 +73,6 @@ export default function TraitInformation({animationManager, lookatManager}){ // Perform any additional actions or logic based on the checkbox state change }; - const handleDisplayCullFaces = (event) =>{ - setDisplayCullFaces(event.target.checked); - local["traitInformation_display_cull"] = event.target.checked; - } - - return ( displayTraitOption != null ? (
@@ -158,22 +151,6 @@ export default function TraitInformation({animationManager, lookatManager}){
-
-
-
- - Display Hidden Faces on click -
- -
-
From 0d379ed603e43739a48569e3fe5495b674ba9651 Mon Sep 17 00:00:00 2001 From: memelotsqui Date: Thu, 23 Nov 2023 14:51:32 -0600 Subject: [PATCH 6/6] move code from selector to cull-mesh.js --- src/components/Selector.jsx | 10 +------ src/library/cull-mesh.js | 53 +++++++++++++++++++++++-------------- src/library/utils.js | 21 +-------------- 3 files changed, 35 insertions(+), 49 deletions(-) diff --git a/src/components/Selector.jsx b/src/components/Selector.jsx index 0a120a77..d7b49e63 100644 --- a/src/components/Selector.jsx +++ b/src/components/Selector.jsx @@ -4,7 +4,6 @@ import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader" import { MToonMaterial, VRMLoaderPlugin, VRMUtils } from "@pixiv/three-vrm" import cancel from "../../public/ui/selector/cancel.png" import { addModelData, disposeVRM } from "../library/utils" -import { computeBoundsTree, disposeBoundsTree, acceleratedRaycast, SAH } from 'three-mesh-bvh'; import {ViewContext} from "../context/ViewContext" import tick from "../../public/ui/selector/tick.svg" import { AudioContext } from "../context/AudioContext" @@ -12,7 +11,6 @@ import { SceneContext } from "../context/SceneContext" import { SoundContext } from "../context/SoundContext" import { renameVRMBones, - createFaceNormals, createBoneDirection, } from "../library/utils" import { LipSync } from '../library/lipsync' @@ -26,9 +24,7 @@ import MenuTitle from "./MenuTitle" import { saveVRMCollidersToUserData } from "../library/load-utils" -THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree; -THREE.BufferGeometry.prototype.disposeBoundsTree = disposeBoundsTree; -THREE.Mesh.prototype.raycast = acceleratedRaycast; + export default function Selector({confirmDialog, uploadVRMURL, templateInfo, animationManager, blinkManager, lookatManager, effectManager}) { const { @@ -623,10 +619,6 @@ export default function Selector({confirmDialog, uploadVRMURL, templateInfo, ani if (cullingIgnore.indexOf(child.name) === -1) cullingMeshes.push(child) - // if (child.geometry.boundsTree == null) - // child.geometry.computeBoundsTree({strategy:SAH}); - - createFaceNormals(child.geometry) if (child.isSkinnedMesh) { createBoneDirection(child) if (vrm.meta?.metaVersion === '0'){ diff --git a/src/library/cull-mesh.js b/src/library/cull-mesh.js index 0450b67f..5fce12eb 100644 --- a/src/library/cull-mesh.js +++ b/src/library/cull-mesh.js @@ -1,10 +1,8 @@ -import { Mesh, BufferAttribute, BackSide, FrontSide, Raycaster, Vector3, Color, BufferGeometry,LineBasicMaterial,Line, MeshBasicMaterial } from "three"; +import { Mesh, Triangle, BufferAttribute, BackSide, FrontSide, Raycaster, Vector3, Color, BufferGeometry,LineBasicMaterial,Line, MeshBasicMaterial } from "three"; import { computeBoundsTree, disposeBoundsTree, acceleratedRaycast, SAH } from 'three-mesh-bvh'; let origin = new Vector3(); let direction = new Vector3(); -let worldScale = new Vector3(); -let worldPosition = new Vector3(); const intersections = []; const raycaster = new Raycaster(); @@ -17,11 +15,37 @@ const backMat = new MeshBasicMaterial({side:BackSide}) let mainScene; +BufferGeometry.prototype.computeBoundsTree = computeBoundsTree; +BufferGeometry.prototype.disposeBoundsTree = disposeBoundsTree; +Mesh.prototype.raycast = acceleratedRaycast; + +const createFaceNormals = (geometry) => { + const pos = geometry.attributes.position; + const idx = geometry.index; + + const tri = new Triangle(); // for re-use + const a = new Vector3(), b = new Vector3(), c = new Vector3(); // for re-use + + const faceNormals = []; + + //set foreach vertex + for (let f = 0; f < (idx.array.length / 3); f++) { + const idxBase = f * 3; + a.fromBufferAttribute(pos, idx.getX(idxBase + 0)); + b.fromBufferAttribute(pos, idx.getX(idxBase + 1)); + c.fromBufferAttribute(pos, idx.getX(idxBase + 2)); + tri.set(a, b, c); + faceNormals.push(tri.getNormal(new Vector3())); + } + geometry.userData.faceNormals = faceNormals; +} + const createCloneCullMesh = (mesh) => { // clone mesh const clonedGeometry = mesh.geometry.clone(); const clonedMaterial = mesh.material.clone(); + // vrm0 mesh rotation if (!mesh.userData.isVRM0){ const positions = clonedGeometry.attributes.position; @@ -35,6 +59,7 @@ const createCloneCullMesh = (mesh) => { const clonedMesh = new Mesh(clonedGeometry, clonedMaterial); // bvh calculation + createFaceNormals(clonedMesh.geometry) clonedMesh.geometry.computeBoundsTree({strategy:SAH}); return clonedMesh; } @@ -63,16 +88,6 @@ export const CullHiddenFaces = async(meshes) => { meshData[mesh.userData.cullLayer] = {origMeshes:[], cloneMeshes:[], posMeshes:[], negMeshes:[], scaleMeshes:[], positionMeshes:[]} } - - // mesh.getWorldScale(worldScale); - // mesh.getWorldPosition(worldPosition); - worldScale.set(1,1,1) - worldPosition.set(0,0,0) - meshData[mesh.userData.cullLayer].scaleMeshes.push(worldScale); - meshData[mesh.userData.cullLayer].positionMeshes.push(worldPosition); - - - if (mesh.userData.cullingClone == null){ mesh.userData.cullingClone = createCloneCullMesh(mesh); mesh.userData.cullingCloneP = mesh.userData.cullingClone.clone(); @@ -118,15 +133,13 @@ export const CullHiddenFaces = async(meshes) => { const mesh = meshData[i].origMeshes[k]; const cloneMesh = meshData[i].cloneMeshes[k]; - const meshScale = meshData[i].scaleMeshes[k]; - const meshPosition = meshData[i].positionMeshes[k]; const index = mesh.userData.origIndexBuffer.array; const vertexData = cloneMesh.geometry.attributes.position.array; const normalsData = cloneMesh.geometry.attributes.normal.array; const faceNormals = cloneMesh.geometry.userData.faceNormals; geomsIndices.push({ geom: mesh.geometry, - index: getIndexBuffer(meshPosition,meshScale, index,vertexData,normalsData, faceNormals, hitArr,mesh.userData.cullDistance/*,i === 0*/) + index: getIndexBuffer(index,vertexData,normalsData, faceNormals, hitArr,mesh.userData.cullDistance/*,i === 0*/) }) } } @@ -163,7 +176,7 @@ const getDistanceInOut = (distanceArr) => { return [distIn, distOut] } -const getIndexBuffer = (meshPosition, meshScale, index, vertexData, normalsData, faceNormals, intersectModels, distanceArr, debug = false) =>{ +const getIndexBuffer = (index, vertexData, normalsData, faceNormals, intersectModels, distanceArr, debug = false) =>{ const indexCustomArr = []; const distArr = getDistanceInOut(distanceArr); @@ -195,9 +208,9 @@ const getIndexBuffer = (meshPosition, meshScale, index, vertexData, normalsData, // move the origin away to have the raycast being casted from outside origin.set( - (vertexData[vi] * meshScale.x ) + meshPosition.x, - (vertexData[vi+1] * meshScale.y ) + meshPosition.y, - (vertexData[vi+2] * meshScale.z) + meshPosition.z) + vertexData[vi], + vertexData[vi+1], + vertexData[vi+2]) .add(direction.clone().multiplyScalar(distIn)) //invert the direction of the raycaster as we moved it away from its origin diff --git a/src/library/utils.js b/src/library/utils.js index 6b20fd42..0bb45c7b 100644 --- a/src/library/utils.js +++ b/src/library/utils.js @@ -429,26 +429,7 @@ export function disposeVRM(vrm) { VRMUtils.deepDispose( model ); } -export const createFaceNormals = (geometry) => { - const pos = geometry.attributes.position; - const idx = geometry.index; - - const tri = new THREE.Triangle(); // for re-use - const a = new THREE.Vector3(), b = new THREE.Vector3(), c = new THREE.Vector3(); // for re-use - - const faceNormals = []; - - //set foreach vertex - for (let f = 0; f < (idx.array.length / 3); f++) { - const idxBase = f * 3; - a.fromBufferAttribute(pos, idx.getX(idxBase + 0)); - b.fromBufferAttribute(pos, idx.getX(idxBase + 1)); - c.fromBufferAttribute(pos, idx.getX(idxBase + 2)); - tri.set(a, b, c); - faceNormals.push(tri.getNormal(new THREE.Vector3())); - } - geometry.userData.faceNormals = faceNormals; -}; + export const createBoneDirection = (skinMesh) => { const geometry = skinMesh.geometry;