From 7047b4dfa4d9cc7d4146d86086bb482e0a9e37a0 Mon Sep 17 00:00:00 2001 From: memelotsqui Date: Sat, 24 Aug 2024 10:34:56 -0600 Subject: [PATCH 1/9] add main trait definition --- src/library/CharacterManifestData.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/library/CharacterManifestData.js b/src/library/CharacterManifestData.js index 407960013..9d2c4a9e6 100644 --- a/src/library/CharacterManifestData.js +++ b/src/library/CharacterManifestData.js @@ -11,6 +11,7 @@ export class CharacterManifestData{ exportScale, displayScale, initialTraits, + mainTrait, requiredTraits, randomTraits, colliderTraits, @@ -35,7 +36,10 @@ export class CharacterManifestData{ this.displayScale = displayScale || exportScale || 1; this.animationPath = getAsArray(animationPath); + this.requiredTraits = getAsArray(requiredTraits); + + this.mainTrait = mainTrait || this.requiredTraits[0] || traits[0].trait; this.randomTraits = getAsArray(randomTraits); this.initialTraits = initialTraits || [...new Set(this.requiredTraits.concat(this.randomTraits))]; this.colliderTraits = getAsArray(colliderTraits); From fc781946e1ced42b30ccc3c9df2dcbc5ef43a6c7 Mon Sep 17 00:00:00 2001 From: memelotsqui Date: Sat, 24 Aug 2024 10:44:31 -0600 Subject: [PATCH 2/9] save skeleton to use for gltf imports --- src/library/characterManager.js | 137 ++++++++++++++++++++++++++------ 1 file changed, 111 insertions(+), 26 deletions(-) diff --git a/src/library/characterManager.js b/src/library/characterManager.js index 6352629fc..9878166cd 100644 --- a/src/library/characterManager.js +++ b/src/library/characterManager.js @@ -51,6 +51,7 @@ export class CharacterManager { this.renderCamera = renderCamera; this.manifestData = null; + this.baseSkeletonVRM = null this.manifest = null if (manifestURL){ this.loadManifest(manifestURL) @@ -199,6 +200,7 @@ export class CharacterManager { clearTraitData.push(new LoadedData({traitGroupID:prop, traitModel:null})) } + this.baseSkeletonVRM = null; clearTraitData.forEach(itemData => { this._addLoadedData(itemData) }); @@ -471,12 +473,12 @@ export class CharacterManager { * or rejects with an error message if not. */ loadInitialTraits() { + return new Promise(async(resolve, reject) => { // Check if manifest data is available if (this.manifestData) { // Load initial traits using the _loadTraits method await this._loadTraits(this.manifestData.getInitialTraits()); - resolve(); } else { // Manifest data is not available, log an error and reject the Promise @@ -859,7 +861,11 @@ export class CharacterManager { async _loadTraits(options, fullAvatarReplace = false){ console.log("laoded traits:", options) + + await this._createSBaseSkeleton(options); + await this.traitLoadManager.loadTraitOptions(getAsArray(options)).then(loadedData=>{ + if (fullAvatarReplace){ // add null loaded options to existingt traits to remove them; const groupTraits = this.getGroupTraits(); @@ -874,8 +880,10 @@ export class CharacterManager { } loadedData.forEach(itemData => { + console.log(itemData); this._addLoadedData(itemData) }); + cullHiddenMeshes(this.avatar); }) } @@ -897,6 +905,13 @@ export class CharacterManager { return data } + _createBoneSphere(radius){ + const geometry = new THREE.SphereGeometry( radius, 32, 16 ); + const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } ); + const sphere = new THREE.Mesh( geometry, material ); + return sphere; + } + _getPortaitScreenshotTexture(getBlob, options){ this.blinkManager.enableScreenshot(); @@ -934,6 +949,8 @@ export class CharacterManager { return screenshot; } + + _setupWireframeMaterial(mesh){ // Set Wireframe material with random colors for each material the object has mesh.origMat = mesh.material; @@ -973,25 +990,28 @@ export class CharacterManager { // } } - _VRMBaseSetup(m, item, traitID, textures, colors){ + _VRMBaseSetup(m, item, traitID, textures, colors, isSkeleton = false){ let vrm = m.userData.vrm; if (m.userData.vrm == null){ - console.error("No valid VRM was provided for " + traitID + " trait, skipping file.") return null; } addModelData(vrm, {isVRM0:vrm.meta?.metaVersion === '0'}) - if (this.manifestData.isColliderRequired(traitID)) + + if (this.manifestData.isColliderRequired(traitID) && !isSkeleton) saveVRMCollidersToUserData(m); + renameVRMBones(vrm); - if (this.manifestData.isLipsyncTrait(traitID)) + + if (this.manifestData.isLipsyncTrait(traitID) && !isSkeleton) this.lipSync = new LipSync(vrm); - this._modelBaseSetup(vrm, item, traitID, textures, colors); + if (!isSkeleton) + this._modelBaseSetup(vrm, item, traitID, textures, colors); // Rotate model 180 degrees @@ -1029,6 +1049,8 @@ export class CharacterManager { return vrm; } + + _modelBaseSetup(model, item, traitID, textures, colors){ const meshTargets = []; @@ -1193,6 +1215,47 @@ export class CharacterManager { disposeVRM(vrm) } + async _createSBaseSkeleton(traitOptions){ + if (this.baseSkeletonVRM == null){ + const mainAsset = traitOptions.find(obj => obj.traitModel?.traitGroup.trait === this.manifestData.mainTrait); + await this.traitLoadManager.loadTraitOptions(getAsArray(mainAsset)).then(loadedData=>{ + this._addLoadedDataSkeleton(loadedData[0]) + }); + } + } + _addLoadedDataSkeleton(itemData){ + const { + models + } = itemData; + + let vrm = null; + models.map((m)=>{ + if (m != null) + vrm = this._VRMBaseSetup(m, null, null, null,null, true); + }) + + this._positionModel(vrm) + + this._applyManagers(vrm) + + let targetSkinnedMesh = null; + vrm.scene.traverse((object) => { + if (object.isSkinnedMesh) { // Check if the object is a SkinnedMesh + targetSkinnedMesh = object; + object.skeleton.bones.forEach((bn)=>{ + const sphere = this._createBoneSphere(.05); + bn.add(sphere); + }) + } + }); + if (targetSkinnedMesh != null ){ + targetSkinnedMesh.parent.remove(targetSkinnedMesh); + } + + this.characterModel.attach(vrm.scene) + + this.baseSkeletonVRM = vrm; + } _addLoadedData(itemData){ const { @@ -1225,32 +1288,54 @@ export class CharacterManager { }) + console.log("vrm",vrm); // do nothing, an error happened - if (vrm == null) + if (vrm == null){ + // found model that is not vrmc + console.log("Loading GLTF Model in " + traitGroupID) return; + let gltfModel = models[0] + if (this.avatar[traitGroupID] && this.avatar[traitGroupID].vrm) { + this._disposeTrait(this.avatar[traitGroupID].vrm) + } + this._positionModel(gltfModel) + this._displayModel(gltfModel) // probably attach to bone instead + console.log(gltfModel); + this.avatar[traitGroupID] = { + traitInfo: traitModel, + textureInfo: textureTrait, + colorInfo: colorTrait, + name: traitModel.name, + model: gltfModel, + vrm: vrm + } + } + else{ // If there was a previous loaded model, remove it (maybe also remove loaded textures?) - if (this.avatar[traitGroupID] && this.avatar[traitGroupID].vrm) { - this._disposeTrait(this.avatar[traitGroupID].vrm) - // XXX restore effects - } + if (this.avatar[traitGroupID] && this.avatar[traitGroupID].vrm) { + this._disposeTrait(this.avatar[traitGroupID].vrm) + // XXX restore effects + } - this._positionModel(vrm) - - this._displayModel(vrm) - - this._applyManagers(vrm) + this._positionModel(vrm) - console.log(this.characterModel) - // and then add the new avatar data - // to do, we are now able to load multiple vrm models per options, set the options to include vrm arrays - this.avatar[traitGroupID] = { - traitInfo: traitModel, - textureInfo: textureTrait, - colorInfo: colorTrait, - name: traitModel.name, - model: vrm && vrm.scene, - vrm: vrm + this._displayModel(vrm) + + this._applyManagers(vrm) + + console.log(this.characterModel) + // and then add the new avatar data + // to do, we are now able to load multiple vrm models per options, set the options to include vrm arrays + this.avatar[traitGroupID] = { + traitInfo: traitModel, + textureInfo: textureTrait, + colorInfo: colorTrait, + name: traitModel.name, + model: vrm && vrm.scene, + vrm: vrm + } + console.log(this.avatar[traitGroupID]); } } } From ae2e0b7270c1e0733e09ff414b4dbaff835b50b7 Mon Sep 17 00:00:00 2001 From: memelotsqui Date: Sat, 24 Aug 2024 17:55:40 -0600 Subject: [PATCH 3/9] set-manifest --- public/manifest.json | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/public/manifest.json b/public/manifest.json index 3c8b1f2b2..69fd585e3 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -4,7 +4,7 @@ "name": "Anata", "description": "Anata", "portrait": "./assets/portraitImages/anata.png", - "manifest":"./character-assets/anata/manifest.json", + "manifest":"./loot-assets/anata/female/manifest.json", "icon": "./assets/icons/class-neural-hacker.svg", "format": "vrm" }, @@ -12,15 +12,7 @@ "name": "Anata Male", "description": "Anata Male", "portrait": "./assets/portraitImages/anata_male.png", - "manifest":"./character-assets/anata_male/manifest.json", - "icon": "|", - "format": "vrm" - }, - { - "name": "Anata Male", - "description": "Anata Male", - "portrait": "./assets/portraitImages/anata_male.png", - "manifest":"./character-assets/test.json", + "manifest":"./loot-assets/anata/male/manifest.json", "icon": "|", "format": "vrm" } @@ -60,25 +52,25 @@ { "name": "T-Pose", "description": "T-Pose", - "location":"./animations/T-Pose.fbx", + "location":"./animations/1_T-Pose.fbx", "icon": "|" }, { - "name": "Dancing", - "description": "Basic Dance Animation", - "location":"./animations/Dancing.fbx", + "name": "Idle", + "description": "Basic Idle Animation", + "location":"./animations/2_Idle.fbx", "icon": "|" }, { "name": "Walking", "description": "Basic Walk Animation", - "location":"./animations/Walking.fbx", + "location":"./animations/3_Walking.fbx", "icon": "|" }, { "name": "Waving", "description": "Basic Waving Animation", - "location":"./animations/Waving.fbx", + "location":"./animations/4_Waving.fbx", "icon": "|" } ] From 55e201779aaaddaf98b938a0a9575fe50c2d2a6f Mon Sep 17 00:00:00 2001 From: memelotsqui Date: Sat, 24 Aug 2024 17:56:20 -0600 Subject: [PATCH 4/9] ssave bones selector in optimizer --- src/pages/Optimizer.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/Optimizer.jsx b/src/pages/Optimizer.jsx index 60a4043a5..1de43c7e7 100644 --- a/src/pages/Optimizer.jsx +++ b/src/pages/Optimizer.jsx @@ -13,6 +13,7 @@ import MergeOptions from "../components/MergeOptions" import { local } from "../library/store" import { ZipManager } from "../library/zipManager" import BottomDisplayMenu from "../components/BottomDisplayMenu" +import { BoneSelector } from "../components/BoneSelector" function Optimizer() { const { @@ -222,6 +223,7 @@ function Optimizer() { nextVrm={loadNextVRM} previousVrm={loadPreviousVRM} /> + {/* */}
Date: Tue, 10 Sep 2024 00:01:24 -0600 Subject: [PATCH 5/9] add bone selector option --- src/components/BoneSelector.css | 51 ++++++++++++++++++++ src/components/BoneSelector.jsx | 83 ++++++++++++++++++++++++++++++++ src/images/humanoid_option.png | Bin 0 -> 6975 bytes src/images/humanoid_ui.png | Bin 0 -> 47856 bytes 4 files changed, 134 insertions(+) create mode 100644 src/components/BoneSelector.css create mode 100644 src/components/BoneSelector.jsx create mode 100644 src/images/humanoid_option.png create mode 100644 src/images/humanoid_ui.png diff --git a/src/components/BoneSelector.css b/src/components/BoneSelector.css new file mode 100644 index 000000000..db0eb6144 --- /dev/null +++ b/src/components/BoneSelector.css @@ -0,0 +1,51 @@ +.bone-selector { + position: absolute; + display: flex; + justify-content: center; /* Center horizontally */ + align-items: center; /* Center vertically */ + height: 100vh; /* Full viewport height */ + width: 100vw; /* Full viewport height */ + pointer-events: none; + top:-30px; +} + +.character-base { + /* position: relative; + display: inline-block; + width: 300px; + height: 600px; + display: flex; + justify-content: center; + text-align: center; */ + user-select: none; + + height: 70%; + position: relative; + display: inline-block; +} + +.character-base img { + height: 100%; + /* width: 100%; */ +} + +.bone-dot { + pointer-events: auto; + user-select: none; + position: relative; + width: 0px; + height: 0px; + background-size: cover; + + cursor: pointer; +} + +.bone-dot img { + width: 30px; + height: 30px; + transform: translate(-50%, -50%); /* Center the dot */ +} + +.bone-dot-position { + position: absolute; +} \ No newline at end of file diff --git a/src/components/BoneSelector.jsx b/src/components/BoneSelector.jsx new file mode 100644 index 000000000..197d0a4d3 --- /dev/null +++ b/src/components/BoneSelector.jsx @@ -0,0 +1,83 @@ +import React, { useState } from 'react'; +import './BoneSelector.css'; // Import CSS for styling +import boneDot from "../images/humanoid_option.png" +import humanoidUI from "../images/humanoid_ui.png" + +// Example CharacterBase component +const CharacterBase = ({}) => { + return ( +
+ +
+ ); +}; + +// Example BoneDot component +const BoneDot = ({ position, onSelect }) => { + return ( +
onSelect(position)}> + +
+ ); +}; + +// Example BoneSelector component +export const BoneSelector = ({onSelect}) => { + const [bonePositions, setBonePositions] = useState([ + { x: 0, y: 10, name:"hips" }, + + { x: 0, y: 60, name:"spine" }, + { x: 0, y: 120, name:"chest" }, + { x: 0, y: 180, name:"upperChest" }, + { x: 0, y: 220, name:"neck" }, + { x: 0, y: 260, name:"head" }, + + { x: -40, y: 180, name:"leftShoulder" }, + { x: 40, y: 180, name:"rightShoulder" }, + + { x: -70, y: 160, name:"leftUpperArm" }, + { x: 70, y: 160, name:"rightUpperArm" }, + + { x: -80, y: 80, name:"leftLowerArm" }, + { x: 80, y: 80, name:"rightLowerArm" }, + + { x: -80, y: -20, name:"leftHand" }, + { x: 80, y: -20, name:"rightHand" }, + + { x: -30, y: -20, name:"leftUpperLeg" }, + { x: 30, y: -20, name:"rightUpperLeg" }, + + { x: -22, y: -120, name:"leftLowerLeg" }, + { x: 20, y: -120, name:"rightLowerLeg" }, + + { x: -17, y: -260, name:"leftFoot" }, + { x: 17, y: -260, name:"rightFoot" }, + // Add more positions as needed + ]); + + const handleSelect = (position) => { + console.log('Bone selected at:', position); + onSelect(position.name); + // Additional logic for selecting a bone can be added here + }; + + return ( +
+ +
+ {bonePositions.map((pos, index) => ( + + ))} +
+
+ ); +}; diff --git a/src/images/humanoid_option.png b/src/images/humanoid_option.png new file mode 100644 index 0000000000000000000000000000000000000000..edd905c528c40a593de5a090eb4b9fa130df2a3d GIT binary patch literal 6975 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 F{{=gl(`o Date: Tue, 10 Sep 2024 00:04:03 -0600 Subject: [PATCH 6/9] add load gltf model call --- src/library/characterManager.js | 64 ++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/src/library/characterManager.js b/src/library/characterManager.js index 9878166cd..431296d2d 100644 --- a/src/library/characterManager.js +++ b/src/library/characterManager.js @@ -585,6 +585,34 @@ export class CharacterManager { }); } + loadCustomModelTrait(groupTraitID, url, parentBoneName){ + return new Promise(async (resolve, reject) => { + // Check if manifest data is available + if (this.manifestData) { + try { + // Retrieve the selected custom trait using manifest data + const selectedTrait = this.manifestData.getCustomTraitOption(groupTraitID, url); + + // If the custom trait is found, load it into the avatar using the _loadTraits method + if (selectedTrait) { + await this._loadTraits(getAsArray(selectedTrait),false, parentBoneName); + resolve(); + } + + } catch (error) { + // Reject the Promise with an error message if there's an error during custom trait retrieval + console.error("Error loading custom trait:", error.message); + reject(new Error("Failed to load custom trait.")); + } + } else { + // Manifest data is not available, log an error and reject the Promise + const errorMessage = "No manifest was loaded, custom trait cannot be loaded."; + console.error(errorMessage); + reject(new Error(errorMessage)); + } + }); + } + /** * Loads a custom texture to the specified group trait's model. * @@ -859,10 +887,12 @@ export class CharacterManager { //const selectedTrait = this.manifestData.getTraitOption(groupTraitID, traitID); } - async _loadTraits(options, fullAvatarReplace = false){ + async _loadTraits(options, fullAvatarReplace = false, parentBoneName = null){ console.log("laoded traits:", options) - await this._createSBaseSkeleton(options); + await this._createBaseSkeleton(options); + + console.log("parent bone name: ", parentBoneName) await this.traitLoadManager.loadTraitOptions(getAsArray(options)).then(loadedData=>{ @@ -881,7 +911,7 @@ export class CharacterManager { loadedData.forEach(itemData => { console.log(itemData); - this._addLoadedData(itemData) + this._addLoadedData(itemData, parentBoneName) }); cullHiddenMeshes(this.avatar); @@ -1162,14 +1192,28 @@ export class CharacterManager { if (this.animationManager) this.animationManager.addVRM(vrm) } - _displayModel(model){ + _displayModel(model, parentBoneName = null){ if(model) { // call transition const m = model.scene; //m.visible = false; // add the now model to the current scene + const targetBone = parentBoneName != null ? this.baseSkeletonVRM.humanoid.humanBones[parentBoneName]?.node : null; + - this.characterModel.attach(m) + + if (targetBone != null){ + targetBone.add(m) + } + else{ + this.characterModel.attach(m) + } + + + + + + //animationManager.update(); // note: update animation to prevent some frames of T pose at start. @@ -1215,7 +1259,7 @@ export class CharacterManager { disposeVRM(vrm) } - async _createSBaseSkeleton(traitOptions){ + async _createBaseSkeleton(traitOptions){ if (this.baseSkeletonVRM == null){ const mainAsset = traitOptions.find(obj => obj.traitModel?.traitGroup.trait === this.manifestData.mainTrait); await this.traitLoadManager.loadTraitOptions(getAsArray(mainAsset)).then(loadedData=>{ @@ -1257,7 +1301,7 @@ export class CharacterManager { this.baseSkeletonVRM = vrm; } - _addLoadedData(itemData){ + _addLoadedData(itemData, parentBoneName){ const { traitGroupID, traitModel, @@ -1288,19 +1332,15 @@ export class CharacterManager { }) - console.log("vrm",vrm); // do nothing, an error happened if (vrm == null){ // found model that is not vrmc - console.log("Loading GLTF Model in " + traitGroupID) - return; let gltfModel = models[0] if (this.avatar[traitGroupID] && this.avatar[traitGroupID].vrm) { this._disposeTrait(this.avatar[traitGroupID].vrm) } this._positionModel(gltfModel) - this._displayModel(gltfModel) // probably attach to bone instead - console.log(gltfModel); + this._displayModel(gltfModel, parentBoneName) // probably attach to bone instead this.avatar[traitGroupID] = { traitInfo: traitModel, textureInfo: textureTrait, From 05917b4bb97acf43eceee4f4ed9ffd0cece7f564 Mon Sep 17 00:00:00 2001 From: memelotsqui Date: Tue, 10 Sep 2024 00:04:27 -0600 Subject: [PATCH 7/9] open bone selector up[on loading gltf file --- src/pages/Appearance.jsx | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/pages/Appearance.jsx b/src/pages/Appearance.jsx index be6dcdd98..e2ef6450f 100644 --- a/src/pages/Appearance.jsx +++ b/src/pages/Appearance.jsx @@ -19,6 +19,7 @@ import randomizeIcon from "../images/randomize.png" import colorPicker from "../images/color-palette.png" import { ChromePicker } from 'react-color' import RightPanel from "../components/RightPanel" +import { BoneSelector } from "../components/BoneSelector" function Appearance() { const { isLoading, setViewMode, setIsLoading } = React.useContext(ViewContext) @@ -55,6 +56,8 @@ function Appearance() { const [loadedAnimationName, setLoadedAnimationName] = React.useState(""); const [isPickingColor, setIsPickingColor] = React.useState(false) const [colorPicked, setColorPicked] = React.useState({ background: '#ffffff' }) + const [selectingBone, setSelectingBone] = React.useState(false) + const [modelFile, setModelFile] = React.useState(null) const next = () => { !isMute && playSound('backNextButton'); @@ -106,6 +109,23 @@ function Appearance() { console.warn("Please select a group trait first.") } } + + // const loadGLTFModel = async (file)=>{ + // const url = URL.createObjectURL(file); + + // URL.revokeObjectURL(url); + // } + + const handleModelDrop = (file) =>{ + if (traitGroupName != ""){ + setSelectingBone(true); + setModelFile(file); + } + else{ + console.warn("Please select a group trait first.") + } + } + const handleVRMDrop = (file) =>{ setIsPickingColor(false); if (traitGroupName != ""){ @@ -113,6 +133,7 @@ function Appearance() { const path = URL.createObjectURL(file); characterManager.loadCustomTrait(traitGroupName, path).then(()=>{ setIsLoading(false); + URL.revokeObjectURL(path); }) } else{ @@ -204,6 +225,9 @@ function Appearance() { if (file && file.name.toLowerCase().endsWith('.json')) { handleJsonDrop(files); } + if (file && (file.name.toLowerCase().endsWith('.gltf') || file.name.toLowerCase().endsWith('.glb') )) { + handleModelDrop(file); + } }; const selectTraitGroup = (traitGroup) => { @@ -243,6 +267,17 @@ function Appearance() { } input.click(); } + const placeModelOnBone = async (boneName) => { + setSelectingBone(false); + setIsLoading(true); + const path = URL.createObjectURL(modelFile); + characterManager.loadCustomModelTrait(traitGroupName, path, boneName).then(()=>{ + setIsLoading(false); + setModelFile(null) + URL.revokeObjectURL(path); + }) + + } return (
@@ -253,6 +288,9 @@ function Appearance() { + {selectingBone && } {/* Main Menu section */}
From bd0b1f20ed0b704cf4a8c3598c7fe1a5e5bbac21 Mon Sep 17 00:00:00 2001 From: memelotsqui Date: Tue, 10 Sep 2024 00:05:01 -0600 Subject: [PATCH 8/9] set higher max distance --- src/context/SceneContext.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/context/SceneContext.jsx b/src/context/SceneContext.jsx index 7efa737fc..d67f1faa0 100644 --- a/src/context/SceneContext.jsx +++ b/src/context/SceneContext.jsx @@ -120,7 +120,7 @@ export const SceneProvider = (props) => { controls.minPolarAngle = 0 controls.maxPolarAngle = 3.1415 controls.minDistance = 0.5 - controls.maxDistance = 10 + controls.maxDistance = 20 controls.minAzimuthAngle = Infinity controls.maxAzimuthAngle = Infinity }) From b85cc91b84e818225263e7b452e974428f3a7cbe Mon Sep 17 00:00:00 2001 From: memelotsqui Date: Tue, 10 Sep 2024 00:07:38 -0600 Subject: [PATCH 9/9] remove debug code --- src/library/characterManager.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/library/characterManager.js b/src/library/characterManager.js index 431296d2d..2df4ec080 100644 --- a/src/library/characterManager.js +++ b/src/library/characterManager.js @@ -1286,10 +1286,10 @@ export class CharacterManager { vrm.scene.traverse((object) => { if (object.isSkinnedMesh) { // Check if the object is a SkinnedMesh targetSkinnedMesh = object; - object.skeleton.bones.forEach((bn)=>{ - const sphere = this._createBoneSphere(.05); - bn.add(sphere); - }) + // object.skeleton.bones.forEach((bn)=>{ + // const sphere = this._createBoneSphere(.05); + // bn.add(sphere); + // }) } }); if (targetSkinnedMesh != null ){