From 3edabb1a71b8c55f5b9e8ac3058f44ef2d3d0f20 Mon Sep 17 00:00:00 2001 From: PlumCantaloupe Date: Sat, 1 Apr 2023 20:36:37 -0400 Subject: [PATCH 01/16] Adding some robot control to network test world --- src/components/circles-button.js | 13 +- src/worlds/Network_TestSpace/index.html | 52 +++++++- .../scripts/robot-controls.js | 115 ++++++++++++++++++ .../Network_TestSpace/scripts/script.js | 18 --- 4 files changed, 171 insertions(+), 27 deletions(-) create mode 100644 src/worlds/Network_TestSpace/scripts/robot-controls.js delete mode 100644 src/worlds/Network_TestSpace/scripts/script.js diff --git a/src/components/circles-button.js b/src/components/circles-button.js index b51dd40f..5862ec78 100644 --- a/src/components/circles-button.js +++ b/src/components/circles-button.js @@ -2,11 +2,12 @@ AFRAME.registerComponent('circles-button', { schema: { - type: {type:'string', default:'box', oneOf:['box', 'cylinder']}, - button_color: {type:'color', default:'rgb(255, 100, 100)'}, + type: {type:'string', default:'box', oneOf:['box', 'cylinder']}, + button_color: {type:'color', default:'rgb(255, 100, 100)'}, button_color_hover: {type:'color', default:'rgb(255, 0, 0)'}, - pedastal_color: {type:'color', default:'rgb(255, 255, 255)'}, - diameter: {type:'number', default:0.5} + pedastal_color: {type:'color', default:'rgb(255, 255, 255)'}, + diameter: {type:'number', default:0.5}, + pedastal_visible: {type:'boolean', default:true} }, init: function () { const CONTEXT_AF = this; @@ -71,5 +72,9 @@ AFRAME.registerComponent('circles-button', { CONTEXT_AF.button.setAttribute('scale', {x:data.diameter, y:1, z:data.diameter}); CONTEXT_AF.button_pedastal.setAttribute('scale', {x:data.diameter, y:1, z:data.diameter}); } + + if ( (oldData.pedastal_visible !== data.pedastal_visible) && (data.pedastal_visible !== '') ) { + CONTEXT_AF.button_pedastal.setAttribute('visible', data.pedastal_visible); + } } }); \ No newline at end of file diff --git a/src/worlds/Network_TestSpace/index.html b/src/worlds/Network_TestSpace/index.html index ec3b9848..41bfe7c6 100644 --- a/src/worlds/Network_TestSpace/index.html +++ b/src/worlds/Network_TestSpace/index.html @@ -2,13 +2,16 @@ + + + - + @@ -23,15 +26,15 @@ - + shadow="cast:true; receive:true;"> --> - + circles-pickup-networked> --> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/worlds/Network_TestSpace/scripts/robot-controls.js b/src/worlds/Network_TestSpace/scripts/robot-controls.js new file mode 100644 index 00000000..c57d4ca4 --- /dev/null +++ b/src/worlds/Network_TestSpace/scripts/robot-controls.js @@ -0,0 +1,115 @@ + +AFRAME.registerComponent('robot-controls', { + schema: { + movementTimeMS: {type:'number', default:500}, //ms + rotationIncrementDeg: {type:'number', default:30}, //deg + }, + init() { + const CONTEXT_AF = this; + const DATA = CONTEXT_AF.data; + + CONTEXT_AF.robot_arm = document.querySelector('#robot_arm'); + CONTEXT_AF.robotShoulder_leftright = robot_arm.querySelector('.pivot.shoulder.leftright'); + CONTEXT_AF.robotShoulder_overback = robot_arm.querySelector('.pivot.shoulder.overback'); + CONTEXT_AF.robotElbow_leftright = robot_arm.querySelector('.pivot.elbow.leftright'); + CONTEXT_AF.robotElbow_overback = robot_arm.querySelector('.pivot.elbow.overback'); + + //get elem refs + CONTEXT_AF.robot_controls = CONTEXT_AF.el; + CONTEXT_AF.shoulderControls = { + overButton: CONTEXT_AF.el.querySelector('.shoulder_controls > .over_button'), + backButton: CONTEXT_AF.el.querySelector('.shoulder_controls > .back_button'), + leftButton: CONTEXT_AF.el.querySelector('.shoulder_controls > .left_button'), + rightButton: CONTEXT_AF.el.querySelector('.shoulder_controls > .right_button') + }; + CONTEXT_AF.elbowControls = { + overButton: CONTEXT_AF.el.querySelector('.elbow_controls > .over_button'), + backButton: CONTEXT_AF.el.querySelector('.elbow_controls > .back_button'), + leftButton: CONTEXT_AF.el.querySelector('.elbow_controls > .left_button'), + rightButton: CONTEXT_AF.el.querySelector('.elbow_controls > .right_button') + }; + + const allRobotPivots = CONTEXT_AF.robot_arm.querySelectorAll('.pivot'); + const allControlButtons = CONTEXT_AF.robot_controls.querySelectorAll('.button'); + + //make sure button doesn't work until animation is complete + CONTEXT_AF.animInProgress = false; + + CONTEXT_AF.animBeginFunc = function(e) { + console.log('animationbegin', e.detail); + CONTEXT_AF.animInProgress = true; + }; + + CONTEXT_AF.animEndFunc = function(e) { + console.log('animationcomplete', e.detail); + CONTEXT_AF.animInProgress = false; + }; + + CONTEXT_AF.ROT_TYPE = { + OVER_BACK: 'overback', + LEFT_RIGHT: 'leftright', + }; + + //add listeners to all pivots + allRobotPivots.array.forEach(p => { + p.addEventListener('animationbegin', CONTEXT_AF.animBeginFunc); + p.addEventListener('animationcomplete', CONTEXT_AF.animEndFunc); + }); + + //add listeners to all buttons + allControlButtons.array.forEach(b => { + if (b.classList.contains('shoulder')) { + + } + else if (b.classList.contains('elbow')) { + + } + }); + + //jointType: ['shoulder', 'elbow'] + //rotType: ['overback', 'leftright'] + CONTEXT_AF.rotateElem = function(elem, rotType, toDeg) { + console.log('click', elem.id); + if (CONTEXT_AF.animInProgress === false) { + console.log('moving'); + let rotAmt = null; + if (rotType === CONTEXT_AF.ROT_TYPE.OVER_BACK) { + rotAmt = {x:THREE.MathUtils.radToDeg(elem.object3D.rotation.x) + toDeg, y:0, z:0}; + } + else { + rotAmt = {x:0, y:THREE.MathUtils.radToDeg(elem.object3D.rotation.y) + toDeg, z:0}; + } + elem.setAttribute('animation__robot', { property:'rotation', + dur:CONTEXT_AF.data.movementTimeMS, + to:rotAmt, + easing:'linear' + }); + } + else { + console.log('not moving'); + } + } + + //animate + CONTEXT_AF.shoulderControls.overButton.addEventListener('click', function() { + CONTEXT_AF.rotateElem(CONTEXT_AF.robotShoulder_overback, CONTEXT_AF.ROT_TYPE.OVER_BACK, CONTEXT_AF.data.rotationIncrementDeg); + }); + CONTEXT_AF.shoulderControls.backButton.addEventListener('click', function() { + CONTEXT_AF.rotateElem(CONTEXT_AF.robotShoulder_overback, CONTEXT_AF.ROT_TYPE.OVER_BACK, -CONTEXT_AF.data.rotationIncrementDeg); + }); + CONTEXT_AF.shoulderControls.leftButton.addEventListener('click', function() { + CONTEXT_AF.rotateElem(CONTEXT_AF.robotShoulder_leftright, CONTEXT_AF.ROT_TYPE.LEFT_RIGHT, -CONTEXT_AF.data.rotationIncrementDeg); + }); + CONTEXT_AF.shoulderControls.rightButton.addEventListener('click', function() { + CONTEXT_AF.rotateElem(CONTEXT_AF.robotShoulder_leftright, CONTEXT_AF.ROT_TYPE.LEFT_RIGHT, CONTEXT_AF.data.rotationIncrementDeg); + }); + }, + update() { + const CONTEXT_AF = this; + const data = this.data; + + if (Object.keys(data).length === 0) { return; } // No need to update. as nothing here yet + }, + remove() {}, + tick(time, timeDelta) {}, +}); \ No newline at end of file diff --git a/src/worlds/Network_TestSpace/scripts/script.js b/src/worlds/Network_TestSpace/scripts/script.js deleted file mode 100644 index 6aba3e04..00000000 --- a/src/worlds/Network_TestSpace/scripts/script.js +++ /dev/null @@ -1,18 +0,0 @@ - -AFRAME.registerComponent('test', { - schema: { - particles: {type:'number', default:1.0}, - }, - init() { - const CONTEXT_AF = this; - const DATA = CONTEXT_AF.data; - }, - update() { - const CONTEXT_AF = this; - const data = this.data; - - if (Object.keys(data).length === 0) { return; } // No need to update. as nothing here yet - }, - remove() {}, - tick(time, timeDelta) {}, -}); \ No newline at end of file From 81b4fa6ecf8b24451394539a0f6890890561140b Mon Sep 17 00:00:00 2001 From: PlumCantaloupe Date: Mon, 3 Apr 2023 14:36:41 -0400 Subject: [PATCH 02/16] adding in robot arm --- src/worlds/Network_TestSpace/index.html | 18 +- .../scripts/robot-controls.js | 182 ++++++++++-------- 2 files changed, 108 insertions(+), 92 deletions(-) diff --git a/src/worlds/Network_TestSpace/index.html b/src/worlds/Network_TestSpace/index.html index 41bfe7c6..b3ed1121 100644 --- a/src/worlds/Network_TestSpace/index.html +++ b/src/worlds/Network_TestSpace/index.html @@ -11,7 +11,7 @@ - + @@ -49,13 +49,13 @@ - - + + - - + + @@ -72,13 +72,17 @@ - + + + + + - + diff --git a/src/worlds/Network_TestSpace/scripts/robot-controls.js b/src/worlds/Network_TestSpace/scripts/robot-controls.js index c57d4ca4..90233ccc 100644 --- a/src/worlds/Network_TestSpace/scripts/robot-controls.js +++ b/src/worlds/Network_TestSpace/scripts/robot-controls.js @@ -1,115 +1,127 @@ AFRAME.registerComponent('robot-controls', { schema: { - movementTimeMS: {type:'number', default:500}, //ms - rotationIncrementDeg: {type:'number', default:30}, //deg + rotationIncrementRad: {type:'number', default:0.03}, //radians + rotationIntervalMS: {type:'number', default:20}, //ms (how fast this moves on button press) }, init() { const CONTEXT_AF = this; - const DATA = CONTEXT_AF.data; + CONTEXT_AF.rotateShoulder_OverBack_Amt = 0.0; + CONTEXT_AF.rotateShoulder_LeftRight_Amt = 0.0; + CONTEXT_AF.rotateElbow_OverBack_Amt = 0.0; + CONTEXT_AF.rotateElbow_LeftRight_Amt = 0.0; + + //jointType: ['shoulder', 'elbow'] + //rotType: ['overback', 'leftright'] + CONTEXT_AF.rotateElem = function(jointType, rotType, angRad) { + if (rotType === CONTEXT_AF.ROT_TYPE.OVER_BACK) { + if (jointType === CONTEXT_AF.JOINT_TYPE.SHOULDER) { + CONTEXT_AF.rotateShoulder_OverBack_Amt = angRad; + } + else if (jointType === CONTEXT_AF.JOINT_TYPE.ELBOW) { + CONTEXT_AF.rotateElbow_OverBack_Amt = angRad; + } + } + else if (rotType === CONTEXT_AF.ROT_TYPE.LEFT_RIGHT) { + if (jointType === CONTEXT_AF.JOINT_TYPE.SHOULDER) { + CONTEXT_AF.rotateShoulder_LeftRight_Amt = angRad; + } + else if (jointType === CONTEXT_AF.JOINT_TYPE.ELBOW) { + CONTEXT_AF.rotateElbow_LeftRight_Amt = angRad; + } + } + } CONTEXT_AF.robot_arm = document.querySelector('#robot_arm'); - CONTEXT_AF.robotShoulder_leftright = robot_arm.querySelector('.pivot.shoulder.leftright'); - CONTEXT_AF.robotShoulder_overback = robot_arm.querySelector('.pivot.shoulder.overback'); - CONTEXT_AF.robotElbow_leftright = robot_arm.querySelector('.pivot.elbow.leftright'); - CONTEXT_AF.robotElbow_overback = robot_arm.querySelector('.pivot.elbow.overback'); + CONTEXT_AF.robotShoulder_leftright = robot_arm.querySelector('.pivot.shoulder.left.right'); + CONTEXT_AF.robotShoulder_overback = robot_arm.querySelector('.pivot.shoulder.over.back'); + CONTEXT_AF.robotElbow_leftright = robot_arm.querySelector('.pivot.elbow.left.right'); + CONTEXT_AF.robotElbow_overback = robot_arm.querySelector('.pivot.elbow.over.back'); //get elem refs - CONTEXT_AF.robot_controls = CONTEXT_AF.el; + CONTEXT_AF.robot_controls = document.querySelector('#robot_controls'); CONTEXT_AF.shoulderControls = { - overButton: CONTEXT_AF.el.querySelector('.shoulder_controls > .over_button'), - backButton: CONTEXT_AF.el.querySelector('.shoulder_controls > .back_button'), - leftButton: CONTEXT_AF.el.querySelector('.shoulder_controls > .left_button'), - rightButton: CONTEXT_AF.el.querySelector('.shoulder_controls > .right_button') + overButton: CONTEXT_AF.robot_controls.querySelector('.shoulder > .over'), + backButton: CONTEXT_AF.robot_controls.querySelector('.shoulder > .back'), + leftButton: CONTEXT_AF.robot_controls.querySelector('.shoulder > .left'), + rightButton: CONTEXT_AF.robot_controls.querySelector('.shoulder > .right') }; CONTEXT_AF.elbowControls = { - overButton: CONTEXT_AF.el.querySelector('.elbow_controls > .over_button'), - backButton: CONTEXT_AF.el.querySelector('.elbow_controls > .back_button'), - leftButton: CONTEXT_AF.el.querySelector('.elbow_controls > .left_button'), - rightButton: CONTEXT_AF.el.querySelector('.elbow_controls > .right_button') - }; - - const allRobotPivots = CONTEXT_AF.robot_arm.querySelectorAll('.pivot'); - const allControlButtons = CONTEXT_AF.robot_controls.querySelectorAll('.button'); - - //make sure button doesn't work until animation is complete - CONTEXT_AF.animInProgress = false; - - CONTEXT_AF.animBeginFunc = function(e) { - console.log('animationbegin', e.detail); - CONTEXT_AF.animInProgress = true; + overButton: CONTEXT_AF.robot_controls.querySelector('.elbow > .over'), + backButton: CONTEXT_AF.robot_controls.querySelector('.elbow > .back'), + leftButton: CONTEXT_AF.robot_controls.querySelector('.elbow > .left'), + rightButton: CONTEXT_AF.robot_controls.querySelector('.elbow > .right') }; - CONTEXT_AF.animEndFunc = function(e) { - console.log('animationcomplete', e.detail); - CONTEXT_AF.animInProgress = false; - }; + CONTEXT_AF.shoulderDisplayElem = CONTEXT_AF.robot_controls.querySelector('.display_shoulder'); + CONTEXT_AF.elbowDisplayElem = CONTEXT_AF.robot_controls.querySelector('.display_elbow'); CONTEXT_AF.ROT_TYPE = { OVER_BACK: 'overback', LEFT_RIGHT: 'leftright', }; - //add listeners to all pivots - allRobotPivots.array.forEach(p => { - p.addEventListener('animationbegin', CONTEXT_AF.animBeginFunc); - p.addEventListener('animationcomplete', CONTEXT_AF.animEndFunc); - }); + CONTEXT_AF.JOINT_TYPE = { + SHOULDER: 'shoulder', + ELBOW: 'elbow', + }; - //add listeners to all buttons - allControlButtons.array.forEach(b => { - if (b.classList.contains('shoulder')) { + //animate shoulder + CONTEXT_AF.shoulderControls.overButton.addEventListener('mousedown', function() { CONTEXT_AF.rotateElem(CONTEXT_AF.JOINT_TYPE.SHOULDER, CONTEXT_AF.ROT_TYPE.OVER_BACK, CONTEXT_AF.data.rotationIncrementRad); }); + CONTEXT_AF.shoulderControls.overButton.addEventListener('mouseup', function() { CONTEXT_AF.rotateElem(CONTEXT_AF.JOINT_TYPE.SHOULDER, CONTEXT_AF.ROT_TYPE.OVER_BACK, 0.0); }); + + CONTEXT_AF.shoulderControls.backButton.addEventListener('mousedown', function() { CONTEXT_AF.rotateElem(CONTEXT_AF.JOINT_TYPE.SHOULDER, CONTEXT_AF.ROT_TYPE.OVER_BACK, -CONTEXT_AF.data.rotationIncrementRad); }); + CONTEXT_AF.shoulderControls.backButton.addEventListener('mouseup', function() { CONTEXT_AF.rotateElem(CONTEXT_AF.JOINT_TYPE.SHOULDER, CONTEXT_AF.ROT_TYPE.OVER_BACK, 0.0); }); + + CONTEXT_AF.shoulderControls.leftButton.addEventListener('mousedown', function() { CONTEXT_AF.rotateElem(CONTEXT_AF.JOINT_TYPE.SHOULDER, CONTEXT_AF.ROT_TYPE.LEFT_RIGHT, -CONTEXT_AF.data.rotationIncrementRad); }); + CONTEXT_AF.shoulderControls.leftButton.addEventListener('mouseup', function() { CONTEXT_AF.rotateElem(CONTEXT_AF.JOINT_TYPE.SHOULDER, CONTEXT_AF.ROT_TYPE.LEFT_RIGHT, 0.0); }); + + CONTEXT_AF.shoulderControls.rightButton.addEventListener('mousedown', function() { CONTEXT_AF.rotateElem(CONTEXT_AF.JOINT_TYPE.SHOULDER, CONTEXT_AF.ROT_TYPE.LEFT_RIGHT, CONTEXT_AF.data.rotationIncrementRad); }); + CONTEXT_AF.shoulderControls.rightButton.addEventListener('mouseup', function() { CONTEXT_AF.rotateElem(CONTEXT_AF.JOINT_TYPE.SHOULDER, CONTEXT_AF.ROT_TYPE.LEFT_RIGHT, 0.0); }); - } - else if (b.classList.contains('elbow')) { + //animate elbow + CONTEXT_AF.elbowControls.overButton.addEventListener('mousedown', function() { CONTEXT_AF.rotateElem(CONTEXT_AF.JOINT_TYPE.ELBOW, CONTEXT_AF.ROT_TYPE.OVER_BACK, CONTEXT_AF.data.rotationIncrementRad); }); + CONTEXT_AF.elbowControls.overButton.addEventListener('mouseup', function() { CONTEXT_AF.rotateElem(CONTEXT_AF.JOINT_TYPE.ELBOW, CONTEXT_AF.ROT_TYPE.OVER_BACK, 0.0); }); + + CONTEXT_AF.elbowControls.backButton.addEventListener('mousedown', function() { CONTEXT_AF.rotateElem(CONTEXT_AF.JOINT_TYPE.ELBOW, CONTEXT_AF.ROT_TYPE.OVER_BACK, -CONTEXT_AF.data.rotationIncrementRad); }); + CONTEXT_AF.elbowControls.backButton.addEventListener('mouseup', function() { CONTEXT_AF.rotateElem(CONTEXT_AF.JOINT_TYPE.ELBOW, CONTEXT_AF.ROT_TYPE.OVER_BACK, 0.0); }); + + CONTEXT_AF.elbowControls.leftButton.addEventListener('mousedown', function() { CONTEXT_AF.rotateElem(CONTEXT_AF.JOINT_TYPE.ELBOW, CONTEXT_AF.ROT_TYPE.LEFT_RIGHT, -CONTEXT_AF.data.rotationIncrementRad); }); + CONTEXT_AF.elbowControls.leftButton.addEventListener('mouseup', function() { CONTEXT_AF.rotateElem(CONTEXT_AF.JOINT_TYPE.ELBOW, CONTEXT_AF.ROT_TYPE.LEFT_RIGHT, 0.0); }); + + CONTEXT_AF.elbowControls.rightButton.addEventListener('mousedown', function() { CONTEXT_AF.rotateElem(CONTEXT_AF.JOINT_TYPE.ELBOW, CONTEXT_AF.ROT_TYPE.LEFT_RIGHT, CONTEXT_AF.data.rotationIncrementRad); }); + CONTEXT_AF.elbowControls.rightButton.addEventListener('mouseup', function() { CONTEXT_AF.rotateElem(CONTEXT_AF.JOINT_TYPE.ELBOW, CONTEXT_AF.ROT_TYPE.LEFT_RIGHT, 0.0); }); + }, + update(oldData) { + const CONTEXT_AF = this; + const data = this.data; - } - }); + if (Object.keys(data).length === 0) { return; } // No need to update. as nothing here yet - //jointType: ['shoulder', 'elbow'] - //rotType: ['overback', 'leftright'] - CONTEXT_AF.rotateElem = function(elem, rotType, toDeg) { - console.log('click', elem.id); - if (CONTEXT_AF.animInProgress === false) { - console.log('moving'); - let rotAmt = null; - if (rotType === CONTEXT_AF.ROT_TYPE.OVER_BACK) { - rotAmt = {x:THREE.MathUtils.radToDeg(elem.object3D.rotation.x) + toDeg, y:0, z:0}; - } - else { - rotAmt = {x:0, y:THREE.MathUtils.radToDeg(elem.object3D.rotation.y) + toDeg, z:0}; - } - elem.setAttribute('animation__robot', { property:'rotation', - dur:CONTEXT_AF.data.movementTimeMS, - to:rotAmt, - easing:'linear' - }); - } - else { - console.log('not moving'); - } + if ( (oldData.rotationIntervalMS !== data.rotationIntervalMS) && (data.rotationIntervalMS !== '') ) { + // Set up the tick throttling. + CONTEXT_AF.tick = AFRAME.utils.throttleTick(CONTEXT_AF.tick, data.rotationIntervalMS, CONTEXT_AF); } - - //animate - CONTEXT_AF.shoulderControls.overButton.addEventListener('click', function() { - CONTEXT_AF.rotateElem(CONTEXT_AF.robotShoulder_overback, CONTEXT_AF.ROT_TYPE.OVER_BACK, CONTEXT_AF.data.rotationIncrementDeg); - }); - CONTEXT_AF.shoulderControls.backButton.addEventListener('click', function() { - CONTEXT_AF.rotateElem(CONTEXT_AF.robotShoulder_overback, CONTEXT_AF.ROT_TYPE.OVER_BACK, -CONTEXT_AF.data.rotationIncrementDeg); - }); - CONTEXT_AF.shoulderControls.leftButton.addEventListener('click', function() { - CONTEXT_AF.rotateElem(CONTEXT_AF.robotShoulder_leftright, CONTEXT_AF.ROT_TYPE.LEFT_RIGHT, -CONTEXT_AF.data.rotationIncrementDeg); - }); - CONTEXT_AF.shoulderControls.rightButton.addEventListener('click', function() { - CONTEXT_AF.rotateElem(CONTEXT_AF.robotShoulder_leftright, CONTEXT_AF.ROT_TYPE.LEFT_RIGHT, CONTEXT_AF.data.rotationIncrementDeg); - }); }, - update() { - const CONTEXT_AF = this; - const data = this.data; + // remove() {}, + tick(time, timeDelta) { + //adjust rotation + this.robotShoulder_leftright.object3D.rotation.y += this.rotateShoulder_LeftRight_Amt; + this.robotShoulder_overback.object3D.rotation.x += this.rotateShoulder_OverBack_Amt; + this.robotElbow_leftright.object3D.rotation.y += this.rotateElbow_LeftRight_Amt; + this.robotElbow_overback.object3D.rotation.x += this.rotateElbow_OverBack_Amt; + + //adjust displays + //Elbow\n\nX-Rot:00\nY-Rot:00 + if (this.shoulderDisplayElem) { + this.shoulderDisplayElem.setAttribute( 'text', {value:'Shoulder\n\nX-Rot: ' + Math.floor(THREE.MathUtils.radToDeg(this.robotShoulder_overback.object3D.rotation.x)) + + '\nY-Rot: ' + Math.floor(THREE.MathUtils.radToDeg(this.robotShoulder_leftright.object3D.rotation.y))}); + } + + if (this.elbowDisplayElem) { + this.elbowDisplayElem.setAttribute( 'text', {value:'Elbow\n\nX-Rot: ' + Math.floor(THREE.MathUtils.radToDeg(this.robotElbow_overback.object3D.rotation.x)) + + '\nY-Rot: ' + Math.floor(THREE.MathUtils.radToDeg(this.robotElbow_leftright.object3D.rotation.y))}); + } - if (Object.keys(data).length === 0) { return; } // No need to update. as nothing here yet }, - remove() {}, - tick(time, timeDelta) {}, }); \ No newline at end of file From cfe396ebbaca1a5b7bbeaefcb4187bc9d917da68 Mon Sep 17 00:00:00 2001 From: PlumCantaloupe Date: Mon, 3 Apr 2023 14:51:56 -0400 Subject: [PATCH 03/16] formatting --- src/components/circles-label.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/circles-label.js b/src/components/circles-label.js index 6580cdf7..779f60da 100644 --- a/src/components/circles-label.js +++ b/src/components/circles-label.js @@ -4,7 +4,7 @@ AFRAME.registerComponent('circles-label', { schema: { text: {type:'string', default:'label_text'}, offset: {type:'vec3', default:{x:0.0, y:0.0, z:0.0}}, - arrow_position: {type:'string', default:'down', oneOf: ['up', 'down', 'left', 'right']}, + arrow_position: {type:'string', default:'down', oneOf: ['up', 'down', 'left', 'right' ]}, arrow_visible: {type:'boolean', default:true}, lookAtCamera: {type:'boolean', default:true}, constrainYAxis: {type:'boolean', default:true}, From 222162060f34a3d653d75381cc478251fcee6561 Mon Sep 17 00:00:00 2001 From: PlumCantaloupe Date: Mon, 3 Apr 2023 14:55:18 -0400 Subject: [PATCH 04/16] Making highlight default again --- src/components/circles-interactive-object.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/circles-interactive-object.js b/src/components/circles-interactive-object.js index df98da5d..ddef3e18 100644 --- a/src/components/circles-interactive-object.js +++ b/src/components/circles-interactive-object.js @@ -4,7 +4,7 @@ AFRAME.registerComponent('circles-interactive-object', { schema: { - type: {type:'string', default:'none', oneOf:['outline','scale','highlight','none']}, + type: {type:'string', default:'highlight', oneOf:['outline','scale','highlight','none']}, highlight_color: {type:'color', default:'rgb(255,255,255)'}, //only for outline and highlight effect neutral_scale: {type:'number', default:1.00}, //only for outline effect hover_scale: {type:'number', default:1.08}, From 1a89f439a6476a7dc42ecb321164089bfe4fc40c Mon Sep 17 00:00:00 2001 From: PlumCantaloupe Date: Mon, 3 Apr 2023 14:57:01 -0400 Subject: [PATCH 05/16] adjusting positions --- src/worlds/Network_TestSpace/index.html | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/worlds/Network_TestSpace/index.html b/src/worlds/Network_TestSpace/index.html index b3ed1121..deeca27a 100644 --- a/src/worlds/Network_TestSpace/index.html +++ b/src/worlds/Network_TestSpace/index.html @@ -26,16 +26,16 @@ - + shadow="cast:true; receive:true;"> - + circles-pickup-networked> - + From 481a200493b54c0717fed14d0a2fdc7f0cc320c1 Mon Sep 17 00:00:00 2001 From: PlumCantaloupe Date: Mon, 3 Apr 2023 21:12:10 -0400 Subject: [PATCH 06/16] performance and adding label event listener --- src/components/circles-pickup-networked.js | 46 ++++++++++++++-------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/src/components/circles-pickup-networked.js b/src/components/circles-pickup-networked.js index 4bb5f83f..ec46e17b 100644 --- a/src/components/circles-pickup-networked.js +++ b/src/components/circles-pickup-networked.js @@ -46,6 +46,16 @@ AFRAME.registerComponent('circles-pickup-networked', { } CONTEXT_AF.origId = CONTEXT_AF.el.components['circles-object-world'].data.id + + //want to make sure clone's label clicks into artefact + if (CONTEXT_AF.isClone === true) { + //make sure label click works on networked elements (if artefact) + const labelEl = document.querySelector('#' + CONTEXT_AF.origId + '_label'); + + if (labelEl) { + labelEl.querySelector('.label_bg').addEventListener('click', CONTEXT_AF.clickLabelFunc); + } + } }, update : function(oldData) { const CONTEXT_AF = this; @@ -230,19 +240,21 @@ AFRAME.registerComponent('circles-pickup-networked', { } CONTEXT_AF.networkAttachedFunc = function(data) { - //console.log('networkAttachedFunc', CONTEXT_AF.el.id); + console.log('networkAttachedFunc', CONTEXT_AF.el.id); - const isSameWorld = (data.world === CIRCLES.getCirclesWorldName()); - const isSameElem = (data.origId === CONTEXT_AF.origId); - if (isSameWorld && isSameElem) { - if (CONTEXT_AF.isClone === true) { - //make sure label click works on networked elements (if artefact) - const labelEl = document.querySelector('#' + CONTEXT_AF.origId + '_label'); - if (labelEl) { - labelEl.querySelector('.label_bg').addEventListener('click', CONTEXT_AF.clickLabelFunc); - } - } - } + // const isSameWorld = (data.world === CIRCLES.getCirclesWorldName()); + // const isSameElem = (data.origId === CONTEXT_AF.origId); + + // if (isSameWorld && isSameElem) { + // if (CONTEXT_AF.isClone === true) { + // //make sure label click works on networked elements (if artefact) + // const labelEl = document.querySelector('#' + CONTEXT_AF.origId + '_label'); + + // if (labelEl) { + // labelEl.querySelector('.label_bg').addEventListener('click', CONTEXT_AF.clickLabelFunc); + // } + // } + // } }; CONTEXT_AF.networkDetachedFunc = function(data) { @@ -280,7 +292,11 @@ AFRAME.registerComponent('circles-pickup-networked', { document.body.addEventListener('entityCreated', function(e) { //console.log('NAF entityCreated', e.detail.el); - if (CONTEXT_AF.isClone === false) { + if (!e.detail.el.components['circles-object-world']) { + return; + } + + if (CONTEXT_AF.isClone === false && e.detail.el.components['circles-object-world'].data.id === CONTEXT_AF.origId) { CONTEXT_AF.initSyncObjects(); } }); @@ -402,12 +418,8 @@ AFRAME.registerComponent('circles-pickup-networked', { //you are now the host/owner of this networked object CONTEXT_AF.showThisElement(true, true); - console.log(CONTEXT_AF.lastKnowObjectData); - if (CONTEXT_AF.lastKnowObjectData) { //determine last state and set this new one to a similar one - console.log(CONTEXT_AF.lastKnowObjectData.circlesObjectWorld.pickedup); - if (CONTEXT_AF.lastKnowObjectData.circlesObjectWorld.pickedup === true) { //pick up CONTEXT_AF.el.components['circles-pickup-object'].pickup(true, CONTEXT_AF.el.components['circles-pickup-object']); From 6bfe4a0758b559b3c878e48771c27b4c6e5c016a Mon Sep 17 00:00:00 2001 From: PlumCantaloupe Date: Mon, 3 Apr 2023 21:15:50 -0400 Subject: [PATCH 07/16] Adding in basic network comp for non-pickup objs --- src/components/circles-networked-basic.js | 320 ++++++++++++++++++ src/components/index.js | 1 + src/core/circles_framework.js | 1 + .../circles_assets.part.html | 5 + .../circles_end_scripts.part.html | 26 ++ src/worlds/Network_TestSpace/index.html | 24 +- .../scripts/robot-controls.js | 4 +- 7 files changed, 367 insertions(+), 14 deletions(-) create mode 100644 src/components/circles-networked-basic.js diff --git a/src/components/circles-networked-basic.js b/src/components/circles-networked-basic.js new file mode 100644 index 00000000..4603c241 --- /dev/null +++ b/src/components/circles-networked-basic.js @@ -0,0 +1,320 @@ +//for when we want an object in the scene that is not duplicated on other clients and can be interacted with by other players + +'use strict'; + +AFRAME.registerComponent('circles-networked-basic', { + schema: { + className: {type:'string', default:''}, //We will randomly generate one if need be. The class we will use to synch with the networked version + networkedEnabled: {type:'boolean', default:true}, + networkedTemplate:{type:'string', default:CIRCLES.NETWORKED_TEMPLATES.BASIC_OBJECT} + }, + init: function() { + const CONTEXT_AF = this; + const data = CONTEXT_AF.data; + CONTEXT_AF.socket = null; + CONTEXT_AF.isShowing = true; + CONTEXT_AF.listenersAttached = false; + + //we will track this so that if a hosting client disappears we can set new data here to synch new hosting client + CONTEXT_AF.lastKnowObjectData = null; + + //CONTEXT_AF.el.setAttribute('circles-interactive-visible', false); //hide on init, then show later + + let regex = /(naf)/i; + CONTEXT_AF.isClone = regex.test(CONTEXT_AF.el.id); //if false, this entity is the sole networked object of all duplicates + + if (CIRCLES.isCirclesWebsocketReady()) { + CONTEXT_AF.createEventFunctions(); //will only do this once at beginning of program + } + else { + const wsReadyFunc = function() { + CONTEXT_AF.createEventFunctions(); //will only do this once at beginning of program + CONTEXT_AF.el.sceneEl.removeEventListener(CIRCLES.EVENTS.WS_CONNECTED, wsReadyFunc); + }; + CONTEXT_AF.el.sceneEl.addEventListener(CIRCLES.EVENTS.WS_CONNECTED, wsReadyFunc); + } + + //this is so we can keep track of which world this object is from so we can share objects, but turning that off for now to reduce duplicate object complexity. + if (CONTEXT_AF.isClone === false) { + CONTEXT_AF.el.setAttribute('circles-object-world', {}); + } + + CONTEXT_AF.origId = CONTEXT_AF.el.components['circles-object-world'].data.id + }, + update : function(oldData) { + const CONTEXT_AF = this; + const data = this.data; + + if (Object.keys(data).length === 0) { return; } // No need to update. as nothing here yet + + if ( oldData.className !== data.className ) { + //autogenerate + if (data.className === '') { + const randomClassName = CIRCLES.UTILS.generateRandomString(5); + CONTEXT_AF.el.classList.add(randomClassName); + CONTEXT_AF.el.setAttribute('circles-networked-basic', {className:randomClassName}); + } + } + + if ( (oldData.networkedEnabled !== data.networkedEnabled) && (data.networkedEnabled !== '') ) { + CONTEXT_AF.enableNetworking(data.networkedEnabled, !CONTEXT_AF.isClone); + } + }, + remove : function() { + if(CIRCLES.isCirclesWebsocketReady() === true) { + //let everyone know (maybe helpful in future) + CIRCLES.getCirclesWebsocket().emit(CIRCLES.EVENTS.OBJECT_NETWORKED_DETACHED, this.getNetworkDataObject()); + } + + if (CIRCLES.isCirclesWebsocketReady()) { + this.removeEventListeners(); + } + }, + createEventFunctions: function() { + const CONTEXT_AF = this; + CONTEXT_AF.socket = CIRCLES.getCirclesWebsocket(); + + //NAf events + CONTEXT_AF.ownerGainedFunc = null; + CONTEXT_AF.ownerLostFunc = null; + + //circles networking events + CONTEXT_AF.networkAttachedFunc = null; + CONTEXT_AF.networkDetachedFunc = null; + + CONTEXT_AF.ownerGainedFunc = function(e) { + //console.log('ownerGainedFunc', CONTEXT_AF.el.id); + }; + + CONTEXT_AF.ownerLostFunc = function(e) { + //console.log('ownerLostFunc', CONTEXT_AF.el.id); + }; + + CONTEXT_AF.captureNewHostingObjectData = function(elem) { + //console.log('captureNewHostingObjectData', elem); + if (elem) { + CONTEXT_AF.lastKnowObjectData = { position: {x:elem.object3D.position.x, y:elem.object3D.position.y, z:elem.object3D.position.z}, + rotation: {x:elem.object3D.rotation.x, y:elem.object3D.rotation.y, z:elem.object3D.rotation.z}, + scale: {x:elem.object3D.scale.x, y:elem.object3D.scale.y, z:elem.object3D.scale.z}, + circlesObjectWorld: elem.getAttribute('circles-object-world') + }; + } + } + + CONTEXT_AF.networkAttachedFunc = function(data) { + //console.log('networkAttachedFunc', CONTEXT_AF.el.id); + + const isSameWorld = (data.world === CIRCLES.getCirclesWorldName()); + const isSameElem = (data.origId === CONTEXT_AF.origId); + if (isSameWorld && isSameElem) { + if (CONTEXT_AF.isClone === true) { + //make sure label click works on networked elements (if artefact) + const labelEl = document.querySelector('#' + CONTEXT_AF.origId + '_label'); + if (labelEl) { + labelEl.querySelector('.label_bg').addEventListener('click', CONTEXT_AF.clickLabelFunc); + } + } + } + }; + + CONTEXT_AF.networkDetachedFunc = function(data) { + //console.log('networkDetachedFunc', CONTEXT_AF.el.id); + + const isSameWorld = (data.world === CIRCLES.getCirclesWorldName()); + const isSameElem = (data.origId === CONTEXT_AF.origId); + if (isSameWorld && isSameElem) { + //CONTEXT_AF.initSyncObjects(); + } + }; + + //need this be always listening so that "hidden" objects can come back + if (CONTEXT_AF.isClone === false) { + document.body.addEventListener('clientConnected', function(e) { + //console.log('NAF clientConnected', e.detail.clientId); + }); + + document.body.addEventListener('clientDisconnected', function(e) { + //console.log('NAF clientDisconnected', e.detail.clientId); + }); + + document.body.addEventListener('entityCreated', function(e) { + //console.log('NAF entityCreated', e.detail.el); + + if (!e.detail.el.components['circles-object-world']) { + return; + } + + if (CONTEXT_AF.isClone === false && e.detail.el.components['circles-object-world'].data.id === CONTEXT_AF.origId) { + CONTEXT_AF.initSyncObjects(); + } + }); + + document.body.addEventListener('entityRemoved', function(e) { + //console.log('NAF entityRemoved', e.detail.networkId); + if (CONTEXT_AF.isClone === false) { + CONTEXT_AF.initSyncObjects(); + } + }); + } + }, + addEventListeners: function() { + //console.log('addEventListeners'); + + const CONTEXT_AF = this; + CONTEXT_AF.listenersAttached = true; + CONTEXT_AF.socket = CIRCLES.getCirclesWebsocket(); + + CONTEXT_AF.el.addEventListener('ownership-gained', CONTEXT_AF.ownerGainedFunc); + CONTEXT_AF.el.addEventListener('ownership-lost', CONTEXT_AF.ownerLostFunc); + + CONTEXT_AF.socket.on(CIRCLES.EVENTS.OBJECT_NETWORKED_ATTACHED, CONTEXT_AF.networkAttachedFunc); + CONTEXT_AF.socket.on(CIRCLES.EVENTS.OBJECT_NETWORKED_DETACHED, CONTEXT_AF.networkDetachedFunc); + + CONTEXT_AF.socket.on('hacky_drop_position', CONTEXT_AF.hackySyncDropPositionFunc); + }, + removeEventListeners: function() { + //console.log('removeEventListeners'); + + const CONTEXT_AF = this; + CONTEXT_AF.listenersAttached = false; + CONTEXT_AF.socket = CIRCLES.getCirclesWebsocket(); + + CONTEXT_AF.el.removeEventListener('ownership-gained', CONTEXT_AF.ownerGainedFunc); + CONTEXT_AF.el.removeEventListener('ownership-lost', CONTEXT_AF.ownerLostFunc); + + CONTEXT_AF.socket.off(CIRCLES.EVENTS.OBJECT_NETWORKED_ATTACHED, CONTEXT_AF.networkAttachedFunc); + CONTEXT_AF.socket.off(CIRCLES.EVENTS.OBJECT_NETWORKED_DETACHED, CONTEXT_AF.networkDetachedFunc); + }, + getNetworkDataObject: function() { + const networkId_ = (this.el.hasAttribute('networked')) ? this.el.components['networked'].data.networkId : ''; + return {id:this.el.id, origId:this.origId, networkId:networkId_, room:CIRCLES.getCirclesGroupName(), world:CIRCLES.getCirclesWorldName()}; + }, + initSyncObjects: function() { + //console.log('initSyncObjects'); + + const CONTEXT_AF = this; + + //console.log('initSyncObjects', this.el.id); + //if there already exists "the same element" then hide this and turn off networking (if this is not a clone) + //get list of the "same" objects, and remove the one that is duplicate ... + let numSimilarNetObjs = 0; + let oldestTime = 0; + let currAgeMS = 0; + let oldestElem = null; + let isSameWorld = false; + let isSameElem = false; + const allNetworkObjects = document.querySelectorAll('[circles-networked-basic]'); + + //console.log(allNetworkObjects); + + allNetworkObjects.forEach(function(netObj) { + isSameWorld = (netObj.components['circles-object-world'].data.world === CIRCLES.getCirclesWorldName()); + isSameElem = (netObj.components['circles-object-world'].data.id === CONTEXT_AF.origId); + + if (isSameWorld && isSameElem) { + currAgeMS = netObj.components['circles-object-world'].data.timeCreated; + if (currAgeMS > oldestTime) { + oldestTime = currAgeMS; + oldestElem = netObj; + } + + if (netObj.components['circles-object-world'].data.id === CONTEXT_AF.origId && netObj.components['circles-networked-basic'].isShowing === true) { + //console.log(CONTEXT_AF.el.id, CONTEXT_AF.origId, netObj.id, netObj.components['circles-object-world'].data.id, netObj.components['circles-networked-basic'].isShowing); + numSimilarNetObjs++; + } + } + }); + + if (oldestElem) { + //if more than one, hide this one, if it is the oldest ... + if (numSimilarNetObjs > 1) { + if (CONTEXT_AF.isShowing === true && oldestElem.id === CONTEXT_AF.el.id) { + CONTEXT_AF.showThisElement(false, true); + } + } + //if 0 elements that means that a remote owner disappeared and we must bring the other one to fruition + else if (numSimilarNetObjs === 0 && oldestElem.id === CONTEXT_AF.el.id) { + //am owner + if (CONTEXT_AF.isShowing === false) { + //you are now the host/owner of this networked object + CONTEXT_AF.showThisElement(true, true); + + if (CONTEXT_AF.lastKnowObjectData) { + //determine last state and set this new one to a similar one + if (CONTEXT_AF.lastKnowObjectData.circlesObjectWorld.pickedup === true) { + //this isn't the pickup component + } + else { + //set world coordinates + CONTEXT_AF.el.object3D.position.set(CONTEXT_AF.lastKnowObjectData.position.x, CONTEXT_AF.lastKnowObjectData.position.y, CONTEXT_AF.lastKnowObjectData.position.z); + CONTEXT_AF.el.object3D.rotation.set(CONTEXT_AF.lastKnowObjectData.rotation.x, CONTEXT_AF.lastKnowObjectData.rotation.y, CONTEXT_AF.lastKnowObjectData.rotation.z); + CONTEXT_AF.el.object3D.scale.set(CONTEXT_AF.lastKnowObjectData.scale.x, CONTEXT_AF.lastKnowObjectData.scale.y, CONTEXT_AF.lastKnowObjectData.scale.z); + } + } + } + } + } + }, + enableNetworking: function(enable, alertNetwork) { + //console.log('enableNetworking', this.el.id, enable, alertNetwork); + + const CONTEXT_AF = this; + + //don't want to touch the networked component if a clone + if (CONTEXT_AF.isClone === false) { + if (enable === true) { + if (CONTEXT_AF.listenersAttached === false) { + CONTEXT_AF.el.setAttribute('networked', {template:'#' + CONTEXT_AF.data.networkedTemplate, attachTemplateToLocal:true, synchWorldTransforms:true}); //broken in NAF - persistent:true}); + } + } + else { + CONTEXT_AF.el.removeAttribute('networked'); + } + } + + //need everyone to send this message + const networkReadyFunc = function() { + //console.log('networkReadyFunc'); + if (enable === true) { + if (alertNetwork === true) { + CIRCLES.getCirclesWebsocket().emit(CIRCLES.EVENTS.OBJECT_NETWORKED_ATTACHED, CONTEXT_AF.getNetworkDataObject() ); + } + if (CONTEXT_AF.listenersAttached === false) { + CONTEXT_AF.addEventListeners(); + } + } + else { + if (alertNetwork === true) { + CIRCLES.getCirclesWebsocket().emit(CIRCLES.EVENTS.OBJECT_NETWORKED_ATTACHED, CONTEXT_AF.getNetworkDataObject() ); + } + CONTEXT_AF.removeEventListeners(); + } + CONTEXT_AF.el.sceneEl.removeEventListener(CIRCLES.EVENTS.WS_CONNECTED, networkReadyFunc); + }; + + if (CIRCLES.isCirclesWebsocketReady()) { + networkReadyFunc(); + } + else { + CONTEXT_AF.el.sceneEl.addEventListener(CIRCLES.EVENTS.WS_CONNECTED, networkReadyFunc); + } + + CONTEXT_AF.data.networkedEnabled = enable; + }, + showThisElement: function (isShowing, alertNetwork) { + //console.log('showThisElement', this.el.id, isShowing, alertNetwork); + + this.isShowing = isShowing; + + this.enableNetworking(isShowing, alertNetwork); + // this.el.setAttribute('circles-networked-basic', {networkedEnabled:isShowing}); + this.el.setAttribute('circles-interactive-visible', isShowing); + + if (this.el.components['circles-artefact']) { + this.el.components['circles-artefact'].removeLabelEventListener(); + if (isShowing) { + this.el.components['circles-artefact'].addLabelEventListener(); + } + } + } +}); \ No newline at end of file diff --git a/src/components/index.js b/src/components/index.js index 7fea4629..f871a8e3 100755 --- a/src/components/index.js +++ b/src/components/index.js @@ -16,6 +16,7 @@ require('./circles-material-extend-fresnel'); require('./circles-material-override'); require('./circles-label'); require('./circles-lookat'); +require('./circles-networked-basic'); require('./circles-object-world'); require('./circles-parent-constraint'); require('./circles-pdf-loader'); diff --git a/src/core/circles_framework.js b/src/core/circles_framework.js index f8fb405f..c3dcdf47 100755 --- a/src/core/circles_framework.js +++ b/src/core/circles_framework.js @@ -126,6 +126,7 @@ const EVENTS = { const NETWORKED_TEMPLATES = { AVATAR : 'circles-user-template', INTERACTIVE_OBJECT : 'circles-interactive-object-template', + BASIC_OBJECT : 'circles-basic-object-template', ARTEFACT : 'circles-artefact-template', TEXT : 'circles-text-template' }; diff --git a/src/webpack.worlds.parts/circles_assets.part.html b/src/webpack.worlds.parts/circles_assets.part.html index e5c08c45..29dfa918 100644 --- a/src/webpack.worlds.parts/circles_assets.part.html +++ b/src/webpack.worlds.parts/circles_assets.part.html @@ -52,6 +52,11 @@ + +