diff --git a/examples/homeContent/whiteboardV2/eraserEntityScript.js b/examples/homeContent/whiteboardV2/eraserEntityScript.js new file mode 100644 index 0000000000..9306c7a5ab --- /dev/null +++ b/examples/homeContent/whiteboardV2/eraserEntityScript.js @@ -0,0 +1,109 @@ +// +// eraserEntityScript.js +// examples/homeContent/eraserEntityScript +// +// Created by Eric Levin on 2/17/15. +// Copyright 2016 High Fidelity, Inc. +// +// This entity script provides logic for an object with attached script to erase nearby marker strokes +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + + + +(function() { + Script.include("../../libraries/utils.js"); + var TRIGGER_CONTROLS = [ + Controller.Standard.LT, + Controller.Standard.RT, + ]; + var _this; + Eraser = function() { + _this = this; + + _this.ERASER_TRIGGER_THRESHOLD = 0.2; + _this.STROKE_NAME = "hifi-marker-stroke"; + _this.ERASER_TO_STROKE_SEARCH_RADIUS = 0.7; + _this.ERASER_RESET_WAIT_TIME = 3000; + }; + + Eraser.prototype = { + + startEquip: function(id, params) { + _this.equipped = true; + _this.hand = params[0] == "left" ? 0 : 1; + // We really only need to grab position of marker strokes once, and then just check to see if eraser comes near enough to those strokes + Overlays.editOverlay(_this.searchSphere, { + visible: true + }); + }, + continueEquip: function() { + _this.eraserPosition = Entities.getEntityProperties(_this.entityID, "position").position; + Overlays.editOverlay(_this.searchSphere, { + position: _this.eraserPosition + }); + this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[_this.hand]); + if (_this.triggerValue > _this.ERASER_TRIGGER_THRESHOLD) { + _this.continueHolding(); + } + }, + + + continueHolding: function() { + var strokeIDs = Entities.findEntities(_this.eraserPosition, _this.ERASER_TO_STROKE_SEARCH_RADIUS); + // Create a map of stroke entities and their positions + + strokeIDs.forEach(function(strokeID) { + var strokeProps = Entities.getEntityProperties(strokeID, ["position", "name"]); + if (strokeProps.name === _this.STROKE_NAME && Vec3.distance(_this.eraserPosition, strokeProps.position) < _this.ERASER_TO_STROKE_SEARCH_RADIUS) { + Entities.deleteEntity(strokeID); + } + }); + }, + + releaseEquip: function() { + Overlays.editOverlay(_this.searchSphere, { + visible: false + }); + + // Once user releases eraser, wait a bit then put marker back to its original position and rotation + Script.setTimeout(function() { + var userData = getEntityUserData(_this.entityID); + Entities.editEntity(_this.entityID, { + position: userData.originalPosition, + rotation: userData.originalRotation, + velocity: { + x: 0, + y: -0.01, + z: 0 + } + }); + }, _this.ERASER_RESET_WAIT_TIME); + }, + + + + preload: function(entityID) { + _this.entityID = entityID; + _this.searchSphere = Overlays.addOverlay('sphere', { + size: _this.ERASER_TO_STROKE_SEARCH_RADIUS, + color: { + red: 200, + green: 10, + blue: 10 + }, + alpha: 0.2, + solid: true, + visible: false + }) + + }, + + unload: function() { + Overlays.deleteOverlay(_this.searchSphere); + } + }; + + // entity scripts always need to return a newly constructed object of our type + return new Eraser(); +}); \ No newline at end of file diff --git a/examples/homeContent/whiteboardV2/markerEntityScript.js b/examples/homeContent/whiteboardV2/markerEntityScript.js new file mode 100644 index 0000000000..19907b283c --- /dev/null +++ b/examples/homeContent/whiteboardV2/markerEntityScript.js @@ -0,0 +1,219 @@ +// +// markerTipEntityScript.js +// examples/homeContent/markerTipEntityScript +// +// Created by Eric Levin on 2/17/15. +// Copyright 2016 High Fidelity, Inc. +// +// This script provides the logic for an object to draw marker strokes on its associated whiteboard + +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + + + +(function() { + Script.include("../../libraries/utils.js"); + var TRIGGER_CONTROLS = [ + Controller.Standard.LT, + Controller.Standard.RT, + ]; + var MAX_POINTS_PER_STROKE = 40; + var _this; + MarkerTip = function() { + _this = this; + _this.MARKER_TEXTURE_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/textures/markerStroke.png"; + _this.strokeForwardOffset = 0.0001; + _this.STROKE_WIDTH_RANGE = { + min: 0.002, + max: 0.01 + }; + _this.MAX_MARKER_TO_BOARD_DISTANCE = 1.4; + _this.MIN_DISTANCE_BETWEEN_POINTS = 0.002; + _this.MAX_DISTANCE_BETWEEN_POINTS = 0.1; + _this.strokes = []; + _this.PAINTING_TRIGGER_THRESHOLD = 0.2; + _this.STROKE_NAME = "hifi-marker-stroke"; + _this.WHITEBOARD_SURFACE_NAME = "hifi-whiteboardDrawingSurface"; + _this.MARKER_RESET_WAIT_TIME = 3000; + }; + + MarkerTip.prototype = { + + startEquip: function(id, params) { + _this.whiteboards = []; + _this.equipped = true; + _this.hand = params[0] == "left" ? 0 : 1; + _this.markerColor = getEntityUserData(_this.entityID).markerColor; + // search for whiteboards + var markerPosition = Entities.getEntityProperties(_this.entityID, "position").position; + var entities = Entities.findEntities(markerPosition, 10); + entities.forEach(function(entity) { + var entityName = Entities.getEntityProperties(entity, "name").name; + if (entityName === _this.WHITEBOARD_SURFACE_NAME) { + + _this.whiteboards.push(entity); + } + }); + + print("intersectable entities " + JSON.stringify(_this.whiteboards)) + }, + + releaseEquip: function() { + _this.resetStroke(); + Overlays.editOverlay(_this.laserPointer, { + visible: false + }); + + // Once user releases marker, wait a bit then put marker back to its original position and rotation + Script.setTimeout(function() { + var userData = getEntityUserData(_this.entityID); + Entities.editEntity(_this.entityID, { + position: userData.originalPosition, + rotation: userData.originalRotation, + velocity: { + x: 0, + y: -0.01, + z: 0 + } + }); + }, _this.MARKER_RESET_WAIT_TIME); + }, + + + continueEquip: function() { + // cast a ray from marker and see if it hits anything + var markerProps = Entities.getEntityProperties(_this.entityID, ["position", "rotation"]); + + var pickRay = { + origin: markerProps.position, + direction: Quat.getFront(markerProps.rotation) + } + var intersection = Entities.findRayIntersectionBlocking(pickRay, true, _this.whiteboards); + + if (intersection.intersects && Vec3.distance(intersection.intersection, markerProps.position) < _this.MAX_MARKER_TO_BOARD_DISTANCE) { + _this.currentWhiteboard = intersection.entityID; + var whiteboardRotation = Entities.getEntityProperties(_this.currentWhiteboard, "rotation").rotation; + _this.whiteboardNormal = Quat.getFront(whiteboardRotation); + Overlays.editOverlay(_this.laserPointer, { + visible: true, + position: intersection.intersection, + rotation: whiteboardRotation + }) + _this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[_this.hand]); + if (_this.triggerValue > _this.PAINTING_TRIGGER_THRESHOLD) { + _this.paint(intersection.intersection) + } else { + _this.resetStroke(); + } + } else { + if (_this.currentStroke) { + _this.resetStroke(); + } + + Overlays.editOverlay(_this.laserPointer, { + visible: false + }); + } + + }, + + newStroke: function(position) { + _this.strokeBasePosition = position; + _this.currentStroke = Entities.addEntity({ + type: "PolyLine", + name: _this.STROKE_NAME, + dimensions: { + x: 10, + y: 10, + z: 10 + }, + position: position, + textures: _this.MARKER_TEXTURE_URL, + color: _this.markerColor, + lifetime: 5000, + }); + + _this.linePoints = []; + _this.normals = []; + _this.strokes.push(_this.currentStroke); + }, + + paint: function(position) { + var basePosition = position; + if (!_this.currentStroke) { + if (_this.oldPosition) { + basePosition = _this.oldPosition; + } + _this.newStroke(basePosition); + } + + var localPoint = Vec3.subtract(basePosition, _this.strokeBasePosition); + localPoint = Vec3.sum(localPoint, Vec3.multiply(_this.whiteboardNormal, _this.strokeForwardOffset)); + + if (_this.linePoints.length > 0) { + var distance = Vec3.distance(localPoint, _this.linePoints[_this.linePoints.length - 1]); + if (distance < _this.MIN_DISTANCE_BETWEEN_POINTS) { + return; + } + } + _this.linePoints.push(localPoint); + _this.normals.push(_this.whiteboardNormal); + + var strokeWidths = []; + for (var i = 0; i < _this.linePoints.length; i++) { + // Create a temp array of stroke widths for calligraphy effect - start and end should be less wide + var pointsFromCenter = Math.abs(_this.linePoints.length / 2 - i); + var pointWidth = map(pointsFromCenter, 0, this.linePoints.length / 2, _this.STROKE_WIDTH_RANGE.max, this.STROKE_WIDTH_RANGE.min); + strokeWidths.push(pointWidth); + } + + Entities.editEntity(_this.currentStroke, { + linePoints: _this.linePoints, + normals: _this.normals, + strokeWidths: strokeWidths + }); + + if (_this.linePoints.length > MAX_POINTS_PER_STROKE) { + Entities.editEntity(_this.currentStroke, { + parentID: _this.currentWhiteboard + }); + _this.currentStroke = null; + _this.oldPosition = position; + } + }, + resetStroke: function() { + + Entities.editEntity(_this.currentStroke, { + parentID: _this.currentWhiteboard + }); + _this.currentStroke = null; + + _this.oldPosition = null; + }, + + preload: function(entityID) { + this.entityID = entityID; + _this.laserPointer = Overlays.addOverlay("circle3d", { + color: { + red: 220, + green: 35, + blue: 53 + }, + solid: true, + size: 0.01, + }); + + }, + + unload: function() { + Overlays.deleteOverlay(_this.laserPointer); + _this.strokes.forEach(function(stroke) { + Entities.deleteEntity(stroke); + }); + } + }; + + // entity scripts always need to return a newly constructed object of our type + return new MarkerTip(); +}); diff --git a/examples/homeContent/whiteboardV2/whiteboardSpawner.js b/examples/homeContent/whiteboardV2/whiteboardSpawner.js new file mode 100644 index 0000000000..32c207499a --- /dev/null +++ b/examples/homeContent/whiteboardV2/whiteboardSpawner.js @@ -0,0 +1,254 @@ +// +// whiteboardSpawner.js +// examples/homeContent/whiteboardV2 +// +// Created by Eric Levina on 2/17/16 +// Copyright 2016 High Fidelity, Inc. +// +// Run this script to spawn a whiteboard, markers, and an eraser. +// To draw on the whiteboard, equip a marker and hold down trigger with marker tip pointed at whiteboard +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +Script.include("../../libraries/utils.js") + +var orientation = MyAvatar.orientation; +orientation = Quat.safeEulerAngles(orientation); +var markerRotation = Quat.fromVec3Degrees({ + x: orientation.x + 10, + y: orientation.y - 90, + z: orientation.z +}) +orientation.x = 0; +var whiteboardRotation = Quat.fromVec3Degrees({ + x: 0, + y: orientation.y, + z: 0 +}); +orientation = Quat.fromVec3Degrees(orientation); +var markers = []; + + +var whiteboardPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(orientation))); +var WHITEBOARD_MODEL_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/Whiteboard-4.fbx"; +var WHITEBOARD_COLLISION_HULL_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/whiteboardCollisionHull.obj"; +var whiteboard = Entities.addEntity({ + type: "Model", + name: "whiteboard", + modelURL: WHITEBOARD_MODEL_URL, + position: whiteboardPosition, + rotation: whiteboardRotation, + shapeType: 'compound', + compoundShapeURL: WHITEBOARD_COLLISION_HULL_URL, + dimensions: { + x: 1.86, + y: 2.7, + z: 0.4636 + }, +}); + +var whiteboardSurfacePosition = Vec3.sum(whiteboardPosition, { + x: 0.0, + y: 0.45, + z: 0.0 +}); +whiteboardSurfacePosition = Vec3.sum(whiteboardSurfacePosition, Vec3.multiply(-0.02, Quat.getRight(whiteboardRotation))); +var moveForwardDistance = 0.02; +whiteboardFrontSurfacePosition = Vec3.sum(whiteboardSurfacePosition, Vec3.multiply(-moveForwardDistance, Quat.getFront(whiteboardRotation))); +var whiteboardSurfaceSettings = { + type: "Box", + name: "hifi-whiteboardDrawingSurface", + dimensions: { + x: 1.82, + y: 1.8, + z: 0.01 + }, + color: { + red: 200, + green: 10, + blue: 200 + }, + position: whiteboardFrontSurfacePosition, + rotation: whiteboardRotation, + visible: false, + parentID: whiteboard +} +var whiteboardFrontDrawingSurface = Entities.addEntity(whiteboardSurfaceSettings); + + +whiteboardBackSurfacePosition = Vec3.sum(whiteboardSurfacePosition, Vec3.multiply(moveForwardDistance, Quat.getFront(whiteboardRotation))); +whiteboardSurfaceSettings.position = whiteboardBackSurfacePosition; + +var whiteboardBackDrawingSurface = Entities.addEntity(whiteboardSurfaceSettings); + + +var WHITEBOARD_RACK_DEPTH = 1.9; + +var ERASER_MODEL_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/eraser-2.fbx"; +var ERASER_SCRIPT_URL = Script.resolvePath("eraserEntityScript.js?v43"); +var eraserPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(WHITEBOARD_RACK_DEPTH, Quat.getFront(whiteboardRotation))); +eraserPosition = Vec3.sum(eraserPosition, Vec3.multiply(-0.5, Quat.getRight(whiteboardRotation))); +var eraserRotation = markerRotation; + +var eraser = Entities.addEntity({ + type: "Model", + modelURL: ERASER_MODEL_URL, + position: eraserPosition, + script: ERASER_SCRIPT_URL, + shapeType: "box", + dimensions: { + x: 0.0858, + y: 0.0393, + z: 0.2083 + }, + rotation: eraserRotation, + dynamic: true, + gravity: { + x: 0, + y: -1, + z: 0 + }, + velocity: { + x: 0, + y: -0.1, + z: 0 + }, + userData: JSON.stringify({ + originalPosition: eraserPosition, + originalRotation: eraserRotation, + wearable: { + joints: { + RightHand: [{ + x: 0.020, + y: 0.120, + z: 0.049 + }, { + x: 0.1004, + y: 0.6424, + z: 0.717, + w: 0.250 + }], + LeftHand: [{ + x: -0.005, + y: 0.1101, + z: 0.053 + }, { + x: 0.723, + y: 0.289, + z: 0.142, + w: 0.610 + }] + } + } + }) +}); + +createMarkers(); + +function createMarkers() { + var modelURLS = [ + "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/marker-blue.fbx", + "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/marker-red.fbx", + "https://s3-us-west-1.amazonaws.com/hifi-content/eric/models/marker-black.fbx", + ]; + + var markerPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(WHITEBOARD_RACK_DEPTH, Quat.getFront(orientation))); + + createMarker(modelURLS[0], markerPosition, { + red: 10, + green: 10, + blue: 200 + }); + + markerPosition = Vec3.sum(markerPosition, Vec3.multiply(-0.2, Quat.getFront(markerRotation))); + createMarker(modelURLS[1], markerPosition, { + red: 200, + green: 10, + blue: 10 + }); + + markerPosition = Vec3.sum(markerPosition, Vec3.multiply(0.4, Quat.getFront(markerRotation))); + createMarker(modelURLS[2], markerPosition, { + red: 10, + green: 10, + blue: 10 + }); +} + + +function createMarker(modelURL, markerPosition, markerColor) { + var MARKER_SCRIPT_URL = Script.resolvePath("markerEntityScript.js?v1" + Math.random()); + var marker = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + rotation: markerRotation, + shapeType: "box", + name: "marker", + dynamic: true, + gravity: { + x: 0, + y: -1, + z: 0 + }, + velocity: { + x: 0, + y: -0.1, + z: 0 + }, + position: markerPosition, + dimensions: { + x: 0.027, + y: 0.027, + z: 0.164 + }, + name: "marker", + script: MARKER_SCRIPT_URL, + userData: JSON.stringify({ + originalPosition: markerPosition, + originalRotation: markerRotation, + markerColor: markerColor, + wearable: { + joints: { + RightHand: [{ + x: 0.001, + y: 0.139, + z: 0.050 + }, { + x: -0.73, + y: -0.043, + z: -0.108, + w: -0.666 + }], + LeftHand: [{ + x: 0.007, + y: 0.151, + z: 0.061 + }, { + x: -0.417, + y: 0.631, + z: -0.389, + w: -0.525 + }] + } + } + }) + }); + + markers.push(marker); + +} + + + +function cleanup() { + Entities.deleteEntity(whiteboard); + Entities.deleteEntity(whiteboardFrontDrawingSurface); + Entities.deleteEntity(whiteboardBackDrawingSurface); + Entities.deleteEntity(eraser); + markers.forEach(function(marker) { + Entities.deleteEntity(marker); + }); +} + +Script.scriptEnding.connect(cleanup); \ No newline at end of file diff --git a/interface/resources/qml/controls-uit/Table.qml b/interface/resources/qml/controls-uit/Table.qml index fc6c310612..de6950c07e 100644 --- a/interface/resources/qml/controls-uit/Table.qml +++ b/interface/resources/qml/controls-uit/Table.qml @@ -133,13 +133,14 @@ TableView { HiFiGlyphs { id: reloadButton text: hifi.glyphs.reloadSmall - color: parent.color + color: reloadButtonArea.pressed ? hifi.colors.white : parent.color anchors { top: parent.top right: stopButton.left verticalCenter: parent.verticalCenter } MouseArea { + id: reloadButtonArea anchors { fill: parent; margins: -2 } onClicked: reloadScript(model.url) } @@ -149,13 +150,14 @@ TableView { HiFiGlyphs { id: stopButton text: hifi.glyphs.closeSmall - color: parent.color + color: stopButtonArea.pressed ? hifi.colors.white : parent.color anchors { top: parent.top right: parent.right verticalCenter: parent.verticalCenter } MouseArea { + id: stopButtonArea anchors { fill: parent; margins: -2 } onClicked: stopScript(model.url) } diff --git a/interface/resources/qml/controls-uit/Tree.qml b/interface/resources/qml/controls-uit/Tree.qml index d100cdcd44..454218b116 100644 --- a/interface/resources/qml/controls-uit/Tree.qml +++ b/interface/resources/qml/controls-uit/Tree.qml @@ -56,14 +56,23 @@ TreeView { branchDelegate: HiFiGlyphs { text: styleData.isExpanded ? hifi.glyphs.disclosureCollapse : hifi.glyphs.disclosureExpand - size: hifi.fontSizes.tableText * 2.5 // tableText is in points; proportionately scale to pixels + size: hifi.fontSizes.tableText * 2.5 color: colorScheme == hifi.colorSchemes.light - ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) - : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) + ? (styleData.selected + ? hifi.colors.black + : (iconArea.pressed ? hifi.colors.white : hifi.colors.baseGrayHighlight)) + : (styleData.selected + ? hifi.colors.black + : (iconArea.pressed ? hifi.colors.white : hifi.colors.lightGrayText)) anchors { left: parent ? parent.left : undefined leftMargin: hifi.dimensions.tablePadding / 2 } + MouseArea { + id: iconArea + anchors.fill: parent + propagateComposedEvents: true + } } handle: Item {