mirror of
https://github.com/overte-org/overte.git
synced 2025-04-12 14:22:11 +02:00
Merge pull request #8294 from imgntn/home_whiteboard_jul21
Add new whiteboard to home
This commit is contained in:
commit
133b59ae42
9 changed files with 845 additions and 494 deletions
|
@ -35,8 +35,6 @@
|
||||||
|
|
||||||
var tiltMazePath = Script.resolvePath("atp:/tiltMaze/wrapper.js")
|
var tiltMazePath = Script.resolvePath("atp:/tiltMaze/wrapper.js")
|
||||||
|
|
||||||
var whiteboardPath = Script.resolvePath("atp:/whiteboard/wrapper.js");
|
|
||||||
|
|
||||||
var cuckooClockPath = Script.resolvePath("atp:/cuckooClock/wrapper.js");
|
var cuckooClockPath = Script.resolvePath("atp:/cuckooClock/wrapper.js");
|
||||||
|
|
||||||
var pingPongGunPath = Script.resolvePath("atp:/pingPongGun/wrapper.js");
|
var pingPongGunPath = Script.resolvePath("atp:/pingPongGun/wrapper.js");
|
||||||
|
@ -51,7 +49,6 @@
|
||||||
|
|
||||||
Script.include(fishTankPath);
|
Script.include(fishTankPath);
|
||||||
Script.include(tiltMazePath);
|
Script.include(tiltMazePath);
|
||||||
Script.include(whiteboardPath);
|
|
||||||
Script.include(cuckooClockPath);
|
Script.include(cuckooClockPath);
|
||||||
Script.include(pingPongGunPath);
|
Script.include(pingPongGunPath);
|
||||||
Script.include(transformerPath);
|
Script.include(transformerPath);
|
||||||
|
@ -201,6 +198,7 @@
|
||||||
Script.setTimeout(function() {
|
Script.setTimeout(function() {
|
||||||
_this.createKineticEntities();
|
_this.createKineticEntities();
|
||||||
_this.createScriptedEntities();
|
_this.createScriptedEntities();
|
||||||
|
_this.createWhiteboard();
|
||||||
_this.setupDressingRoom();
|
_this.setupDressingRoom();
|
||||||
_this.createMilkPailBalls();
|
_this.createMilkPailBalls();
|
||||||
_this.createTarget();
|
_this.createTarget();
|
||||||
|
@ -296,6 +294,21 @@
|
||||||
print('HOME after deleting home entities');
|
print('HOME after deleting home entities');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
createWhiteboard: function() {
|
||||||
|
var WHITEBOARD_URL = "atp:/whiteboard/whiteboardWithSwiper.json"
|
||||||
|
var success = Clipboard.importEntities(WHITEBOARD_URL);
|
||||||
|
if (success === true) {
|
||||||
|
created = Clipboard.pasteEntities({
|
||||||
|
x: 1105.0955,
|
||||||
|
y: 460.5000,
|
||||||
|
z: -77.4409
|
||||||
|
})
|
||||||
|
print('created ' + created);
|
||||||
|
} else {
|
||||||
|
print('failed to import whiteboard');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
createScriptedEntities: function() {
|
createScriptedEntities: function() {
|
||||||
var fishTank = new FishTank({
|
var fishTank = new FishTank({
|
||||||
x: 1099.2200,
|
x: 1099.2200,
|
||||||
|
@ -313,16 +326,6 @@
|
||||||
z: -80.4891
|
z: -80.4891
|
||||||
});
|
});
|
||||||
|
|
||||||
var whiteboard = new Whiteboard({
|
|
||||||
x: 1105.0955,
|
|
||||||
y: 460.5000,
|
|
||||||
z: -77.4409
|
|
||||||
}, {
|
|
||||||
x: -0.0013,
|
|
||||||
y: -133.0056,
|
|
||||||
z: -0.0013
|
|
||||||
});
|
|
||||||
|
|
||||||
var pingPongGun = new HomePingPongGun({
|
var pingPongGun = new HomePingPongGun({
|
||||||
x: 1101.2123,
|
x: 1101.2123,
|
||||||
y: 460.2328,
|
y: 460.2328,
|
||||||
|
|
|
@ -1,128 +1,59 @@
|
||||||
//
|
//
|
||||||
// eraserEntityScript.js
|
// eraserEntityScript.js
|
||||||
// examples/homeContent/eraserEntityScript
|
|
||||||
//
|
//
|
||||||
// Created by Eric Levin on 2/17/15.
|
// Created by Eric Levin on 2/17/15.
|
||||||
|
// Additions by James B. Pollack @imgntn 6/9/2016
|
||||||
// Copyright 2016 High Fidelity, Inc.
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
//
|
//
|
||||||
// This entity script provides logic for an object with attached script to erase nearby marker strokes
|
// This entity script provides logic for an object with attached script to erase nearby marker strokes
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
Script.include('../utils.js');
|
Script.include('atp:/whiteboard/utils.js');
|
||||||
var TRIGGER_CONTROLS = [
|
|
||||||
Controller.Standard.LT,
|
|
||||||
Controller.Standard.RT,
|
|
||||||
];
|
|
||||||
var _this;
|
var _this;
|
||||||
|
|
||||||
Eraser = function() {
|
Eraser = function() {
|
||||||
_this = this;
|
_this = this;
|
||||||
|
_this.STROKE_NAME = "hifi_polyline_markerStroke";
|
||||||
_this.ERASER_TRIGGER_THRESHOLD = 0.2;
|
_this.ERASER_TO_STROKE_SEARCH_RADIUS = 0.1;
|
||||||
_this.STROKE_NAME = "home_polyline_markerStroke";
|
|
||||||
_this.ERASER_TO_STROKE_SEARCH_RADIUS = 0.7;
|
|
||||||
_this.ERASER_RESET_WAIT_TIME = 3000;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Eraser.prototype = {
|
Eraser.prototype = {
|
||||||
|
|
||||||
startEquip: function(id, params) {
|
continueNearGrab: function() {
|
||||||
_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;
|
_this.eraserPosition = Entities.getEntityProperties(_this.entityID, "position").position;
|
||||||
Overlays.editOverlay(_this.searchSphere, {
|
_this.continueHolding();
|
||||||
position: _this.eraserPosition
|
|
||||||
});
|
|
||||||
this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[_this.hand]);
|
|
||||||
if (_this.triggerValue > _this.ERASER_TRIGGER_THRESHOLD) {
|
|
||||||
_this.continueHolding();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
continueHolding: function() {
|
continueHolding: function() {
|
||||||
var strokeIDs = Entities.findEntities(_this.eraserPosition, _this.ERASER_TO_STROKE_SEARCH_RADIUS);
|
var results = Entities.findEntities(_this.eraserPosition, _this.ERASER_TO_STROKE_SEARCH_RADIUS);
|
||||||
// Create a map of stroke entities and their positions
|
// Create a map of stroke entities and their positions
|
||||||
|
|
||||||
strokeIDs.forEach(function(strokeID) {
|
results.forEach(function(stroke) {
|
||||||
var strokeProps = Entities.getEntityProperties(strokeID, ["position", "name"]);
|
var props = Entities.getEntityProperties(stroke, ["position", "name"]);
|
||||||
if (strokeProps.name === _this.STROKE_NAME && Vec3.distance(_this.eraserPosition, strokeProps.position) < _this.ERASER_TO_STROKE_SEARCH_RADIUS) {
|
if (props.name === _this.STROKE_NAME && Vec3.distance(_this.eraserPosition, props.position) < _this.ERASER_TO_STROKE_SEARCH_RADIUS) {
|
||||||
Entities.deleteEntity(strokeID);
|
Entities.deleteEntity(stroke);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
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);
|
|
||||||
},
|
|
||||||
collisionWithEntity: function(myID, otherID, collision) {
|
|
||||||
var otherProps = Entities.getEntityProperties(otherID);
|
|
||||||
if (otherProps.name === 'home_model_homeset') {
|
|
||||||
var userData = getEntityUserData(_this.entityID);
|
|
||||||
Entities.editEntity(_this.entityID, {
|
|
||||||
position: userData.originalPosition,
|
|
||||||
rotation: userData.originalRotation,
|
|
||||||
velocity: {
|
|
||||||
x: 0,
|
|
||||||
y: -0.01,
|
|
||||||
z: 0
|
|
||||||
},
|
|
||||||
angularVelocity: {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
z: 0
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
preload: function(entityID) {
|
preload: function(entityID) {
|
||||||
_this.entityID = 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() {
|
startEquip: function() {
|
||||||
Overlays.deleteOverlay(_this.searchSphere);
|
_this.startNearGrab();
|
||||||
}
|
},
|
||||||
|
|
||||||
|
continueEquip: function() {
|
||||||
|
_this.continueNearGrab();
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// entity scripts always need to return a newly constructed object of our type
|
|
||||||
return new Eraser();
|
return new Eraser();
|
||||||
});
|
});
|
|
@ -1,8 +1,8 @@
|
||||||
//
|
//
|
||||||
// markerTipEntityScript.js
|
// markerTipEntityScript.js
|
||||||
// examples/homeContent/markerTipEntityScript
|
|
||||||
//
|
//
|
||||||
// Created by Eric Levin on 2/17/15.
|
// Created by Eric Levin on 2/17/15.
|
||||||
|
// Additions by James B. Pollack @imgntn 6/9/2016
|
||||||
// Copyright 2016 High Fidelity, Inc.
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
//
|
//
|
||||||
// This script provides the logic for an object to draw marker strokes on its associated whiteboard
|
// This script provides the logic for an object to draw marker strokes on its associated whiteboard
|
||||||
|
@ -10,15 +10,9 @@
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
Script.include('../utils.js');
|
Script.include('atp:/whiteboard/utils.js');
|
||||||
var TRIGGER_CONTROLS = [
|
|
||||||
Controller.Standard.LT,
|
|
||||||
Controller.Standard.RT,
|
|
||||||
];
|
|
||||||
|
|
||||||
var MAX_POINTS_PER_STROKE = 40;
|
var MAX_POINTS_PER_STROKE = 40;
|
||||||
var _this;
|
var _this;
|
||||||
|
@ -31,95 +25,74 @@
|
||||||
min: 0.002,
|
min: 0.002,
|
||||||
max: 0.01
|
max: 0.01
|
||||||
};
|
};
|
||||||
_this.MAX_MARKER_TO_BOARD_DISTANCE = 1.4;
|
|
||||||
_this.MIN_DISTANCE_BETWEEN_POINTS = 0.002;
|
_this.MIN_DISTANCE_BETWEEN_POINTS = 0.002;
|
||||||
_this.MAX_DISTANCE_BETWEEN_POINTS = 0.1;
|
_this.MAX_DISTANCE_BETWEEN_POINTS = 0.1;
|
||||||
_this.strokes = [];
|
_this.strokes = [];
|
||||||
_this.PAINTING_TRIGGER_THRESHOLD = 0.2;
|
_this.STROKE_NAME = "hifi_polyline_markerStroke";
|
||||||
_this.STROKE_NAME = "home_polyline_markerStroke";
|
_this.WHITEBOARD_SURFACE_NAME = "hifi-whiteboardDrawingSurface"
|
||||||
_this.WHITEBOARD_SURFACE_NAME = "home_box_whiteboardDrawingSurface"
|
|
||||||
_this.MARKER_RESET_WAIT_TIME = 3000;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
MarkerTip.prototype = {
|
MarkerTip.prototype = {
|
||||||
|
|
||||||
startEquip: function(id, params) {
|
startNearGrab: function() {
|
||||||
print('start equip')
|
|
||||||
_this.whiteboards = [];
|
_this.whiteboards = [];
|
||||||
_this.equipped = true;
|
|
||||||
_this.hand = params[0] == "left" ? 0 : 1;
|
|
||||||
_this.markerColor = getEntityUserData(_this.entityID).markerColor;
|
_this.markerColor = getEntityUserData(_this.entityID).markerColor;
|
||||||
// search for whiteboards
|
var markerProps = Entities.getEntityProperties(_this.entityID);
|
||||||
var markerPosition = Entities.getEntityProperties(_this.entityID, "position").position;
|
_this.DRAW_ON_BOARD_DISTANCE = markerProps.dimensions.z / 2;
|
||||||
var entities = Entities.findEntities(markerPosition, 10);
|
var markerPosition = markerProps.position;
|
||||||
entities.forEach(function(entity) {
|
var results = Entities.findEntities(markerPosition, 5);
|
||||||
|
results.forEach(function(entity) {
|
||||||
var entityName = Entities.getEntityProperties(entity, "name").name;
|
var entityName = Entities.getEntityProperties(entity, "name").name;
|
||||||
if (entityName === _this.WHITEBOARD_SURFACE_NAME) {
|
if (entityName === _this.WHITEBOARD_SURFACE_NAME) {
|
||||||
_this.whiteboards.push(entity);
|
_this.whiteboards.push(entity);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
releaseEquip: function() {
|
releaseGrab: function() {
|
||||||
_this.resetStroke();
|
_this.resetStroke();
|
||||||
Overlays.editOverlay(_this.laserPointer, {
|
|
||||||
visible: false
|
|
||||||
});
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
collisionWithEntity: function(myID, otherID, collision) {
|
continueNearGrab: function() {
|
||||||
var otherProps = Entities.getEntityProperties(otherID);
|
|
||||||
if (otherProps.name === 'home_model_homeset') {
|
|
||||||
var userData = getEntityUserData(_this.entityID);
|
|
||||||
Entities.editEntity(_this.entityID, {
|
|
||||||
position: userData.originalPosition,
|
|
||||||
rotation: userData.originalRotation,
|
|
||||||
velocity: {
|
|
||||||
x: 0,
|
|
||||||
y: -0.01,
|
|
||||||
z: 0
|
|
||||||
},
|
|
||||||
angularVelocity: {x: 0, y: 0, z: 0}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
continueEquip: function() {
|
|
||||||
// cast a ray from marker and see if it hits anything
|
// cast a ray from marker and see if it hits anything
|
||||||
var markerProps = Entities.getEntityProperties(_this.entityID, ["position", "rotation"]);
|
var markerProps = Entities.getEntityProperties(_this.entityID);
|
||||||
|
|
||||||
|
//need to back up the ray to the back of the marker
|
||||||
|
|
||||||
|
var markerFront = Quat.getFront(markerProps.rotation);
|
||||||
|
var howFarBack = markerProps.dimensions.z / 2;
|
||||||
|
var pulledBack = Vec3.multiply(markerFront, -howFarBack);
|
||||||
|
var backedOrigin = Vec3.sum(markerProps.position, pulledBack);
|
||||||
|
|
||||||
var pickRay = {
|
var pickRay = {
|
||||||
origin: markerProps.position,
|
origin: backedOrigin,
|
||||||
direction: Quat.getFront(markerProps.rotation)
|
direction: Quat.getFront(markerProps.rotation)
|
||||||
}
|
}
|
||||||
var intersection = Entities.findRayIntersectionBlocking(pickRay, true, _this.whiteboards);
|
var intersection = Entities.findRayIntersection(pickRay, true, _this.whiteboards);
|
||||||
|
|
||||||
if (intersection.intersects && Vec3.distance(intersection.intersection, markerProps.position) < _this.MAX_MARKER_TO_BOARD_DISTANCE) {
|
if (intersection.intersects && Vec3.distance(intersection.intersection, markerProps.position) <= _this.DRAW_ON_BOARD_DISTANCE) {
|
||||||
_this.currentWhiteboard = intersection.entityID;
|
_this.currentWhiteboard = intersection.entityID;
|
||||||
var whiteboardRotation = Entities.getEntityProperties(_this.currentWhiteboard, "rotation").rotation;
|
var whiteboardRotation = Entities.getEntityProperties(_this.currentWhiteboard, "rotation").rotation;
|
||||||
_this.whiteboardNormal = Quat.getFront(whiteboardRotation);
|
_this.whiteboardNormal = Quat.getFront(whiteboardRotation);
|
||||||
Overlays.editOverlay(_this.laserPointer, {
|
|
||||||
visible: true,
|
_this.paint(intersection.intersection)
|
||||||
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 {
|
} else {
|
||||||
if (_this.currentStroke) {
|
if (_this.currentStroke) {
|
||||||
_this.resetStroke();
|
_this.resetStroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
Overlays.editOverlay(_this.laserPointer, {
|
|
||||||
visible: false
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
startEquip: function() {
|
||||||
|
_this.startNearGrab();
|
||||||
|
},
|
||||||
|
|
||||||
|
continueEquip: function() {
|
||||||
|
_this.continueNearGrab();
|
||||||
},
|
},
|
||||||
|
|
||||||
newStroke: function(position) {
|
newStroke: function(position) {
|
||||||
|
@ -135,12 +108,7 @@
|
||||||
position: position,
|
position: position,
|
||||||
textures: _this.MARKER_TEXTURE_URL,
|
textures: _this.MARKER_TEXTURE_URL,
|
||||||
color: _this.markerColor,
|
color: _this.markerColor,
|
||||||
lifetime: 5000,
|
lifetime: 5000
|
||||||
userData: JSON.stringify({
|
|
||||||
'hifiHomeKey': {
|
|
||||||
'reset': true
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
_this.linePoints = [];
|
_this.linePoints = [];
|
||||||
|
@ -170,7 +138,8 @@
|
||||||
_this.normals.push(_this.whiteboardNormal);
|
_this.normals.push(_this.whiteboardNormal);
|
||||||
|
|
||||||
var strokeWidths = [];
|
var strokeWidths = [];
|
||||||
for (var i = 0; i < _this.linePoints.length; i++) {
|
var i;
|
||||||
|
for (i = 0; i < _this.linePoints.length; i++) {
|
||||||
// Create a temp array of stroke widths for calligraphy effect - start and end should be less wide
|
// 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 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);
|
var pointWidth = map(pointsFromCenter, 0, this.linePoints.length / 2, _this.STROKE_WIDTH_RANGE.max, this.STROKE_WIDTH_RANGE.min);
|
||||||
|
@ -191,6 +160,7 @@
|
||||||
_this.oldPosition = position;
|
_this.oldPosition = position;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
resetStroke: function() {
|
resetStroke: function() {
|
||||||
|
|
||||||
Entities.editEntity(_this.currentStroke, {
|
Entities.editEntity(_this.currentStroke, {
|
||||||
|
@ -203,26 +173,9 @@
|
||||||
|
|
||||||
preload: function(entityID) {
|
preload: function(entityID) {
|
||||||
this.entityID = 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();
|
return new MarkerTip();
|
||||||
});
|
});
|
|
@ -1,23 +0,0 @@
|
||||||
//
|
|
||||||
//
|
|
||||||
// Created by The Content Team 4/10/216
|
|
||||||
// Copyright 2016 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
|
|
||||||
//
|
|
||||||
|
|
||||||
var whiteboardPath = Script.resolvePath('wrapper.js');
|
|
||||||
Script.include(whiteboardPath);
|
|
||||||
|
|
||||||
var whiteboard = new Whiteboard({
|
|
||||||
x: 1104,
|
|
||||||
y: 460.5,
|
|
||||||
z: -77
|
|
||||||
}, {
|
|
||||||
x: 0,
|
|
||||||
y: -133,
|
|
||||||
z: 0
|
|
||||||
});
|
|
62
unpublishedScripts/DomainContent/Home/whiteboard/swiper.js
Normal file
62
unpublishedScripts/DomainContent/Home/whiteboard/swiper.js
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
// Created by James B. Pollack @imgntn 6/8/2016
|
||||||
|
// Copyright 2016 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() {
|
||||||
|
|
||||||
|
var _this;
|
||||||
|
|
||||||
|
Swiper = function() {
|
||||||
|
_this = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Swiper.prototype = {
|
||||||
|
busy: false,
|
||||||
|
preload: function(entityID) {
|
||||||
|
this.entityID = entityID;
|
||||||
|
Script.update.connect(this.update);
|
||||||
|
},
|
||||||
|
clickReleaseOnEntity: function() {
|
||||||
|
this.createSupplies();
|
||||||
|
},
|
||||||
|
update: function() {
|
||||||
|
if (_this.busy === true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var position = Entities.getEntityProperties(_this.entityID).position;
|
||||||
|
var TRIGGER_THRESHOLD = 0.11;
|
||||||
|
|
||||||
|
var leftHandPosition = MyAvatar.getLeftPalmPosition();
|
||||||
|
var rightHandPosition = MyAvatar.getRightPalmPosition();
|
||||||
|
|
||||||
|
var leftDistance = Vec3.distance(leftHandPosition, position)
|
||||||
|
var rightDistance = Vec3.distance(rightHandPosition, position)
|
||||||
|
|
||||||
|
if (rightDistance < TRIGGER_THRESHOLD || leftDistance < TRIGGER_THRESHOLD) {
|
||||||
|
_this.createSupplies();
|
||||||
|
_this.busy = true;
|
||||||
|
Script.setTimeout(function() {
|
||||||
|
_this.busy = false;
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
createSupplies: function() {
|
||||||
|
var myProperties = Entities.getEntityProperties(this.entityID);
|
||||||
|
|
||||||
|
Entities.callEntityMethod(myProperties.parentID, "createMarkers");
|
||||||
|
Entities.callEntityMethod(myProperties.parentID, "createEraser");
|
||||||
|
|
||||||
|
},
|
||||||
|
unload: function() {
|
||||||
|
Script.update.disconnect(this.update);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return new Swiper
|
||||||
|
})
|
347
unpublishedScripts/DomainContent/Home/whiteboard/utils.js
Normal file
347
unpublishedScripts/DomainContent/Home/whiteboard/utils.js
Normal file
|
@ -0,0 +1,347 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2015/08/29
|
||||||
|
// Copyright 2015 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
|
||||||
|
//
|
||||||
|
|
||||||
|
map = function(value, min1, max1, min2, max2) {
|
||||||
|
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3toStr = function(v, digits) {
|
||||||
|
if (!digits) {
|
||||||
|
digits = 3;
|
||||||
|
}
|
||||||
|
return "{ " + v.x.toFixed(digits) + ", " + v.y.toFixed(digits) + ", " + v.z.toFixed(digits) + " }";
|
||||||
|
}
|
||||||
|
|
||||||
|
quatToStr = function(q, digits) {
|
||||||
|
if (!digits) {
|
||||||
|
digits = 3;
|
||||||
|
}
|
||||||
|
return "{ " + q.w.toFixed(digits) + ", " + q.x.toFixed(digits) + ", " +
|
||||||
|
q.y.toFixed(digits) + ", " + q.z.toFixed(digits) + " }";
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3equal = function(v0, v1) {
|
||||||
|
return (v0.x == v1.x) && (v0.y == v1.y) && (v0.z == v1.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
colorMix = function(colorA, colorB, mix) {
|
||||||
|
var result = {};
|
||||||
|
for (var key in colorA) {
|
||||||
|
result[key] = (colorA[key] * (1 - mix)) + (colorB[key] * mix);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
scaleLine = function(start, end, scale) {
|
||||||
|
var v = Vec3.subtract(end, start);
|
||||||
|
var length = Vec3.length(v);
|
||||||
|
v = Vec3.multiply(scale, v);
|
||||||
|
return Vec3.sum(start, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
findAction = function(name) {
|
||||||
|
return Controller.findAction(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
addLine = function(origin, vector, color) {
|
||||||
|
if (!color) {
|
||||||
|
color = COLORS.WHITE
|
||||||
|
}
|
||||||
|
return Entities.addEntity(mergeObjects(LINE_PROTOTYPE, {
|
||||||
|
position: origin,
|
||||||
|
linePoints: [
|
||||||
|
ZERO_VECTOR,
|
||||||
|
vector,
|
||||||
|
],
|
||||||
|
color: color
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME fetch from a subkey of user data to support non-destructive modifications
|
||||||
|
setEntityUserData = function(id, data) {
|
||||||
|
var json = JSON.stringify(data)
|
||||||
|
Entities.editEntity(id, {
|
||||||
|
userData: json
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME do non-destructive modification of the existing user data
|
||||||
|
getEntityUserData = function(id) {
|
||||||
|
var results = null;
|
||||||
|
var properties = Entities.getEntityProperties(id, "userData");
|
||||||
|
if (properties.userData) {
|
||||||
|
try {
|
||||||
|
results = JSON.parse(properties.userData);
|
||||||
|
} catch (err) {
|
||||||
|
logDebug(err);
|
||||||
|
logDebug(properties.userData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results ? results : {};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Non-destructively modify the user data of an entity.
|
||||||
|
setEntityCustomData = function(customKey, id, data) {
|
||||||
|
var userData = getEntityUserData(id);
|
||||||
|
if (data == null) {
|
||||||
|
delete userData[customKey];
|
||||||
|
} else {
|
||||||
|
userData[customKey] = data;
|
||||||
|
}
|
||||||
|
setEntityUserData(id, userData);
|
||||||
|
}
|
||||||
|
|
||||||
|
getEntityCustomData = function(customKey, id, defaultValue) {
|
||||||
|
var userData = getEntityUserData(id);
|
||||||
|
if (undefined != userData[customKey]) {
|
||||||
|
return userData[customKey];
|
||||||
|
} else {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mergeObjects = function(proto, custom) {
|
||||||
|
var result = {};
|
||||||
|
for (var attrname in proto) {
|
||||||
|
result[attrname] = proto[attrname];
|
||||||
|
}
|
||||||
|
for (var attrname in custom) {
|
||||||
|
result[attrname] = custom[attrname];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_WARN = 1;
|
||||||
|
|
||||||
|
logWarn = function(str) {
|
||||||
|
if (LOG_WARN) {
|
||||||
|
print(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_ERROR = 1;
|
||||||
|
|
||||||
|
logError = function(str) {
|
||||||
|
if (LOG_ERROR) {
|
||||||
|
print(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO = 1;
|
||||||
|
|
||||||
|
logInfo = function(str) {
|
||||||
|
if (LOG_INFO) {
|
||||||
|
print(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG = 0;
|
||||||
|
|
||||||
|
logDebug = function(str) {
|
||||||
|
if (LOG_DEBUG) {
|
||||||
|
print(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_TRACE = 0;
|
||||||
|
|
||||||
|
logTrace = function(str) {
|
||||||
|
if (LOG_TRACE) {
|
||||||
|
print(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes the penetration between a point and a sphere (centered at the origin)
|
||||||
|
// if point is inside sphere: returns true and stores the result in 'penetration'
|
||||||
|
// (the vector that would move the point outside the sphere)
|
||||||
|
// otherwise returns false
|
||||||
|
findSphereHit = function(point, sphereRadius) {
|
||||||
|
var EPSILON = 0.000001; //smallish positive number - used as margin of error for some computations
|
||||||
|
var vectorLength = Vec3.length(point);
|
||||||
|
if (vectorLength < EPSILON) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
var distance = vectorLength - sphereRadius;
|
||||||
|
if (distance < 0.0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
findSpherePointHit = function(sphereCenter, sphereRadius, point) {
|
||||||
|
return findSphereHit(Vec3.subtract(point, sphereCenter), sphereRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
findSphereSphereHit = function(firstCenter, firstRadius, secondCenter, secondRadius) {
|
||||||
|
return findSpherePointHit(firstCenter, firstRadius + secondRadius, secondCenter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a vec3 v, return a vec3 that is the same vector relative to the avatars
|
||||||
|
// DEFAULT eye position, rotated into the avatars reference frame.
|
||||||
|
getEyeRelativePosition = function(v) {
|
||||||
|
return Vec3.sum(MyAvatar.getDefaultEyePosition(), Vec3.multiplyQbyV(MyAvatar.orientation, v));
|
||||||
|
}
|
||||||
|
|
||||||
|
getAvatarRelativeRotation = function(q) {
|
||||||
|
return Quat.multiply(MyAvatar.orientation, q);
|
||||||
|
}
|
||||||
|
|
||||||
|
pointInExtents = function(point, minPoint, maxPoint) {
|
||||||
|
return (point.x >= minPoint.x && point.x <= maxPoint.x) &&
|
||||||
|
(point.y >= minPoint.y && point.y <= maxPoint.y) &&
|
||||||
|
(point.z >= minPoint.z && point.z <= maxPoint.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an HSL color value to RGB. Conversion formula
|
||||||
|
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
|
||||||
|
* Assumes h, s, and l are contained in the set [0, 1] and
|
||||||
|
* returns r, g, and b in the set [0, 255].
|
||||||
|
*
|
||||||
|
* @param Number h The hue
|
||||||
|
* @param Number s The saturation
|
||||||
|
* @param Number l The lightness
|
||||||
|
* @return Array The RGB representation
|
||||||
|
*/
|
||||||
|
hslToRgb = function(hsl) {
|
||||||
|
var r, g, b;
|
||||||
|
if (hsl.s == 0) {
|
||||||
|
r = g = b = hsl.l; // achromatic
|
||||||
|
} else {
|
||||||
|
var hue2rgb = function hue2rgb(p, q, t) {
|
||||||
|
if (t < 0) t += 1;
|
||||||
|
if (t > 1) t -= 1;
|
||||||
|
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
||||||
|
if (t < 1 / 2) return q;
|
||||||
|
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
var q = hsl.l < 0.5 ? hsl.l * (1 + hsl.s) : hsl.l + hsl.s - hsl.l * hsl.s;
|
||||||
|
var p = 2 * hsl.l - q;
|
||||||
|
r = hue2rgb(p, q, hsl.h + 1 / 3);
|
||||||
|
g = hue2rgb(p, q, hsl.h);
|
||||||
|
b = hue2rgb(p, q, hsl.h - 1 / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
red: Math.round(r * 255),
|
||||||
|
green: Math.round(g * 255),
|
||||||
|
blue: Math.round(b * 255)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
orientationOf = function(vector) {
|
||||||
|
var Y_AXIS = {
|
||||||
|
x: 0,
|
||||||
|
y: 1,
|
||||||
|
z: 0
|
||||||
|
};
|
||||||
|
var X_AXIS = {
|
||||||
|
x: 1,
|
||||||
|
y: 0,
|
||||||
|
z: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
var theta = 0.0;
|
||||||
|
|
||||||
|
var RAD_TO_DEG = 180.0 / Math.PI;
|
||||||
|
var direction, yaw, pitch;
|
||||||
|
direction = Vec3.normalize(vector);
|
||||||
|
yaw = Quat.angleAxis(Math.atan2(direction.x, direction.z) * RAD_TO_DEG, Y_AXIS);
|
||||||
|
pitch = Quat.angleAxis(Math.asin(-direction.y) * RAD_TO_DEG, X_AXIS);
|
||||||
|
return Quat.multiply(yaw, pitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
randFloat = function(low, high) {
|
||||||
|
return low + Math.random() * (high - low);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
randInt = function(low, high) {
|
||||||
|
return Math.floor(randFloat(low, high));
|
||||||
|
}
|
||||||
|
|
||||||
|
hexToRgb = function(hex) {
|
||||||
|
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||||
|
return result ? {
|
||||||
|
red: parseInt(result[1], 16),
|
||||||
|
green: parseInt(result[2], 16),
|
||||||
|
blue: parseInt(result[3], 16)
|
||||||
|
} : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateHandSizeRatio = function() {
|
||||||
|
// Get the ratio of the current avatar's hand to Owen's hand
|
||||||
|
|
||||||
|
var standardCenterHandPoint = 0.11288;
|
||||||
|
var jointNames = MyAvatar.getJointNames();
|
||||||
|
//get distance from handJoint up to leftHandIndex3 as a proxy for center of hand
|
||||||
|
var wristToFingertipDistance = 0;;
|
||||||
|
for (var i = 0; i < jointNames.length; i++) {
|
||||||
|
var jointName = jointNames[i];
|
||||||
|
print(jointName)
|
||||||
|
if (jointName.indexOf("LeftHandIndex") !== -1) {
|
||||||
|
// translations are relative to parent joint, so simply add them together
|
||||||
|
// joints face down the y-axis
|
||||||
|
var translation = MyAvatar.getDefaultJointTranslation(i).y;
|
||||||
|
wristToFingertipDistance += translation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Right now units are in cm, so convert to meters
|
||||||
|
wristToFingertipDistance /= 100;
|
||||||
|
|
||||||
|
var centerHandPoint = wristToFingertipDistance / 2;
|
||||||
|
|
||||||
|
// Compare against standard hand (Owen)
|
||||||
|
var handSizeRatio = centerHandPoint / standardCenterHandPoint;
|
||||||
|
return handSizeRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
clamp = function(val, min, max) {
|
||||||
|
return Math.max(min, Math.min(max, val))
|
||||||
|
}
|
||||||
|
|
||||||
|
attachChildToParent = function(childName, parentName, position, searchRadius) {
|
||||||
|
var childEntity, parentEntity;
|
||||||
|
var entities = Entities.findEntities(position, searchRadius)
|
||||||
|
for (var i = 0; i < entities.length; i++) {
|
||||||
|
// first go through and find the entity we want to attach to its parent
|
||||||
|
var entity = entities[i];
|
||||||
|
var name = Entities.getEntityProperties(entity, "name").name;
|
||||||
|
if (name === childName) {
|
||||||
|
childEntity = entity;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!childEntity) {
|
||||||
|
print("You are trying to attach an entity that doesn't exist! Returning");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < entities.length; i++) {
|
||||||
|
// first go through and find the entity we want to attach to its parent
|
||||||
|
var entity = entities[i];
|
||||||
|
var name = Entities.getEntityProperties(entity, "name").name;
|
||||||
|
if (name === parentName) {
|
||||||
|
parentEntity = entity;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parentEntity) {
|
||||||
|
print("You are trying to attach an entity to a parent that doesn't exist! Returning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Successfully attached " + childName + " to " + parentName);
|
||||||
|
Entities.editEntity(childEntity, {parentID: parentEntity});
|
||||||
|
}
|
211
unpublishedScripts/DomainContent/Home/whiteboard/whiteboard.js
Normal file
211
unpublishedScripts/DomainContent/Home/whiteboard/whiteboard.js
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
// Created by James B. Pollack @imgntn 6/8/2016
|
||||||
|
// Copyright 2016 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() {
|
||||||
|
|
||||||
|
var _this;
|
||||||
|
var MARKER_SCRIPT_URL = "atp:/whiteboard/markerEntityScript.js";
|
||||||
|
var ERASER_SCRIPT_URL = "atp:/whiteboard/eraserEntityScript.js";
|
||||||
|
|
||||||
|
Whiteboard = function() {
|
||||||
|
_this = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Whiteboard.prototype = {
|
||||||
|
preload: function(entityID) {
|
||||||
|
this.entityID = entityID;
|
||||||
|
this.setup();
|
||||||
|
},
|
||||||
|
unload: function() {
|
||||||
|
|
||||||
|
},
|
||||||
|
setup: function() {
|
||||||
|
|
||||||
|
var props = Entities.getEntityProperties(_this.entityID);
|
||||||
|
this.spawnRotation = Quat.safeEulerAngles(props.rotation);
|
||||||
|
this.spawnPosition = props.position;
|
||||||
|
this.orientation = Quat.fromPitchYawRollDegrees(this.spawnRotation.x, this.spawnRotation.y, this.spawnRotation.z);
|
||||||
|
this.markerRotation = Quat.fromVec3Degrees({
|
||||||
|
x: this.spawnRotation.x + 10,
|
||||||
|
y: this.spawnRotation.y - 90,
|
||||||
|
z: this.spawnRotation.z
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
createMarkers: function() {
|
||||||
|
_this.setup();
|
||||||
|
var modelURLS = [
|
||||||
|
"atp:/whiteboard/marker-blue.fbx",
|
||||||
|
"atp:/whiteboard/marker-red.fbx",
|
||||||
|
"atp:/whiteboard/marker-black.fbx",
|
||||||
|
];
|
||||||
|
|
||||||
|
var markerPosition = Vec3.sum(_this.spawnPosition, Vec3.multiply(Quat.getFront(_this.orientation), -0.1));
|
||||||
|
|
||||||
|
_this.createMarker(modelURLS[0], markerPosition, {
|
||||||
|
red: 10,
|
||||||
|
green: 10,
|
||||||
|
blue: 200
|
||||||
|
});
|
||||||
|
|
||||||
|
_this.createMarker(modelURLS[1], markerPosition, {
|
||||||
|
red: 200,
|
||||||
|
green: 10,
|
||||||
|
blue: 10
|
||||||
|
});
|
||||||
|
|
||||||
|
_this.createMarker(modelURLS[2], markerPosition, {
|
||||||
|
red: 10,
|
||||||
|
green: 10,
|
||||||
|
blue: 10
|
||||||
|
});
|
||||||
|
},
|
||||||
|
createMarker: function(modelURL, markerPosition, markerColor) {
|
||||||
|
|
||||||
|
var markerProperties = {
|
||||||
|
type: "Model",
|
||||||
|
modelURL: modelURL,
|
||||||
|
rotation: _this.markerRotation,
|
||||||
|
shapeType: "box",
|
||||||
|
name: "hifi_model_marker",
|
||||||
|
dynamic: true,
|
||||||
|
gravity: {
|
||||||
|
x: 0,
|
||||||
|
y: -5,
|
||||||
|
z: 0
|
||||||
|
},
|
||||||
|
velocity: {
|
||||||
|
x: 0,
|
||||||
|
y: -0.1,
|
||||||
|
z: 0
|
||||||
|
},
|
||||||
|
position: markerPosition,
|
||||||
|
dimensions: {
|
||||||
|
x: 0.027,
|
||||||
|
y: 0.027,
|
||||||
|
z: 0.164
|
||||||
|
},
|
||||||
|
lifetime: 86400,
|
||||||
|
script: MARKER_SCRIPT_URL,
|
||||||
|
userData: JSON.stringify({
|
||||||
|
'grabbableKey': {
|
||||||
|
'grabbable': true
|
||||||
|
},
|
||||||
|
'hifiHomeKey': {
|
||||||
|
'reset': true
|
||||||
|
},
|
||||||
|
originalPosition: markerPosition,
|
||||||
|
originalRotation: _this.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
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var marker = Entities.addEntity(markerProperties);
|
||||||
|
|
||||||
|
},
|
||||||
|
createEraser: function() {
|
||||||
|
_this.setup();
|
||||||
|
var ERASER_MODEL_URL = "atp:/whiteboard/eraser-2.fbx";
|
||||||
|
|
||||||
|
var eraserPosition = Vec3.sum(_this.spawnPosition, Vec3.multiply(Quat.getFront(_this.orientation), -0.1));
|
||||||
|
eraserPosition = Vec3.sum(eraserPosition, Vec3.multiply(-0.5, Quat.getRight(_this.orientation)));
|
||||||
|
var eraserRotation = _this.markerRotation;
|
||||||
|
|
||||||
|
var eraserProps = {
|
||||||
|
type: "Model",
|
||||||
|
name: "hifi_model_whiteboardEraser",
|
||||||
|
modelURL: ERASER_MODEL_URL,
|
||||||
|
position: eraserPosition,
|
||||||
|
script: ERASER_SCRIPT_URL,
|
||||||
|
shapeType: "box",
|
||||||
|
lifetime: 86400,
|
||||||
|
dimensions: {
|
||||||
|
x: 0.0858,
|
||||||
|
y: 0.0393,
|
||||||
|
z: 0.2083
|
||||||
|
},
|
||||||
|
rotation: eraserRotation,
|
||||||
|
dynamic: true,
|
||||||
|
gravity: {
|
||||||
|
x: 0,
|
||||||
|
y: -10,
|
||||||
|
z: 0
|
||||||
|
},
|
||||||
|
velocity: {
|
||||||
|
x: 0,
|
||||||
|
y: -0.1,
|
||||||
|
z: 0
|
||||||
|
},
|
||||||
|
userData: JSON.stringify({
|
||||||
|
'hifiHomeKey': {
|
||||||
|
'reset': true
|
||||||
|
},
|
||||||
|
'grabbableKey': {
|
||||||
|
'grabbable': true
|
||||||
|
},
|
||||||
|
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
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var eraser = Entities.addEntity(eraserProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return new Whiteboard();
|
||||||
|
})
|
|
@ -0,0 +1,149 @@
|
||||||
|
{
|
||||||
|
"Entities": [{
|
||||||
|
"clientOnly": 0,
|
||||||
|
"compoundShapeURL": "atp:/whiteboard/handScannerSwitch_phys.obj",
|
||||||
|
"created": "2016-06-14T18:08:49Z",
|
||||||
|
"dimensions": {
|
||||||
|
"x": 0.11707025021314621,
|
||||||
|
"y": 0.16469044983386993,
|
||||||
|
"z": 0.10442595928907394
|
||||||
|
},
|
||||||
|
"id": "{21d2185f-4161-484d-bb0f-70bdc307700d}",
|
||||||
|
"modelURL": "atp:/whiteboard/handScannerSwitch_tall_VR.fbx",
|
||||||
|
"name": "hifi_whiteboardSwiper",
|
||||||
|
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||||
|
"parentID": "{7b380903-a6d5-46e8-a4b3-4d58eaaa0419}",
|
||||||
|
"position": {
|
||||||
|
"x": -0.86872124671936035,
|
||||||
|
"y": -0.33092412352561951,
|
||||||
|
"z": 0.072394371032714844
|
||||||
|
},
|
||||||
|
"queryAACube": {
|
||||||
|
"scale": 0.22744926810264587,
|
||||||
|
"x": 4.9953713417053223,
|
||||||
|
"y": 0.33575963973999023,
|
||||||
|
"z": 1.1766284704208374
|
||||||
|
},
|
||||||
|
"rotation": {
|
||||||
|
"w": 0.71929502487182617,
|
||||||
|
"x": -4.57763671875e-05,
|
||||||
|
"y": -0.69472801685333252,
|
||||||
|
"z": -1.52587890625e-05
|
||||||
|
},
|
||||||
|
"script": "atp:/whiteboard/swiper.js",
|
||||||
|
"shapeType": "compound",
|
||||||
|
"type": "Model",
|
||||||
|
"userData": "{\"hifiHomeKey\":{\"reset\":true}}"
|
||||||
|
}, {
|
||||||
|
"clientOnly": 0,
|
||||||
|
"color": {
|
||||||
|
"blue": 200,
|
||||||
|
"green": 10,
|
||||||
|
"red": 200
|
||||||
|
},
|
||||||
|
"created": "2016-06-14T18:08:49Z",
|
||||||
|
"dimensions": {
|
||||||
|
"x": 1.8200000524520874,
|
||||||
|
"y": 1.7999999523162842,
|
||||||
|
"z": 0.0099999997764825821
|
||||||
|
},
|
||||||
|
"id": "{5572abff-1575-48e8-bc79-1d6373046fe9}",
|
||||||
|
"name": "hifi-whiteboardDrawingSurface",
|
||||||
|
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||||
|
"parentID": "{7b380903-a6d5-46e8-a4b3-4d58eaaa0419}",
|
||||||
|
"position": {
|
||||||
|
"x": -0.019987192004919052,
|
||||||
|
"y": 0.45400944352149963,
|
||||||
|
"z": 0.020008884370326996
|
||||||
|
},
|
||||||
|
"queryAACube": {
|
||||||
|
"scale": 2.5597851276397705,
|
||||||
|
"x": 3.3173346519470215,
|
||||||
|
"y": -0.045509219169616699,
|
||||||
|
"z": -0.66857552528381348
|
||||||
|
},
|
||||||
|
"rotation": {
|
||||||
|
"w": 1,
|
||||||
|
"x": -1.52587890625e-05,
|
||||||
|
"y": -1.52587890625e-05,
|
||||||
|
"z": -1.52587890625e-05
|
||||||
|
},
|
||||||
|
"shape": "Cube",
|
||||||
|
"type": "Box",
|
||||||
|
"visible": 0,
|
||||||
|
"userData": "{\"hifiHomeKey\":{\"reset\":true}}"
|
||||||
|
}, {
|
||||||
|
"clientOnly": 0,
|
||||||
|
"color": {
|
||||||
|
"blue": 200,
|
||||||
|
"green": 10,
|
||||||
|
"red": 200
|
||||||
|
},
|
||||||
|
"created": "2016-06-14T18:08:49Z",
|
||||||
|
"dimensions": {
|
||||||
|
"x": 1.8200000524520874,
|
||||||
|
"y": 1.7999999523162842,
|
||||||
|
"z": 0.0099999997764825821
|
||||||
|
},
|
||||||
|
"id": "{22152131-5ac7-4758-a5bc-3dda3f034eb4}",
|
||||||
|
"name": "hifi-whiteboardDrawingSurface",
|
||||||
|
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||||
|
"parentID": "{7b380903-a6d5-46e8-a4b3-4d58eaaa0419}",
|
||||||
|
"position": {
|
||||||
|
"x": -0.020041000097990036,
|
||||||
|
"y": 0.45001220703125,
|
||||||
|
"z": -0.019985578954219818
|
||||||
|
},
|
||||||
|
"queryAACube": {
|
||||||
|
"scale": 2.5597851276397705,
|
||||||
|
"x": 3.2840065956115723,
|
||||||
|
"y": -0.049505829811096191,
|
||||||
|
"z": -0.6464693546295166
|
||||||
|
},
|
||||||
|
"rotation": {
|
||||||
|
"w": 1,
|
||||||
|
"x": -1.52587890625e-05,
|
||||||
|
"y": -1.52587890625e-05,
|
||||||
|
"z": -1.52587890625e-05
|
||||||
|
},
|
||||||
|
"shape": "Cube",
|
||||||
|
"type": "Box",
|
||||||
|
"visible": 0,
|
||||||
|
"userData": "{\"hifiHomeKey\":{\"reset\":true}}"
|
||||||
|
}, {
|
||||||
|
"clientOnly": 0,
|
||||||
|
"compoundShapeURL": "atp:/whiteboard/whiteboardCollisionHull.obj",
|
||||||
|
"created": "2016-06-14T18:08:49Z",
|
||||||
|
"dimensions": {
|
||||||
|
"x": 1.8600000143051147,
|
||||||
|
"y": 2.7000000476837158,
|
||||||
|
"z": 0.46360000967979431
|
||||||
|
},
|
||||||
|
"gravity": {
|
||||||
|
"x": 0,
|
||||||
|
"y": -9.8,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"id": "{7b380903-a6d5-46e8-a4b3-4d58eaaa0419}",
|
||||||
|
"modelURL": "atp:/whiteboard/Whiteboard-4.fbx",
|
||||||
|
"name": "hifi_whiteboard",
|
||||||
|
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||||
|
"queryAACube": {
|
||||||
|
"scale": 3.3112723827362061,
|
||||||
|
"x": -1.655636191368103,
|
||||||
|
"y": -1.655636191368103,
|
||||||
|
"z": -1.655636191368103
|
||||||
|
},
|
||||||
|
"rotation": {
|
||||||
|
"w": -0.30053186416625977,
|
||||||
|
"x": -2.1577336156042293e-05,
|
||||||
|
"y": 0.95376050472259521,
|
||||||
|
"z": 2.824626790243201e-07
|
||||||
|
},
|
||||||
|
"script": "atp:/whiteboard/whiteboard.js",
|
||||||
|
"shapeType": "compound",
|
||||||
|
"type": "Model",
|
||||||
|
"userData": "{\"hifiHomeKey\":{\"reset\":true}}"
|
||||||
|
}],
|
||||||
|
"Version": 60
|
||||||
|
}
|
|
@ -1,282 +0,0 @@
|
||||||
//
|
|
||||||
// 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
|
|
||||||
var ERASER_SCRIPT_URL = Script.resolvePath("eraserEntityScript.js");
|
|
||||||
|
|
||||||
var MARKER_SCRIPT_URL = Script.resolvePath("markerEntityScript.js");
|
|
||||||
|
|
||||||
Whiteboard = function(spawnPosition, spawnRotation) {
|
|
||||||
|
|
||||||
var orientation = Quat.fromPitchYawRollDegrees(spawnRotation.x, spawnRotation.y, spawnRotation.z);
|
|
||||||
var markers = [];
|
|
||||||
var markerRotation = Quat.fromVec3Degrees({
|
|
||||||
x: spawnRotation.x + 10,
|
|
||||||
y: spawnRotation.y - 90,
|
|
||||||
z: spawnRotation.z
|
|
||||||
});
|
|
||||||
var whiteboardPosition = spawnPosition;
|
|
||||||
var whiteboardRotation = orientation;
|
|
||||||
|
|
||||||
var WHITEBOARD_MODEL_URL = "atp:/whiteboard/Whiteboard-6.fbx";
|
|
||||||
var WHITEBOARD_COLLISION_HULL_URL = "atp:/whiteboard/whiteboardCollisionHull.obj";
|
|
||||||
|
|
||||||
var whiteboard = Entities.addEntity({
|
|
||||||
type: "Model",
|
|
||||||
name: "home_model_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
|
|
||||||
},
|
|
||||||
userData: JSON.stringify({
|
|
||||||
'hifiHomeKey': {
|
|
||||||
'reset': true
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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 WHITEBOARD_SURFACE_NAME = "home_box_whiteboardDrawingSurface";
|
|
||||||
var whiteboardSurfaceSettings = {
|
|
||||||
type: "Box",
|
|
||||||
name: WHITEBOARD_SURFACE_NAME,
|
|
||||||
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,
|
|
||||||
userData: JSON.stringify({
|
|
||||||
'hifiHomeKey': {
|
|
||||||
'reset': true
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ************ ERASER ************************************************
|
|
||||||
var ERASER_MODEL_URL = "atp:/whiteboard/eraser-2.fbx";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var eraserPosition = Vec3.sum(spawnPosition, Vec3.multiply(Quat.getFront(whiteboardRotation), -0.1));
|
|
||||||
eraserPosition = Vec3.sum(eraserPosition, Vec3.multiply(-0.5, Quat.getRight(whiteboardRotation)));
|
|
||||||
var eraserRotation = markerRotation;
|
|
||||||
|
|
||||||
var eraserProps = {
|
|
||||||
type: "Model",
|
|
||||||
name: "home_model_whiteboardEraser",
|
|
||||||
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: -10,
|
|
||||||
z: 0
|
|
||||||
},
|
|
||||||
velocity: {
|
|
||||||
x: 0,
|
|
||||||
y: -0.1,
|
|
||||||
z: 0
|
|
||||||
},
|
|
||||||
userData: JSON.stringify({
|
|
||||||
'hifiHomeKey': {
|
|
||||||
'reset': true
|
|
||||||
},
|
|
||||||
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
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// *************************************************************************************************
|
|
||||||
|
|
||||||
function createMarkers() {
|
|
||||||
var modelURLS = [
|
|
||||||
"atp:/whiteboard/marker-blue.fbx",
|
|
||||||
"atp:/whiteboard/marker-red.fbx",
|
|
||||||
"atp:/whiteboard/marker-black.fbx",
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
var markerPosition = Vec3.sum(spawnPosition, Vec3.multiply(Quat.getFront(whiteboardRotation), -0.1));
|
|
||||||
|
|
||||||
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 = Entities.addEntity({
|
|
||||||
type: "Model",
|
|
||||||
modelURL: modelURL,
|
|
||||||
rotation: markerRotation,
|
|
||||||
shapeType: "box",
|
|
||||||
name: "home_model_marker",
|
|
||||||
dynamic: true,
|
|
||||||
gravity: {
|
|
||||||
x: 0,
|
|
||||||
y: -5,
|
|
||||||
z: 0
|
|
||||||
},
|
|
||||||
velocity: {
|
|
||||||
x: 0,
|
|
||||||
y: -0.1,
|
|
||||||
z: 0
|
|
||||||
},
|
|
||||||
position: markerPosition,
|
|
||||||
dimensions: {
|
|
||||||
x: 0.027,
|
|
||||||
y: 0.027,
|
|
||||||
z: 0.164
|
|
||||||
},
|
|
||||||
script: MARKER_SCRIPT_URL,
|
|
||||||
userData: JSON.stringify({
|
|
||||||
'hifiHomeKey': {
|
|
||||||
'reset': true
|
|
||||||
},
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
|
||||||
var eraser;
|
|
||||||
Script.setTimeout(function() {
|
|
||||||
eraser = Entities.addEntity(eraserProps);
|
|
||||||
createMarkers();
|
|
||||||
}, 1500)
|
|
||||||
|
|
||||||
function cleanup() {
|
|
||||||
print('WHITEBOARD CLEANUP')
|
|
||||||
Entities.deleteEntity(whiteboard);
|
|
||||||
Entities.deleteEntity(whiteboardFrontDrawingSurface);
|
|
||||||
Entities.deleteEntity(whiteboardBackDrawingSurface);
|
|
||||||
Entities.deleteEntity(eraser);
|
|
||||||
markers.forEach(function(marker) {
|
|
||||||
Entities.deleteEntity(marker);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.cleanup = cleanup;
|
|
||||||
|
|
||||||
print('CREATED WHITEBOARD')
|
|
||||||
}
|
|
Loading…
Reference in a new issue