3496 lines · 140.2 KB · JavaScript
RawDownload
//  edit.js////  Created by Brad Hefta-Gaub on October 2nd, 2014.//  Persist toolbar by HRS on June 2nd, 2015.//  Copyright 2014 High Fidelity, Inc.//  Copyright 2020 Vircadia contributors.//  Copyright 2022-2025 Overte e.V.////  This script allows you to edit entities with a new UI/UX for mouse and trackpad based editing////  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//"use strict"; /* global Script, SelectionDisplay, LightOverlayManager, CameraManager, Grid, GridTool, EditTools, EditVoxels, EntityListTool, Vec3, SelectionManager,   Overlays, OverlayWebWindow, UserActivityLogger, Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera,   progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, OverlaySystemWindow */ (function() { // BEGIN LOCAL_SCOPE    var createApp = {};     var EDIT_TOGGLE_BUTTON = "com.highfidelity.interface.system.editButton";     var CONTROLLER_MAPPING_NAME = "com.highfidelity.editMode";     Script.include([        "../libraries/stringHelpers.js",        "../libraries/dataViewHelpers.js",        "../libraries/progressDialog.js",        "../libraries/ToolTip.js",        "../libraries/entityCameraTool.js",        "../libraries/utils.js",        "../libraries/entityIconOverlayManager.js",        "../libraries/gridTool.js",        "entityList/entityList.js",        "entitySelectionTool/entitySelectionTool.js",        "audioFeedback/audioFeedback.js",        "modules/brokenURLReport.js",        "modules/renderWithZonesManager.js",        "editModes/editModes.js",        "editModes/editVoxels.js"    ]);     var CreateWindow = Script.require('./modules/createWindow.js');     var TITLE_OFFSET = 60;    var CREATE_TOOLS_WIDTH = 750;    var MAX_DEFAULT_ENTITY_LIST_HEIGHT = 942;    var ENTIRE_DOMAIN_SCAN_RADIUS = 27713;     var DEFAULT_IMAGE = Script.getExternalPath(Script.ExternalPaths.Assets, "Bazaar/Assets/Textures/Defaults/Interface/default_image.jpg");    var DEFAULT_PARTICLE = Script.getExternalPath(Script.ExternalPaths.Assets, "Bazaar/Assets/Textures/Defaults/Interface/default_particle.png");     var createToolsWindow = new CreateWindow(        Script.resolvePath("qml/EditTools.qml"),        'Create Tools',        'com.highfidelity.create.createToolsWindow',        function () {            var windowHeight = Window.innerHeight - TITLE_OFFSET;            if (windowHeight > MAX_DEFAULT_ENTITY_LIST_HEIGHT) {                windowHeight = MAX_DEFAULT_ENTITY_LIST_HEIGHT;            }            return {                size: {                    x: CREATE_TOOLS_WIDTH,                    y: windowHeight                },                position: {                    x: Window.x + Window.innerWidth - CREATE_TOOLS_WIDTH,                    y: Window.y + TITLE_OFFSET                }            }        },        false    );     /**     * @description Returns true in case we should use the tablet version of the CreateApp     * @returns boolean     */    var shouldUseEditTabletApp = function() {        return HMD.active || (!HMD.active && !Settings.getValue("desktopTabletBecomesToolbar", true));    };      var selectionDisplay = SelectionDisplay;    selectionDisplay.createApp = createApp;    var selectionManager = SelectionManager;    selectionManager.createApp = createApp;     var PARTICLE_SYSTEM_URL = Script.resolvePath("assets/images/icon-particles.svg");    var POINT_LIGHT_URL = Script.resolvePath("assets/images/icon-point-light.svg");    var SPOT_LIGHT_URL = Script.resolvePath("assets/images/icon-spot-light.svg");    var ZONE_URL = Script.resolvePath("assets/images/icon-zone.svg");    var MATERIAL_URL = Script.resolvePath("assets/images/icon-material.svg");    var SOUND_URL = Script.resolvePath("assets/images/icon-sound.svg");    var CANVAS_URL = Script.resolvePath("assets/images/icon-canvas.svg");    var EMPTY_URL = Script.resolvePath("assets/images/icon-empty.svg");    var SCRIPT_URL = Script.resolvePath("assets/images/icon-script.svg");     var entityIconOverlayManager = new EntityIconOverlayManager(["Light", "ParticleEffect", "ProceduralParticleEffect", "Zone", "Material", "Sound", "Canvas", "Empty", "Script"], function(entityID) {        var properties = Entities.getEntityProperties(entityID, ["type", "isSpotlight", "parentID", "name"]);        if (properties.type === "Light") {            return {                imageURL: properties.isSpotlight ? SPOT_LIGHT_URL : POINT_LIGHT_URL            };        } else if (properties.type === "Zone") {            return { imageURL: ZONE_URL };        } else if (properties.type === "Material") {            if (properties.parentID !== Uuid.NONE && properties.name !== "MATERIAL_" + entityShapeVisualizerSessionName) {                return { imageURL: MATERIAL_URL };            } else {                return { imageURL: "" };            }        } else if (properties.type === "Sound") {            return { imageURL: SOUND_URL, rotation: Quat.fromPitchYawRollDegrees(0, 0, 0) };        } else if (properties.type === "Canvas") {            return { imageURL: CANVAS_URL, rotation: Quat.fromPitchYawRollDegrees(0, 0, 0) };        } else if (properties.type === "Empty") {            return { imageURL: EMPTY_URL, rotation: Quat.fromPitchYawRollDegrees(0, 0, 0) };        } else if (properties.type === "Script") {            return { imageURL: SCRIPT_URL, rotation: Quat.fromPitchYawRollDegrees(0, 0, 0) };        } else {            return { imageURL: PARTICLE_SYSTEM_URL };        }    });     createApp.hmdMultiSelectMode = false;    createApp.expectingRotateAsClickedSurface = false;    var keepSelectedOnNextClick = false;     var copiedPosition;    var copiedRotation;    var copiedDimensions;     var importUiPersistedData = {        "elJsonUrl": "",        "elImportAtAvatar": true,        "elImportAtSpecificPosition": false,        "elPositionX": 0,        "elPositionY": 0,        "elPositionZ": 0,        "elEntityHostTypeDomain": true,        "elEntityHostTypeAvatar": false    };     var cameraManager = new CameraManager();     var grid = new Grid();    selectionDisplay.grid = grid;    var gridTool = new GridTool({        horizontalGrid: grid,        createToolsWindow: createToolsWindow,        shouldUseEditTabletApp: shouldUseEditTabletApp    });    gridTool.selectionDisplay = selectionDisplay;    gridTool.createApp = createApp;    gridTool.setVisible(false);     var editTools = new EditTools({        createToolsWindow: createToolsWindow,    });     var editVoxels = new EditVoxels();    editVoxels.editTools = editTools;     editTools.addListener(editVoxels.updateEditSettings);    editTools.addListener(selectionManager.updateEditSettings);     var entityShapeVisualizerSessionName = "SHAPE_VISUALIZER_" + Uuid.generate();    createApp.entityShapeVisualizerLocalEntityToExclude = [];    var EntityShapeVisualizer = Script.require('./modules/entityShapeVisualizer.js');    var entityShapeVisualizer = new EntityShapeVisualizer(["Zone"], entityShapeVisualizerSessionName);     var entityListTool = new EntityListTool(shouldUseEditTabletApp, selectionManager);    entityListTool.createApp = createApp;    entityListTool.grid = grid;    entityListTool.cameraManager = cameraManager;    entityListTool.selectionDisplay = selectionDisplay;     selectionManager.addEventListener(function () {        selectionDisplay.updateHandles();        entityIconOverlayManager.updatePositions(selectionManager.selections);        entityShapeVisualizer.setEntities(selectionManager.selections);        createApp.entityShapeVisualizerLocalEntityToExclude = entityShapeVisualizer.getLocalEntityToExclude();    });     var DEGREES_TO_RADIANS = Math.PI / 180.0;    var RADIANS_TO_DEGREES = 180.0 / Math.PI;     var MIN_ANGULAR_SIZE = 2;    var MAX_ANGULAR_SIZE = 45;    var allowLargeModels = true;    var allowSmallModels = true;     var DEFAULT_DIMENSION = 0.20;     var DEFAULT_DIMENSIONS = {        x: DEFAULT_DIMENSION,        y: DEFAULT_DIMENSION,        z: DEFAULT_DIMENSION    };     var DEFAULT_LIGHT_DIMENSIONS = Vec3.multiply(20, DEFAULT_DIMENSIONS);     var MENU_IMPORT_FROM_FILE = "Import Entities (.json) From a File";    var MENU_IMPORT_FROM_URL = "Import Entities (.json) From a URL";    var MENU_CREATE_SEPARATOR = "Create Application";    var SUBMENU_ENTITY_EDITOR_PREFERENCES = "Edit > Preferences";    var MENU_AUTO_FOCUS_ON_SELECT = "Auto Focus on Select";    var MENU_EASE_ON_FOCUS = "Ease Orientation on Focus";    createApp.MENU_EASE_ON_FOCUS = MENU_EASE_ON_FOCUS;    var MENU_SHOW_ICONS_IN_CREATE_MODE = "Show Icons in Create Mode";    var MENU_CREATE_ENTITIES_GRABBABLE = "Create Entities As Grabbable (except Zones, Particles, and Lights)";    var MENU_ALLOW_SELECTION_LARGE = "Allow Selecting of Large Models";    var MENU_ALLOW_SELECTION_SMALL = "Allow Selecting of Small Models";    var MENU_ALLOW_SELECTION_LIGHTS = "Allow Selecting of Lights";    var MENU_ENTITY_LIST_DEFAULT_RADIUS = "Entity List Default Radius";     var SETTING_AUTO_FOCUS_ON_SELECT = "autoFocusOnSelect";    var SETTING_EASE_ON_FOCUS = "cameraEaseOnFocus";    var SETTING_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE = "showLightsAndParticlesInEditMode";    createApp.SETTING_SHOW_ZONES_IN_EDIT_MODE = "showZonesInEditMode";    createApp.SETTING_EDITOR_COLUMNS_SETUP = "editorColumnsSetup";    createApp.SETTING_ENTITY_LIST_DEFAULT_RADIUS = "entityListDefaultRadius";     var SETTING_EDIT_PREFIX = "Edit/";      var CREATE_ENABLED_ICON = "icons/tablet-icons/edit-i.svg";    var CREATE_DISABLED_ICON = "icons/tablet-icons/edit-disabled.svg";     // marketplace info, etc.  not quite ready yet.    var SHOULD_SHOW_PROPERTY_MENU = false;    var INSUFFICIENT_PERMISSIONS_ERROR_MSG = "You do not have the necessary permissions to edit on this domain.";    var INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG = "You do not have the necessary permissions to place items on this domain.";     var isActive = false;    var createButton = null;     var IMPORTING_SVO_OVERLAY_WIDTH = 144;    var IMPORTING_SVO_OVERLAY_HEIGHT = 30;    var IMPORTING_SVO_OVERLAY_MARGIN = 5;    var IMPORTING_SVO_OVERLAY_LEFT_MARGIN = 34;    var importingSVOImageOverlay = Overlays.addOverlay("image", {        imageURL: Script.resolvePath("assets/images/hourglass.svg"),        width: 20,        height: 20,        alpha: 1.0,        x: Window.innerWidth - IMPORTING_SVO_OVERLAY_WIDTH,        y: Window.innerHeight - IMPORTING_SVO_OVERLAY_HEIGHT,        visible: false    });    var importingSVOTextOverlay = Overlays.addOverlay("text", {        font: {            size: 14        },        text: "Importing SVO...",        leftMargin: IMPORTING_SVO_OVERLAY_LEFT_MARGIN,        x: Window.innerWidth - IMPORTING_SVO_OVERLAY_WIDTH - IMPORTING_SVO_OVERLAY_MARGIN,        y: Window.innerHeight - IMPORTING_SVO_OVERLAY_HEIGHT - IMPORTING_SVO_OVERLAY_MARGIN,        width: IMPORTING_SVO_OVERLAY_WIDTH,        height: IMPORTING_SVO_OVERLAY_HEIGHT,        backgroundColor: {            red: 80,            green: 80,            blue: 80        },        backgroundAlpha: 0.7,        visible: false    });     var savedClippingEnabled = false;     function adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation) {        // Adjust the position such that the bounding box (registration, dimensions and orientation) lies behind the original        // position in the given direction.        var CORNERS = [            { x: 0, y: 0, z: 0 },            { x: 0, y: 0, z: 1 },            { x: 0, y: 1, z: 0 },            { x: 0, y: 1, z: 1 },            { x: 1, y: 0, z: 0 },            { x: 1, y: 0, z: 1 },            { x: 1, y: 1, z: 0 },            { x: 1, y: 1, z: 1 },        ];         // Go through all corners and find least (most negative) distance in front of position.        var distance = 0;        for (var i = 0, length = CORNERS.length; i < length; i++) {            var cornerVector =                Vec3.multiplyQbyV(orientation, Vec3.multiplyVbyV(Vec3.subtract(CORNERS[i], registration), dimensions));            var cornerDistance = Vec3.dot(cornerVector, direction);            distance = Math.min(cornerDistance, distance);        }        position = Vec3.sum(Vec3.multiply(distance, direction), position);        return position;    }     // Handles any edit mode updates required when domains have switched    function checkEditPermissionsAndUpdate() {        if ((createButton === null) || (createButton === undefined)) {            //--EARLY EXIT--( nothing to safely update )            return;        }         var hasRezPermissions = (Entities.canRez() || Entities.canRezTmp());        createButton.editProperties({            icon: (hasRezPermissions ? CREATE_ENABLED_ICON : CREATE_DISABLED_ICON),            captionColor: (hasRezPermissions ? "#ffffff" : "#888888"),        });         if (!hasRezPermissions && isActive) {            that.setActive(false);            tablet.gotoHomeScreen();        }    }     // Copies the properties in `b` into `a`. `a` will be modified.    function copyProperties(a, b) {        for (var key in b) {            a[key] = b[key];        }        return a;    }     const DEFAULT_DYNAMIC_PROPERTIES = {        dynamic: true,        damping: 0.39347,        angularDamping: 0.39347,        gravity: { x: 0, y: -9.8, z: 0 },    };     const DEFAULT_NON_DYNAMIC_PROPERTIES = {        dynamic: false,        damping: 0,        angularDamping: 0,        gravity: { x: 0, y: 0, z: 0 },    };     const DEFAULT_ENTITY_PROPERTIES = {        All: {            description: "",            rotation: { x: 0, y: 0, z: 0, w: 1 },            collidesWith: "static,dynamic,kinematic,otherAvatar,myAvatar",            collisionSoundURL: "",            cloneable: false,            ignoreIK: true,            canCastShadow: true,            href: "",            script: "",            serverScripts: "",            velocity: {                x: 0,                y: 0,                z: 0            },            angularVelocity: {                x: 0,                y: 0,                z: 0            },            restitution: 0.5,            friction: 0.5,            density: 1000,            dynamic: false,        },        Shape: {            shape: "Box",            dimensions: { x: 0.2, y: 0.2, z: 0.2 },            color: { red: 0, green: 180, blue: 239 },        },        Text: {            text: "Text",            dimensions: {                x: 0.65,                y: 0.3,                z: 0.01            },            textColor: { red: 255, green: 255, blue: 255 },            backgroundColor: { red: 0, green: 0, blue: 0 },            lineHeight: 0.06,            faceCamera: false,        },        Zone: {            dimensions: {                x: 10,                y: 10,                z: 10            },            flyingAllowed: true,            ghostingAllowed: true,            filter: "",            keyLightMode: "inherit",            keyLightColor: { red: 255, green: 255, blue: 255 },            keyLight: {                intensity: 1.0,                direction: {                    x: 0.0,                    y: -0.707106769084930, // 45 degrees                    z: 0.7071067690849304                },                castShadows: true            },            ambientLightMode: "inherit",            ambientLight: {                ambientIntensity: 0.5,                ambientURL: ""            },            hazeMode: "inherit",            haze: {                hazeRange: 1000,                hazeAltitudeEffect: false,                hazeBaseRef: 0,                hazeColor: {                    red: 128,                    green: 154,                    blue: 179                },                hazeBackgroundBlend: 0,                hazeEnableGlare: false,                hazeGlareColor: {                    red: 255,                    green: 229,                    blue: 179                },            },            shapeType: "box",            bloomMode: "inherit",            avatarPriority: "inherit"        },        Model: {            collisionShape: "none",            compoundShapeURL: "",            animation: {                url: "",                running: false,                allowTranslation: false,                loop: true,                hold: false,                currentFrame: 0,                firstFrame: 0,                lastFrame: 100000,                fps: 30.0,            }        },        Image: {            dimensions: {                x: 0.5385,                y: 0.2819,                z: 0.0092            },            shapeType: "box",            collisionless: true,            keepAspectRatio: false,            imageURL: DEFAULT_IMAGE        },        Web: {            dimensions: {                x: 1.6,                y: 0.9,                z: 0.01            },            sourceUrl: "https://overte.org/",            dpi: 30,        },        ParticleEffect: {            lifespan: 1.5,            maxParticles: 10,            textures: DEFAULT_PARTICLE,            emitRate: 5.5,            emitSpeed: 0,            speedSpread: 0,            emitDimensions: { x: 0, y: 0, z: 0 },            emitOrientation: { x: 0, y: 0, z: 0, w: 1 },            emitterShouldTrail: true,            particleRadius: 0.25,            radiusStart: 0,            radiusSpread: 0,            particleColor: {                red: 255,                green: 255,                blue: 255            },            colorSpread: {                red: 0,                green: 0,                blue: 0            },            alpha: 0,            alphaStart: 1,            alphaSpread: 0,            emitAcceleration: {                x: 0,                y: 2.5,                z: 0            },

Lines 1–500 of 3496.