Newer
Older
import { getBox, getCenterAndHalfExtents, getScaleCoefficient } from "./auto-box-collider";
const whitelistedHosts = [/^.*\.reticulum\.io$/, /^.*hubs\.mozilla\.com$/, /^hubs\.local$/];
const isHostWhitelisted = hostname => !!whitelistedHosts.filter(r => r.test(hostname)).length;
let resolveMediaUrl = "/api/v1/media";
if (process.env.NODE_ENV === "development") {
resolveMediaUrl = `https://${process.env.DEV_RETICULUM_SERVER}${resolveMediaUrl}`;
}
export const resolveFarsparkUrl = async url => {
const parsedUrl = new URL(url);
if ((parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") || isHostWhitelisted(parsedUrl.hostname))
return { raw: url, origin: url };
return await fetch(resolveMediaUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ media: { url } })
}).then(r => r.json());
const fetchContentType = async url => fetch(url, { method: "HEAD" }).then(r => r.headers.get("content-type"));
let interactableId = 0;
const offset = { x: 0, y: 0, z: -1.5 };
export const spawnNetworkedImage = (entity, src, contentType) => {
entity.id = "interactable-image-" + interactableId++;
// entity.setAttribute("networked", { template: "#interactable-image" });
// entity.addEventListener("image-loaded", function onBodyLoaded() {
// entity.removeEventListener("image-loaded", onBodyLoaded);
// });
entity.setAttribute("image-plus", { src, contentType });
return entity;
export const spawnNetworkedInteractable = (entity, src, basePath) => {
entity.id = "interactable-model-" + interactableId++;
// entity.setAttribute("networked", { template: "#interactable-model" });
entity.addEventListener("model-loaded", function onModelLoaded(evt) {
entity.removeEventListener("model-loaded", onModelLoaded);
setShapeAndScale(entity, evt.detail.didInflate);
});
entity.setAttribute("gltf-model-plus", { src: src, basePath: basePath });
return entity;
AFRAME.registerComponent("media-loader", {
schema: {
src: { type: "string" }
},
async update() {
const entity = this.el;
const url = this.data.src;
try {
// show loading mesh
entity.setObject3D("mesh", new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshBasicMaterial()));
setShapeAndScale(entity);
const { raw, origin, meta } = await resolveFarsparkUrl(url);
console.log("resolved", url, raw, origin, meta);
const contentType = (meta && meta.expected_content_type) || (await fetchContentType(raw));
if (contentType.startsWith("image/") || contentType.startsWith("video/")) {
return spawnNetworkedImage(entity, raw, contentType);
} else if (contentType.startsWith("model/gltf") || url.endsWith(".gltf") || url.endsWith(".glb")) {
return spawnNetworkedInteractable(entity, raw, THREE.LoaderUtils.extractUrlBase(origin));
} else {
throw new Error(`Unsupported content type: ${contentType}`);
}
} catch (e) {
console.error("Error adding media", e);
return spawnNetworkedImage(entity, "error");
}
}
});
export const addMedia = url => {
const scene = AFRAME.scenes[0];
const entity = document.createElement("a-entity");
entity.setAttribute("networked", { template: "#interactable-media" });
// entity.setObject3D("mesh", new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshBasicMaterial()));
// entity.classList.add("interactable");
// entity.setAttribute("body", { type: "dynamic", shape: "none", mass: "1" });
// entity.setAttribute("grabbable", "");
// entity.setAttribute("hoverable", "");
// entity.setAttribute("stretchable", { useWorldPosition: true, usePhysics: "never" });
// entity.setAttribute("sticky-object", { autoLockOnRelease: true, autoLockOnLoad: true });
// entity.setAttribute("destroy-at-extreme-distances", "");
target: "#player-camera",
offset: offset,
selfDestruct: true
});
entity.setAttribute("media-loader", { src: url });
// setShapeAndScale(entity);
return entity;
function setShapeAndScale(entity, didInflate) {
const mesh = entity.getObject3D("mesh");
const boxRoot = didInflate ? mesh.parent : mesh;
const box = getBox(entity, boxRoot);
const scaleCoefficient = getScaleCoefficient(0.5, box);
if (entity.components.body && entity.components.body.body && entity.components.body.body.shapes.length > 1) {
entity.removeAttribute("shape");
} else {
const center = new THREE.Vector3();
const halfExtents = new THREE.Vector3();
getCenterAndHalfExtents(entity, box, center, halfExtents);
boxRoot.position.sub(center);
entity.setAttribute("shape", {
shape: "box",
halfExtents: halfExtents
});
}