Skip to content

Commit fc19e7d

Browse files
authored
Merge pull request #524 from mozilla/feature/drawing
Feature/drawing also closes #517
2 parents 4244294 + 9c201bc commit fc19e7d

28 files changed

+1893
-336
lines changed

package-lock.json

Lines changed: 220 additions & 111 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
"react-dom": "^16.1.1",
5555
"react-intl": "^2.4.0",
5656
"screenfull": "^3.3.2",
57-
"super-hands": "github:mozillareality/aframe-super-hands-component#f8f9781d8b4c487bb544b3986000e85ed5f82fcc",
57+
"super-hands": "github:mozillareality/aframe-super-hands-component#feature/drawing",
5858
"three": "github:mozillareality/three.js#8b1886c384371c3e6305b757d1db7577c5201a9b",
5959
"three-pathfinding": "^0.7.0",
6060
"three-to-cannon": "1.3.0",

src/assets/hud/spawn_pen-hover.png

5.25 KB
Loading

src/assets/hud/spawn_pen.png

4.6 KB
Loading

src/assets/stylesheets/2d-hud.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,13 @@
113113
background-image: url(../hud/bubble_on-hover.png);
114114
}
115115

116+
:local(.iconButton.spawn_pen) {
117+
background-image: url(../hud/spawn_pen.png);
118+
}
119+
:local(.iconButton.spawn_pen:hover) {
120+
background-image: url(../hud/spawn_pen-hover.png);
121+
}
122+
116123
:local(.iconButton.freeze) {
117124
background-image: url(../hud/freeze_off.png);
118125
}

