mirror of
https://github.com/imgfloat/server.git
synced 2026-02-05 11:49:25 +00:00
Add support for 3d models in assets and attachments
This commit is contained in:
@@ -16,6 +16,13 @@ export function isVideoAsset(asset) {
|
||||
return asset?.mediaType?.startsWith("video/");
|
||||
}
|
||||
|
||||
export function isModelAsset(asset) {
|
||||
if (asset?.assetType) {
|
||||
return asset.assetType === "MODEL";
|
||||
}
|
||||
return asset?.mediaType?.startsWith("model/");
|
||||
}
|
||||
|
||||
export function isVideoElement(element) {
|
||||
return element?.tagName === "VIDEO";
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { AssetKind, MIN_FRAME_TIME, VISIBILITY_THRESHOLD } from "./constants.js";
|
||||
import { createBroadcastState } from "./state.js";
|
||||
import { getAssetKind, isCodeAsset, isVisualAsset, isVideoElement } from "./assetKinds.js";
|
||||
import { getAssetKind, isCodeAsset, isModelAsset, isVisualAsset, isVideoElement } from "./assetKinds.js";
|
||||
import { ensureLayerPosition, getLayerOrder, getRenderOrder } from "./layers.js";
|
||||
import { getVisibilityState, smoothState } from "./visibility.js";
|
||||
import { createAudioManager } from "./audioManager.js";
|
||||
import { createMediaManager } from "./mediaManager.js";
|
||||
import { createModelManager } from "../media/modelManager.js";
|
||||
|
||||
export class BroadcastRenderer {
|
||||
constructor({ canvas, scriptCanvas, broadcaster, showToast }) {
|
||||
@@ -37,6 +38,7 @@ export class BroadcastRenderer {
|
||||
supportsAnimatedDecode: this.supportsAnimatedDecode,
|
||||
canPlayProbe: this.canPlayProbe,
|
||||
});
|
||||
this.modelManager = createModelManager({ requestDraw: () => this.draw() });
|
||||
|
||||
this.applyCanvasSettings(this.state.canvasSettings);
|
||||
globalThis.addEventListener("resize", () => {
|
||||
@@ -105,6 +107,7 @@ export class BroadcastRenderer {
|
||||
this.state.assets.delete(assetId);
|
||||
this.state.layerOrder = this.state.layerOrder.filter((id) => id !== assetId);
|
||||
this.mediaManager.clearMedia(assetId);
|
||||
this.modelManager.clearModel(assetId);
|
||||
this.stopUserJavaScriptWorker(assetId);
|
||||
this.state.renderStates.delete(assetId);
|
||||
this.state.visibilityStates.delete(assetId);
|
||||
@@ -348,9 +351,17 @@ export class BroadcastRenderer {
|
||||
return;
|
||||
}
|
||||
|
||||
const media = this.mediaManager.ensureMedia(asset);
|
||||
const drawSource = media?.isAnimated ? media.bitmap : media;
|
||||
const ready = this.isDrawable(media);
|
||||
let drawSource = null;
|
||||
let ready = false;
|
||||
if (isModelAsset(asset)) {
|
||||
const model = this.modelManager.ensureModel(asset);
|
||||
drawSource = model?.canvas || null;
|
||||
ready = !!model?.ready;
|
||||
} else {
|
||||
const media = this.mediaManager.ensureMedia(asset);
|
||||
drawSource = media?.isAnimated ? media.bitmap : media;
|
||||
ready = this.isDrawable(media);
|
||||
}
|
||||
if (ready && drawSource) {
|
||||
this.ctx.drawImage(drawSource, -halfWidth, -halfHeight, renderState.width, renderState.height);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,37 @@ let lastTick = 0;
|
||||
let startTime = 0;
|
||||
const tickIntervalMs = 1000 / 60;
|
||||
const errorKeys = new Set();
|
||||
const allowedImportUrls = new Set();
|
||||
const nativeImportScripts = typeof self.importScripts === "function" ? self.importScripts.bind(self) : null;
|
||||
const sharedDependencyUrls = ["/js/vendor/three.min.js", "/js/vendor/GLTFLoader.js", "/js/vendor/OBJLoader.js"];
|
||||
|
||||
function normalizeUrl(url) {
|
||||
try {
|
||||
return new URL(url, self.location?.href || "http://localhost").toString();
|
||||
} catch (_error) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function registerAllowedImport(url) {
|
||||
const normalized = normalizeUrl(url);
|
||||
if (normalized) {
|
||||
allowedImportUrls.add(normalized);
|
||||
}
|
||||
}
|
||||
|
||||
sharedDependencyUrls.forEach(registerAllowedImport);
|
||||
|
||||
function importAllowedScripts(...urls) {
|
||||
if (!nativeImportScripts) {
|
||||
throw new Error("Network access is disabled in asset scripts.");
|
||||
}
|
||||
const resolved = urls.map((url) => normalizeUrl(url));
|
||||
if (resolved.some((url) => !allowedImportUrls.has(url))) {
|
||||
throw new Error("Network access is disabled in asset scripts.");
|
||||
}
|
||||
return nativeImportScripts(...resolved);
|
||||
}
|
||||
|
||||
function disableNetworkApis() {
|
||||
const nativeFetch = typeof self.fetch === "function" ? self.fetch.bind(self) : null;
|
||||
@@ -26,9 +57,7 @@ function disableNetworkApis() {
|
||||
XMLHttpRequest: undefined,
|
||||
WebSocket: undefined,
|
||||
EventSource: undefined,
|
||||
importScripts: () => {
|
||||
throw new Error("Network access is disabled in asset scripts.");
|
||||
},
|
||||
importScripts: (...urls) => importAllowedScripts(...urls),
|
||||
};
|
||||
|
||||
Object.entries(blockedApis).forEach(([key, value]) => {
|
||||
@@ -53,14 +82,15 @@ function disableNetworkApis() {
|
||||
|
||||
disableNetworkApis();
|
||||
|
||||
function normalizeUrl(url) {
|
||||
try {
|
||||
return new URL(url, self.location?.href || "http://localhost").toString();
|
||||
} catch (_error) {
|
||||
return "";
|
||||
function loadSharedDependencies() {
|
||||
if (!nativeImportScripts || sharedDependencyUrls.length === 0) {
|
||||
return;
|
||||
}
|
||||
importAllowedScripts(...sharedDependencyUrls);
|
||||
}
|
||||
|
||||
loadSharedDependencies();
|
||||
|
||||
function refreshAllowedFetchUrls() {
|
||||
allowedFetchUrls.clear();
|
||||
scripts.forEach((script) => {
|
||||
|
||||
Reference in New Issue
Block a user