491 lines · 20.3 KB · JavaScript
RawDownload
"use strict"; //  entityList.js////  Created by Ryan Huffman on November 19th, 2014//  Copyright 2014 High Fidelity, Inc.//  Copyright 2020 Vircadia contributors.//  Copyright 2023-2025 Overte e.V.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html//  SPDX-License-Identifier: Apache-2.0// /* global EntityListTool, Tablet, Entities, Camera, MyAvatar, Vec3, Menu, Messages,   MENU_EASE_ON_FOCUS,   Script, Clipboard */ var PROFILING_ENABLED = false;var profileIndent = ''; const PROFILE_NOOP = function(_name, fn, args) {    fn.apply(this, args);};const PROFILE = !PROFILING_ENABLED ? PROFILE_NOOP : function(name, fn, args) {    console.log("PROFILE-Script " + profileIndent + "(" + name + ") Begin");    var previousIndent = profileIndent;    profileIndent += '  ';    var before = Date.now();    fn.apply(this, args);    var delta = Date.now() - before;    profileIndent = previousIndent;    console.log("PROFILE-Script " + profileIndent + "(" + name + ") End " + delta + "ms");}; var EntityListTool = function(shouldUseEditTabletApp, selectionManager) {    var that = {};    that.selectionManager = selectionManager;     var CreateWindow = Script.require('../modules/createWindow.js');     var TITLE_OFFSET = 60;    var ENTITY_LIST_WIDTH = 495;    var MAX_DEFAULT_CREATE_TOOLS_HEIGHT = 778;    var entityListWindow = new CreateWindow(        Script.resolvePath("./qml/EditEntityList.qml"),        'Entity List',        'com.highfidelity.create.entityListWindow',        function () {            var windowHeight = Window.innerHeight - TITLE_OFFSET;            if (windowHeight > MAX_DEFAULT_CREATE_TOOLS_HEIGHT) {                windowHeight = MAX_DEFAULT_CREATE_TOOLS_HEIGHT;            }            return {                size: {                    x: ENTITY_LIST_WIDTH,                    y: windowHeight                },                position: {                    x: Window.x,                    y: Window.y + TITLE_OFFSET                }            };        },        false    );     var webView = null;    webView = Tablet.getTablet("com.highfidelity.interface.tablet.system");    webView.setVisible = function(value){ };     var filterInView = false;    var searchRadius = 100;    var localEntityFilter = false;     var visible = false;     that.webView = webView;     that.setVisible = function(newVisible) {        visible = newVisible;        webView.setVisible(shouldUseEditTabletApp() && visible);        entityListWindow.setVisible(!shouldUseEditTabletApp() && visible);    };     that.isVisible = function() {        return entityListWindow.isVisible();    };     that.setVisible(false);     function emitJSONScriptEvent(data) {        var dataString;        PROFILE("Script-JSON.stringify", function() {            dataString = JSON.stringify(data);        });        PROFILE("Script-emitScriptEvent", function() {            webView.emitScriptEvent(dataString);            if (entityListWindow.window) {                entityListWindow.window.emitScriptEvent(dataString);            }        });    }     that.toggleVisible = function() {        that.setVisible(!visible);    };     selectionManager.addEventListener(function(isSelectionUpdate, caller) {        if (caller === that) {            // ignore events that we emitted from the entity list itself            return;        }        // Otherwise this will emit tens of events every second when objects are moved.        if (!isSelectionUpdate) {            return;        }        var selectedIDs = [];         for (var i = 0; i < that.selectionManager.selections.length; i++) {            selectedIDs.push(that.selectionManager.selections[i]);        }         emitJSONScriptEvent({            type: 'selectionUpdate',            selectedIDs: selectedIDs        });    });     that.setSpaceMode = function(spaceMode) {        emitJSONScriptEvent({            type: 'setSpaceMode',            spaceMode: spaceMode        });    };     that.clearEntityList = function() {        emitJSONScriptEvent({            type: 'clearEntityList'        });    };     that.removeEntities = function (deletedIDs, selectedIDs) {        emitJSONScriptEvent({            type: 'removeEntities',            deletedIDs: deletedIDs,            selectedIDs: selectedIDs        });    };     that.deleteEntities = function (deletedIDs) {        emitJSONScriptEvent({            type: "deleted",            ids: deletedIDs        });    };     that.setListMenuSnapToGrid = function (isSnapToGrid) {        emitJSONScriptEvent({ "type": "setSnapToGrid", "snap": isSnapToGrid });    };     that.toggleSnapToGrid = function () {        if (!grid.getSnapToGrid()) {            grid.setSnapToGrid(true);            emitJSONScriptEvent({ "type": "setSnapToGrid", "snap": true });        } else {            grid.setSnapToGrid(false);            emitJSONScriptEvent({ "type": "setSnapToGrid", "snap": false });        }    };     function valueIfDefined(value) {        return value !== undefined ? value : "";    }     function entityIsBaked(properties) {        if (properties.type === "Model") {            var lowerModelURL = properties.modelURL.toLowerCase();            return lowerModelURL.endsWith(".baked.fbx") || lowerModelURL.endsWith(".baked.fst");        } else if (properties.type === "Zone") {            var lowerSkyboxURL = properties.skybox ? properties.skybox.url.toLowerCase() : "";            var lowerAmbientURL = properties.ambientLight ? properties.ambientLight.ambientURL.toLowerCase() : "";            return (lowerSkyboxURL === "" || lowerSkyboxURL.endsWith(".texmeta.json")) &&                (lowerAmbientURL === "" || lowerAmbientURL.endsWith(".texmeta.json"));        } else {            return false;        }    }     that.sendUpdate = function() {        var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");        if (HMD.active) {            tablet.setLandscape(true);        }        emitJSONScriptEvent({            "type": "confirmHMDstate",            "isHmd": HMD.active        });                PROFILE('Script-sendUpdate', function() {            var entities = [];             var ids;            PROFILE("findEntities", function() {                var domainAndAvatarIds;                if (filterInView) {                    domainAndAvatarIds = Entities.findEntitiesInFrustum(Camera.frustum);                } else {                    domainAndAvatarIds = Entities.findEntities(MyAvatar.position, searchRadius);                }                 const avatarFilterProps = Entities.getMultipleEntityProperties(domainAndAvatarIds, ['entityHostType', 'owningAvatarID']);                 // Exclude any avatar entities that we don't own                domainAndAvatarIds = domainAndAvatarIds.filter((_id, index) => {                    const { entityHostType, owningAvatarID } = avatarFilterProps[index];                    return entityHostType !== "avatar" ||                           owningAvatarID === MyAvatar.SELF_ID ||                           owningAvatarID === MyAvatar.sessionUUID;                });                 var localIds = [];                if (localEntityFilter) {                    localIds = Overlays.findOverlays(MyAvatar.position, searchRadius);                    var tabletLocalEntityToExclude = [                        HMD.tabletID,                        HMD.tabletScreenID,                        HMD.homeButtonID,                        HMD.homeButtonHighlightID,                        HMD.miniTabletID,                        HMD.miniTabletScreenID,                        that.grid.getGridEntityToolID()                    ];                    var seltoolsIds = SelectionDisplay.toolEntityMaterial.concat(                        SelectionDisplay.allToolEntities,                         allOverlays,                        that.createApp.entityShapeVisualizerLocalEntityToExclude,                        tabletLocalEntityToExclude                    );                    for (var i = localIds.length - 1; i >= 0; i--) {                        if (seltoolsIds.includes(localIds[i]) || Keyboard.containsID(localIds[i])) {                            localIds.splice(i, 1);                        }                    }                }                 ids = domainAndAvatarIds.concat(localIds);            });             var cameraPosition = Camera.position;            PROFILE("getMultipleProperties", function () {                var multipleProperties = Entities.getMultipleEntityProperties(ids, ['position', 'name', 'type', 'locked',                    'visible', 'renderInfo', 'modelURL', 'materialURL', 'imageURL', 'script', 'serverScripts',                    'skybox.url', 'ambientLight.url', 'soundURL', 'scriptURL', 'created', 'lastEdited', 'entityHostType']);                for (var i = 0; i < multipleProperties.length; i++) {                    var properties = multipleProperties[i];                     if (!filterInView || Vec3.distance(properties.position, cameraPosition) <= searchRadius) {                        var url = "";                        if (properties.type === "Model") {                            url = properties.modelURL;                        } else if (properties.type === "Material") {                            url = properties.materialURL;                        } else if (properties.type === "Image") {                            url = properties.imageURL;                        } else if (properties.type === "Sound") {                            url = properties.soundURL;                        } else if (properties.type === "Script") {                            url = properties.scriptURL;                        }                        //print("Global object before getParentState call: " + JSON.stringify(globalThis));                        var parentStatus = that.createApp.getParentState(ids[i]);                        var parentState = "";                        if (parentStatus === "PARENT") {                            parentState = "A";                        } else if (parentStatus === "CHILDREN") {                            parentState = "C";                        } else if (parentStatus === "PARENT_CHILDREN") {                            parentState = "B";                        }                         entities.push({                            id: ids[i],                            name: properties.name,                            type: properties.type,                            url: url,                            locked: properties.locked,                            visible: properties.visible,                            verticesCount: (properties.renderInfo !== undefined ?                                valueIfDefined(properties.renderInfo.verticesCount) : ""),                            texturesCount: (properties.renderInfo !== undefined ?                                valueIfDefined(properties.renderInfo.texturesCount) : ""),                            texturesSize: (properties.renderInfo !== undefined ?                                valueIfDefined(properties.renderInfo.texturesSize) : ""),                            hasTransparent: (properties.renderInfo !== undefined ?                                valueIfDefined(properties.renderInfo.hasTransparent) : ""),                            isBaked: entityIsBaked(properties),                            drawCalls: (properties.renderInfo !== undefined ?                                valueIfDefined(properties.renderInfo.drawCalls) : ""),                            hasScript: (properties.script !== "" || properties.serverScripts !== ""),                            parentState: parentState,                            created: formatToStringDateTime(properties.created),                            lastEdited: formatToStringDateTime(properties.lastEdited),                            entityHostType: properties.entityHostType                        });                    }                }            });             var selectedIDs = [];            for (var j = 0; j < that.selectionManager.selections.length; j++) {                selectedIDs.push(that.selectionManager.selections[j]);            }             emitJSONScriptEvent({                type: "update",                entities: entities,                selectedIDs: selectedIDs,                spaceMode: SelectionDisplay.getSpaceMode(),            });        });    };     function formatToStringDateTime(timestamp) {        var d = new Date(Math.floor(timestamp/1000));        var dateTime = d.getUTCFullYear() + "-" + zeroPad((d.getUTCMonth() + 1), 2) + "-" + zeroPad(d.getUTCDate(), 2);        dateTime = dateTime + " " + zeroPad(d.getUTCHours(), 2) + ":" + zeroPad(d.getUTCMinutes(), 2) + ":" + zeroPad(d.getUTCSeconds(), 2);         dateTime = dateTime + "." + zeroPad(d.getUTCMilliseconds(), 3);        return dateTime;    }     function zeroPad(num, size) {        num = num.toString();        while (num.length < size) {            num = "0" + num;        }        return num;    }     function onFileSaveChanged(filename) {        Window.saveFileChanged.disconnect(onFileSaveChanged);        if (filename !== "") {            var success = Clipboard.exportEntities(filename, that.selectionManager.selections);            if (!success) {                Window.notifyEditError("Export failed.");            }        }    }     var onWebEventReceived = function(data) {        //print("entityList.js onWebEventReceived: " + data);        try {            data = JSON.parse(data);        } catch(e) {            print("entityList.js: Error parsing JSON");            return;        }         if (data.type === "selectionUpdate") {            var ids = data.entityIds;            var entityIDs = [];            for (var i = 0; i < ids.length; i++) {                entityIDs.push(ids[i]);            }            that.selectionManager.setSelections(entityIDs, that);            if (data.focus) {                that.cameraManager.enable();                that.cameraManager.focus(that.selectionManager.worldPosition,                                    that.selectionManager.worldDimensions,                                    Menu.isOptionChecked(MENU_EASE_ON_FOCUS));            }        } else if (data.type === "refresh") {            that.sendUpdate();        } else if (data.type === "teleport") {            if (that.selectionManager.hasSelection()) {                MyAvatar.position = that.selectionManager.worldPosition;            }        } else if (data.type === "export") {            if (!that.selectionManager.hasSelection()) {                Window.notifyEditError("No entities have been selected.");            } else {                Window.saveFileChanged.connect(onFileSaveChanged);                Window.saveAsync("Select Where to Save", "", "*.json");            }        } else if (data.type === "delete") {            that.createApp.deleteSelectedEntities();        } else if (data.type === "toggleLocked") {            that.createApp.toggleSelectedEntitiesLocked();        } else if (data.type === "toggleVisible") {            that.createApp.toggleSelectedEntitiesVisible();        } else if (data.type === "filterInView") {            filterInView = data.filterInView === true;        } else if (data.type === "radius") {            searchRadius = data.radius;        } else if (data.type === "cut") {            that.selectionManager.cutSelectedEntities();        } else if (data.type === "copy") {            that.selectionManager.copySelectedEntities();        } else if (data.type === "copyID") {            that.selectionManager.copyIdsFromSelectedEntities();        } else if (data.type === "paste") {            that.selectionManager.pasteEntities();        } else if (data.type === "duplicate") {            that.selectionManager.duplicateSelection();            that.sendUpdate();        } else if (data.type === "rename") {            Entities.editEntity(data.entityID, {name: data.name});            // make sure that the name also gets updated in the properties window            that.selectionManager._update();        } else if (data.type === "toggleSpaceMode") {            SelectionDisplay.toggleSpaceMode();        } else if (data.type === 'keyUpEvent') {            that.createApp.keyUpEventFromUIWindow(data.keyUpEvent);        } else if (data.type === 'undo') {            that.createApp.undoHistory.undo();        } else if (data.type === 'redo') {            that.createApp.undoHistory.redo();        } else if (data.type === 'parent') {            that.createApp.parentSelectedEntities();        } else if (data.type === 'unparent') {            that.createApp.unparentSelectedEntities();        } else if (data.type === 'hmdMultiSelectMode') {            that.createApp.hmdMultiSelectMode = data.value;        } else if (data.type === 'selectAllInBox') {            that.createApp.selectAllEntitiesInCurrentSelectionBox(false);        } else if (data.type === 'selectAllTouchingBox') {            that.createApp.selectAllEntitiesInCurrentSelectionBox(true);        } else if (data.type === 'selectParent') {            that.selectionManager.selectParent();        } else if (data.type === 'selectTopParent') {            that.selectionManager.selectTopParent();        } else if (data.type === 'addChildrenToSelection') {            that.selectionManager.addChildrenToSelection();        } else if (data.type === 'selectFamily') {            that.selectionManager.selectFamily();        } else if (data.type === 'selectTopFamily') {            that.selectionManager.selectTopFamily();        } else if (data.type === 'teleportToEntity') {            that.selectionManager.teleportToEntity();        } else if (data.type === 'rotateAsTheNextClickedSurface') {            that.createApp.rotateAsNextClickedSurface();        } else if (data.type === 'quickRotate90x') {            that.selectionDisplay.rotate90degreeSelection("X");        } else if (data.type === 'quickRotate90y') {            that.selectionDisplay.rotate90degreeSelection("Y");        } else if (data.type === 'quickRotate90z') {            that.selectionDisplay.rotate90degreeSelection("Z");        } else if (data.type === 'moveEntitySelectionToAvatar') {            that.selectionManager.moveEntitiesSelectionToAvatar();        } else if (data.type === 'loadConfigSetting') {            var columnsData = Settings.getValue(that.createApp.SETTING_EDITOR_COLUMNS_SETUP, "NO_DATA");            var defaultRadius = Settings.getValue(that.createApp.SETTING_ENTITY_LIST_DEFAULT_RADIUS, 100);            emitJSONScriptEvent({                "type": "loadedConfigSetting",                "columnsData": columnsData,                "defaultRadius": defaultRadius,                "localEntityFilter": localEntityFilter            });        } else if (data.type === 'saveColumnsConfigSetting') {            Settings.setValue(that.createApp.SETTING_EDITOR_COLUMNS_SETUP, data.columnsData);        } else if (data.type === 'importFromFile') {            that.createApp.importEntitiesFromFile();        } else if (data.type === 'importFromUrl') {            that.createApp.importEntitiesFromUrl();        } else if (data.type === 'setCameraFocusToSelection') {            that.createApp.setCameraFocusToSelection();        } else if (data.type === 'alignGridToSelection') {            that.createApp.alignGridToSelection();        } else if (data.type === 'alignGridToAvatar') {            that.createApp.alignGridToAvatar();        } else if (data.type === 'brokenURLReport') {            brokenURLReport(that.selectionManager.selections);        } else if (data.type === 'renderWithZonesManager') {            renderWithZonesManager(that.selectionManager.selections);        } else if (data.type === 'toggleGridVisibility') {            that.createApp.toggleGridVisibility();        } else if (data.type === 'toggleSnapToGrid') {            that.toggleSnapToGrid();        } else if (data.type === 'localEntityFilter') {            localEntityFilter = data.localEntityFilter;            that.sendUpdate();        }     };     webView.webEventReceived.connect(onWebEventReceived);    entityListWindow.webEventReceived.addListener(onWebEventReceived);    that.interactiveWindowHidden = entityListWindow.interactiveWindowHidden;     return that;};