mind-ar
Version:
web augmented reality framework
141 lines (140 loc) • 7.23 kB
JavaScript
import { Scene as m, WebGLRenderer as g, sRGBEncoding as y, PerspectiveCamera as w, Mesh as M, MeshStandardMaterial as b, Group as p, BufferGeometry as x, BufferAttribute as A } from "three";
import { CSS3DRenderer as E } from "three/addons/renderers/CSS3DRenderer.js";
import { C as R } from "./controller-d1-OMKPY.js";
import { U as S } from "./ui-fBadYuor.js";
const I = { BufferGeometry: x, BufferAttribute: A };
class C {
constructor({
container: a,
uiLoading: t = "yes",
uiScanning: o = "yes",
uiError: n = "yes",
filterMinCF: e = null,
filterBeta: h = null,
userDeviceId: s = null,
environmentDeviceId: c = null,
disableFaceMirror: l = !1
}) {
this.container = a, this.ui = new S({ uiLoading: t, uiScanning: o, uiError: n }), this.controller = new R({
filterMinCF: e,
filterBeta: h
}), this.disableFaceMirror = l, this.scene = new m(), this.cssScene = new m(), this.renderer = new g({ antialias: !0, alpha: !0 }), this.cssRenderer = new E({ antialias: !0 }), this.renderer.outputEncoding = y, this.renderer.setPixelRatio(window.devicePixelRatio), this.camera = new w(), this.userDeviceId = s, this.environmentDeviceId = c, this.anchors = [], this.faceMeshes = [], this.latestEstimate = null, this.container.appendChild(this.renderer.domElement), this.container.appendChild(this.cssRenderer.domElement), this.shouldFaceUser = !0, window.addEventListener("resize", this._resize.bind(this));
}
async start() {
this.ui.showLoading(), await this._startVideo(), await this._startAR(), this.ui.hideLoading();
}
stop() {
this.video.srcObject.getTracks().forEach(function(t) {
t.stop();
}), this.video.remove(), this.controller.stopProcessVideo();
}
switchCamera() {
this.shouldFaceUser = !this.shouldFaceUser, this.stop(), this.start();
}
addFaceMesh() {
const a = this.controller.createThreeFaceGeometry(I), t = new M(a, new b({ color: 16777215 }));
return t.visible = !1, t.matrixAutoUpdate = !1, this.faceMeshes.push(t), t;
}
addAnchor(a) {
const t = new p();
t.matrixAutoUpdate = !1;
const o = { group: t, landmarkIndex: a, css: !1 };
return this.anchors.push(o), this.scene.add(t), o;
}
addCSSAnchor(a) {
const t = new p();
t.matrixAutoUpdate = !1;
const o = { group: t, landmarkIndex: a, css: !0 };
return this.anchors.push(o), this.cssScene.add(t), o;
}
getLatestEstimate() {
return this.latestEstimate;
}
_startVideo() {
return new Promise((a, t) => {
if (this.video = document.createElement("video"), this.video.setAttribute("autoplay", ""), this.video.setAttribute("muted", ""), this.video.setAttribute("playsinline", ""), this.video.style.position = "absolute", this.video.style.top = "0px", this.video.style.left = "0px", this.video.style.zIndex = "-2", this.container.appendChild(this.video), !navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
this.ui.showCompatibility(), t();
return;
}
const o = {
audio: !1,
video: {}
};
this.shouldFaceUser ? this.userDeviceId ? o.video.deviceId = { exact: this.userDeviceId } : o.video.facingMode = "user" : this.environmentDeviceId ? o.video.deviceId = { exact: this.environmentDeviceId } : o.video.facingMode = "environment", navigator.mediaDevices.getUserMedia(o).then((n) => {
this.video.addEventListener("loadedmetadata", () => {
this.video.setAttribute("width", this.video.videoWidth), this.video.setAttribute("height", this.video.videoHeight), a();
}), this.video.srcObject = n;
}).catch((n) => {
console.log("getUserMedia error", n), t();
});
});
}
_startAR() {
return new Promise(async (a, t) => {
const o = this.video;
this.container, this.controller.onUpdate = ({ hasFace: e, estimateResult: h }) => {
for (let s = 0; s < this.anchors.length; s++)
this.anchors[s].css ? this.anchors[s].group.children.forEach((c) => {
c.element.style.visibility = e ? "visible" : "hidden";
}) : this.anchors[s].group.visible = e;
for (let s = 0; s < this.faceMeshes.length; s++)
this.faceMeshes[s].visible = e;
if (e) {
const { metricLandmarks: s, faceMatrix: c, faceScale: l, blendshapes: d } = h;
this.latestEstimate = h;
for (let r = 0; r < this.anchors.length; r++) {
const v = this.anchors[r].landmarkIndex, i = this.controller.getLandmarkMatrix(v);
if (this.anchors[r].css) {
const u = [
1e-3 * i[0],
1e-3 * i[1],
i[2],
i[3],
1e-3 * i[4],
1e-3 * i[5],
i[6],
i[7],
1e-3 * i[8],
1e-3 * i[9],
i[10],
i[11],
1e-3 * i[12],
1e-3 * i[13],
i[14],
i[15]
];
this.anchors[r].group.matrix.set(...u);
} else
this.anchors[r].group.matrix.set(...i);
}
for (let r = 0; r < this.faceMeshes.length; r++)
this.faceMeshes[r].matrix.set(...c);
} else
this.latestEstimate = null;
}, this._resize();
const n = this.shouldFaceUser && !this.disableFaceMirror;
await this.controller.setup(n), await this.controller.dummyRun(o), this._resize(), this.controller.processVideo(o), a();
});
}
_resize() {
const { renderer: a, cssRenderer: t, camera: o, container: n, video: e } = this;
if (!e)
return;
{
this.video.setAttribute("width", this.video.videoWidth), this.video.setAttribute("height", this.video.videoHeight), this.controller.onInputResized(e);
const { fov: v, aspect: i, near: f, far: u } = this.controller.getCameraParams();
this.camera.fov = v, this.camera.aspect = i, this.camera.near = f, this.camera.far = u, this.camera.updateProjectionMatrix(), this.renderer.setSize(this.video.videoWidth, this.video.videoHeight), this.cssRenderer.setSize(this.video.videoWidth, this.video.videoHeight);
}
let h, s;
const c = e.videoWidth / e.videoHeight, l = n.clientWidth / n.clientHeight;
c > l ? (s = n.clientHeight, h = s * c) : (h = n.clientWidth, s = h / c), e.style.top = -(s - n.clientHeight) / 2 + "px", e.style.left = -(h - n.clientWidth) / 2 + "px", e.style.width = h + "px", e.style.height = s + "px", this.shouldFaceUser && !this.disableFaceMirror ? e.style.transform = "scaleX(-1)" : e.style.transform = "scaleX(1)";
const d = a.domElement, r = t.domElement;
d.style.position = "absolute", d.style.top = e.style.top, d.style.left = e.style.left, d.style.width = e.style.width, d.style.height = e.style.height, r.style.position = "absolute", r.style.top = e.style.top, r.style.left = e.style.left, r.style.transformOrigin = "top left", r.style.transform = "scale(" + h / parseFloat(r.style.width) + "," + s / parseFloat(r.style.height) + ")";
}
}
window.MINDAR || (window.MINDAR = {});
window.MINDAR.FACE || (window.MINDAR.FACE = {});
window.MINDAR.FACE.MindARThree = C;
export {
C as MindARThree
};