diff --git a/src/js/main.js b/src/js/main.js
index 239f12a12a..162a3ae0e6 100644
--- a/src/js/main.js
+++ b/src/js/main.js
@@ -1,8 +1,8 @@
const env = require('../../env.json')
-if (env.mode) {
+/* if (env.mode) {
process.env.NODE_ENV = env.mode
-}
+} */
const {app, ipcMain, BrowserWindow, dialog, powerSaveBlocker} = electron = require('electron')
diff --git a/src/js/shared/IK/utils/jsUtils.js b/src/js/shared/IK/utils/jsUtils.js
new file mode 100644
index 0000000000..3c2b6a075e
--- /dev/null
+++ b/src/js/shared/IK/utils/jsUtils.js
@@ -0,0 +1,6 @@
+Array.prototype.remove = function(object) {
+ let indexOf = this.indexOf(object)
+ if(indexOf !== -1) {
+ this.splice(indexOf, 1)
+ }
+}
\ No newline at end of file
diff --git a/src/js/shared/reducers/shot-generator.js b/src/js/shared/reducers/shot-generator.js
index e3c870004b..9b0efc94df 100644
--- a/src/js/shared/reducers/shot-generator.js
+++ b/src/js/shared/reducers/shot-generator.js
@@ -1020,6 +1020,16 @@ const sceneObjectsReducer = (state = {}, action) => {
}
return withDisplayNames(draft)
+
+ case 'CREATE_OBJECTS':
+ if (
+ action.payload.objects == null ||
+ action.payload.objects.length === 0
+ ) return
+ for(let object of action.payload.objects) {
+ draft[object.id] = object
+ }
+ return withDisplayNames(draft)
case 'DELETE_OBJECTS':
if (
@@ -1221,19 +1231,22 @@ const sceneObjectsReducer = (state = {}, action) => {
draft[action.payload.id].skeleton = draft[action.payload.id].skeleton || {}
for (let bone of action.payload.skeleton) {
let rotation = bone.rotation
- let { x, y, z } = bone.position
+ let position = bone.position
if(draft[action.payload.id].skeleton[bone.name]) {
draft[action.payload.id].skeleton[bone.name].rotation = !rotation ?
draft[action.payload.id].skeleton[bone.name].rotation :
{ x: rotation.x, y: rotation.y, z: rotation.z }
draft[action.payload.id].skeleton[bone.name].position = !bone.position ?
draft[action.payload.id].skeleton[bone.name].position :
- { x: x, y: y, z: z }
+ { x: position.x, y: position.y, z: position.z }
} else {
- draft[action.payload.id].skeleton[bone.name] = {
- rotation: { x: rotation.x, y: rotation.y, z: rotation.z },
- position: { x: x, y: y, z: z },
- }
+ draft[action.payload.id].skeleton[bone.name] = {}
+ draft[action.payload.id].skeleton[bone.name].rotation = !rotation ?
+ {} :
+ { x: rotation.x, y: rotation.y, z: rotation.z }
+ draft[action.payload.id].skeleton[bone.name].position = !bone.position ?
+ {} :
+ { x: position.x, y: position.y, z: position.z }
}
}
@@ -1708,6 +1721,7 @@ module.exports = {
deselectAttachable: id => ({ type: 'DESELECT_ATTACHABLE', payload: id}),
createObject: values => ({ type: 'CREATE_OBJECT', payload: values }),
+ createObjects: objects => ({ type: 'CREATE_OBJECTS', payload: {objects} }),
updateObject: (id, values) => ({ type: 'UPDATE_OBJECT', payload: { id, ...values } }),
// batch update
diff --git a/src/js/shot-generator/Character.js b/src/js/shot-generator/Character.js
index 9398cc3240..5a0b822b54 100644
--- a/src/js/shot-generator/Character.js
+++ b/src/js/shot-generator/Character.js
@@ -181,6 +181,7 @@ const Character = React.memo(({
largeRenderer,
deleteObjects,
updateObjects,
+ defaultPosePreset,
...props
}) => {
const [ready, setReady] = useState(false) // ready to load?
@@ -199,11 +200,13 @@ const Character = React.memo(({
scene.remove(object.current.bonesHelper)
scene.remove(object.current.orthoIcon)
scene.remove(object.current)
+ object.current.userData.id === null
object.current.remove(SGIkHelper.getInstance())
SGIkHelper.getInstance().deselectControlPoint()
SGIkHelper.getInstance().removeFromParent(id)
objectRotationControl.current.deselectObject()
object.current.bonesHelper = null
+ scene.remove(object.current)
object.current = null
}
}
@@ -212,7 +215,11 @@ const Character = React.memo(({
useEffect(() => {
setReady(false)
setLoaded(false)
- // return function cleanup () { }
+ return () => {
+ // Because when we switch models we recreate character(create new component) we need to set prev component object's id to null
+ if(object.current)
+ object.current.userData.id = null
+ }
}, [props.model])
useEffect(() => {
@@ -276,10 +283,10 @@ const Character = React.memo(({
object.current.userData.boneLengthScale = boneLengthScale
object.current.userData.parentRotation = parentRotation
object.current.userData.parentPosition = parentPosition
+ object.current.resetToStandardSkeleton = resetToStandardSkeleton
scene.add(object.current.bonesHelper)
let domElement = largeRenderer.current.domElement
-
objectRotationControl.current = new ObjectRotationControl(scene, camera, domElement, object.current.uuid)
let boneRotation = objectRotationControl.current
boneRotation.setUpdateCharacter((name, rotation) => {updateCharacterSkeleton({
@@ -297,7 +304,7 @@ const Character = React.memo(({
}
return function cleanup () {
- setAttachables(object.current ? object.current.attachables : null)
+ setAttachables(!object.current ? null : object.current.attachables ? object.current.attachables.concat([]) : null)
setModelChange(!object.current ? false : object.current.attachables ? true : false)
doCleanup()
// setLoaded(false)
@@ -333,17 +340,17 @@ const Character = React.memo(({
const fullyUpdateSkeleton = () => {
let skeleton = object.current.userData.skeleton
let changedSkeleton = []
-
- let inverseMatrixWorld = object.current.getInverseMatrixWorld()
let position = new THREE.Vector3()
+ let scalarForBones = 1
+ if(props.posePresetId === defaultPosePreset.id)
+ scalarForBones = object.current.userData.boneLengthScale === 100 ? 100 : 1
for(let i = 0; i < skeleton.bones.length; i++) {
let bone = skeleton.bones[i]
-
+ if(bone.name.includes("leaf")) continue
let rotation = bone.rotation
- bone.applyMatrix(object.current.matrixWorld)
- position = bone.position.clone()
- bone.applyMatrix(inverseMatrixWorld)
- position.multiplyScalar( object.current.userData.boneLengthScale === 100 ? 100 : 1)
+
+ position = bone.position.clone().applyMatrix4(object.current.getInverseMatrixWorld())
+ position.multiplyScalar(scalarForBones)
changedSkeleton.push({
name: bone.name,
position: {
@@ -361,14 +368,22 @@ const Character = React.memo(({
updateCharacterIkSkeleton({id, skeleton:changedSkeleton})
}
+ const saveAttachablesPositions = () => {
+ if(!object.current || !object.current.attachables) return
+ object.current.updateWorldMatrix(true, true)
+ for(let i = 0; i < object.current.attachables.length; i++) {
+ object.current.attachables[i].saveToStore()
+ }
+ }
+
let startingDeviceRotation = useRef(null)
let currentBoneSelected = useRef(null)
const updateSkeleton = () => {
let skeleton = object.current.userData.skeleton
+
if (Object.values(props.skeleton).length) {
fixRootBone()
-
for (bone of skeleton.bones) {
let userState = props.skeleton[bone.name]
let systemState = originalSkeleton.current.getBoneByName(bone.name).clone()
@@ -376,6 +391,7 @@ const Character = React.memo(({
bone.rotation.x = state.rotation.x
bone.rotation.y = state.rotation.y
bone.rotation.z = state.rotation.z
+
}
} else {
let skeleton = object.current.userData.skeleton
@@ -461,6 +477,8 @@ const Character = React.memo(({
object.current.position.z = props.y
object.current.position.y = props.z
object.current.orthoIcon.position.copy(object.current.position)
+ saveAttachablesPositions()
+
}
}, [props.model, props.x, props.y, props.z, ready])
@@ -480,7 +498,6 @@ const Character = React.memo(({
object.current.rotation.y = props.rotation
object.current.orthoIcon.icon.material.rotation = props.rotation + Math.PI
}
-
}
}, [props.model, props.rotation, ready])
@@ -491,6 +508,22 @@ const Character = React.memo(({
updateSkeleton()
}
+ const resetToStandardSkeleton = () => {
+ let skeleton = object.current.userData.skeleton
+ if (Object.values(props.skeleton).length) {
+ fixRootBone()
+ for (bone of skeleton.bones) {
+ let userState = defaultPosePreset.state.skeleton[bone.name]
+ let systemState = originalSkeleton.current.getBoneByName(bone.name).clone()
+ let state = userState || systemState
+ bone.rotation.x = state.rotation.x
+ bone.rotation.y = state.rotation.y
+ bone.rotation.z = state.rotation.z
+ bone.updateMatrixWorld(true)
+ }
+ }
+ }
+
const fixRootBone = () => {
let { boneLengthScale, parentRotation, parentPosition } = object.current.userData
let skeleton = object.current.userData.skeleton
@@ -512,6 +545,7 @@ const Character = React.memo(({
if (!props.posePresetId) return
resetPose()
fullyUpdateSkeleton()
+ saveAttachablesPositions()
}, [props.posePresetId])
useEffect(() => {
@@ -520,8 +554,20 @@ const Character = React.memo(({
if (!props.handSkeleton) return
resetPose()
updateSkeletonHand()
+ saveAttachablesPositions()
}, [props.handPosePresetId, props.handSkeleton])
+ useEffect(() => {
+ if(!props.characterPresetId) return
+ if(object.current && object.current.attachables) {
+ let attachablesToDelete = []
+ for(let i = 0; i < object.current.attachables.length; i++) {
+ attachablesToDelete.push(object.current.attachables[i].userData.id)
+ }
+ deleteObjects(attachablesToDelete)
+ }
+ }, [props.characterPresetId])
+
// HACK force reset skeleton pose on Board UUID change
useEffect(() => {
if (!ready) return
@@ -529,15 +575,16 @@ const Character = React.memo(({
console.log(type, id, 'changed boards')
resetPose()
+ if(props.handSkeleton && Object.keys(props.handSkeleton).length > 0)
+ updateSkeletonHand()
}, [boardUid])
useEffect(() => {
if (!ready) return
if (!object.current) return
- // console.log(type, id, 'skeleton')
+ //console.log(type, id, 'skeleton')
updateSkeleton()
-
if(props.handSkeleton && Object.keys(props.handSkeleton).length > 0)
updateSkeletonHand()
}, [props.model, props.skeleton, ready])
@@ -545,11 +592,14 @@ const Character = React.memo(({
useEffect(() => {
+ if (!ready) return
+ if (props.model !== object.current.userData.modelSettings.id) return
if (object.current) {
if (object.current.userData.modelSettings.height) {
let originalHeight = object.current.userData.originalHeight
let scale = props.height / originalHeight
object.current.scale.set( scale, scale, scale )
+ object.current.userData.height = props.height
} else {
object.current.scale.setScalar( props.height )
}
@@ -663,7 +713,7 @@ const Character = React.memo(({
}
}
- setAttachables( null)
+ setAttachables(null)
}
setModelChange(false)
}
diff --git a/src/js/shot-generator/Components.js b/src/js/shot-generator/Components.js
index 4b1f66f0f4..9cc25954bb 100644
--- a/src/js/shot-generator/Components.js
+++ b/src/js/shot-generator/Components.js
@@ -36,6 +36,7 @@ const {
selectObjectToggle,
// createObject,
+ createObjects,
updateObject,
deleteObjects,
groupObjects,
@@ -49,6 +50,8 @@ const {
// loadScene,
// saveScene,
updateCharacterSkeleton,
+ updateCharacterIkSkeleton,
+ getDefaultPosePreset,
setActiveCamera,
setCameraShot,
// resetScene,
@@ -934,14 +937,19 @@ const saveCharacterPresets = state => presetsStorage.saveCharacterPresets({ char
const CharacterPresetsEditor = connect(
state => ({
characterPresets: state.presets.characters,
- models: state.models
+ models: state.models,
+ sceneObjects: getSceneObjects(state),
+ defaultPosePreset: getDefaultPosePreset()
}),
{
updateObject,
- selectCharacterPreset: (sceneObject, characterPresetId, preset) => (dispatch, getState) => {
+ createObjects,
+ updateCharacterIkSkeleton,
+ selectCharacterPreset: (sceneObject, characterPresetId, preset, scene, defaultPosePreset) => (dispatch, getState) => {
dispatch(updateObject(sceneObject.id, {
// set characterPresetId
characterPresetId,
+ posePresetId: null,
// apply preset values to character model
height: preset.state.height,
@@ -959,10 +967,22 @@ const CharacterPresetsEditor = connect(
endomorphic: preset.state.morphTargets.endomorphic
},
- name: sceneObject.name || preset.name
+ name: sceneObject.name || preset.name,
+ skeleton: defaultPosePreset.state.skeleton
}))
+ if(preset.state.attachables) {
+
+ }
+ let character = scene.children.filter(child => child.userData.id === sceneObject.id)[0]
+ let skinnedMesh = character.getObjectByProperty("type", "SkinnedMesh")
+ skinnedMesh.skeleton.pose()
+ character.resetToStandardSkeleton()
+ character.updateWorldMatrix(true, true)
+ let attachables = initializeAttachables(sceneObject, preset)
+ if(attachables)
+ dispatch(createObjects(attachables))
},
- createCharacterPreset: ({ id, name, sceneObject }) => (dispatch, getState) => {
+ createCharacterPreset: ({ id, name, sceneObject, attachables, defaultPosePreset }) => (dispatch, getState) => {
// add the character data to a named preset
let preset = {
id,
@@ -986,6 +1006,12 @@ const CharacterPresetsEditor = connect(
}
}
+ if(attachables.length) {
+ preset.state.attachables = attachables
+ preset.state.presetPosition = { x:sceneObject.x, y: sceneObject.y, z: sceneObject.z },
+ preset.state.presetRotation = sceneObject.rotation
+ }
+
// start the undo-able operation
dispatch(undoGroupStart())
@@ -999,17 +1025,24 @@ const CharacterPresetsEditor = connect(
dispatch(updateObject(sceneObject.id, {
// set the preset id
characterPresetId: id,
+ posePresetId: null,
// use the preset’s name (if none assigned)
- name: sceneObject.name || preset.name
+ name: sceneObject.name || preset.name,
+ skeleton: defaultPosePreset.state.skeleton
}))
// end the undo-able operation
dispatch(undoGroupEnd())
+
+ let attachablesList = initializeAttachables(sceneObject, preset)
+ if(attachablesList)
+ dispatch(createObjects(attachablesList))
}
}
)(
// TODO could optimize by only passing sceneObject properties we actually care about
- React.memo(({ sceneObject, characterPresets, selectCharacterPreset, createCharacterPreset }) => {
+ React.memo(({ sceneObject, characterPresets, selectCharacterPreset, createCharacterPreset, sceneObjects, defaultPosePreset, updateCharacterIkSkeleton }) => {
+ const { scene } = useContext(SceneContext)
const onCreateCharacterPresetClick = event => {
// show a prompt to get the desired preset name
let id = THREE.Math.generateUUID()
@@ -1019,10 +1052,43 @@ const CharacterPresetsEditor = connect(
value: `Character ${shortId(id)}`
}, require('electron').remote.getCurrentWindow()).then(name => {
if (name != null && name != '' && name != ' ') {
+ let character = scene.children.filter(child => child.userData.id === sceneObject.id)[0]
+
+ let attachables = []
+ if(character && character.attachables) {
+ let skinnedMesh = character.getObjectByProperty("type", "SkinnedMesh")
+ skinnedMesh.skeleton.pose()
+ character.resetToStandardSkeleton()
+ character.updateWorldMatrix(true, true)
+ for(let i = 0; i < character.attachables.length; i++) {
+ let attachable = character.attachables[i]
+ let attachableSceneObject = sceneObjects[character.attachables[i].userData.id]
+ let position = character.attachables[i].worldPosition()
+ let rotation = character.attachables[i].worldQuaternion()
+ let matrix = attachable.matrix.clone()
+ matrix.premultiply(attachable.parent.matrixWorld)
+ matrix.decompose(position, rotation, new THREE.Vector3())
+ let euler = new THREE.Euler().setFromQuaternion(rotation)
+ attachables.push({
+ x: position.x,
+ y: position.y,
+ z: position.z,
+ model: attachableSceneObject.model,
+ name: attachableSceneObject.name,
+ size: attachableSceneObject.size,
+ rotation: {x: euler.x, y: euler.y, z: euler.z},
+ bindBone: attachableSceneObject.bindBone,
+
+ })
+ }
+
+ }
createCharacterPreset({
id,
name,
- sceneObject
+ sceneObject,
+ attachables: attachables,
+ defaultPosePreset
})
}
}).catch(err => {
@@ -1033,7 +1099,7 @@ const CharacterPresetsEditor = connect(
const onSelectCharacterPreset = event => {
let characterPresetId = event.target.value
let preset = characterPresets[characterPresetId]
- selectCharacterPreset(sceneObject, characterPresetId, preset)
+ selectCharacterPreset(sceneObject, characterPresetId, preset, scene, defaultPosePreset)
}
return h(
@@ -1063,6 +1129,57 @@ const CharacterPresetsEditor = connect(
})
)
+const initializeAttachables = (sceneObject, preset) => {
+ let attachables = preset.state.attachables
+ if(attachables) {
+ let newAttachables = []
+ let currentParent = new THREE.Group()
+ currentParent.position.set(sceneObject.x, sceneObject.z, sceneObject.y)
+ currentParent.rotation.set(0, sceneObject.rotation, 0 )
+ currentParent.updateMatrixWorld(true)
+ let prevParent = new THREE.Group()
+ let attachableObject = new THREE.Object3D()
+ for(let i = 0; i < attachables.length; i++) {
+ let attachable = attachables[i]
+ prevParent.position.set(preset.state.presetPosition.x, preset.state.presetPosition.z, preset.state.presetPosition.y)
+ prevParent.rotation.set(0, preset.state.presetRotation, 0 )
+ prevParent.updateMatrixWorld(true)
+ let newAttachable = {}
+ newAttachable.attachToId = sceneObject.id
+ newAttachable.id = THREE.Math.generateUUID()
+ newAttachable.loaded = false
+ newAttachable.model = attachable.model
+ newAttachable.name = attachable.name
+ newAttachable.type = attachable.type
+ newAttachable.size = attachable.size
+ newAttachable.type = "attachable"
+ newAttachable.bindBone = attachable.bindBone
+
+ attachableObject.position.set(attachable.x, attachable.y, attachable.z)
+ attachableObject.rotation.set(attachable.rotation.x, attachable.rotation.y, attachable.rotation.z)
+ attachableObject.updateMatrixWorld(true)
+ prevParent.add(attachableObject)
+ attachableObject.applyMatrix(prevParent.getInverseMatrixWorld())
+
+ prevParent.position.copy(currentParent.position)
+ prevParent.rotation.copy(currentParent.rotation)
+ prevParent.updateMatrixWorld(true)
+ attachableObject.updateMatrixWorld(true)
+ let {x, y, z } = attachableObject.worldPosition()
+ newAttachable.x = x
+ newAttachable.y = y
+ newAttachable.z = z
+ let quaternion = attachableObject.worldQuaternion()
+ let euler = new THREE.Euler().setFromQuaternion(quaternion)
+ newAttachable.rotation = { x: euler.x, y: euler.y, z: euler.z }
+ newAttachables.push(newAttachable)
+ }
+ return newAttachables
+ } else {
+ return false
+ }
+}
+
const CHARACTER_HEIGHT_RANGE = {
character: { min: 1.4732, max: 2.1336 },
child: { min: 1.003, max: 1.384 },
diff --git a/src/js/shot-generator/SceneManager.js b/src/js/shot-generator/SceneManager.js
index 168e42f955..9a4dbe43f1 100644
--- a/src/js/shot-generator/SceneManager.js
+++ b/src/js/shot-generator/SceneManager.js
@@ -19,6 +19,7 @@ const {
updateCharacterIkSkeleton,
createPosePreset,
updateWorldEnvironment,
+ getDefaultPosePreset,
getSceneObjects,
getSelections,
@@ -68,7 +69,8 @@ const SceneManager = connect(
devices: state.devices,
meta: state.meta,
// HACK force reset skeleton pose on Board UUID change
- _boardUid: state.board.uid
+ _boardUid: state.board.uid,
+ defaultPosePreset: getDefaultPosePreset()
}),
{
updateObject,
@@ -86,7 +88,7 @@ const SceneManager = connect(
undoGroupEnd
}
)(
- ({ world, sceneObjects, updateObject, selectObject, selectObjectToggle, remoteInput, largeCanvasRef, smallCanvasRef, selections, selectedBone, machineState, transition, animatedUpdate, selectBone, mainViewCamera, updateCharacterSkeleton, updateCharacterIkSkeleton, largeCanvasSize, activeCamera, aspectRatio, devices, meta, _boardUid, updateWorldEnvironment, attachments, undoGroupStart, undoGroupEnd, orthoCamera, camera, setCamera, selectedAttachable, updateObjects, deleteObjects }) => {
+ ({ world, sceneObjects, updateObject, selectObject, selectObjectToggle, remoteInput, largeCanvasRef, smallCanvasRef, selections, selectedBone, machineState, transition, animatedUpdate, selectBone, mainViewCamera, updateCharacterSkeleton, updateCharacterIkSkeleton, largeCanvasSize, activeCamera, aspectRatio, devices, meta, _boardUid, updateWorldEnvironment, attachments, undoGroupStart, undoGroupEnd, orthoCamera, camera, setCamera, selectedAttachable, updateObjects, deleteObjects, defaultPosePreset }) => {
const { scene } = useContext(SceneContext)
// const modelCacheDispatch = useContext(CacheContext)
@@ -642,6 +644,7 @@ const SceneManager = connect(
largeRenderer,
deleteObjects,
updateObjects:updateObjects,
+ defaultPosePreset:defaultPosePreset,
...props
}
]
diff --git a/src/js/shot-generator/attachables/Attachable.js b/src/js/shot-generator/attachables/Attachable.js
index 7256b396b6..8738b0d33b 100644
--- a/src/js/shot-generator/attachables/Attachable.js
+++ b/src/js/shot-generator/attachables/Attachable.js
@@ -4,7 +4,7 @@ window.THREE = window.THREE || THREE
const React = require('react')
const { useRef, useEffect, useState } = React
const ObjectRotationControl = require("../../shared/IK/objects/ObjectRotationControl")
-
+require("../../shared/IK/utils/jsUtils")
// return a group which can report intersections
const groupFactory = () => {
let group = new THREE.Group()
@@ -55,6 +55,7 @@ const Attachable = React.memo(({ scene, id, updateObject, sceneObject, loaded, m
const characterObject = useRef()
const objectRotationControl = useRef();
const [ready, setReady] = useState(false) // ready to load?
+ const [needsInitialization, setInitialization] = useState(false)
const setLoaded = loaded => updateObject(id, { loaded })
const domElement = useRef()
const isBoneSelected = useRef()
@@ -67,11 +68,20 @@ const Attachable = React.memo(({ scene, id, updateObject, sceneObject, loaded, m
container.current.userData.bindedId = props.attachToId
container.current.userData.isRotationEnabled = false
container.current.rebindAttachable = rebindAttachable
+ container.current.saveToStore = saveToStore
isBoneSelected.current = false
return function cleanup () {
- container.current.parent.remove(container.current)
- let indexOf = characterObject.current.attachables.indexOf(container.current)
- characterObject.current.attachables.splice(indexOf, 1)
+ setReady(false)
+ setLoaded(false)
+ if(container.current.parent)
+ container.current.parent.remove(container.current)
+ if( characterObject.current) {
+ characterObject.current.attachables.remove(container.current)
+ characterObject.current = null
+ }
+
+ objectRotationControl.current = null
+ container.current = null
}
}, [])
@@ -81,7 +91,14 @@ const Attachable = React.memo(({ scene, id, updateObject, sceneObject, loaded, m
}, [props.model])
useEffect(() => {
- if (ready) {
+ if (needsInitialization && !loaded) {
+
+ characterObject.current = scene.children.filter(child => child.userData.id === props.attachToId)[0]
+ if(!characterObject.current) {
+ setReady(false)
+ setInitialization(false)
+ return
+ }
container.current.remove(...container.current.children)
let isSkinnedMesh = false
// Traverses passed model and clones it's meshes to container
@@ -108,7 +125,6 @@ const Attachable = React.memo(({ scene, id, updateObject, sceneObject, loaded, m
deleteObjects([id])
}
// Sets up bind bone
- characterObject.current = scene.children.filter(child => child.userData.id === props.attachToId)[0]
let skinnedMesh = characterObject.current.getObjectByProperty("type", "SkinnedMesh")
let skeleton = skinnedMesh.skeleton
let bone = skeleton.getBoneByName(props.bindBone)
@@ -116,12 +132,20 @@ const Attachable = React.memo(({ scene, id, updateObject, sceneObject, loaded, m
container.current.userData.bindBone = props.bindBone
// Applies character scale for case when character is scaled up to 100
- container.current.scale.multiplyScalar(props.size / characterObject.current.scale.x)
+ let scale = props.size / characterObject.current.scale.x
+ container.current.scale.set(scale, scale, scale)
bone.add(container.current)
container.current.updateMatrixWorld(true, true)
// Adds a container of attachable to character if it doesn't exist and adds current attachable
- if(!skinnedMesh.parent.attachables) skinnedMesh.parent.attachables = []
- skinnedMesh.parent.attachables.push(container.current)
+ if(!characterObject.current.attachables) {
+ characterObject.current.attachables = []
+ characterObject.current.attachables.push(container.current)
+ } else {
+ let isAdded = characterObject.current.attachables.some(attachable => attachable.uuid === container.current.uuid)
+ if(!isAdded) {
+ characterObject.current.attachables.push(container.current)
+ }
+ }
// Sets up object rotation control for manipulation of attachale rotation
objectRotationControl.current = new ObjectRotationControl(scene, camera, domElement.current, characterObject.current.uuid)
@@ -137,12 +161,14 @@ const Attachable = React.memo(({ scene, id, updateObject, sceneObject, loaded, m
z : euler.z,
}
} )})
+ setReady(true)
}
- }, [ready])
+ }, [ready, characterObject.current, needsInitialization])
useEffect(() => {
if ( !ready ) return
+ if ( !characterObject.current ) return
// Applies position to container.
// Position for container should always be in world space but container always attached to bone
// We need to take it out of bone space and apply world position
@@ -155,11 +181,12 @@ const Attachable = React.memo(({ scene, id, updateObject, sceneObject, loaded, m
container.current.updateMatrixWorld(true)
container.current.applyMatrix(parentInverseMatrixWorld)
container.current.updateMatrixWorld(true)
- }, [props.x, props.y, props.z, ready])
+ }, [props.x, props.y, props.z, ready, characterObject.current])
useEffect(() => {
if ( !ready ) return
if ( !props.rotation ) return
+ if ( !characterObject.current ) return
characterObject.current.updateWorldMatrix(true, true)
let parentMatrixWorld = container.current.parent.matrixWorld
let parentInverseMatrixWorld = container.current.parent.getInverseMatrixWorld()
@@ -169,10 +196,11 @@ const Attachable = React.memo(({ scene, id, updateObject, sceneObject, loaded, m
container.current.updateMatrixWorld(true)
container.current.applyMatrix(parentInverseMatrixWorld)
container.current.updateMatrixWorld(true)
- }, [props.rotation, ready])
+ }, [props.rotation, ready, characterObject.current])
useEffect(() => {
if(!ready) return
+ if ( !characterObject.current ) return
let outlineParameters = {}
if(isSelected) {
window.addEventListener("keydown", keyDownEvent, false)
@@ -212,38 +240,50 @@ const Attachable = React.memo(({ scene, id, updateObject, sceneObject, loaded, m
useEffect(() => {
let character = scene.children.filter(child => child.userData.id === props.attachToId)[0]
- if(character && modelData){
- setReady(true)
+ if(character && modelData) {
+ setInitialization(true)
+ } else {
+ setReady(false)
+ setInitialization(false)
}
- }, [modelData, ready, scene.children.length])
+
+ }, [modelData, scene.children.length, characterObject.current, needsInitialization ])
useEffect(() => {
if(!ready) return
+ if(!objectRotationControl.current) return
objectRotationControl.current.setCamera(camera)
}, [ready, camera])
useEffect(() => {
if(!ready) return
+ if(! container.current) return
container.current.userData.bindBone = props.bindBone
}, [props.bindBone])
useEffect(() => {
if(!ready) return
+ if(!characterObject.current) return
let scale = container.current.parent.uuid === scene.uuid ? props.size : props.size / characterObject.current.scale.x
container.current.scale.set( scale, scale, scale )
- }, [props.size])
+ }, [props.size, characterObject.current])
const rebindAttachable = (characterScale) => {
+ if(!container.current) return
+
let prevCharacter = characterObject.current
characterObject.current = scene.children.filter(child => child.userData.id === props.attachToId)[0]
-
+ characterObject.current.updateMatrixWorld(true, true)
let skinnedMesh = characterObject.current.getObjectByProperty("type", "SkinnedMesh")
let skeleton = skinnedMesh.skeleton
let bone = skeleton.getBoneByName(props.bindBone)
domElement.current = largeRenderer.current.domElement
container.current.userData.bindBone = props.bindBone
-
+ prevCharacter.updateMatrixWorld(true)
+ prevCharacter.updateWorldMatrix(true, true)
let prevParent = container.current.parent
+ prevCharacter.attachables.remove(container.current)
+
prevParent.remove(container.current)
container.current.applyMatrix(prevCharacter.matrixWorld)
container.current.applyMatrix(characterObject.current.getInverseMatrixWorld())
@@ -251,13 +291,31 @@ const Attachable = React.memo(({ scene, id, updateObject, sceneObject, loaded, m
container.current.scale.set( props.size, props.size, props.size )
container.current.scale.multiplyScalar(1 / characterScale)
container.current.updateWorldMatrix(true, true)
+
// Adds a container of attachable to character if it doesn't exist and adds current attachable
- if(!skinnedMesh.parent.attachables) skinnedMesh.parent.attachables = []
- skinnedMesh.parent.attachables.push(container.current)
+ if(!characterObject.current.attachables) {
+ characterObject.current.attachables = []
+ characterObject.current.attachables.push(container.current)
+ } else {
+ let isAdded = characterObject.current.attachables.some(attachable => attachable.uuid === container.current.uuid)
+ if(!isAdded) {
+ characterObject.current.attachables.push(container.current)
+ }
+ }
let position = container.current.worldPosition()
updateObject(id, { x: position.x, y: position.y, z: position.z})
}
+ const saveToStore = () => {
+ let position = container.current.worldPosition()
+ let quaternion = container.current.worldQuaternion()
+ let matrix = container.current.matrix.clone()
+ matrix.premultiply(container.current.parent.matrixWorld)
+ matrix.decompose(position, quaternion, new THREE.Vector3())
+ let euler = new THREE.Euler().setFromQuaternion(quaternion)
+ updateObject(id, { x: position.x, y: position.y, z: position.z, rotation: {x: euler.x, y: euler.y, z: euler.z}})
+ }
+
const keyDownEvent = (event) => { switchManipulationState(event) }
const switchManipulationState = (event) => {
diff --git a/src/js/shot-generator/attachables/AttachableInfo.js b/src/js/shot-generator/attachables/AttachableInfo.js
index 84557d5a1c..e544ba63c5 100644
--- a/src/js/shot-generator/attachables/AttachableInfo.js
+++ b/src/js/shot-generator/attachables/AttachableInfo.js
@@ -22,10 +22,10 @@ const AttachableInfoItem = React.memo(({
}
const buttonName = useMemo(() => bindBoneName, [bindBoneName])
const attachableName = useMemo(() => {
- return !sceneObject.displayName ? '' : sceneObject.displayName
+ return !sceneObject? '' : !sceneObject.displayName ? '' : sceneObject.displayName
})
- return h(['div.attachable-card',
+ return sceneObject ? h(['div.attachable-card',
['div.attachable-card___title',
['div.attachable-card___label', attachableName],
['a.attachable-card__discard[href=#]', { onClick: () => { onDelete(sceneObject) }}, 'X']
@@ -39,7 +39,7 @@ const AttachableInfoItem = React.memo(({
}, buttonName]
]]],
getNumberSlider(sceneObject)
- ])
+ ]) : []
})
const ListItem = React.memo(({ props, attachable }) => {
diff --git a/src/js/xr/src/SceneManagerXR.js b/src/js/xr/src/SceneManagerXR.js
index db19344b1d..c0b410e8a7 100644
--- a/src/js/xr/src/SceneManagerXR.js
+++ b/src/js/xr/src/SceneManagerXR.js
@@ -29,6 +29,7 @@ const {
// action creators
selectObject,
updateObject,
+ updateCharacterIkSkeleton,
getSelectedAttachable
} = require('../../shared/reducers/shot-generator')
@@ -119,13 +120,14 @@ const SceneContent = connect(
}),
{
selectObject,
- updateObject
+ updateObject,
+ updateCharacterIkSkeleton,
}
)(
({
aspectRatio, sceneObjects, world, activeCamera, selections, models,
- characterIds, modelObjectIds, lightIds, virtualCameraIds, imageIds, attachablesIds, selectedAttachable,
+ characterIds, modelObjectIds, lightIds, virtualCameraIds, imageIds, attachablesIds, selectedAttachable, updateCharacterIkSkeleton, updateObject,
resources, getAsset
}) => {
@@ -619,16 +621,18 @@ const SceneContent = connect(
{
characterIds.map(id =>
- getAsset(getFilepathForModelByType(sceneObjects[id]))
- ?
+ // getAsset(getFilepathForModelByType(sceneObjects[id]))
+ // ?
+
+ isSelected={selections.includes(id)}
+ updateSkeleton= {updateCharacterIkSkeleton} />
- : null
+ // : null
)
}
@@ -670,7 +674,8 @@ const SceneContent = connect(
gltf={getAsset(getFilepathForModelByType(sceneObjects[id]))}
sceneObject={sceneObjects[id]}
isSelected={ selectedAttachable === id ? true : false}
- modelSettings={models[sceneObjects[id].model] || undefined}/>
+ modelSettings={models[sceneObjects[id].model] || undefined}
+ updateObject={updateObject}/>
: null
)
diff --git a/src/js/xr/src/components/Attachable.js b/src/js/xr/src/components/Attachable.js
index e71da8c632..754ffd72b5 100644
--- a/src/js/xr/src/components/Attachable.js
+++ b/src/js/xr/src/components/Attachable.js
@@ -1,5 +1,5 @@
const THREE = require('three')
-const { useMemo, useEffect, useRef } = React = require('react')
+const { useMemo, useEffect, useRef, useState } = React = require('react')
const { useUpdate, useThree } = require('react-three-fiber')
@@ -36,7 +36,7 @@ const meshFactory = source => {
return mesh
}
-const Attachable = React.memo(({ gltf, sceneObject, isSelected }) => {
+const Attachable = React.memo(({ gltf, sceneObject, isSelected, updateObject}) => {
const characterObject = useRef(null)
const { scene } = useThree()
const ref = useUpdate(
@@ -64,11 +64,14 @@ const Attachable = React.memo(({ gltf, sceneObject, isSelected }) => {
return []
}, [sceneObject.model, gltf])
-
useEffect(() => {
ref.current.rebindAttachable = rebindAttachable
+ ref.current.saveToStore = saveToStore
}, [])
+ useEffect(() => {
+ }, [ref.current])
+
useEffect(() => {
traverseMeshMaterials(ref.current, material => {
if (material.emissive) {
@@ -90,10 +93,17 @@ const Attachable = React.memo(({ gltf, sceneObject, isSelected }) => {
let skinnedMesh = characterObject.current.getObjectByProperty("type", "SkinnedMesh")
let bone = skinnedMesh.skeleton.bones.find(b => b.name === sceneObject.bindBone)
bone.add(ref.current)
- if(!characterObject.current.attachables) characterObject.current.attachables = []
- characterObject.current.attachables.push(ref.current)
+ if(!characterObject.current.attachables) {
+ characterObject.current.attachables = []
+ characterObject.current.attachables.push(ref.current)
+ } else {
+ let isAdded = characterObject.current.attachables.some(attachable => attachable.uuid === ref.current.uuid)
+ if(!isAdded) {
+ characterObject.current.attachables.push(ref.current)
+ }
+ }
ref.current.updateMatrixWorld(true)
- }, [scene.children])
+ }, [scene.children.length])
useEffect(() => {
if(!characterObject.current) return
@@ -130,26 +140,38 @@ const Attachable = React.memo(({ gltf, sceneObject, isSelected }) => {
let prevCharacter = characterObject.current
characterObject.current = scene.children[1].children.filter(child => child.userData.id === sceneObject.attachToId)[0]
if(!characterObject.current) return
+
let skinnedMesh = characterObject.current.getObjectByProperty("type", "SkinnedMesh")
let skeleton = skinnedMesh.skeleton
let bone = skeleton.getBoneByName(sceneObject.bindBone)
- ref.current.applyMatrix(prevCharacter.matrixWorld)
- ref.current.applyMatrix(characterObject.current.getInverseMatrixWorld())
bone.add(ref.current)
+ let scale = sceneObject.size / characterObject.current.scale.x
+ ref.current.scale.set(scale, scale, scale)
ref.current.updateWorldMatrix(true, true)
- if(!ref.current.children.length) {
- gltf.scene.traverse(child => {
- if (child.isMesh) {
- let mesh = meshFactory(child)
- mesh.userData.type === 'attachable'
- ref.current.add(mesh)
- }
- })
- }
// Adds a ref of attachable to character if it doesn't exist and adds current attachable
- if(!characterObject.current.attachables) characterObject.current.attachables = []
- characterObject.current.attachables.push(ref.current)
+ if(!characterObject.current.attachables) {
+ characterObject.current.attachables = []
+ characterObject.current.attachables.push(ref.current)
+ } else {
+ let isAdded = characterObject.current.attachables.some(attachable => attachable.uuid === ref.current.uuid)
+ if(!isAdded) {
+ characterObject.current.attachables.push(ref.current)
+ }
+ }
+
+ saveToStore()
+ }
+
+ const saveToStore = () => {
+ let position = ref.current.worldPosition()// new THREE.Vector3()
+ let quaternion = ref.current.worldQuaternion()
+ let matrix = ref.current.matrix.clone()
+ matrix.premultiply(ref.current.parent.matrixWorld)
+ matrix.decompose(position, quaternion, new THREE.Vector3())
+ let rot = new THREE.Euler().setFromQuaternion(quaternion, 'XYZ')
+ updateObject(sceneObject.id, { x: position.x, y: position.y, z: position.z,
+ rotation: {x: rot.x, y: rot.y, z: rot.z}})
}
return {
+const Character = React.memo(({ gltf, sceneObject, modelSettings, isSelected, updateSkeleton }) => {
+ const [ready, setReady] = useState(false)
+ const attachablesList = useRef([])
const ref = useUpdate(
self => {
self.traverse(child => child.layers.enable(VirtualCamera.VIRTUAL_CAMERA_LAYER))
}
)
- useEffect(() => {
- return () => {
- if(ref.current.attachables && attachablesList.length ) {
- attachablesList = ref.current.attachables.concat([])
- for(let i = 0; i < attachablesList.length; i++) {
- if(attachablesList[i].parent)
- attachablesList[i].parent.remove(attachablesList[i])
- }
- isUnmounted[sceneObject.id] = true
- }
- }
- }, [])
-
- useEffect(() => {
-
- }, [ref.current])
-
const [skeleton, lod, originalSkeleton, armature, originalHeight] = useMemo(
() => {
+ if(ref.current && ref.current.attachables) {
+ attachablesList.current = ref.current.attachables.concat([])
+ for(let i = 0; i < attachablesList.current.length; i++) {
+ if(attachablesList.current[i].parent) {
+ attachablesList.current[i].parent.remove(attachablesList.current[i])
+ }
+ }
+ }
+ if(!gltf) {
+ setReady(false)
+ return [null, null, null, null, null]
+ }
let lod = new THREE.LOD()
let { scene } = cloneGltf(gltf)
@@ -107,7 +101,27 @@ const Character = React.memo(({ gltf, sceneObject, modelSettings, isSelected })
let bbox = new THREE.Box3().setFromObject(lod)
originalHeight = bbox.max.y - bbox.min.y
}
- isClonned[sceneObject.id] = true
+ // We need to override skeleton when model is changed because in store skeleton position is still has values for prevModel
+ let newBones = []
+ for(let i = 0; i < skeleton.bones.length; i++) {
+ let bone = skeleton.bones[i]
+ let position = bone.position
+ let rotation = sceneObject.skeleton[bone.name] ? sceneObject.skeleton[bone.name].rotation : bone.rotation
+ newBones.push({
+ name: bone.name,
+ position: {
+ x: position.x,
+ y: position.y,
+ z: position.z
+ },
+ rotation: {
+ x: rotation.x,
+ y: rotation.y,
+ z: rotation.z
+ }
+ })
+ }
+ setReady(true)
return [skeleton, lod, originalSkeleton, armature, originalHeight]
},
[gltf]
@@ -116,23 +130,22 @@ const Character = React.memo(({ gltf, sceneObject, modelSettings, isSelected })
useEffect(() => {
if(!lod) return
if(!ref.current) return
- if(attachablesList.length && isUnmounted[sceneObject.id] && isClonned[sceneObject.id]) {
+ if(attachablesList.current.length) {
ref.current.attachables = []
- for(let i = 0; i < attachablesList.length; i++) {
- attachablesList[i].rebindAttachable(sceneObject.height / ref.current.userData.originalHeight)
+ // Updating skeleton to original bones position
+ for (let i = 0; i < skeleton.bones.length; i++) {
+ let bone = skeleton.bones[i]
+ if(!bone) continue
+ let originalbone = originalSkeleton.bones[i]
+ bone.position.copy(originalbone.position)
+ bone.updateMatrixWorld()
}
- attachablesList = []
- isUnmounted[sceneObject.id] = false
- isClonned[sceneObject.id] = false
- }
- return () => {
- if(ref.current.attachables && ref.current) {
- attachablesList = ref.current.attachables.concat([])
- isUnmounted[sceneObject.id] = true
+ for(let i = 0; i < attachablesList.current.length; i++) {
+ attachablesList.current[i].rebindAttachable(sceneObject.height / ref.current.userData.originalHeight)
}
+ attachablesList.current = []
}
- }, [ref.current, lod, attachablesList.length])
-
+ }, [ref.current, attachablesList.current.length, lod, ready])
useMemo(() => {
if (!skeleton) return
@@ -140,6 +153,7 @@ const Character = React.memo(({ gltf, sceneObject, modelSettings, isSelected })
let hasModifications = Object.values(sceneObject.skeleton).length > 0
if (hasModifications) {
+ // let position = new THREE.Vector3()
// go through all the bones in the skeleton
for (bone of skeleton.bones) {
// if user data exists for a bone, use it
@@ -154,17 +168,16 @@ const Character = React.memo(({ gltf, sceneObject, modelSettings, isSelected })
if (bone.rotation.equals(state.rotation) == false) {
// rotate the bone
bone.rotation.setFromVector3(state.rotation)
- if(state.position)
- bone.position.set(state.position.x, state.position.y, state.position.z)
// and update
bone.updateMatrixWorld()
}
+
}
} else {
// reset the pose
skeleton.pose()
}
- }, [skeleton, sceneObject.skeleton])
+ }, [skeleton, sceneObject.skeleton, ready])
useMemo(() => {
if (!skeleton) return
@@ -183,15 +196,16 @@ const Character = React.memo(({ gltf, sceneObject, modelSettings, isSelected })
bone.rotation.z = handBone.rotation.z
}
}
- }, [skeleton, sceneObject.skeleton, sceneObject.handSkeleton])
+ }, [skeleton, sceneObject.skeleton, sceneObject.handSkeleton, ready])
const bodyScale = useMemo(
() => sceneObject.height / originalHeight,
- [sceneObject.height]
+ [sceneObject.height, ready]
)
// headScale (0.8...1.2)
useMemo(() => {
+ if(!skeleton) return
let headBone = skeleton.getBoneByName('Head')
if (headBone) {
// in prior versions, the head was scaled proportionally to the body
@@ -200,15 +214,17 @@ const Character = React.memo(({ gltf, sceneObject, modelSettings, isSelected })
// now we just use the user's percentage value directly
headBone.scale.setScalar(sceneObject.headScale)
}
- }, [skeleton, sceneObject.headScale])
+ }, [skeleton, sceneObject.headScale, ready])
useMemo(() => {
+ if(!lod) return
lod.children.forEach(skinnedMesh => {
skinnedMesh.material.emissive.set(sceneObject.tintColor)
})
- }, [sceneObject.tintColor])
+ }, [sceneObject.tintColor, ready])
useMemo(() => {
+ if(!lod) return
if (modelSettings && modelSettings.validMorphTargets && modelSettings.validMorphTargets.length) {
lod.children.forEach(skinnedMesh => {
skinnedMesh.material.morphTargets = skinnedMesh.material.morphNormals = true
@@ -221,7 +237,7 @@ const Character = React.memo(({ gltf, sceneObject, modelSettings, isSelected })
skinnedMesh.material.morphTargets = skinnedMesh.material.morphNormals = false
})
}
- }, [modelSettings, sceneObject.morphTargets])
+ }, [modelSettings, sceneObject.morphTargets, ready])
useMemo(() => {
if(!ref.current) return
@@ -238,10 +254,9 @@ const Character = React.memo(({ gltf, sceneObject, modelSettings, isSelected })
ref.current.remove(BonesHelper.getInstance())
ref.current.remove(IKHelper.getInstance())
}
- }, [ref.current, isSelected])
+ }, [ref.current, isSelected, ready])
- return lod
- ? null : null}
@@ -255,10 +270,9 @@ const Character = React.memo(({ gltf, sceneObject, modelSettings, isSelected })
rotation={[0, sceneObject.rotation, 0]}
scale={[bodyScale, bodyScale, bodyScale]}
>
-
-
+
+
- : null
})
module.exports = Character
diff --git a/src/js/xr/src/use-interactions-manager.js b/src/js/xr/src/use-interactions-manager.js
index 11b1c708fe..fe7d213477 100644
--- a/src/js/xr/src/use-interactions-manager.js
+++ b/src/js/xr/src/use-interactions-manager.js
@@ -555,7 +555,6 @@ const useInteractionsManager = ({
// include all interactables (Model Object, Character, Virtual Camera, etc)
let list = scene.__interaction.filter(o => o.userData.type !== 'ui')
-
// setup the GPU picker
getGpuPicker().setupScene(list, getExcludeList(scene))