@needle-tools/engine
Version:
Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.
278 lines (277 loc) • 10.3 kB
JavaScript
import { getParam } from "../engine_utils.js";
import { getErrorCount } from "./debug_overlay.js";
let consoleInstance = undefined;
let consoleHtmlElement = null;
let consoleSwitchButton = null;
let isLoading = false;
let isVisible = false;
let watchInterval = null;
const defaultButtonIcon = "terminal";
const showConsole = getParam("console");
if (showConsole) {
showDebugConsole();
}
const $defaultConsoleParent = Symbol("consoleParent");
export function showDebugConsole() {
if (consoleInstance) {
isVisible = true;
consoleInstance.showSwitch();
return;
}
createConsole();
}
export function hideDebugConsole() {
if (!consoleInstance)
return;
isVisible = false;
consoleInstance.hide();
consoleInstance.hideSwitch();
}
function beginWatchingLogs() {
if (watchInterval)
return;
watchInterval = setInterval(consoleElementUpdateInterval, 500);
}
let lastErrorCount = 0;
function consoleElementUpdateInterval() {
const currentCount = getErrorCount();
const receivedNewErrors = currentCount !== lastErrorCount;
lastErrorCount = currentCount;
if (receivedNewErrors) {
onNewConsoleErrors();
}
}
function onNewConsoleErrors() {
showDebugConsole();
if (consoleSwitchButton) {
consoleSwitchButton.setAttribute("error", "true");
consoleSwitchButton.innerText = "🤬";
}
}
function onConsoleSwitchButtonClicked() {
if (consoleSwitchButton) {
consoleSwitchButton.removeAttribute("error");
consoleSwitchButton.innerText = defaultButtonIcon;
}
}
function onResetConsoleElementToDefaultParent() {
if (consoleHtmlElement && consoleHtmlElement[$defaultConsoleParent]) {
consoleHtmlElement[$defaultConsoleParent].appendChild(consoleHtmlElement);
}
}
function createConsole(startHidden = false) {
if (consoleInstance !== undefined)
return;
if (isLoading)
return;
isLoading = true;
const script = document.createElement("script");
script.onload = () => {
// check if VConsole is now defined on globalThis
if (!globalThis.VConsole) {
console.warn("🌵 Debug console failed to load.");
isLoading = false;
consoleInstance = null;
return;
}
isLoading = false;
isVisible = true;
beginWatchingLogs();
consoleInstance = new VConsole({
// defaultPlugins: ['system', 'network'],
pluginOrder: ['default', 'needle-console'],
});
const files = globalThis["needle:codegen_files"];
if (files && files.length > 0) {
consoleInstance.addPlugin(createInspectPlugin());
}
consoleHtmlElement = getConsoleElement();
if (consoleHtmlElement) {
consoleHtmlElement[$defaultConsoleParent] = consoleHtmlElement.parentElement;
consoleHtmlElement.style.position = "absolute";
consoleHtmlElement.style.zIndex = Number.MAX_SAFE_INTEGER.toString();
// const styleSheetList = document.styleSheets;
// for (let i = 0; i < styleSheetList.length; i++) {
// const styleSheet = styleSheetList[i];
// const firstRule = styleSheet.cssRules[0] as CSSStyleRule;
// if(firstRule && firstRule.selectorText === "#__vconsole") {
// console.log("found vconsole style sheet");
// const styleTag = document.createElement("style");
// styleTag.innerHTML = "#__needleconsole {}";
// for (let j = 0; j < styleSheet.cssRules.length; j++) {
// const rule = styleSheet.cssRules[j] as CSSStyleRule;
// styleTag.innerHTML += rule.cssText;
// }
// consoleHtmlElement.appendChild(styleTag);
// }
// }
}
consoleInstance.setSwitchPosition(20, 30);
consoleSwitchButton = getConsoleSwitchButton();
if (consoleSwitchButton) {
consoleSwitchButton.innerText = defaultButtonIcon;
consoleSwitchButton.addEventListener("click", onConsoleSwitchButtonClicked);
const styles = document.createElement("style");
const size = 40;
styles.innerHTML = `
#__vconsole .vc-switch {
border: 1px solid rgba(255, 255, 255, .1);
border-radius: 50%;
width: ${size}px;
height: ${size}px;
padding: 0;
line-height: ${size}px;
font-size: ${size * .4}px;
text-align: center;
background: #ffffff5c;
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
user-select: none;
pointer-events: auto;
transition: transform .2s ease-in-out;
box-shadow: 0px 7px 0.5rem 0px rgb(0 0 0 / 6%), inset 0px 0px 1.3rem rgba(0,0,0,.05);
font-family: 'Material Symbols Outlined';
color: black;
font-size: 2.3em;
font-weight: 100;
}
#__vconsole .vc-switch:hover {
cursor: pointer;
transform: scale(1.1);
transition: transform .1s ease-in-out, background .1s linear;
background: rgba(245, 245, 245, .8);
outline: rgba(0, 0, 0, .05) 1px solid;
}
#__vconsole .vc-switch[error] {
background: rgba(255,0,0,.2);
animation: vconsole-notify 1s ease-in-out;
line-height: 35px;
}
@keyframes vconsole-notify {
from {
transform: scale(1, 1);
}
10% {
transform: scale(1.3, 1.3);
}
70% {
transform: scale(1.4, 1.4);
}
to {
transform: scale(1, 1);
}
}
#__vconsole .vc-panel {
font-family: monospace;
font-size: 11px;
}
#__vconsole .vc-plugin-box.vc-actived {
height: 100%;
}
#__vconsole .vc-mask {
overflow: hidden;
}
`;
consoleHtmlElement?.prepend(styles);
if (startHidden === true && getErrorCount() <= 0)
hideDebugConsole();
console.log("🌵 Debug console has loaded");
}
};
script.onerror = () => {
console.warn("🌵 Debug console failed to load." + (window.crossOriginIsolated ? "This page is using cross-origin isolation, so external scripts can't be loaded." : ""));
isLoading = false;
consoleInstance = null;
};
script.src = "https://cdn.jsdelivr.net/npm/vconsole@3.15.1/dist/vconsole.min.js";
document.body.appendChild(script);
}
function createInspectPlugin() {
if (!globalThis.VConsole)
return;
const plugin = new VConsole.VConsolePlugin("needle-console", "🌵 Inspect glTF");
const getIframe = () => {
return document.querySelector("#__vc_plug_" + plugin._id + " iframe");
};
plugin.on('renderTab', function (callback) {
const files = globalThis["needle:codegen_files"];
if (!files || files.length === 0)
return;
let query = globalThis["needle:codegen_files"][0];
const index = query.indexOf("?");
if (index > -1)
query = query.substring(0, index);
const currentAbsolutePath = location.protocol + '//' + location.host + location.pathname;
const currentPath = currentAbsolutePath + "/" + query;
const urlEncoded = encodeURIComponent(currentPath);
plugin.fullUrl = "https://viewer.needle.tools?inspect&file=" + urlEncoded;
var html = `<iframe src="" style="width: 100%; height: 99%; border: none;"></iframe>`;
callback(html);
});
plugin.on('show', function () {
const elem = getIframe();
if (elem && elem.src !== plugin.fullUrl)
elem.src = plugin.fullUrl;
});
plugin.on('hide', function () {
const elem = getIframe();
if (elem)
elem.src = "";
});
/* bottom tool bar
plugin.on('addTool', function(callback) {
var button = {
name: 'Reload',
onClick: function(event) {
location.reload();
}
};
callback([button]);
});
*/
plugin.on('addTopBar', function (callback) {
var btnList = new Array();
btnList.push({
name: 'Open in new window ↗',
onClick: function (_event) {
window.open(plugin.fullUrl, '_blank');
consoleInstance?.hide();
}
});
btnList.push({
name: 'Reload',
onClick: function (_event) {
const iframe = getIframe();
if (iframe)
iframe.src = plugin.fullUrl;
}
});
btnList.push({
name: 'Fullscreen',
onClick: function (_event) {
const iframe = getIframe();
if (iframe.requestFullscreen) {
iframe.requestFullscreen();
}
else if (iframe["webkitRequestFullscreen"] instanceof Function) {
iframe["webkitRequestFullscreen"]();
}
}
});
callback(btnList);
});
return plugin;
}
function getConsoleSwitchButton() {
const el = document.querySelector("#__vconsole .vc-switch");
if (el)
return el;
return null;
}
function getConsoleElement() {
const el = document.querySelector("#__vconsole");
if (el)
return el;
return null;
}
//# sourceMappingURL=debug_console.js.map