src/components/css-class.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ AFRAME.registerComponent("css-class", {
66
schema: {
77
type: "string"
88
},
9+
multiple: true,
910
init() {
1011
this.el.classList.add(this.data);
1112
},

src/components/cursor-controller.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ AFRAME.registerComponent("cursor-controller", {
7373
this.updateRay();
7474
}
7575

76-
const isGrabbing = this.data.cursor.components["super-hands"].state.has("grab-start");
76+
const isGrabbing = this.isInteracting();
7777
if (isGrabbing) {
7878
const distance = Math.min(
7979
this.data.maxDistance,
@@ -153,6 +153,10 @@ AFRAME.registerComponent("cursor-controller", {
153153
this.data.cursor.components["static-body"].syncToPhysics();
154154
},
155155

156+
isInteracting: function() {
157+
return this.data.cursor.components["super-hands"].state.has("grab-start");
158+
},
159+
156160
startInteraction: function() {
157161
if (this._isTargetOfType(TARGET_TYPE_INTERACTABLE_OR_UI)) {
158162
this.data.cursor.emit("cursor-grab", {});
@@ -174,9 +178,11 @@ AFRAME.registerComponent("cursor-controller", {
174178
const targetDistanceMod = this.currentDistanceMod + delta;
175179
const moddedDistance = this.currentDistance - targetDistanceMod;
176180
if (moddedDistance > maxDistance || moddedDistance < minDistance) {
177-
return;
181+
return false;
178182
}
183+
179184
this.currentDistanceMod = targetDistanceMod;
185+
return true;
180186
},
181187

182188
_handleCursorLoaded: function() {

src/components/grabbable-toggle.js

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/* global AFRAME, THREE */
2+
const inherit = AFRAME.utils.extendDeep;
3+
const physicsCore = require("super-hands/reaction_components/prototypes/physics-grab-proto.js");
4+
const buttonsCore = require("super-hands/reaction_components/prototypes/buttons-proto.js");
5+
// new object with all core modules
6+
const base = inherit({}, physicsCore, buttonsCore);
7+
AFRAME.registerComponent(
8+
"grabbable-toggle",
9+
inherit(base, {
10+
schema: {
11+
maxGrabbers: { type: "int", default: NaN },
12+
invert: { default: false },
13+
suppressY: { default: false },
14+
primaryReleaseEvents: { default: ["primary_hand_release"] },
15+
secondaryReleaseEvents: { default: ["secondary_hand_release"] }
16+
},
17+
init: function() {
18+
this.GRABBED_STATE = "grabbed";
19+
this.GRAB_EVENT = "grab-start";
20+
this.UNGRAB_EVENT = "grab-end";
21+
this.grabbed = false;
22+
this.grabbers = [];
23+
this.constraints = new Map();
24+
this.deltaPositionIsValid = false;
25+
this.grabDistance = undefined;
26+
this.grabDirection = { x: 0, y: 0, z: -1 };
27+
this.grabOffset = { x: 0, y: 0, z: 0 };
28+
// persistent object speeds up repeat setAttribute calls
29+
this.destPosition = { x: 0, y: 0, z: 0 };
30+
this.deltaPosition = new THREE.Vector3();
31+
this.targetPosition = new THREE.Vector3();
32+
this.physicsInit();
33+
34+
this.el.addEventListener(this.GRAB_EVENT, e => this.start(e));
35+
this.el.addEventListener(this.UNGRAB_EVENT, e => this.end(e));
36+
this.el.addEventListener("mouseout", e => this.lostGrabber(e));
37+
38+
this.toggle = false;
39+
this.lastGrabber = null;
40+
},
41+
update: function() {
42+
this.physicsUpdate();
43+
this.xFactor = this.data.invert ? -1 : 1;
44+
this.zFactor = this.data.invert ? -1 : 1;
45+
this.yFactor = (this.data.invert ? -1 : 1) * !this.data.suppressY;
46+
},
47+
tick: (function() {
48+
const q = new THREE.Quaternion();
49+
const v = new THREE.Vector3();
50+
51+
return function() {
52+
let entityPosition;
53+
if (this.grabber) {
54+
// reflect on z-axis to point in same direction as the laser
55+
this.targetPosition.copy(this.grabDirection);
56+
this.targetPosition
57+
.applyQuaternion(this.grabber.object3D.getWorldQuaternion(q))
58+
.setLength(this.grabDistance)
59+
.add(this.grabber.object3D.getWorldPosition(v))
60+
.add(this.grabOffset);
61+
if (this.deltaPositionIsValid) {
62+
// relative position changes work better with nested entities
63+
this.deltaPosition.sub(this.targetPosition);
64+
entityPosition = this.el.getAttribute("position");
65+
this.destPosition.x = entityPosition.x - this.deltaPosition.x * this.xFactor;
66+
this.destPosition.y = entityPosition.y - this.deltaPosition.y * this.yFactor;
67+
this.destPosition.z = entityPosition.z - this.deltaPosition.z * this.zFactor;
68+
this.el.setAttribute("position", this.destPosition);
69+
} else {
70+
this.deltaPositionIsValid = true;
71+
}
72+
this.deltaPosition.copy(this.targetPosition);
73+
}
74+
};
75+
})(),
76+
remove: function() {
77+
this.el.removeEventListener(this.GRAB_EVENT, this.start);
78+
this.el.removeEventListener(this.UNGRAB_EVENT, this.end);
79+
this.physicsRemove();
80+
},
81+
start: function(evt) {
82+
if (evt.defaultPrevented || !this.startButtonOk(evt)) {
83+
return;
84+
}
85+
// room for more grabbers?
86+
let grabAvailable = !Number.isFinite(this.data.maxGrabbers) || this.grabbers.length < this.data.maxGrabbers;
87+
if (Number.isFinite(this.data.maxGrabbers) && !grabAvailable && this.grabbed) {
88+
this.grabbers[0].components["super-hands"].onGrabEndButton();
89+
grabAvailable = true;
90+
}
91+
if (this.grabbers.indexOf(evt.detail.hand) === -1 && grabAvailable) {
92+
if (!evt.detail.hand.object3D) {
93+
console.warn("grabbable entities must have an object3D");
94+
return;
95+
}
96+
this.grabbers.push(evt.detail.hand);
97+
// initiate physics if available, otherwise manual
98+
if (!this.physicsStart(evt) && !this.grabber) {
99+
this.grabber = evt.detail.hand;
100+
this.resetGrabber();
101+
}
102+
// notify super-hands that the gesture was accepted
103+
if (evt.preventDefault) {
104+
evt.preventDefault();
105+
}
106+
this.grabbed = true;
107+
this.el.addState(this.GRABBED_STATE);
108+
}
109+
},
110+
end: function(evt) {
111+
const handIndex = this.grabbers.indexOf(evt.detail.hand);
112+
if (evt.defaultPrevented || !this.endButtonOk(evt)) {
113+
return;
114+
}
115+
116+
const type = evt.detail && evt.detail.buttonEvent ? evt.detail.buttonEvent.type : null;
117+
118+
if (this.toggle && this.lastGrabber !== this.grabbers[0]) {
119+
this.toggle = false;
120+
this.lastGrabber = null;
121+
}
122+
123+
if (handIndex !== -1) {
124+
this.grabber = this.grabbers[0];
125+
}
126+
127+
if ((this.isPrimaryRelease(type) && !this.toggle) || this.isSecondaryRelease(type)) {
128+
this.toggle = true;
129+
this.lastGrabber = this.grabbers[0];
130+
return;
131+
} else if (this.toggle && this.isPrimaryRelease(type)) {
132+
this.toggle = false;
133+
this.lastGrabber = null;
134+
}
135+
136+
if (handIndex !== -1) {
137+
this.grabbers.splice(handIndex, 1);
138+
this.grabber = this.grabbers[0];
139+
}
140+
141+
this.physicsEnd(evt);
142+
if (!this.resetGrabber()) {
143+
this.grabbed = false;
144+
this.el.removeState(this.GRABBED_STATE);
145+
}
146+
if (evt.preventDefault) {
147+
evt.preventDefault();
148+
}
149+
},
150+
resetGrabber: (() => {
151+
const objPos = new THREE.Vector3();
152+
const grabPos = new THREE.Vector3();
153+
return function() {
154+
if (!this.grabber) {
155+
return false;
156+
}
157+
const raycaster = this.grabber.getAttribute("raycaster");
158+
this.deltaPositionIsValid = false;
159+
this.grabDistance = this.el.object3D
160+
.getWorldPosition(objPos)
161+
.distanceTo(this.grabber.object3D.getWorldPosition(grabPos));
162+
if (raycaster) {
163+
this.grabDirection = raycaster.direction;
164+
this.grabOffset = raycaster.origin;
165+
}
166+
return true;
167+
};
168+
})(),
169+
lostGrabber: function(evt) {
170+
const i = this.grabbers.indexOf(evt.relatedTarget);
171+
// if a queued, non-physics grabber leaves the collision zone, forget it
172+
if (i !== -1 && evt.relatedTarget !== this.grabber && !this.physicsIsConstrained(evt.relatedTarget)) {
173+
this.grabbers.splice(i, 1);
174+
}
175+
},
176+
177+
isPrimaryRelease(type) {
178+
return this.data.primaryReleaseEvents.indexOf(type) !== -1;
179+
},
180+
181+
isSecondaryRelease(type) {
182+
return this.data.secondaryReleaseEvents.indexOf(type) !== -1;
183+
}
184+
})
185+
);

src/components/in-world-hud.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,23 @@ AFRAME.registerComponent("in-world-hud", {
1111
init() {
1212
this.mic = this.el.querySelector(".mic");
1313
this.freeze = this.el.querySelector(".freeze");
14-
this.bubble = this.el.querySelector(".bubble");
14+
this.pen = this.el.querySelector(".pen");
1515
this.background = this.el.querySelector(".bg");
1616
const renderOrder = window.APP.RENDER_ORDER;
1717
this.mic.object3DMap.mesh.renderOrder = renderOrder.HUD_ICONS;
1818
this.freeze.object3DMap.mesh.renderOrder = renderOrder.HUD_ICONS;
19-
this.bubble.object3DMap.mesh.renderOrder = renderOrder.HUD_ICONS;
19+
this.pen.object3DMap.mesh.renderOrder = renderOrder.HUD_ICONS;
2020
this.background.object3DMap.mesh.renderORder = renderOrder.HUD_BACKGROUND;
2121

2222
this.updateButtonStates = () => {
2323
this.mic.setAttribute("icon-button", "active", this.el.sceneEl.is("muted"));
2424
this.freeze.setAttribute("icon-button", "active", this.el.sceneEl.is("frozen"));
25-
this.bubble.setAttribute("icon-button", "active", this.el.sceneEl.is("spacebubble"));
25+
this.pen.setAttribute("icon-button", "active", this.el.sceneEl.is("pen"));
2626
};
2727
this.updateButtonStates();
2828

2929
this.onStateChange = evt => {
30-
if (!(evt.detail === "muted" || evt.detail === "frozen" || evt.detail === "spacebubble")) return;
30+
if (!(evt.detail === "muted" || evt.detail === "frozen" || evt.detail === "pen")) return;
3131
this.updateButtonStates();
3232
};
3333

@@ -39,8 +39,8 @@ AFRAME.registerComponent("in-world-hud", {
3939
this.el.emit("action_freeze");
4040
};
4141

42-
this.onBubbleClick = () => {
43-
this.el.emit("action_space_bubble");
42+
this.onPenClick = () => {
43+
this.el.emit("spawn_pen");
4444
};
4545
},
4646

@@ -50,7 +50,7 @@ AFRAME.registerComponent("in-world-hud", {
5050

5151
this.mic.addEventListener("click", this.onMicClick);
5252
this.freeze.addEventListener("click", this.onFreezeClick);
53-
this.bubble.addEventListener("click", this.onBubbleClick);
53+
this.pen.addEventListener("mousedown", this.onPenClick);
5454
},
5555

5656
pause() {
@@ -59,6 +59,6 @@ AFRAME.registerComponent("in-world-hud", {
5959

6060
this.mic.removeEventListener("click", this.onMicClick);
6161
this.freeze.removeEventListener("click", this.onFreezeClick);
62-
this.bubble.removeEventListener("click", this.onBubbleClick);
62+
this.pen.removeEventListener("mousedown", this.onPenClick);
6363
}
6464
});

src/components/input-configurator.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ AFRAME.registerComponent("input-configurator", {
162162
this.cursor.el.setAttribute("cursor-controller", { rayObject: this.data.gazeCursorRayObject });
163163
}
164164

165-
if (this.actionEventHandler) {
165+
if (this.actionEventHandler && this.controller) {
166166
this.actionEventHandler.setHandThatAlsoDrivesCursor(this.controller);
167167
}
168168
}

0 commit comments

Comments
 (0)