Skip to content
Snippets Groups Projects
Commit 8c4a8120 authored by netpro2k's avatar netpro2k
Browse files

Rework interactable spawners to use media-tools

parent 50c9c9b9
No related branches found
No related tags found
No related merge requests found
...@@ -24,13 +24,13 @@ AFRAME.registerComponent("auto-box-collider", { ...@@ -24,13 +24,13 @@ AFRAME.registerComponent("auto-box-collider", {
this.el.object3D.worldToLocal(center); this.el.object3D.worldToLocal(center);
this.el.object3DMap.mesh.position.sub(center); this.el.object3DMap.mesh.position.sub(center);
if (this.data.resize) {
this.resize(min, max);
}
this.el.setAttribute("shape", { this.el.setAttribute("shape", {
shape: "box", shape: "box",
halfExtents: halfExtents halfExtents: halfExtents
}); });
if (this.data.resize) {
this.resize(min, max);
}
this.el.object3D.rotation.copy(rotation); this.el.object3D.rotation.copy(rotation);
this.el.removeAttribute("auto-box-collider"); this.el.removeAttribute("auto-box-collider");
}, },
......
import { addMedia } from "../utils/media-utils";
const waitForEvent = function(eventName, eventObj) {
return new Promise(resolve => {
eventObj.addEventListener(eventName, resolve, { once: true });
});
};
let nextGrabId = 0;
/** /**
* Spawns networked objects when grabbed. * Spawns networked objects when grabbed.
* @namespace network * @namespace network
...@@ -5,108 +14,98 @@ ...@@ -5,108 +14,98 @@
*/ */
AFRAME.registerComponent("super-spawner", { AFRAME.registerComponent("super-spawner", {
schema: { schema: {
template: { default: "" }, src: { default: "https://asset-bundles-prod.reticulum.io/interactables/Ducky/DuckyMesh-438ff8e022.gltf" },
useCustomSpawnPosition: { default: false }, useCustomSpawnPosition: { default: false },
spawnPosition: { type: "vec3" }, spawnPosition: { type: "vec3" },
useCustomSpawnRotation: { default: false }, useCustomSpawnRotation: { default: false },
spawnRotation: { type: "vec4" }, spawnRotation: { type: "vec4" },
events: { default: ["cursor-grab", "hand_grab"] },
grabEvents: { default: ["cursor-grab", "hand_grab"] },
releaseEvents: { default: ["cursor-release", "hand_release"] },
spawnCooldown: { default: 1 } spawnCooldown: { default: 1 }
}, },
init: function() { init() {
this.entities = new Map(); this.heldEntities = new Map();
this.timeout = null; this.cooldownTimeout = null;
this.handleGrabStart = this.handleGrabStart.bind(this);
this.onGrabEnd = this.onGrabEnd.bind(this);
}, },
play: function() { play() {
this.handleGrabStart = this._handleGrabStart.bind(this);
this.el.addEventListener("grab-start", this.handleGrabStart); this.el.addEventListener("grab-start", this.handleGrabStart);
this.el.addEventListener("grab-end", this.onGrabEnd);
}, },
pause: function() { pause() {
this.el.removeEventListener("grab-start", this.handleGrabStart); this.el.removeEventListener("grab-start", this.handleGrabStart);
this.el.removeEventListener("grab-end", this.onGrabEnd);
if (this.timeout) { if (this.cooldownTimeout) {
clearTimeout(this.timeout); clearTimeout(this.cooldownTimeout);
this.timeout = null; this.cooldownTimeout = null;
this.el.setAttribute("visible", true); this.el.setAttribute("visible", true);
this.el.classList.add("interactable"); this.el.classList.add("interactable");
} }
}, },
remove: function() { remove() {
for (const entity of this.entities.keys()) { this.heldEntities.clear();
const data = this.entities.get(entity);
entity.removeEventListener("componentinitialized", data.componentinInitializedListener);
entity.removeEventListener("body-loaded", data.bodyLoadedListener);
}
this.entities.clear();
}, },
_handleGrabStart: function(e) { onGrabEnd(e) {
if (this.timeout) { this.heldEntities.delete(e.detail.hand);
return; // This tells super-hands we are handling this releae
} e.preventDefault();
const hand = e.detail.hand; },
const entity = document.createElement("a-entity");
entity.setAttribute("networked", "template:" + this.data.template);
const componentinInitializedListener = this._handleComponentInitialzed.bind(this, entity);
const bodyLoadedListener = this._handleBodyLoaded.bind(this, entity);
this.entities.set(entity, {
hand: hand,
componentInitialized: false,
bodyLoaded: false,
componentinInitializedListener: componentinInitializedListener,
bodyLoadedListener: bodyLoadedListener
});
entity.addEventListener("componentinitialized", componentinInitializedListener);
entity.addEventListener("body-loaded", bodyLoadedListener);
const pos = this.data.useCustomSpawnPosition ? this.data.spawnPosition : this.el.getAttribute("position");
entity.setAttribute("position", pos);
const rot = this.data.useCustomSpawnRotation ? this.data.spawnRotation : this.el.getAttribute("rotation");
entity.setAttribute("rotation", rot);
this.el.sceneEl.appendChild(entity);
activateCooldown() {
if (this.data.spawnCooldown > 0) { if (this.data.spawnCooldown > 0) {
this.el.setAttribute("visible", false); this.el.setAttribute("visible", false);
this.el.classList.remove("interactable"); this.el.classList.remove("interactable");
this.timeout = setTimeout(() => { this.cooldownTimeout = setTimeout(() => {
this.el.setAttribute("visible", true); this.el.setAttribute("visible", true);
this.el.classList.add("interactable"); this.el.classList.add("interactable");
this.timeout = null; this.cooldownTimeout = null;
}, this.data.spawnCooldown * 1000); }, this.data.spawnCooldown * 1000);
} }
}, },
_handleComponentInitialzed: function(entity, e) { async handleGrabStart(e) {
if (e.detail.name === "grabbable") { if (this.cooldownTimeout) {
this.entities.get(entity).componentInitialized = true; return;
this._emitEvents.call(this, entity);
} }
},
_handleBodyLoaded: function(entity) { // This tells super-hands we are handling this grab. The user is now "grabbing" the spawner
this.entities.get(entity).bodyLoaded = true; e.preventDefault();
this._emitEvents.call(this, entity);
},
_emitEvents: function(entity) { const hand = e.detail.hand;
const data = this.entities.get(entity); const thisGrabId = nextGrabId++;
if (data.componentInitialized && data.bodyLoaded) { this.heldEntities.set(hand, thisGrabId);
for (let i = 0; i < this.data.events.length; i++) {
data.hand.emit(this.data.events[i], { targetEntity: entity }); const entity = await addMedia(this.data.src);
entity.object3D.position.copy(
this.data.useCustomSpawnPosition ? this.data.spawnPosition : this.el.object3D.position
);
entity.object3D.rotation.copy(
this.data.useCustomSpawnRotation ? this.data.spawnRotation : this.el.object3D.rotation
);
this.activateCooldown();
await waitForEvent("body-loaded", entity);
// If we are still holding the spawner with the hand that grabbed to create this entity, release the spawner and grab the entity
if (this.heldEntities.get(hand) === thisGrabId) {
entity.body.position.copy(hand.object3D.position);
entity.body.velocity.set(0, 0, 0);
for (let i = 0; i < this.data.grabEvents.length; i++) {
hand.emit(this.data.releaseEvents[i]);
hand.emit(this.data.grabEvents[i], { targetEntity: entity });
} }
entity.removeEventListener("componentinitialized", data.componentinInitializedListener);
entity.removeEventListener("body-loaded", data.bodyLoadedListener);
this.entities.delete(entity);
} }
} }
}); });
...@@ -159,21 +159,6 @@ ...@@ -159,21 +159,6 @@
</a-entity> </a-entity>
</template> </template>
<template id="interactable-template">
<a-entity
gltf-model-plus="src: #interactable-duck; inflate: true;"
class="interactable"
super-networked-interactable="counter: #counter; mass: 1;"
body="type: dynamic; shape: none; mass: 1;"
auto-scale-cannon-physics-body
grabbable
stretchable="useWorldPosition: true; usePhysics: never"
hoverable
duck
sticky-object="autoLockOnRelease: true;"
></a-entity>
</template>
<template id="interactable-model"> <template id="interactable-model">
<a-entity <a-entity
gltf-model-plus="inflate: false;" gltf-model-plus="inflate: false;"
......
...@@ -81,21 +81,6 @@ function registerNetworkSchemas() { ...@@ -81,21 +81,6 @@ function registerNetworkSchemas() {
] ]
}); });
NAF.schemas.add({
template: "#interactable-template",
components: [
{
component: "position",
requiresNetworkUpdate: vectorRequiresUpdate(0.001)
},
{
component: "rotation",
requiresNetworkUpdate: vectorRequiresUpdate(0.5)
},
"scale"
]
});
NAF.schemas.add({ NAF.schemas.add({
template: "#interactable-image", template: "#interactable-image",
components: [ components: [
......
...@@ -57,7 +57,7 @@ export const spawnNetworkedInteractable = src => { ...@@ -57,7 +57,7 @@ export const spawnNetworkedInteractable = src => {
model.id = "interactable-model-" + interactableId++; model.id = "interactable-model-" + interactableId++;
model.setAttribute("networked", { template: "#interactable-model" }); model.setAttribute("networked", { template: "#interactable-model" });
model.setAttribute("offset-relative-to", { model.setAttribute("offset-relative-to", {
on: "model-loaded", // on: "model-loaded",
target: "#player-camera", target: "#player-camera",
offset: offset, offset: offset,
selfDestruct: true selfDestruct: true
...@@ -76,9 +76,9 @@ export const addMedia = async url => { ...@@ -76,9 +76,9 @@ export const addMedia = async url => {
const contentType = await fetchContentType(farsparkUrl); const contentType = await fetchContentType(farsparkUrl);
if (contentType.startsWith("image/") || contentType.startsWith("video/")) { if (contentType.startsWith("image/") || contentType.startsWith("video/")) {
spawnNetworkedImage(farsparkUrl, contentType); return spawnNetworkedImage(farsparkUrl, contentType);
} else if (contentType.startsWith("model/gltf") || url.endsWith(".gltf") || url.endsWith(".glb")) { } else if (contentType.startsWith("model/gltf") || url.endsWith(".gltf") || url.endsWith(".glb")) {
spawnNetworkedInteractable(farsparkUrl); return spawnNetworkedInteractable(farsparkUrl);
} else { } else {
throw new Error(`Unsupported content type: ${contentType}`); throw new Error(`Unsupported content type: ${contentType}`);
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment