1344 lines · 61.6 KB · JavaScript
RawDownload
//// automaticLookAt.js// This script controls the avatar's look-at-target for the head and eyes, according to other avatar's actions// It tries to simulate human interaction during group conversations////  Created by Luis Cuenca on 11/11/19//  Copyright 2019 High Fidelity, Inc.//////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// (function() {        //////////////////////////////////////////////    // debugger.js ///////    //////////////////////////////////////////////        var TEXT_BOX_WIDTH = 350;    var TEXT_BOX_MIN_HEIGHT = 40;    var TEXT_BOX_TOP_MARGIN = 100;    var TEXT_CAPTION_HEIGHT = 30;    var TEXT_CAPTION_COLOR = { red: 0, green: 0, blue: 0 };    var TEXT_CAPTION_SIZE = 18;    var TEXT_CAPTION_MARGIN = 6;    var CHECKBOX_MARK_MARGIN = 3;        var DEGREE_TO_RADIAN = 0.0174533;    var ENGAGED_AVATARS_DEBUG_COLOR = { red: 0, green: 255, blue: 255 };    var ENGAGED_AVATARS_DEBUG_ALPHA = 0.3;    var FOCUS_AVATAR_DEBUG_COLOR = { red: 255, green: 0, blue: 0 };    var FOCUS_AVATAR_DEBUG_ALPHA = 1.0;    var TALKER_AVATAR_DEBUG_COLOR = { red: 0, green: 0, blue: 0 };    var TALKER_AVATAR_DEBUG_ALPHA = 0.8;    var DEFAULT_OUTLINE_COLOR = { red: 155, green: 155, blue: 255 };    var DEFAULT_OUTLINE_WIDTH = 2;        var LookAtDebugger = function() {        var self = this;        var IMAGE_DIMENSIONS = {x: 0.2, y: 0.2, z:0.2};        var TARGET_ICON_PATH = Script.getExternalPath(Script.ExternalPaths.HF_Content, "/luis/test_scripts/LookAtApp/eyeFocus.png");        var INFINITY_ICON_PATH = Script.getExternalPath(Script.ExternalPaths.HF_Content, "/luis/test_scripts/LookAtApp/noFocus.png");        this.items = {};        this.active = false;                // UI elements        this.textBox;        this.activeCheckBox;        this.activeCheckBoxMark;        this.activeCaption;        this.textBoxHeight = 0.0;        this.logs = [];         this.getStyleProps = function(color, alpha) {            return {                fillUnoccludedColor: color,                fillUnoccludedAlpha: alpha,                                   fillOccludedColor: color,                fillOccludedAlpha: alpha,                outlineUnoccludedColor: DEFAULT_OUTLINE_COLOR,                outlineUnoccludedAlpha: alpha,                outlineOccludedColor: DEFAULT_OUTLINE_COLOR,                outlineOccludedAlpha: alpha,                outlineWidth: DEFAULT_OUTLINE_WIDTH,                isOutlineSmooth: false            }        }        this.Styles = {            "engaged" : {                "style": self.getStyleProps(ENGAGED_AVATARS_DEBUG_COLOR, ENGAGED_AVATARS_DEBUG_ALPHA),                "name" : "engagedSel"            },            "focus" : {                "style": self.getStyleProps(FOCUS_AVATAR_DEBUG_COLOR, FOCUS_AVATAR_DEBUG_ALPHA),                "name" : "focusSel"            },            "talker" : {                "style": self.getStyleProps(TALKER_AVATAR_DEBUG_COLOR, TALKER_AVATAR_DEBUG_ALPHA),                "name" : "talkerSel"            }        };         this.eyeTarget;        this.eyesTargetProps = {            name: "Eyes-Target-Image",            position: { x: 0.0, y: 0.0, z: 0.0 },            color: {red: 255, green: 0, blue: 255},            url: TARGET_ICON_PATH,            dimensions: IMAGE_DIMENSIONS,            alpha: 1.0,            visible: false,            emissive: true,            ignoreRayIntersection: true,            drawInFront: true,            grabbable: false,            isFacingAvatar: true        };        this.headTarget;        this.headTargetProps = {            name: "Head-Target-Image",            position: { x: 0.0, y: 0.0, z: 0.0 },            color: {red: 0, green: 255, blue: 255},            url: TARGET_ICON_PATH,            dimensions: IMAGE_DIMENSIONS,            alpha: 1.0,            visible: false,            emissive: true,            ignoreRayIntersection: true,            drawInFront: true,            grabbable: false,            isFacingAvatar: true        };                 this.textBoxProps = {            x: Window.innerWidth - TEXT_BOX_WIDTH,            y: TEXT_BOX_TOP_MARGIN,            width: TEXT_BOX_WIDTH,            height: TEXT_BOX_MIN_HEIGHT,            alpha: 0.7,            color: { red: 255, green: 255, blue: 255 }        };                this.activeCheckBoxProps = {            x: TEXT_CAPTION_MARGIN + self.textBoxProps.x,            y: 2 * TEXT_CAPTION_MARGIN + self.textBoxProps.y,            width: TEXT_CAPTION_SIZE,            height: TEXT_CAPTION_SIZE,            alpha: 0.0,            borderWidth: 2,            borderColor: TEXT_CAPTION_COLOR        };                this.activeCheckBoxMarkProps = {            x: self.activeCheckBoxProps.x + CHECKBOX_MARK_MARGIN,            y: self.activeCheckBoxProps.y + CHECKBOX_MARK_MARGIN,            width: TEXT_CAPTION_SIZE - 2.0 * CHECKBOX_MARK_MARGIN,            height: TEXT_CAPTION_SIZE - 2.0 * CHECKBOX_MARK_MARGIN,            visible: false,            alpha: 1.0,            color: TEXT_CAPTION_COLOR        }                this.captionProps = {                        x: 2 * TEXT_CAPTION_SIZE + self.textBoxProps.x,            y: TEXT_CAPTION_MARGIN + self.textBoxProps.y,            width: self.textBoxProps.width,            height: 30,            alpha: 1.0,            backgroundAlpha: 0.0,            visible: true,            text: "Debug Auto Look At",            font: {                size: TEXT_CAPTION_SIZE            },            color: TEXT_CAPTION_COLOR,            topMargin: 0.5 * TEXT_CAPTION_MARGIN        };                this.log = function(txt) {            if (self.active) {                self.logs.push(Overlays.addOverlay("text", self.captionProps));                var y = self.textBoxProps.y + self.captionProps.height * (self.logs.length);                Overlays.editOverlay(self.logs[self.logs.length - 1], {y: y, text: txt});                var height = (TEXT_CAPTION_SIZE + 2.0 * TEXT_CAPTION_MARGIN)  * (self.logs.length + 2);                if (this.textBoxHeight !== height) {                    this.textBoxHeight = height                    Overlays.editOverlay(self.textBox, {height: height});                }            }        }                this.clearLog = function() {            for (var n = 0; n < self.logs.length; n++) {                Overlays.deleteOverlay(self.logs[n]);            }            Overlays.editOverlay(self.textBox, self.textBoxProps);            self.logs = [];            self.log("____________________________");        }                this.setActive = function(isActive) {            self.active = isActive;            if (!self.active) {                self.turnOff();            } else {                self.turnOn();            }        };                                this.onClick = function(event) {            if (event.x > self.activeCheckBoxProps.x && event.x < (self.activeCheckBoxProps.x + self.activeCheckBoxProps.width) &&                event.y > self.activeCheckBoxProps.y && event.y < (self.activeCheckBoxProps.y + self.activeCheckBoxProps.height)) {                self.setActive(!self.active);                Overlays.editOverlay(self.activeCheckBoxMark, {visible: self.active});            }        }                this.turnOn = function() {            for (var key in self.Styles) {                var selStyle = self.Styles[key];                Selection.enableListHighlight(selStyle.name, selStyle.style);            }            if (!self.eyeTarget) {                self.eyeTarget = Overlays.addOverlay("image3d", self.eyesTargetProps);            }            if (!self.headTarget) {                self.headTarget = Overlays.addOverlay("image3d", self.headTargetProps);            }        }                this.init = function() {            if (!self.textBox) {                self.textBox = Overlays.addOverlay("rectangle", self.textBoxProps);            }            if (!self.activeCheckBox) {                self.activeCheckBox = Overlays.addOverlay("rectangle", self.activeCheckBoxProps);            }            if (!self.activeCheckBoxMark) {                self.activeCheckBoxMark = Overlays.addOverlay("rectangle", self.activeCheckBoxMarkProps);            }            if (!self.activeCaption) {                self.activeCaption = Overlays.addOverlay("text", self.captionProps);            }        };                this.highLightAvatars = function(engagedIDs, focusID, talkerID) {            if (self.active) {                self.clearSelection();                engagedIDs = !engagedIDs ? [] : engagedIDs;                var focusIDs = !focusID ? [] : [focusID];                                var talkerIDs = !talkerID ? [] : [talkerID];                self.highLightIDs(self.Styles.engaged.name, engagedIDs);                self.highLightIDs(self.Styles.focus.name, focusIDs);                self.highLightIDs(self.Styles.talker.name, talkerIDs);                return true;            } else {                return false;            }        };                this.getTargetProps = function(target) {            var distance = Vec3.length(Vec3.subtract(target, Camera.getPosition()));            RETARGET_MAX_DISTANCE = 100.0;            DIMENSION_SCALE = 0.05;            var isInfinite = false;            if (distance > RETARGET_MAX_DISTANCE) {                isInfinite = true;                var eyesToTarget = Vec3.multiply(RETARGET_MAX_DISTANCE, Vec3.normalize(Vec3.subtract(target, MyAvatar.getDefaultEyePosition())));                var newTarget = Vec3.sum(MyAvatar.getDefaultEyePosition(), eyesToTarget);                var cameraToTarget = Vec3.normalize(Vec3.subtract(newTarget, Camera.getPosition()));                target = Vec3.sum(Camera.getPosition(), cameraToTarget);                distance = Vec3.length(cameraToTarget);            }            // Scale the target to appear always with the same size on screen            var fov = DEGREE_TO_RADIAN * Camera.frustum.fieldOfView;            var scale = (Camera.frustum.aspectRatio < 1.0 ? Camera.frustum.aspectRatio : 1.0) * DIMENSION_SCALE;            var dimensionRatio = scale * (distance / (0.5 * Math.tan(0.5 * fov)));            var dimensions = Vec3.multiply(dimensionRatio, IMAGE_DIMENSIONS);            return {"dimensions": dimensions, "visible": true, "position": target, "url": isInfinite ? INFINITY_ICON_PATH : TARGET_ICON_PATH};        }                this.showTarget = function(headTarget, eyeTarget) {            if (!self.active) {                return;            }            var targetProps = self.getTargetProps(eyeTarget);            Overlays.editOverlay(self.eyeTarget, targetProps);             var headTargetProps = self.getTargetProps(headTarget);            Overlays.editOverlay(self.headTarget, headTargetProps);        }                this.hideEyeTarget = function() {            Overlays.editOverlay(self.eyeTarget, {"visible": false});         }                this.highLightIDs = function(selectionName, ids) {            self.items[selectionName] = ids;            for (var idx in ids) {                Selection.addToSelectedItemsList(selectionName, "avatar", ids[idx]);             }        }        this.clearSelection = function() {            for (key in self.Styles) {                var selStyle = self.Styles[key];                Selection.clearSelectedItemsList(selStyle.name);            }        }                this.turnOff = function() {            self.clearLog();            for (var key in self.Styles) {                var selStyle = self.Styles[key];                Selection.disableListHighlight(selStyle.name);            }            Overlays.deleteOverlay(self.headTarget);            self.headTarget = undefined;            Overlays.deleteOverlay(self.eyeTarget);            self.eyeTarget = undefined;        }                this.finish = function() {            if (self.active) {                self.setActive(false);            }            Overlays.deleteOverlay(self.textBox);            self.textBox = undefined;            Overlays.deleteOverlay(self.activeCheckBox);            self.activeCheckBox = undefined;            Overlays.deleteOverlay(self.activeCaption);            self.activeCaption = undefined;            Overlays.deleteOverlay(self.activeCheckBoxMark);            self.activeCheckBoxMark = undefined;        }                this.init();    }         //////////////////////////////////////////////    // randomHelper.js //////////    //////////////////////////////////////////////        var RandomHelper = function() {        var self = this;                this.createRandomIndexes = function(count) {            var indexes = [];            for (var n = 0; n < count; n++) {                indexes.push(n);            }            var randomIndexes = [];            for (var n = 0; n < count; n++) {                var indexesCount = indexes.length;                var randomIndex = 0;                if (indexesCount > 1) {                    var randFactor = Math.random();                    randomIndex = randFactor !== 1.0 ? Math.floor(randFactor * indexes.length) : indexes.length - 1;                }                randomIndexes.push(indexes[randomIndex]);                indexes.splice(randomIndex, 1);            }            return randomIndexes;        }                this.getRandomKey = function(keypool) {            // keypool can be an object. {key1: percentage1, key2: percentage2} or            // keypool can be an array. [key1, key2] Equal percentage each component             var equalChance = Array.isArray(keypool);            var totalPercentage = 0.0;            var percentages = {};            var normalizedPercentages = {};            var keys = equalChance ? keypool : Object.keys(keypool);            for (var n = 0; n < keys.length; n++) {                var key = keys[n];                percentages[key] = equalChance ? 1.0 / keys.length : keypool[key].chance;                totalPercentage += equalChance ? percentages[key] : keypool[key].chance;            }            var accumulatedVal = 0.0;            for (var n = 0; n < keys.length; n++) {                var key = keys[n];                var val = accumulatedVal + (percentages[key] / totalPercentage);                normalizedPercentages[key] = val;                accumulatedVal = val;            }            var dice = Math.random();            var floor = 0.0;            var hit = normalizedPercentages[keys[0]];            for (var n = 0; n < keys.length; n++) {                var key = keys[n];                if (dice > floor && dice < normalizedPercentages[key]) {                    hit = key;                    break;                }                floor = normalizedPercentages[key];            }            return { randomKey: hit, chance: percentages[hit]};        }    }            //////////////////////////////////////////////    // automaticMachine.js //////////    //////////////////////////////////////////////    var MIN_LOOKAT_HEAD_MIX_ALPHA = 0.04;    var MAX_LOOKAT_HEAD_MIX_ALPHA = 0.08;    var CAMERA_HEAD_MIX_ALPHA = 0.06;        var TargetType = {        "unknown" : 0,        "avatar" : 1,        "entity" : 2    }        var TargetOffsetMode = {        "noOffset" : 0,        "onlyHead" : 1,        "onlyEyes" : 2,        "headAndEyes" : 3,        "print" : function(sta) {            return ("OffsetMode: " + (Object.keys(TargetOffsetMode))[sta]);        }    }        var TargetMode = {        "noTarget" : 0,        "leftEye" : 1,        "rightEye" : 2,        "mouth" : 3,        "leftHand" : 4,        "rightHand" : 5,        "random" : 6,        "print" : function(sta) {            return ("TargetMode: " + (Object.keys(TargetMode))[sta]);        }    }        var ACTION_CONFIGURATION = {        "TargetMode.mouth": {                "joint": "Head",             "stareTimeRange" : {"min": 0.2, "max": 2.0},            "headSpeedRange" : {"min": MAX_LOOKAT_HEAD_MIX_ALPHA, "max": MIN_LOOKAT_HEAD_MIX_ALPHA},            "offsetChances" : {                "TargetOffsetMode.noOffset": {"chance": 0.7},                "TargetOffsetMode.onlyHead": {"chance":  0.3},                "TargetOffsetMode.onlyEyes": {"chance":  0.0},                "TargetOffsetMode.headAndEyes": {"chance": 0.0}            },            "offsetAngleRange" : {"min": 1.0, "max": 5.0},            "chanceWhileListening" : 0.45,            "chanceWhileTalking" : 0.25        },        "TargetMode.leftEye": {                "joint": "LeftEye",             "stareTimeRange" : {"min": 0.2, "max": 2.0},            "headSpeedRange" : {"min": MAX_LOOKAT_HEAD_MIX_ALPHA, "max": MIN_LOOKAT_HEAD_MIX_ALPHA},            "offsetChances" : {                "TargetOffsetMode.noOffset": {"chance": 0.5},                "TargetOffsetMode.onlyHead": {"chance": 0.3},                "TargetOffsetMode.onlyEyes": {"chance": 0.1},                "TargetOffsetMode.headAndEyes": {"chance": 0.1}            },            "offsetAngleRange" : {"min": 1.0, "max": 5.0},            "chanceWhileListening" : 0.20,            "chanceWhileTalking" : 0.30        },        "TargetMode.rightEye": {               "joint": "RightEye",             "stareTimeRange" : {"min": 0.2, "max": 2.0},            "headSpeedRange" : {"min": MAX_LOOKAT_HEAD_MIX_ALPHA, "max": MIN_LOOKAT_HEAD_MIX_ALPHA},            "offsetChances" : {                "TargetOffsetMode.noOffset": {"chance": 0.5},                "TargetOffsetMode.onlyHead": {"chance": 0.3},                "TargetOffsetMode.onlyEyes": {"chance": 0.1},                "TargetOffsetMode.headAndEyes": {"chance": 0.1}            },            "offsetAngleRange" : {"min": 1.0, "max": 5.0},            "chanceWhileListening" : 0.20,            "chanceWhileTalking" : 0.30        },        "TargetMode.leftHand": {                "joint": "LeftHand",             "stareTimeRange" : {"min": 0.2, "max": 2.0},            "headSpeedRange" : {"min": MAX_LOOKAT_HEAD_MIX_ALPHA, "max": MIN_LOOKAT_HEAD_MIX_ALPHA},            "offsetChances" : {                "TargetOffsetMode.noOffset": {"chance": 0.9},                "TargetOffsetMode.onlyHead": {"chance": 0.1},                "TargetOffsetMode.onlyEyes": {"chance": 0.0},                "TargetOffsetMode.headAndEyes": {"chance": 0.0}            },            "offsetAngleRange" : {"min": 1.0, "max": 10.0},            "chanceWhileListening" : 0.05,            "chanceWhileTalking" : 0.05          },        "TargetMode.rightHand": {               "joint": "RightHand",             "stareTimeRange" : {"min": 0.2, "max": 2.0},            "headSpeedRange" : {"min": MAX_LOOKAT_HEAD_MIX_ALPHA, "max": MIN_LOOKAT_HEAD_MIX_ALPHA},            "offsetChances" : {                "TargetOffsetMode.noOffset": {"chance": 0.9},                "TargetOffsetMode.onlyHead": {"chance": 0.1},                "TargetOffsetMode.onlyEyes": {"chance": 0.0},                "TargetOffsetMode.headAndEyes": {"chance": 0.0}            },            "offsetAngleRange" : {"min": 1.0, "max": 10.0},            "chanceWhileListening" : 0.05,            "chanceWhileTalking" : 0.05                },        "TargetMode.random": {               "joint": "Head",             "stareTimeRange" : {"min": 0.2, "max": 1.0},            "headSpeedRange" : {"min": MAX_LOOKAT_HEAD_MIX_ALPHA, "max": MIN_LOOKAT_HEAD_MIX_ALPHA},            "offsetChances" : {                "TargetOffsetMode.noOffset": {"chance": 0.0},                "TargetOffsetMode.onlyHead": {"chance": 0.0},                "TargetOffsetMode.onlyEyes": {"chance": 0.4},                "TargetOffsetMode.headAndEyes": {"chance": 0.6}            },

Lines 1–500 of 1344.