diff --git a/examples/acScripts/rain.js b/examples/acScripts/rain.js
index 3f38b8d36a..fc02b7163f 100644
--- a/examples/acScripts/rain.js
+++ b/examples/acScripts/rain.js
@@ -18,7 +18,7 @@ var RainSquall = function (properties) {
dropFallSpeed = 1, // m/s
dropLifetime = 60, // Seconds
dropSpinMax = 0, // Maximum angular velocity per axis; deg/s
- debug = false, // Display origin circle; don't use running on Stack Manager
+ debug = true, // Display origin circle; don't use running on Stack Manager
// Other
squallCircle,
SQUALL_CIRCLE_COLOR = { red: 255, green: 0, blue: 0 },
@@ -151,8 +151,10 @@ var RainSquall = function (properties) {
};
};
+var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));
+center.y += 10;
var rainSquall1 = new RainSquall({
- origin: { x: 1195, y: 1223, z: 1020 },
+ origin:center,
radius: 25,
dropsPerMinute: 120,
dropSize: { x: 0.1, y: 0.1, z: 0.1 },
diff --git a/examples/closePaint.js b/examples/closePaint.js
new file mode 100644
index 0000000000..d9f70aab3c
--- /dev/null
+++ b/examples/closePaint.js
@@ -0,0 +1,289 @@
+//
+// closePaint.js
+// examples
+//
+// Created by Eric Levina on 9/30/15.
+// Copyright 2015 High Fidelity, Inc.
+//
+// Run this script to be able to paint on entities you are close to, with hydras.
+//
+// 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 RIGHT_HAND = 1;
+var LEFT_HAND = 0;
+
+var MIN_POINT_DISTANCE = 0.02;
+var MAX_POINT_DISTANCE = 0.5;
+
+var SPATIAL_CONTROLLERS_PER_PALM = 2;
+var TIP_CONTROLLER_OFFSET = 1;
+
+var TRIGGER_ON_VALUE = 0.3;
+
+var MAX_DISTANCE = 10;
+
+var MAX_POINTS_PER_LINE = 40;
+
+var RIGHT_4_ACTION = 18;
+var RIGHT_2_ACTION = 16;
+
+var LEFT_4_ACTION = 17;
+var LEFT_2_ACTION = 16;
+
+var HUE_INCREMENT = 0.02;
+
+var MIN_STROKE_WIDTH = 0.002;
+var MAX_STROKE_WIDTH = 0.04;
+
+
+var center = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(Camera.getOrientation())));
+
+
+
+function MyController(hand, triggerAction) {
+ this.hand = hand;
+ this.strokes = [];
+ this.painting = false;
+ this.currentStrokeWidth = MIN_STROKE_WIDTH;
+
+ if (this.hand === RIGHT_HAND) {
+ this.getHandPosition = MyAvatar.getRightPalmPosition;
+ this.getHandRotation = MyAvatar.getRightPalmRotation;
+ } else {
+ this.getHandPosition = MyAvatar.getLeftPalmPosition;
+ this.getHandRotation = MyAvatar.getLeftPalmRotation;
+ }
+
+ this.triggerAction = triggerAction;
+ this.palm = SPATIAL_CONTROLLERS_PER_PALM * hand;
+ this.tip = SPATIAL_CONTROLLERS_PER_PALM * hand + TIP_CONTROLLER_OFFSET;
+
+
+ this.strokeColor = {
+ h: 0.8,
+ s: 0.8,
+ l: 0.4
+ };
+
+
+ this.laserPointer = Overlays.addOverlay("circle3d", {
+ color: hslToRgb(this.strokeColor),
+ solid: true,
+ position: center
+ });
+ this.triggerValue = 0;
+ this.prevTriggerValue = 0;
+ var _this = this;
+
+ this.update = function() {
+ this.updateControllerState();
+ this.search();
+ if (this.canPaint === true) {
+ this.paint(this.intersection.intersection, this.intersection.surfaceNormal);
+ }
+ };
+
+ this.paint = function(position, normal) {
+ if (this.painting === false) {
+ if (this.oldPosition) {
+ this.newStroke(this.oldPosition);
+ } else {
+ this.newStroke(position);
+ }
+ this.painting = true;
+ }
+
+
+
+ var localPoint = Vec3.subtract(position, this.strokeBasePosition);
+ //Move stroke a bit forward along normal so it doesnt zfight with mesh its drawing on
+ localPoint = Vec3.sum(localPoint, Vec3.multiply(normal, 0.001 + Math.random() * .001)); //rand avoid z fighting
+
+ var distance = Vec3.distance(localPoint, this.strokePoints[this.strokePoints.length - 1]);
+ if (this.strokePoints.length > 0 && distance < MIN_POINT_DISTANCE) {
+ //need a minimum distance to avoid binormal NANs
+ return;
+ }
+ if (this.strokePoints.length > 0 && distance > MAX_POINT_DISTANCE) {
+ //Prevents drawing lines accross models
+ this.painting = false;
+ return;
+ }
+ if (this.strokePoints.length === 0) {
+ localPoint = {
+ x: 0,
+ y: 0,
+ z: 0
+ };
+ }
+
+ this.strokePoints.push(localPoint);
+ this.strokeNormals.push(normal);
+ this.strokeWidths.push(this.currentStrokeWidth);
+ Entities.editEntity(this.currentStroke, {
+ linePoints: this.strokePoints,
+ normals: this.strokeNormals,
+ strokeWidths: this.strokeWidths
+ });
+ if (this.strokePoints.length === MAX_POINTS_PER_LINE) {
+ this.painting = false;
+ return;
+ }
+ this.oldPosition = position;
+ }
+
+ this.newStroke = function(position) {
+ this.strokeBasePosition = position;
+ this.currentStroke = Entities.addEntity({
+ position: position,
+ type: "PolyLine",
+ color: hslToRgb(this.strokeColor),
+ dimensions: {
+ x: 50,
+ y: 50,
+ z: 50
+ },
+ lifetime: 200
+ });
+ this.strokePoints = [];
+ this.strokeNormals = [];
+ this.strokeWidths = [];
+
+ this.strokes.push(this.currentStroke);
+
+ }
+
+ this.updateControllerState = function() {
+ this.triggerValue = Controller.getActionValue(this.triggerAction);
+ if (this.triggerValue > TRIGGER_ON_VALUE && this.prevTriggerValue <= TRIGGER_ON_VALUE) {
+ this.squeeze();
+ } else if (this.triggerValue < TRIGGER_ON_VALUE && this.prevTriggerValue >= TRIGGER_ON_VALUE) {
+ this.release();
+ }
+
+ this.prevTriggerValue = this.triggerValue;
+ }
+
+ this.squeeze = function() {
+ this.tryPainting = true;
+
+ }
+ this.release = function() {
+ this.painting = false;
+ this.tryPainting = false;
+ this.canPaint = false;
+ this.oldPosition = null;
+ }
+ this.search = function() {
+
+ // the trigger is being pressed, do a ray test
+ var handPosition = this.getHandPosition();
+ var pickRay = {
+ origin: handPosition,
+ direction: Quat.getUp(this.getHandRotation())
+ };
+
+
+ this.intersection = Entities.findRayIntersection(pickRay, true);
+ if (this.intersection.intersects) {
+ var distance = Vec3.distance(handPosition, this.intersection.intersection);
+ if (distance < MAX_DISTANCE) {
+ var displayPoint = this.intersection.intersection;
+ displayPoint = Vec3.sum(displayPoint, Vec3.multiply(this.intersection.surfaceNormal, .01));
+ if (this.tryPainting) {
+ this.canPaint = true;
+ }
+ this.currentStrokeWidth = map(this.triggerValue, TRIGGER_ON_VALUE, 1, MIN_STROKE_WIDTH, MAX_STROKE_WIDTH);
+ var laserSize = map(distance, 1, MAX_DISTANCE, 0.01, 0.1);
+ laserSize += this.currentStrokeWidth / 2;
+ Overlays.editOverlay(this.laserPointer, {
+ visible: true,
+ position: displayPoint,
+ rotation: orientationOf(this.intersection.surfaceNormal),
+ size: {
+ x: laserSize,
+ y: laserSize
+ }
+ });
+
+ } else {
+ this.hitFail();
+ }
+ } else {
+ this.hitFail();
+ }
+ };
+
+ this.hitFail = function() {
+ this.canPaint = false;
+
+ Overlays.editOverlay(this.laserPointer, {
+ visible: false
+ });
+
+ }
+
+ this.cleanup = function() {
+ Overlays.deleteOverlay(this.laserPointer);
+ this.strokes.forEach(function(stroke) {
+ Entities.deleteEntity(stroke);
+ });
+ }
+
+ this.cycleColorDown = function() {
+ this.strokeColor.h -= HUE_INCREMENT;
+ if (this.strokeColor.h < 0) {
+ this.strokeColor = 1;
+ }
+ Overlays.editOverlay(this.laserPointer, {
+ color: hslToRgb(this.strokeColor)
+ });
+ }
+
+ this.cycleColorUp = function() {
+ this.strokeColor.h += HUE_INCREMENT;
+ if (this.strokeColor.h > 1) {
+ this.strokeColor.h = 0;
+ }
+ Overlays.editOverlay(this.laserPointer, {
+ color: hslToRgb(this.strokeColor)
+ });
+ }
+}
+
+var rightController = new MyController(RIGHT_HAND, Controller.findAction("RIGHT_HAND_CLICK"));
+var leftController = new MyController(LEFT_HAND, Controller.findAction("LEFT_HAND_CLICK"));
+
+Controller.actionEvent.connect(function(action, state) {
+ if (state === 0) {
+ return;
+ }
+ if (action === RIGHT_4_ACTION) {
+ rightController.cycleColorUp();
+ } else if (action === RIGHT_2_ACTION) {
+ rightController.cycleColorDown();
+ }
+ if (action === LEFT_4_ACTION) {
+ leftController.cycleColorUp();
+ } else if (action === LEFT_2_ACTION) {
+ leftController.cycleColorDown();
+ }
+});
+
+function update() {
+ rightController.update();
+ leftController.update();
+}
+
+function cleanup() {
+ rightController.cleanup();
+ leftController.cleanup();
+}
+
+Script.scriptEnding.connect(cleanup);
+Script.update.connect(update);
diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js
index e756866b1c..5705bd4498 100644
--- a/examples/controllers/handControllerGrab.js
+++ b/examples/controllers/handControllerGrab.js
@@ -66,7 +66,7 @@ var MSEC_PER_SEC = 1000.0;
// these control how long an abandoned pointer line will hang around
var startTime = Date.now();
var LIFETIME = 10;
-var ACTION_LIFETIME = 120; // 2 minutes
+var ACTION_LIFETIME = 10; // seconds
// states for the state machine
var STATE_OFF = 0;
@@ -242,8 +242,7 @@ function MyController(hand, triggerAction) {
var intersection = Entities.findRayIntersection(pickRay, true);
if (intersection.intersects &&
intersection.properties.collisionsWillMove === 1 &&
- intersection.properties.locked === 0 &&
- !entityIsGrabbedByOther(intersection.entityID)) {
+ intersection.properties.locked === 0) {
// the ray is intersecting something we can move.
var handControllerPosition = Controller.getSpatialControlPosition(this.palm);
var intersectionDistance = Vec3.distance(handControllerPosition, intersection.intersection);
@@ -258,6 +257,10 @@ function MyController(hand, triggerAction) {
this.state = STATE_NEAR_GRABBING;
} else {
+ if (entityIsGrabbedByOther(intersection.entityID)) {
+ // don't allow two people to distance grab the same object
+ return;
+ }
// the hand is far from the intersected object. go into distance-holding mode
this.state = STATE_DISTANCE_HOLDING;
this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
@@ -441,16 +444,13 @@ function MyController(hand, triggerAction) {
var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset);
this.actionID = NULL_ACTION_ID;
- if (!entityIsGrabbedByOther(this.grabbedEntity)) {
- this.actionID = Entities.addAction("hold", this.grabbedEntity, {
- hand: this.hand === RIGHT_HAND ? "right" : "left",
- timeScale: NEAR_GRABBING_ACTION_TIMEFRAME,
- relativePosition: offsetPosition,
- relativeRotation: offsetRotation,
- tag: getTag(),
- lifetime: ACTION_LIFETIME
- });
- }
+ this.actionID = Entities.addAction("hold", this.grabbedEntity, {
+ hand: this.hand === RIGHT_HAND ? "right" : "left",
+ timeScale: NEAR_GRABBING_ACTION_TIMEFRAME,
+ relativePosition: offsetPosition,
+ relativeRotation: offsetRotation,
+ lifetime: ACTION_LIFETIME
+ });
if (this.actionID === NULL_ACTION_ID) {
this.actionID = null;
} else {
diff --git a/examples/entityScripts/sprayPaintCan.js b/examples/entityScripts/sprayPaintCan.js
deleted file mode 100644
index 21613bdeb5..0000000000
--- a/examples/entityScripts/sprayPaintCan.js
+++ /dev/null
@@ -1,252 +0,0 @@
-(function() {
- // Script.include("../libraries/utils.js");
- //Need absolute path for now, for testing before PR merge and s3 cloning. Will change post-merge
-
- Script.include("../libraries/utils.js");
- GRAB_FRAME_USER_DATA_KEY = "grabFrame";
- this.userData = {};
-
- var TIP_OFFSET_Z = 0.14;
- var TIP_OFFSET_Y = 0.04;
-
- var ZERO_VEC = {
- x: 0,
- y: 0,
- z: 0
- }
-
- var MAX_POINTS_PER_LINE = 40;
- var MIN_POINT_DISTANCE = 0.01;
- var STROKE_WIDTH = 0.02;
-
- var self = this;
-
- var timeSinceLastMoved = 0;
- var RESET_TIME_THRESHOLD = 5;
- var DISTANCE_FROM_HOME_THRESHOLD = 0.5;
- var HOME_POSITION = {
- x: 549.12,
- y: 495.555,
- z: 503.77
- };
- this.getUserData = function() {
-
-
- if (this.properties.userData) {
- this.userData = JSON.parse(this.properties.userData);
- }
- }
-
- this.updateUserData = function() {
- Entities.editEntity(this.entityId, {
- userData: JSON.stringify(this.userData)
- });
- }
-
- this.update = function(deltaTime) {
- self.getUserData();
- self.properties = Entities.getEntityProperties(self.entityId);
-
- if (Vec3.length(self.properties.velocity) < 0.1 && Vec3.distance(HOME_POSITION, self.properties.position) > DISTANCE_FROM_HOME_THRESHOLD) {
- timeSinceLastMoved += deltaTime;
- if (timeSinceLastMoved > RESET_TIME_THRESHOLD) {
- self.reset();
- timeSinceLastMoved = 0;
- }
- } else {
- timeSinceLastMoved = 0;
- }
-
- //Only activate for the user who grabbed the object
- if (self.userData.grabKey && self.userData.grabKey.activated === true && self.userData.grabKey.avatarId == MyAvatar.sessionUUID) {
- if (self.activated !== true) {
- //We were just grabbed, so create a particle system
- self.grab();
- }
- //Move emitter to where entity is always when its activated
- self.sprayStream();
- } else if (self.userData.grabKey && self.userData.grabKey.activated === false && self.activated) {
- self.letGo();
- }
- }
-
- this.grab = function() {
- this.activated = true;
- var animationSettings = JSON.stringify({
- fps: 30,
- loop: true,
- firstFrame: 1,
- lastFrame: 10000,
- running: true
- });
- var PI = 3.141593;
- var DEG_TO_RAD = PI / 180.0;
-
- this.paintStream = Entities.addEntity({
- type: "ParticleEffect",
- animationSettings: animationSettings,
- position: this.properties.position,
- textures: "https://raw.githubusercontent.com/ericrius1/SantasLair/santa/assets/smokeparticle.png",
- emitSpeed: 0,
- speedSpread: 0.02,
- polarFinish: 2 * DEG_TO_RAD,
- emitAcceleration: ZERO_VEC,
- emitRate: 100,
- particleRadius: 0.01,
- color: {
- red: 170,
- green: 20,
- blue: 150
- },
- lifetime: 50, //probably wont be holding longer than this straight
- });
- }
-
- this.letGo = function() {
- this.activated = false;
- Entities.deleteEntity(this.paintStream);
- this.paintStream = null;
- }
-
- this.reset = function() {
- Entities.editEntity(self.entityId, {
- position: HOME_POSITION,
- rotation: Quat.fromPitchYawRollDegrees(0, 0, 0),
- angularVelocity: ZERO_VEC,
- velocity: ZERO_VEC
- });
- }
-
- this.sprayStream = function() {
- var forwardVec = Quat.getFront(Quat.multiply(self.properties.rotation , Quat.fromPitchYawRollDegrees(0, 90, 0)));
- forwardVec = Vec3.normalize(forwardVec);
-
- var upVec = Quat.getUp(self.properties.rotation);
- var position = Vec3.sum(self.properties.position, Vec3.multiply(forwardVec, TIP_OFFSET_Z));
- position = Vec3.sum(position, Vec3.multiply(upVec, TIP_OFFSET_Y))
- Entities.editEntity(self.paintStream, {
- position: position,
- emitOrientation: forwardVec,
- emitSpeed: 5
- });
-
- //Now check for an intersection with an entity
- //move forward so ray doesnt intersect with gun
- var origin = Vec3.sum(position, forwardVec);
- var pickRay = {
- origin: origin,
- direction: Vec3.multiply(forwardVec, 2)
- }
-
- var intersection = Entities.findRayIntersection(pickRay, true);
- if (intersection.intersects) {
- var normal = Vec3.multiply(-1, Quat.getFront(intersection.properties.rotation));
- this.paint(intersection.intersection, normal);
- }
-
-
- }
-
- this.paint = function(position, normal) {
- if (!this.painting) {
-
- this.newStroke(position);
- this.painting = true;
- }
-
- if (this.strokePoints.length > MAX_POINTS_PER_LINE) {
- this.painting = false;
- return;
- }
-
- var localPoint = Vec3.subtract(position, this.strokeBasePosition);
- //Move stroke a bit forward along normal so it doesnt zfight with mesh its drawing on
- localPoint = Vec3.sum(localPoint, Vec3.multiply(normal, .1));
-
- if (this.strokePoints.length > 0 && Vec3.distance(localPoint, this.strokePoints[this.strokePoints.length - 1]) < MIN_POINT_DISTANCE) {
- //need a minimum distance to avoid binormal NANs
- return;
- }
-
- this.strokePoints.push(localPoint);
- this.strokeNormals.push(normal);
- this.strokeWidths.push(STROKE_WIDTH);
- Entities.editEntity(this.currentStroke, {
- linePoints: this.strokePoints,
- normals: this.strokeNormals,
- strokeWidths: this.strokeWidths
- });
-
-
- }
-
- this.newStroke = function(position) {
- this.strokeBasePosition = position;
- this.currentStroke = Entities.addEntity({
- position: position,
- type: "PolyLine",
- color: {
- red: randInt(160, 250),
- green: randInt(10, 20),
- blue: randInt(190, 250)
- },
- dimensions: {
- x: 50,
- y: 50,
- z: 50
- },
- lifetime: 100
- });
- this.strokePoints = [];
- this.strokeNormals = [];
- this.strokeWidths = [];
-
- this.strokes.push(this.currentStroke);
- }
-
- this.preload = function(entityId) {
- this.strokes = [];
- this.activated = false;
- this.entityId = entityId;
- this.properties = Entities.getEntityProperties(self.entityId);
- this.getUserData();
-
- //Only activate for the avatar who is grabbing the can!
- if (this.userData.grabKey && this.userData.grabKey.activated) {
- this.activated = true;
- }
- if (!this.userData.grabFrame) {
- var data = {
- relativePosition: {
- x: 0,
- y: 0,
- z: 0
- },
- relativeRotation: Quat.fromPitchYawRollDegrees(0, 0, 0)
- }
- setEntityCustomData(GRAB_FRAME_USER_DATA_KEY, this.entityId, data);
- }
- }
-
-
- this.unload = function() {
- Script.update.disconnect(this.update);
- if(this.paintStream) {
- Entities.deleteEntity(this.paintStream);
- }
- this.strokes.forEach(function(stroke) {
- Entities.deleteEntity(stroke);
- });
- }
- Script.update.connect(this.update);
-});
-
-
-
-function randFloat(min, max) {
- return Math.random() * (max - min) + min;
-}
-
-function randInt(min, max) {
- return Math.floor(Math.random() * (max - min)) + min;
-}
\ No newline at end of file
diff --git a/examples/grab.js b/examples/grab.js
index 43a5ed220b..03a227931d 100644
--- a/examples/grab.js
+++ b/examples/grab.js
@@ -9,10 +9,42 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+/*global print, Mouse, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */
+
+Script.include("libraries/utils.js");
+// objects that appear smaller than this can't be grabbed
+var MAX_SOLID_ANGLE = 0.01;
+
+var ZERO_VEC3 = {
+ x: 0,
+ y: 0,
+ z: 0
+};
+var IDENTITY_QUAT = {
+ x: 0,
+ y: 0,
+ z: 0,
+ w: 0
+};
+var GRABBABLE_DATA_KEY = "grabbableKey";
+
+var defaultGrabbableData = {
+ grabbable: true
+};
+
var MAX_SOLID_ANGLE = 0.01; // objects that appear smaller than this can't be grabbed
-var ZERO_VEC3 = {x: 0, y: 0, z: 0};
-var IDENTITY_QUAT = {x: 0, y: 0, z: 0, w: 0};
+var ZERO_VEC3 = {
+ x: 0,
+ y: 0,
+ z: 0
+};
+var IDENTITY_QUAT = {
+ x: 0,
+ y: 0,
+ z: 0,
+ w: 0
+};
var ACTION_LIFETIME = 120; // 2 minutes
function getTag() {
@@ -21,14 +53,18 @@ function getTag() {
function entityIsGrabbedByOther(entityID) {
var actionIDs = Entities.getActionIDs(entityID);
- for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) {
- var actionID = actionIDs[actionIndex];
- var actionArguments = Entities.getActionArguments(entityID, actionID);
- var tag = actionArguments["tag"];
- if (tag == getTag()) {
+ var actionIndex;
+ var actionID;
+ var actionArguments;
+ var tag;
+ for (actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) {
+ actionID = actionIDs[actionIndex];
+ actionArguments = Entities.getActionArguments(entityID, actionID);
+ tag = actionArguments.tag;
+ if (tag === getTag()) {
continue;
}
- if (tag.slice(0, 5) == "grab-") {
+ if (tag.slice(0, 5) === "grab-") {
return true;
}
}
@@ -79,36 +115,72 @@ function mouseIntersectionWithPlane(pointOnPlane, planeNormal, event, maxDistanc
// Mouse class stores mouse click and drag info
Mouse = function() {
- this.current = {x: 0, y: 0 };
- this.previous = {x: 0, y: 0 };
- this.rotateStart = {x: 0, y: 0 };
- this.cursorRestore = {x: 0, y: 0};
+ this.current = {
+ x: 0,
+ y: 0
+ };
+ this.previous = {
+ x: 0,
+ y: 0
+ };
+ this.rotateStart = {
+ x: 0,
+ y: 0
+ };
+ this.cursorRestore = {
+ x: 0,
+ y: 0
+ };
}
Mouse.prototype.startDrag = function(position) {
- this.current = {x: position.x, y: position.y};
+ this.current = {
+ x: position.x,
+ y: position.y
+ };
this.startRotateDrag();
}
Mouse.prototype.updateDrag = function(position) {
- this.current = {x: position.x, y: position.y };
+ this.current = {
+ x: position.x,
+ y: position.y
+ };
}
Mouse.prototype.startRotateDrag = function() {
- this.previous = {x: this.current.x, y: this.current.y};
- this.rotateStart = {x: this.current.x, y: this.current.y};
- this.cursorRestore = { x: Window.getCursorPositionX(), y: Window.getCursorPositionY() };
+ this.previous = {
+ x: this.current.x,
+ y: this.current.y
+ };
+ this.rotateStart = {
+ x: this.current.x,
+ y: this.current.y
+ };
+ this.cursorRestore = {
+ x: Window.getCursorPositionX(),
+ y: Window.getCursorPositionY()
+ };
}
Mouse.prototype.getDrag = function() {
- var delta = {x: this.current.x - this.previous.x, y: this.current.y - this.previous.y};
- this.previous = {x: this.current.x, y: this.current.y};
+ var delta = {
+ x: this.current.x - this.previous.x,
+ y: this.current.y - this.previous.y
+ };
+ this.previous = {
+ x: this.current.x,
+ y: this.current.y
+ };
return delta;
}
Mouse.prototype.restoreRotateCursor = function() {
Window.setCursorPosition(this.cursorRestore.x, this.cursorRestore.y);
- this.current = {x: this.rotateStart.x, y: this.rotateStart.y};
+ this.current = {
+ x: this.rotateStart.x,
+ y: this.rotateStart.y
+ };
}
var mouse = new Mouse();
@@ -118,19 +190,27 @@ var mouse = new Mouse();
Beacon = function() {
this.height = 0.10;
this.overlayID = Overlays.addOverlay("line3d", {
- color: {red: 200, green: 200, blue: 200},
+ color: {
+ red: 200,
+ green: 200,
+ blue: 200
+ },
alpha: 1,
visible: false,
- lineWidth: 2
+ lineWidth: 2
});
}
Beacon.prototype.enable = function() {
- Overlays.editOverlay(this.overlayID, { visible: true });
+ Overlays.editOverlay(this.overlayID, {
+ visible: true
+ });
}
Beacon.prototype.disable = function() {
- Overlays.editOverlay(this.overlayID, { visible: false });
+ Overlays.editOverlay(this.overlayID, {
+ visible: false
+ });
}
Beacon.prototype.updatePosition = function(position) {
@@ -178,13 +258,17 @@ Grabber = function() {
// verticalCylinder (SHIFT)
// rotate (CONTROL)
this.mode = "xzplane";
-
+
// offset allows the user to grab an object off-center. It points from the object's center
// to the point where the ray intersects the grab plane (at the moment the grab is initiated).
// Future target positions of the ray intersection are on the same plane, and the offset is subtracted
// to compute the target position of the object's center.
- this.offset = {x: 0, y: 0, z: 0 };
-
+ this.offset = {
+ x: 0,
+ y: 0,
+ z: 0
+ };
+
this.targetPosition;
this.targetRotation;
@@ -199,7 +283,11 @@ Grabber.prototype.computeNewGrabPlane = function() {
var modeWasRotate = (this.mode == "rotate");
this.mode = "xzPlane";
- this.planeNormal = {x: 0, y: 1, z: 0 };
+ this.planeNormal = {
+ x: 0,
+ y: 1,
+ z: 0
+ };
if (this.rotateKey) {
this.mode = "rotate";
mouse.startRotateDrag();
@@ -212,7 +300,7 @@ Grabber.prototype.computeNewGrabPlane = function() {
this.mode = "verticalCylinder";
// NOTE: during verticalCylinder mode a new planeNormal will be computed each move
}
- }
+ }
this.pointOnPlane = Vec3.sum(this.currentPosition, this.offset);
var xzOffset = Vec3.subtract(this.pointOnPlane, Camera.getPosition());
@@ -237,6 +325,12 @@ Grabber.prototype.pressEvent = function(event) {
return;
}
+
+ var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, pickResults.entityID, defaultGrabbableData);
+ if (grabbableData.grabbable === false) {
+ return;
+ }
+
mouse.startDrag(event);
var clickedEntity = pickResults.entityID;
@@ -253,13 +347,19 @@ Grabber.prototype.pressEvent = function(event) {
return;
}
- Entities.editEntity(clickedEntity, { gravity: ZERO_VEC3 });
+ Entities.editEntity(clickedEntity, {
+ gravity: ZERO_VEC3
+ });
this.isGrabbing = true;
this.entityID = clickedEntity;
this.currentPosition = entityProperties.position;
this.originalGravity = entityProperties.gravity;
- this.targetPosition = {x: this.startPosition.x, y: this.startPosition.y, z: this.startPosition.z};
+ this.targetPosition = {
+ x: this.startPosition.x,
+ y: this.startPosition.y,
+ z: this.startPosition.z
+ };
// compute the grab point
var nearestPoint = Vec3.subtract(this.startPosition, cameraPosition);
@@ -281,7 +381,9 @@ Grabber.prototype.pressEvent = function(event) {
Grabber.prototype.releaseEvent = function() {
if (this.isGrabbing) {
if (Vec3.length(this.originalGravity) != 0) {
- Entities.editEntity(this.entityID, { gravity: this.originalGravity});
+ Entities.editEntity(this.entityID, {
+ gravity: this.originalGravity
+ });
}
this.isGrabbing = false
@@ -308,7 +410,10 @@ Grabber.prototype.moveEvent = function(event) {
}
this.currentPosition = entityProperties.position;
- var actionArgs = {tag: getTag(), lifetime: ACTION_LIFETIME};
+ var actionArgs = {
+ tag: getTag(),
+ lifetime: ACTION_LIFETIME
+ };
if (this.mode === "rotate") {
var drag = mouse.getDrag();
@@ -323,7 +428,14 @@ Grabber.prototype.moveEvent = function(event) {
// var qZero = entityProperties.rotation;
//var qZero = this.lastRotation;
this.lastRotation = Quat.multiply(deltaQ, this.lastRotation);
- actionArgs = {targetRotation: this.lastRotation, angularTimeScale: 0.1, tag: getTag(), lifetime: ACTION_LIFETIME};
+
+ actionArgs = {
+ targetRotation: this.lastRotation,
+ angularTimeScale: 0.1,
+ tag: getTag(),
+ lifetime: ACTION_LIFETIME
+ };
+
} else {
var newPointOnPlane;
if (this.mode === "verticalCylinder") {
@@ -334,7 +446,11 @@ Grabber.prototype.moveEvent = function(event) {
var pointOnCylinder = Vec3.multiply(planeNormal, this.xzDistanceToGrab);
pointOnCylinder = Vec3.sum(Camera.getPosition(), pointOnCylinder);
this.pointOnPlane = mouseIntersectionWithPlane(pointOnCylinder, planeNormal, mouse.current, this.maxDistance);
- newPointOnPlane = {x: this.pointOnPlane.x, y: this.pointOnPlane.y, z: this.pointOnPlane.z};
+ newPointOnPlane = {
+ x: this.pointOnPlane.x,
+ y: this.pointOnPlane.y,
+ z: this.pointOnPlane.z
+ };
} else {
var cameraPosition = Camera.getPosition();
newPointOnPlane = mouseIntersectionWithPlane(this.pointOnPlane, this.planeNormal, mouse.current, this.maxDistance);
@@ -347,7 +463,14 @@ Grabber.prototype.moveEvent = function(event) {
}
}
this.targetPosition = Vec3.subtract(newPointOnPlane, this.offset);
- actionArgs = {targetPosition: this.targetPosition, linearTimeScale: 0.1, tag: getTag(), lifetime: ACTION_LIFETIME};
+
+ actionArgs = {
+ targetPosition: this.targetPosition,
+ linearTimeScale: 0.1,
+ tag: getTag(),
+ lifetime: ACTION_LIFETIME
+ };
+
beacon.updatePosition(this.targetPosition);
}
@@ -407,4 +530,4 @@ Controller.mousePressEvent.connect(pressEvent);
Controller.mouseMoveEvent.connect(moveEvent);
Controller.mouseReleaseEvent.connect(releaseEvent);
Controller.keyPressEvent.connect(keyPressEvent);
-Controller.keyReleaseEvent.connect(keyReleaseEvent);
+Controller.keyReleaseEvent.connect(keyReleaseEvent);
\ No newline at end of file
diff --git a/examples/libraries/utils.js b/examples/libraries/utils.js
index f6f635c73a..fa0f36cbb1 100644
--- a/examples/libraries/utils.js
+++ b/examples/libraries/utils.js
@@ -179,3 +179,69 @@ pointInExtents = function(point, minPoint, maxPoint) {
(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, hueOffset) {
+ 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)
+ };
+}
+
+map = function(value, min1, max1, min2, max2) {
+ return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
+}
+
+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);
+}
+
diff --git a/examples/particle_explorer/dat.gui.min.js b/examples/particle_explorer/dat.gui.min.js
new file mode 100644
index 0000000000..8ea141a966
--- /dev/null
+++ b/examples/particle_explorer/dat.gui.min.js
@@ -0,0 +1,95 @@
+/**
+ * dat-gui JavaScript Controller Library
+ * http://code.google.com/p/dat-gui
+ *
+ * Copyright 2011 Data Arts Team, Google Creative Lab
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ */
+var dat=dat||{};dat.gui=dat.gui||{};dat.utils=dat.utils||{};dat.controllers=dat.controllers||{};dat.dom=dat.dom||{};dat.color=dat.color||{};dat.utils.css=function(){return{load:function(f,a){a=a||document;var d=a.createElement("link");d.type="text/css";d.rel="stylesheet";d.href=f;a.getElementsByTagName("head")[0].appendChild(d)},inject:function(f,a){a=a||document;var d=document.createElement("style");d.type="text/css";d.innerHTML=f;a.getElementsByTagName("head")[0].appendChild(d)}}}();
+dat.utils.common=function(){var f=Array.prototype.forEach,a=Array.prototype.slice;return{BREAK:{},extend:function(d){this.each(a.call(arguments,1),function(a){for(var c in a)this.isUndefined(a[c])||(d[c]=a[c])},this);return d},defaults:function(d){this.each(a.call(arguments,1),function(a){for(var c in a)this.isUndefined(d[c])&&(d[c]=a[c])},this);return d},compose:function(){var d=a.call(arguments);return function(){for(var e=a.call(arguments),c=d.length-1;0<=c;c--)e=[d[c].apply(this,e)];return e[0]}},
+each:function(a,e,c){if(a)if(f&&a.forEach&&a.forEach===f)a.forEach(e,c);else if(a.length===a.length+0)for(var b=0,p=a.length;b
this.__max&&(a=this.__max);void 0!==this.__step&&0!=a%this.__step&&(a=Math.round(a/this.__step)*this.__step);return e.superclass.prototype.setValue.call(this,a)},min:function(a){this.__min=a;return this},max:function(a){this.__max=a;return this},step:function(a){this.__impliedStep=this.__step=a;this.__precision=d(a);return this}});return e}(dat.controllers.Controller,dat.utils.common);
+dat.controllers.NumberControllerBox=function(f,a,d){var e=function(c,b,f){function q(){var a=parseFloat(n.__input.value);d.isNaN(a)||n.setValue(a)}function l(a){var b=u-a.clientY;n.setValue(n.getValue()+b*n.__impliedStep);u=a.clientY}function r(){a.unbind(window,"mousemove",l);a.unbind(window,"mouseup",r)}this.__truncationSuspended=!1;e.superclass.call(this,c,b,f);var n=this,u;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"change",q);a.bind(this.__input,
+"blur",function(){q();n.__onFinishChange&&n.__onFinishChange.call(n,n.getValue())});a.bind(this.__input,"mousedown",function(b){a.bind(window,"mousemove",l);a.bind(window,"mouseup",r);u=b.clientY});a.bind(this.__input,"keydown",function(a){13===a.keyCode&&(n.__truncationSuspended=!0,this.blur(),n.__truncationSuspended=!1)});this.updateDisplay();this.domElement.appendChild(this.__input)};e.superclass=f;d.extend(e.prototype,f.prototype,{updateDisplay:function(){var a=this.__input,b;if(this.__truncationSuspended)b=
+this.getValue();else{b=this.getValue();var d=Math.pow(10,this.__precision);b=Math.round(b*d)/d}a.value=b;return e.superclass.prototype.updateDisplay.call(this)}});return e}(dat.controllers.NumberController,dat.dom.dom,dat.utils.common);
+dat.controllers.NumberControllerSlider=function(f,a,d,e,c){function b(a,b,c,e,d){return e+(a-b)/(c-b)*(d-e)}var p=function(c,e,d,f,u){function A(c){c.preventDefault();var e=a.getOffset(k.__background),d=a.getWidth(k.__background);k.setValue(b(c.clientX,e.left,e.left+d,k.__min,k.__max));return!1}function g(){a.unbind(window,"mousemove",A);a.unbind(window,"mouseup",g);k.__onFinishChange&&k.__onFinishChange.call(k,k.getValue())}p.superclass.call(this,c,e,{min:d,max:f,step:u});var k=this;this.__background=
+document.createElement("div");this.__foreground=document.createElement("div");a.bind(this.__background,"mousedown",function(b){a.bind(window,"mousemove",A);a.bind(window,"mouseup",g);A(b)});a.addClass(this.__background,"slider");a.addClass(this.__foreground,"slider-fg");this.updateDisplay();this.__background.appendChild(this.__foreground);this.domElement.appendChild(this.__background)};p.superclass=f;p.useDefaultStyles=function(){d.inject(c)};e.extend(p.prototype,f.prototype,{updateDisplay:function(){var a=
+(this.getValue()-this.__min)/(this.__max-this.__min);this.__foreground.style.width=100*a+"%";return p.superclass.prototype.updateDisplay.call(this)}});return p}(dat.controllers.NumberController,dat.dom.dom,dat.utils.css,dat.utils.common,"/**\n * dat-gui JavaScript Controller Library\n * http://code.google.com/p/dat-gui\n *\n * Copyright 2011 Data Arts Team, Google Creative Lab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n */\n\n.slider {\n box-shadow: inset 0 2px 4px rgba(0,0,0,0.15);\n height: 1em;\n border-radius: 1em;\n background-color: #eee;\n padding: 0 0.5em;\n overflow: hidden;\n}\n\n.slider-fg {\n padding: 1px 0 2px 0;\n background-color: #aaa;\n height: 1em;\n margin-left: -0.5em;\n padding-right: 0.5em;\n border-radius: 1em 0 0 1em;\n}\n\n.slider-fg:after {\n display: inline-block;\n border-radius: 1em;\n background-color: #fff;\n border: 1px solid #aaa;\n content: '';\n float: right;\n margin-right: -1em;\n margin-top: -1px;\n height: 0.9em;\n width: 0.9em;\n}");
+dat.controllers.FunctionController=function(f,a,d){var e=function(c,b,d){e.superclass.call(this,c,b);var f=this;this.__button=document.createElement("div");this.__button.innerHTML=void 0===d?"Fire":d;a.bind(this.__button,"click",function(a){a.preventDefault();f.fire();return!1});a.addClass(this.__button,"button");this.domElement.appendChild(this.__button)};e.superclass=f;d.extend(e.prototype,f.prototype,{fire:function(){this.__onChange&&this.__onChange.call(this);this.getValue().call(this.object);
+this.__onFinishChange&&this.__onFinishChange.call(this,this.getValue())}});return e}(dat.controllers.Controller,dat.dom.dom,dat.utils.common);
+dat.controllers.BooleanController=function(f,a,d){var e=function(c,b){e.superclass.call(this,c,b);var d=this;this.__prev=this.getValue();this.__checkbox=document.createElement("input");this.__checkbox.setAttribute("type","checkbox");a.bind(this.__checkbox,"change",function(){d.setValue(!d.__prev)},!1);this.domElement.appendChild(this.__checkbox);this.updateDisplay()};e.superclass=f;d.extend(e.prototype,f.prototype,{setValue:function(a){a=e.superclass.prototype.setValue.call(this,a);this.__onFinishChange&&
+this.__onFinishChange.call(this,this.getValue());this.__prev=this.getValue();return a},updateDisplay:function(){!0===this.getValue()?(this.__checkbox.setAttribute("checked","checked"),this.__checkbox.checked=!0):this.__checkbox.checked=!1;return e.superclass.prototype.updateDisplay.call(this)}});return e}(dat.controllers.Controller,dat.dom.dom,dat.utils.common);
+dat.color.toString=function(f){return function(a){if(1==a.a||f.isUndefined(a.a)){for(a=a.hex.toString(16);6>a.length;)a="0"+a;return"#"+a}return"rgba("+Math.round(a.r)+","+Math.round(a.g)+","+Math.round(a.b)+","+a.a+")"}}(dat.utils.common);
+dat.color.interpret=function(f,a){var d,e,c=[{litmus:a.isString,conversions:{THREE_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i);return null===a?!1:{space:"HEX",hex:parseInt("0x"+a[1].toString()+a[1].toString()+a[2].toString()+a[2].toString()+a[3].toString()+a[3].toString())}},write:f},SIX_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9]{6})$/i);return null===a?!1:{space:"HEX",hex:parseInt("0x"+a[1].toString())}},write:f},CSS_RGB:{read:function(a){a=a.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/);
+return null===a?!1:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3])}},write:f},CSS_RGBA:{read:function(a){a=a.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/);return null===a?!1:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3]),a:parseFloat(a[4])}},write:f}}},{litmus:a.isNumber,conversions:{HEX:{read:function(a){return{space:"HEX",hex:a,conversionName:"HEX"}},write:function(a){return a.hex}}}},{litmus:a.isArray,conversions:{RGB_ARRAY:{read:function(a){return 3!=
+a.length?!1:{space:"RGB",r:a[0],g:a[1],b:a[2]}},write:function(a){return[a.r,a.g,a.b]}},RGBA_ARRAY:{read:function(a){return 4!=a.length?!1:{space:"RGB",r:a[0],g:a[1],b:a[2],a:a[3]}},write:function(a){return[a.r,a.g,a.b,a.a]}}}},{litmus:a.isObject,conversions:{RGBA_OBJ:{read:function(b){return a.isNumber(b.r)&&a.isNumber(b.g)&&a.isNumber(b.b)&&a.isNumber(b.a)?{space:"RGB",r:b.r,g:b.g,b:b.b,a:b.a}:!1},write:function(a){return{r:a.r,g:a.g,b:a.b,a:a.a}}},RGB_OBJ:{read:function(b){return a.isNumber(b.r)&&
+a.isNumber(b.g)&&a.isNumber(b.b)?{space:"RGB",r:b.r,g:b.g,b:b.b}:!1},write:function(a){return{r:a.r,g:a.g,b:a.b}}},HSVA_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)&&a.isNumber(b.a)?{space:"HSV",h:b.h,s:b.s,v:b.v,a:b.a}:!1},write:function(a){return{h:a.h,s:a.s,v:a.v,a:a.a}}},HSV_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)?{space:"HSV",h:b.h,s:b.s,v:b.v}:!1},write:function(a){return{h:a.h,s:a.s,v:a.v}}}}}];return function(){e=!1;
+var b=1\n\n Here\'s the new load parameter for your GUI
\'s constructor:\n\n \n\n \n\n
Automatically save\n values to
localStorage
on exit.\n\n
The values saved to localStorage
will\n override those passed to dat.GUI
\'s constructor. This makes it\n easier to work incrementally, but localStorage
is fragile,\n and your friends may not see the same values you do.\n \n
\n \n
\n\n',
+".dg {\n /** Clear list styles */\n /* Auto-place container */\n /* Auto-placed GUI's */\n /* Line items that don't contain folders. */\n /** Folder names */\n /** Hides closed items */\n /** Controller row */\n /** Name-half (left) */\n /** Controller-half (right) */\n /** Controller placement */\n /** Shorter number boxes when slider is present. */\n /** Ensure the entire boolean and function row shows a hand */ }\n .dg ul {\n list-style: none;\n margin: 0;\n padding: 0;\n width: 100%;\n clear: both; }\n .dg.ac {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n height: 0;\n z-index: 0; }\n .dg:not(.ac) .main {\n /** Exclude mains in ac so that we don't hide close button */\n overflow: hidden; }\n .dg.main {\n -webkit-transition: opacity 0.1s linear;\n -o-transition: opacity 0.1s linear;\n -moz-transition: opacity 0.1s linear;\n transition: opacity 0.1s linear; }\n .dg.main.taller-than-window {\n overflow-y: auto; }\n .dg.main.taller-than-window .close-button {\n opacity: 1;\n /* TODO, these are style notes */\n margin-top: -1px;\n border-top: 1px solid #2c2c2c; }\n .dg.main ul.closed .close-button {\n opacity: 1 !important; }\n .dg.main:hover .close-button,\n .dg.main .close-button.drag {\n opacity: 1; }\n .dg.main .close-button {\n /*opacity: 0;*/\n -webkit-transition: opacity 0.1s linear;\n -o-transition: opacity 0.1s linear;\n -moz-transition: opacity 0.1s linear;\n transition: opacity 0.1s linear;\n border: 0;\n position: absolute;\n line-height: 19px;\n height: 20px;\n /* TODO, these are style notes */\n cursor: pointer;\n text-align: center;\n background-color: #000; }\n .dg.main .close-button:hover {\n background-color: #111; }\n .dg.a {\n float: right;\n margin-right: 15px;\n overflow-x: hidden; }\n .dg.a.has-save > ul {\n margin-top: 27px; }\n .dg.a.has-save > ul.closed {\n margin-top: 0; }\n .dg.a .save-row {\n position: fixed;\n top: 0;\n z-index: 1002; }\n .dg li {\n -webkit-transition: height 0.1s ease-out;\n -o-transition: height 0.1s ease-out;\n -moz-transition: height 0.1s ease-out;\n transition: height 0.1s ease-out; }\n .dg li:not(.folder) {\n cursor: auto;\n height: 27px;\n line-height: 27px;\n overflow: hidden;\n padding: 0 4px 0 5px; }\n .dg li.folder {\n padding: 0;\n border-left: 4px solid rgba(0, 0, 0, 0); }\n .dg li.title {\n cursor: pointer;\n margin-left: -4px; }\n .dg .closed li:not(.title),\n .dg .closed ul li,\n .dg .closed ul li > * {\n height: 0;\n overflow: hidden;\n border: 0; }\n .dg .cr {\n clear: both;\n padding-left: 3px;\n height: 27px; }\n .dg .property-name {\n cursor: default;\n float: left;\n clear: left;\n width: 40%;\n overflow: hidden;\n text-overflow: ellipsis; }\n .dg .c {\n float: left;\n width: 60%; }\n .dg .c input[type=text] {\n border: 0;\n margin-top: 4px;\n padding: 3px;\n width: 100%;\n float: right; }\n .dg .has-slider input[type=text] {\n width: 30%;\n /*display: none;*/\n margin-left: 0; }\n .dg .slider {\n float: left;\n width: 66%;\n margin-left: -5px;\n margin-right: 0;\n height: 19px;\n margin-top: 4px; }\n .dg .slider-fg {\n height: 100%; }\n .dg .c input[type=checkbox] {\n margin-top: 9px; }\n .dg .c select {\n margin-top: 5px; }\n .dg .cr.function,\n .dg .cr.function .property-name,\n .dg .cr.function *,\n .dg .cr.boolean,\n .dg .cr.boolean * {\n cursor: pointer; }\n .dg .selector {\n display: none;\n position: absolute;\n margin-left: -9px;\n margin-top: 23px;\n z-index: 10; }\n .dg .c:hover .selector,\n .dg .selector.drag {\n display: block; }\n .dg li.save-row {\n padding: 0; }\n .dg li.save-row .button {\n display: inline-block;\n padding: 0px 6px; }\n .dg.dialogue {\n background-color: #222;\n width: 460px;\n padding: 15px;\n font-size: 13px;\n line-height: 15px; }\n\n/* TODO Separate style and structure */\n#dg-new-constructor {\n padding: 10px;\n color: #222;\n font-family: Monaco, monospace;\n font-size: 10px;\n border: 0;\n resize: none;\n box-shadow: inset 1px 1px 1px #888;\n word-wrap: break-word;\n margin: 12px 0;\n display: block;\n width: 440px;\n overflow-y: scroll;\n height: 100px;\n position: relative; }\n\n#dg-local-explain {\n display: none;\n font-size: 11px;\n line-height: 17px;\n border-radius: 3px;\n background-color: #333;\n padding: 8px;\n margin-top: 10px; }\n #dg-local-explain code {\n font-size: 10px; }\n\n#dat-gui-save-locally {\n display: none; }\n\n/** Main type */\n.dg {\n color: #eee;\n font: 11px 'Lucida Grande', sans-serif;\n text-shadow: 0 -1px 0 #111;\n /** Auto place */\n /* Controller row, */\n /** Controllers */ }\n .dg.main {\n /** Scrollbar */ }\n .dg.main::-webkit-scrollbar {\n width: 5px;\n background: #1a1a1a; }\n .dg.main::-webkit-scrollbar-corner {\n height: 0;\n display: none; }\n .dg.main::-webkit-scrollbar-thumb {\n border-radius: 5px;\n background: #676767; }\n .dg li:not(.folder) {\n background: #1a1a1a;\n border-bottom: 1px solid #2c2c2c; }\n .dg li.save-row {\n line-height: 25px;\n background: #dad5cb;\n border: 0; }\n .dg li.save-row select {\n margin-left: 5px;\n width: 108px; }\n .dg li.save-row .button {\n margin-left: 5px;\n margin-top: 1px;\n border-radius: 2px;\n font-size: 9px;\n line-height: 7px;\n padding: 4px 4px 5px 4px;\n background: #c5bdad;\n color: #fff;\n text-shadow: 0 1px 0 #b0a58f;\n box-shadow: 0 -1px 0 #b0a58f;\n cursor: pointer; }\n .dg li.save-row .button.gears {\n background: #c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;\n height: 7px;\n width: 8px; }\n .dg li.save-row .button:hover {\n background-color: #bab19e;\n box-shadow: 0 -1px 0 #b0a58f; }\n .dg li.folder {\n border-bottom: 0; }\n .dg li.title {\n padding-left: 16px;\n background: black url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;\n cursor: pointer;\n border-bottom: 1px solid rgba(255, 255, 255, 0.2); }\n .dg .closed li.title {\n background-image: url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==); }\n .dg .cr.boolean {\n border-left: 3px solid #806787; }\n .dg .cr.function {\n border-left: 3px solid #e61d5f; }\n .dg .cr.number {\n border-left: 3px solid #2fa1d6; }\n .dg .cr.number input[type=text] {\n color: #2fa1d6; }\n .dg .cr.string {\n border-left: 3px solid #1ed36f; }\n .dg .cr.string input[type=text] {\n color: #1ed36f; }\n .dg .cr.function:hover, .dg .cr.boolean:hover {\n background: #111; }\n .dg .c input[type=text] {\n background: #303030;\n outline: none; }\n .dg .c input[type=text]:hover {\n background: #3c3c3c; }\n .dg .c input[type=text]:focus {\n background: #494949;\n color: #fff; }\n .dg .c .slider {\n background: #303030;\n cursor: ew-resize; }\n .dg .c .slider-fg {\n background: #2fa1d6; }\n .dg .c .slider:hover {\n background: #3c3c3c; }\n .dg .c .slider:hover .slider-fg {\n background: #44abda; }\n",
+dat.controllers.factory=function(f,a,d,e,c,b,p){return function(q,l,r,n){var u=q[l];if(p.isArray(r)||p.isObject(r))return new f(q,l,r);if(p.isNumber(u))return p.isNumber(r)&&p.isNumber(n)?new d(q,l,r,n):new a(q,l,{min:r,max:n});if(p.isString(u))return new e(q,l);if(p.isFunction(u))return new c(q,l,"");if(p.isBoolean(u))return new b(q,l)}}(dat.controllers.OptionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.StringController=function(f,a,d){var e=
+function(c,b){function d(){f.setValue(f.__input.value)}e.superclass.call(this,c,b);var f=this;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"keyup",d);a.bind(this.__input,"change",d);a.bind(this.__input,"blur",function(){f.__onFinishChange&&f.__onFinishChange.call(f,f.getValue())});a.bind(this.__input,"keydown",function(a){13===a.keyCode&&this.blur()});this.updateDisplay();this.domElement.appendChild(this.__input)};e.superclass=f;d.extend(e.prototype,
+f.prototype,{updateDisplay:function(){a.isActive(this.__input)||(this.__input.value=this.getValue());return e.superclass.prototype.updateDisplay.call(this)}});return e}(dat.controllers.Controller,dat.dom.dom,dat.utils.common),dat.controllers.FunctionController,dat.controllers.BooleanController,dat.utils.common),dat.controllers.Controller,dat.controllers.BooleanController,dat.controllers.FunctionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.OptionController,
+dat.controllers.ColorController=function(f,a,d,e,c){function b(a,b,d,e){a.style.background="";c.each(l,function(c){a.style.cssText+="background: "+c+"linear-gradient("+b+", "+d+" 0%, "+e+" 100%); "})}function p(a){a.style.background="";a.style.cssText+="background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);";a.style.cssText+="background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";
+a.style.cssText+="background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"}var q=function(f,n){function u(b){v(b);a.bind(window,"mousemove",v);a.bind(window,
+"mouseup",l)}function l(){a.unbind(window,"mousemove",v);a.unbind(window,"mouseup",l)}function g(){var a=e(this.value);!1!==a?(t.__color.__state=a,t.setValue(t.__color.toOriginal())):this.value=t.__color.toString()}function k(){a.unbind(window,"mousemove",w);a.unbind(window,"mouseup",k)}function v(b){b.preventDefault();var c=a.getWidth(t.__saturation_field),d=a.getOffset(t.__saturation_field),e=(b.clientX-d.left+document.body.scrollLeft)/c;b=1-(b.clientY-d.top+document.body.scrollTop)/c;1
+b&&(b=0);1e&&(e=0);t.__color.v=b;t.__color.s=e;t.setValue(t.__color.toOriginal());return!1}function w(b){b.preventDefault();var c=a.getHeight(t.__hue_field),d=a.getOffset(t.__hue_field);b=1-(b.clientY-d.top+document.body.scrollTop)/c;1b&&(b=0);t.__color.h=360*b;t.setValue(t.__color.toOriginal());return!1}q.superclass.call(this,f,n);this.__color=new d(this.getValue());this.__temp=new d(0);var t=this;this.domElement=document.createElement("div");a.makeSelectable(this.domElement,!1);
+this.__selector=document.createElement("div");this.__selector.className="selector";this.__saturation_field=document.createElement("div");this.__saturation_field.className="saturation-field";this.__field_knob=document.createElement("div");this.__field_knob.className="field-knob";this.__field_knob_border="2px solid ";this.__hue_knob=document.createElement("div");this.__hue_knob.className="hue-knob";this.__hue_field=document.createElement("div");this.__hue_field.className="hue-field";this.__input=document.createElement("input");
+this.__input.type="text";this.__input_textShadow="0 1px 1px ";a.bind(this.__input,"keydown",function(a){13===a.keyCode&&g.call(this)});a.bind(this.__input,"blur",g);a.bind(this.__selector,"mousedown",function(b){a.addClass(this,"drag").bind(window,"mouseup",function(b){a.removeClass(t.__selector,"drag")})});var y=document.createElement("div");c.extend(this.__selector.style,{width:"122px",height:"102px",padding:"3px",backgroundColor:"#222",boxShadow:"0px 1px 3px rgba(0,0,0,0.3)"});c.extend(this.__field_knob.style,
+{position:"absolute",width:"12px",height:"12px",border:this.__field_knob_border+(.5>this.__color.v?"#fff":"#000"),boxShadow:"0px 1px 3px rgba(0,0,0,0.5)",borderRadius:"12px",zIndex:1});c.extend(this.__hue_knob.style,{position:"absolute",width:"15px",height:"2px",borderRight:"4px solid #fff",zIndex:1});c.extend(this.__saturation_field.style,{width:"100px",height:"100px",border:"1px solid #555",marginRight:"3px",display:"inline-block",cursor:"pointer"});c.extend(y.style,{width:"100%",height:"100%",
+background:"none"});b(y,"top","rgba(0,0,0,0)","#000");c.extend(this.__hue_field.style,{width:"15px",height:"100px",display:"inline-block",border:"1px solid #555",cursor:"ns-resize"});p(this.__hue_field);c.extend(this.__input.style,{outline:"none",textAlign:"center",color:"#fff",border:0,fontWeight:"bold",textShadow:this.__input_textShadow+"rgba(0,0,0,0.7)"});a.bind(this.__saturation_field,"mousedown",u);a.bind(this.__field_knob,"mousedown",u);a.bind(this.__hue_field,"mousedown",function(b){w(b);a.bind(window,
+"mousemove",w);a.bind(window,"mouseup",k)});this.__saturation_field.appendChild(y);this.__selector.appendChild(this.__field_knob);this.__selector.appendChild(this.__saturation_field);this.__selector.appendChild(this.__hue_field);this.__hue_field.appendChild(this.__hue_knob);this.domElement.appendChild(this.__input);this.domElement.appendChild(this.__selector);this.updateDisplay()};q.superclass=f;c.extend(q.prototype,f.prototype,{updateDisplay:function(){var a=e(this.getValue());if(!1!==a){var f=!1;
+c.each(d.COMPONENTS,function(b){if(!c.isUndefined(a[b])&&!c.isUndefined(this.__color.__state[b])&&a[b]!==this.__color.__state[b])return f=!0,{}},this);f&&c.extend(this.__color.__state,a)}c.extend(this.__temp.__state,this.__color.__state);this.__temp.a=1;var l=.5>this.__color.v||.5a&&(a+=1);return{h:360*a,s:c/b,v:b/255}},rgb_to_hex:function(a,d,e){a=this.hex_with_component(0,2,a);a=this.hex_with_component(a,1,d);return a=this.hex_with_component(a,0,e)},component_from_hex:function(a,d){return a>>8*d&255},hex_with_component:function(a,d,e){return e<<(f=8*d)|a&~(255<
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/particle_explorer/main.js b/examples/particle_explorer/main.js
new file mode 100644
index 0000000000..4be8168407
--- /dev/null
+++ b/examples/particle_explorer/main.js
@@ -0,0 +1,513 @@
+//
+// main.js
+//
+// Created by James B. Pollack @imgntn on 9/26/2015
+// Copyright 2015 High Fidelity, Inc.
+// Web app side of the App - contains GUI.
+// This is an example of a new, easy way to do two way bindings between dynamically created GUI and in-world entities.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+/*global window, alert, EventBridge, dat, convertBinaryToBoolean, listenForSettingsUpdates,createVec3Folder,createQuatFolder,writeVec3ToInterface,writeDataToInterface*/
+
+var Settings = function() {
+ this.exportSettings = function() {
+ //copyExportSettingsToClipboard();
+ showPreselectedPrompt();
+ };
+ this.importSettings = function() {
+ importSettings();
+ };
+};
+
+//2-way bindings-aren't quite ready yet. see bottom of file.
+var AUTO_UPDATE = false;
+var UPDATE_ALL_FREQUENCY = 100;
+
+var controllers = [];
+var colorControllers = [];
+var folders = [];
+var gui = null;
+var settings = new Settings();
+var updateInterval;
+
+var currentInputField;
+var storedController;
+var keysToIgnore = [
+ 'importSettings',
+ 'exportSettings',
+ 'script',
+ 'visible',
+ 'locked',
+ 'userData',
+ 'position',
+ 'dimensions',
+ 'rotation',
+ 'id',
+ 'description',
+ 'type',
+ 'created',
+ 'age',
+ 'ageAsText',
+ 'boundingBox',
+ 'naturalDimensions',
+ 'naturalPosition',
+ 'velocity',
+ 'gravity',
+ 'acceleration',
+ 'damping',
+ 'restitution',
+ 'friction',
+ 'density',
+ 'lifetime',
+ 'scriptTimestamp',
+ 'registrationPoint',
+ 'angularVelocity',
+ 'angularDamping',
+ 'ignoreForCollisions',
+ 'collisionsWillMove',
+ 'href',
+ 'actionData',
+ 'marketplaceID',
+ 'collisionSoundURL',
+ 'shapeType',
+ 'animationSettings',
+ 'animationFrameIndex',
+ 'animationIsPlaying',
+ 'animationFPS',
+ 'sittingPoints',
+ 'originalTextures'
+];
+
+var individualKeys = [];
+var vec3Keys = [];
+var quatKeys = [];
+var colorKeys = [];
+
+window.onload = function() {
+ if (typeof EventBridge !== 'undefined') {
+
+ var stringifiedData = JSON.stringify({
+ messageType: 'page_loaded'
+ });
+
+ EventBridge.emitWebEvent(
+ stringifiedData
+ );
+
+ listenForSettingsUpdates();
+ window.onresize = setGUIWidthToWindowWidth;
+ } else {
+ console.log('No event bridge, probably not in interface.');
+ }
+};
+
+function loadGUI() {
+ //whether or not to autoplace
+ gui = new dat.GUI({
+ autoPlace: false
+ });
+
+ //if not autoplacing, put gui in a custom container
+ if (gui.autoPlace === false) {
+ var customContainer = document.getElementById('my-gui-container');
+ customContainer.appendChild(gui.domElement);
+ }
+
+ // presets for the GUI itself. a little confusing and import/export is mostly what we want to do at the moment.
+ // gui.remember(settings);
+
+ var keys = _.keys(settings);
+
+ _.each(keys, function(key) {
+ var shouldIgnore = _.contains(keysToIgnore, key);
+
+ if (shouldIgnore) {
+ return;
+ }
+
+ var subKeys = _.keys(settings[key]);
+ var hasX = _.contains(subKeys, 'x');
+ var hasY = _.contains(subKeys, 'y');
+ var hasZ = _.contains(subKeys, 'z');
+ var hasW = _.contains(subKeys, 'w');
+ var hasRed = _.contains(subKeys, 'red');
+ var hasGreen = _.contains(subKeys, 'green');
+ var hasBlue = _.contains(subKeys, 'blue');
+
+ if ((hasX && hasY && hasZ) && hasW === false) {
+ vec3Keys.push(key);
+ } else if (hasX && hasY && hasZ && hasW) {
+ quatKeys.push(key);
+ } else if (hasRed || hasGreen || hasBlue) {
+ colorKeys.push(key);
+
+ } else {
+ individualKeys.push(key);
+ }
+
+ });
+
+ //alphabetize our keys
+ individualKeys.sort();
+ vec3Keys.sort();
+ quatKeys.sort();
+ colorKeys.sort();
+
+ //add to gui in the order they should appear
+ gui.add(settings, 'importSettings');
+ gui.add(settings, 'exportSettings');
+ addIndividualKeys();
+ addFolders();
+
+ //set the gui width to match the web window width
+ gui.width = window.innerWidth;
+
+ //2-way binding stuff
+ // if (AUTO_UPDATE) {
+ // setInterval(manuallyUpdateDisplay, UPDATE_ALL_FREQUENCY);
+ // registerDOMElementsForListenerBlocking();
+ // }
+
+}
+
+function addIndividualKeys() {
+ _.each(individualKeys, function(key) {
+ //temporary patch for not crashing when this goes below 0
+ var controller;
+
+ if (key.indexOf('emitRate') > -1) {
+ controller = gui.add(settings, key).min(0);
+ } else {
+ controller = gui.add(settings, key);
+
+ }
+
+ //2-way - need to fix not being able to input exact values if constantly listening
+ //controller.listen();
+
+ //keep track of our controller
+ controllers.push(controller);
+
+ //hook into change events for this gui controller
+ controller.onChange(function(value) {
+ // Fires on every change, drag, keypress, etc.
+ writeDataToInterface(this.property, value);
+ });
+
+ });
+}
+
+function addFolders() {
+ _.each(colorKeys, function(key) {
+ createColorPicker(key);
+ });
+ _.each(vec3Keys, function(key) {
+ createVec3Folder(key);
+ });
+ _.each(quatKeys, function(key) {
+ createQuatFolder(key);
+ });
+}
+
+function createColorPicker(key) {
+ var colorObject = settings[key];
+ var colorArray = convertColorObjectToArray(colorObject);
+ settings[key] = colorArray;
+ var controller = gui.addColor(settings, key);
+ controller.onChange(function(value) {
+ var obj = {};
+ obj[key] = convertColorArrayToObject(value);
+ writeVec3ToInterface(obj);
+ });
+
+ return;
+}
+
+function createVec3Folder(category) {
+ var folder = gui.addFolder(category);
+
+ folder.add(settings[category], 'x').step(0.1).onChange(function(value) {
+ // Fires when a controller loses focus.
+ var obj = {};
+ obj[category] = {};
+ obj[category][this.property] = value;
+ obj[category].y = settings[category].y;
+ obj[category].z = settings[category].z;
+ writeVec3ToInterface(obj);
+ });
+
+ folder.add(settings[category], 'y').step(0.1).onChange(function(value) {
+ // Fires when a controller loses focus.
+ var obj = {};
+ obj[category] = {};
+ obj[category].x = settings[category].x;
+ obj[category][this.property] = value;
+ obj[category].z = settings[category].z;
+ writeVec3ToInterface(obj);
+ });
+
+ folder.add(settings[category], 'z').step(0.1).onChange(function(value) {
+ // Fires when a controller loses focus.
+ var obj = {};
+ obj[category] = {};
+ obj[category].y = settings[category].y;
+ obj[category].x = settings[category].x;
+ obj[category][this.property] = value;
+ writeVec3ToInterface(obj);
+ });
+
+ folders.push(folder);
+ folder.open();
+}
+
+function createQuatFolder(category) {
+ var folder = gui.addFolder(category);
+
+ folder.add(settings[category], 'x').step(0.1).onChange(function(value) {
+ // Fires when a controller loses focus.
+ var obj = {};
+ obj[category] = {};
+ obj[category][this.property] = value;
+ obj[category].y = settings[category].y;
+ obj[category].z = settings[category].z;
+ obj[category].w = settings[category].w;
+ writeVec3ToInterface(obj);
+ });
+
+ folder.add(settings[category], 'y').step(0.1).onChange(function(value) {
+ // Fires when a controller loses focus.
+ var obj = {};
+ obj[category] = {};
+ obj[category].x = settings[category].x;
+ obj[category][this.property] = value;
+ obj[category].z = settings[category].z;
+ obj[category].w = settings[category].w;
+ writeVec3ToInterface(obj);
+ });
+
+ folder.add(settings[category], 'z').step(0.1).onChange(function(value) {
+ // Fires when a controller loses focus.
+ var obj = {};
+ obj[category] = {};
+ obj[category].x = settings[category].x;
+ obj[category].y = settings[category].y;
+ obj[category][this.property] = value;
+ obj[category].w = settings[category].w;
+ writeVec3ToInterface(obj);
+ });
+
+ folder.add(settings[category], 'w').step(0.1).onChange(function(value) {
+ // Fires when a controller loses focus.
+ var obj = {};
+ obj[category] = {};
+ obj[category].x = settings[category].x;
+ obj[category].y = settings[category].y;
+ obj[category].z = settings[category].z;
+ obj[category][this.property] = value;
+ writeVec3ToInterface(obj);
+ });
+
+ folders.push(folder);
+ folder.open();
+}
+
+function convertColorObjectToArray(colorObject) {
+ var colorArray = [];
+
+ _.each(colorObject, function(singleColor) {
+ colorArray.push(singleColor);
+ });
+
+ return colorArray;
+}
+
+function convertColorArrayToObject(colorArray) {
+ var colorObject = {
+ red: colorArray[0],
+ green: colorArray[1],
+ blue: colorArray[2]
+ };
+
+ return colorObject;
+}
+
+function writeDataToInterface(property, value) {
+ var data = {};
+ data[property] = value;
+
+ var sendData = {
+ messageType: "settings_update",
+ updatedSettings: data
+ };
+
+ var stringifiedData = JSON.stringify(sendData);
+
+ EventBridge.emitWebEvent(stringifiedData);
+}
+
+function writeVec3ToInterface(obj) {
+ var sendData = {
+ messageType: "settings_update",
+ updatedSettings: obj
+ };
+
+ var stringifiedData = JSON.stringify(sendData);
+
+ EventBridge.emitWebEvent(stringifiedData);
+}
+
+function listenForSettingsUpdates() {
+ EventBridge.scriptEventReceived.connect(function(data) {
+ data = JSON.parse(data);
+
+ //2-way
+ // if (data.messageType === 'object_update') {
+ // _.each(data.objectSettings, function(value, key) {
+ // settings[key] = value;
+ // });
+ // }
+
+ if (data.messageType === 'initial_settings') {
+ _.each(data.initialSettings, function(value, key) {
+ settings[key] = {};
+ settings[key] = value;
+ });
+
+ loadGUI();
+ }
+ });
+}
+
+function manuallyUpdateDisplay() {
+ // Iterate over all controllers
+ // this is expensive, write a method for indiviudal controllers and use it when the value is different than a cached value, perhaps.
+ var i;
+ for (i in gui.__controllers) {
+ gui.__controllers[i].updateDisplay();
+ }
+}
+
+function setGUIWidthToWindowWidth() {
+ if (gui !== null) {
+ gui.width = window.innerWidth;
+ }
+}
+
+function handleInputKeyPress(e) {
+ if (e.keyCode === 13) {
+ importSettings();
+ }
+ return false;
+}
+
+function importSettings() {
+ var importInput = document.getElementById('importer-input');
+
+ try {
+ var importedSettings = JSON.parse(importInput.value);
+
+ var keys = _.keys(importedSettings);
+
+ _.each(keys, function(key) {
+ var shouldIgnore = _.contains(keysToIgnore, key);
+
+ if (shouldIgnore) {
+ return;
+ }
+
+ settings[key] = importedSettings[key];
+ });
+
+ writeVec3ToInterface(settings);
+
+ manuallyUpdateDisplay();
+ } catch (e) {
+ alert('Not properly formatted JSON');
+ }
+}
+
+function prepareSettingsForExport() {
+ var keys = _.keys(settings);
+
+ var exportSettings = {};
+
+ _.each(keys, function(key) {
+ var shouldIgnore = _.contains(keysToIgnore, key);
+
+ if (shouldIgnore) {
+ return;
+ }
+
+ if (key.indexOf('color') > -1) {
+ var colorObject = convertColorArrayToObject(settings[key]);
+ settings[key] = colorObject;
+ }
+
+ exportSettings[key] = settings[key];
+ });
+
+ return JSON.stringify(exportSettings);
+}
+
+function showPreselectedPrompt() {
+ window.prompt("Ctrl-C to copy, then Enter.", prepareSettingsForExport());
+}
+
+function removeContainerDomElement() {
+ var elem = document.getElementById("my-gui-container");
+ elem.parentNode.removeChild(elem);
+}
+
+function removeListenerFromGUI(key) {
+ _.each(gui.__listening, function(controller, index) {
+ if (controller.property === key) {
+ storedController = controller;
+ gui.__listening.splice(index, 1);
+ }
+ });
+}
+
+//the section below is to try to work at achieving two way bindings;
+function addListenersBackToGUI() {
+ gui.__listening.push(storedController);
+ storedController = null;
+}
+
+function registerDOMElementsForListenerBlocking() {
+ _.each(gui.__controllers, function(controller) {
+ var input = controller.domElement.childNodes[0];
+ input.addEventListener('focus', function() {
+ console.log('INPUT ELEMENT GOT FOCUS!' + controller.property);
+ removeListenerFromGUI(controller.property);
+ });
+ });
+
+ _.each(gui.__controllers, function(controller) {
+ var input = controller.domElement.childNodes[0];
+ input.addEventListener('blur', function() {
+ console.log('INPUT ELEMENT GOT BLUR!' + controller.property);
+ addListenersBackToGUI();
+ });
+ });
+
+ // also listen to inputs inside of folders
+ _.each(gui.__folders, function(folder) {
+ _.each(folder.__controllers, function(controller) {
+ var input = controller.__input;
+ input.addEventListener('focus', function() {
+ console.log('FOLDER ELEMENT GOT FOCUS!' + controller.property);
+ });
+ });
+ });
+}
+
+///utility method for converting weird collisionWillMove type propertyies from binary to new Boolean()
+//
+// function convertBinaryToBoolean(value) {
+// if (value === 0) {
+// return false;
+// }
+// return true;
+// }
\ No newline at end of file
diff --git a/examples/particle_explorer/particleExplorer.js b/examples/particle_explorer/particleExplorer.js
new file mode 100644
index 0000000000..6f03eb3c61
--- /dev/null
+++ b/examples/particle_explorer/particleExplorer.js
@@ -0,0 +1,226 @@
+//
+// particleExplorer.js
+//
+// Created by James B. Pollack @imgntn on 9/26/2015
+// includes setup from @ctrlaltdavid's particlesTest.js
+// Copyright 2015 High Fidelity, Inc.
+//
+// Interface side of the App.
+// Quickly edit the aesthetics of a particle system.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+// next version: 2 way bindings, integrate with edit.js
+//
+/*global print, WebWindow, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
+
+var box,
+ sphere,
+ sphereDimensions = {
+ x: 0.4,
+ y: 0.8,
+ z: 0.2
+ },
+ pointDimensions = {
+ x: 0.0,
+ y: 0.0,
+ z: 0.0
+ },
+ sphereOrientation = Quat.fromPitchYawRollDegrees(-60.0, 30.0, 0.0),
+ verticalOrientation = Quat.fromPitchYawRollDegrees(-90.0, 0.0, 0.0),
+ particles,
+ particleExample = -1,
+ PARTICLE_RADIUS = 0.04,
+ SLOW_EMIT_RATE = 2.0,
+ HALF_EMIT_RATE = 50.0,
+ FAST_EMIT_RATE = 100.0,
+ SLOW_EMIT_SPEED = 0.025,
+ FAST_EMIT_SPEED = 1.0,
+ GRAVITY_EMIT_ACCELERATON = {
+ x: 0.0,
+ y: -0.3,
+ z: 0.0
+ },
+ ZERO_EMIT_ACCELERATON = {
+ x: 0.0,
+ y: 0.0,
+ z: 0.0
+ },
+ PI = 3.141593,
+ DEG_TO_RAD = PI / 180.0,
+ NUM_PARTICLE_EXAMPLES = 18;
+
+var particleProperties;
+
+function setUp() {
+ var boxPoint,
+ spawnPoint,
+ animation = {
+ fps: 30,
+ frameIndex: 0,
+ running: true,
+ firstFrame: 0,
+ lastFrame: 30,
+ loop: true
+ };
+
+ boxPoint = Vec3.sum(MyAvatar.position, Vec3.multiply(4.0, Quat.getFront(Camera.getOrientation())));
+ boxPoint = Vec3.sum(boxPoint, {
+ x: 0.0,
+ y: -0.5,
+ z: 0.0
+ });
+ spawnPoint = Vec3.sum(boxPoint, {
+ x: 0.0,
+ y: 1.0,
+ z: 0.0
+ });
+
+ box = Entities.addEntity({
+ type: "Box",
+ name: "ParticlesTest Box",
+ position: boxPoint,
+ rotation: verticalOrientation,
+ dimensions: {
+ x: 0.3,
+ y: 0.3,
+ z: 0.3
+ },
+ color: {
+ red: 128,
+ green: 128,
+ blue: 128
+ },
+ lifetime: 3600, // 1 hour; just in case
+ visible: true
+ });
+
+ // Same size and orientation as emitter when ellipsoid.
+ sphere = Entities.addEntity({
+ type: "Sphere",
+ name: "ParticlesTest Sphere",
+ position: boxPoint,
+ rotation: sphereOrientation,
+ dimensions: sphereDimensions,
+ color: {
+ red: 128,
+ green: 128,
+ blue: 128
+ },
+ lifetime: 3600, // 1 hour; just in case
+ visible: false
+ });
+
+ // 1.0m above the box or ellipsoid.
+ particles = Entities.addEntity({
+ type: "ParticleEffect",
+ name: "ParticlesTest Emitter",
+ position: spawnPoint,
+ emitOrientation: verticalOrientation,
+ particleRadius: PARTICLE_RADIUS,
+ radiusSpread: 0.0,
+ emitRate: SLOW_EMIT_RATE,
+ emitSpeed: FAST_EMIT_SPEED,
+ speedSpread: 0.0,
+ emitAcceleration: GRAVITY_EMIT_ACCELERATON,
+ accelerationSpread: {
+ x: 0.0,
+ y: 0.0,
+ z: 0.0
+ },
+ textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
+ color: {
+ red: 255,
+ green: 255,
+ blue: 255
+ },
+ lifespan: 5.0,
+ visible: false,
+ locked: false,
+ animationSettings: animation,
+ animationIsPlaying: true,
+ lifetime: 3600 // 1 hour; just in case
+ });
+
+}
+
+SettingsWindow = function() {
+ var _this = this;
+
+ this.webWindow = null;
+
+ this.init = function() {
+ Script.update.connect(waitForObjectAuthorization);
+ _this.webWindow = new WebWindow('Particle Explorer', Script.resolvePath('index.html'), 400, 600, false);
+ _this.webWindow.eventBridge.webEventReceived.connect(_this.onWebEventReceived);
+ };
+
+ this.sendData = function(data) {
+ _this.webWindow.eventBridge.emitScriptEvent(JSON.stringify(data));
+ };
+
+ this.onWebEventReceived = function(data) {
+ var _data = JSON.parse(data);
+ if (_data.messageType === 'page_loaded') {
+ _this.webWindow.setVisible(true);
+ _this.pageLoaded = true;
+ sendInitialSettings(particleProperties);
+ }
+ if (_data.messageType === 'settings_update') {
+ editEntity(_data.updatedSettings);
+ return;
+ }
+
+ };
+};
+
+function waitForObjectAuthorization() {
+ var properties = Entities.getEntityProperties(particles, "isKnownID");
+ var isKnownID = properties.isKnownID;
+ if (isKnownID === false || SettingsWindow.pageLoaded === false) {
+ return;
+ }
+ var currentProperties = Entities.getEntityProperties(particles);
+ particleProperties = currentProperties;
+ Script.update.connect(sendObjectUpdates);
+ Script.update.disconnect(waitForObjectAuthorization);
+}
+
+function sendObjectUpdates() {
+ var currentProperties = Entities.getEntityProperties(particles);
+ sendUpdatedObject(currentProperties);
+}
+
+function sendInitialSettings(properties) {
+ var settings = {
+ messageType: 'initial_settings',
+ initialSettings: properties
+ };
+
+ settingsWindow.sendData(settings);
+}
+
+function sendUpdatedObject(properties) {
+ var settings = {
+ messageType: 'object_update',
+ objectSettings: properties
+ };
+ settingsWindow.sendData(settings);
+}
+
+function editEntity(properties) {
+ Entities.editEntity(particles, properties);
+}
+
+function tearDown() {
+ Entities.deleteEntity(particles);
+ Entities.deleteEntity(box);
+ Entities.deleteEntity(sphere);
+ Script.update.disconnect(sendObjectUpdates);
+}
+
+var settingsWindow = new SettingsWindow();
+settingsWindow.init();
+setUp();
+Script.scriptEnding.connect(tearDown);
\ No newline at end of file
diff --git a/examples/particle_explorer/underscore-min.js b/examples/particle_explorer/underscore-min.js
new file mode 100644
index 0000000000..f01025b7bc
--- /dev/null
+++ b/examples/particle_explorer/underscore-min.js
@@ -0,0 +1,6 @@
+// Underscore.js 1.8.3
+// http://underscorejs.org
+// (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+// Underscore may be freely distributed under the MIT license.
+(function(){function n(n){function t(t,r,e,u,i,o){for(;i>=0&&o>i;i+=n){var a=u?u[i]:i;e=r(e,t[a],a,t)}return e}return function(r,e,u,i){e=b(e,i,4);var o=!k(r)&&m.keys(r),a=(o||r).length,c=n>0?0:a-1;return arguments.length<3&&(u=r[o?o[c]:c],c+=n),t(r,e,u,o,c,a)}}function t(n){return function(t,r,e){r=x(r,e);for(var u=O(t),i=n>0?0:u-1;i>=0&&u>i;i+=n)if(r(t[i],i,t))return i;return-1}}function r(n,t,r){return function(e,u,i){var o=0,a=O(e);if("number"==typeof i)n>0?o=i>=0?i:Math.max(i+a,o):a=i>=0?Math.min(i+1,a):i+a+1;else if(r&&i&&a)return i=r(e,u),e[i]===u?i:-1;if(u!==u)return i=t(l.call(e,o,a),m.isNaN),i>=0?i+o:-1;for(i=n>0?o:a-1;i>=0&&a>i;i+=n)if(e[i]===u)return i;return-1}}function e(n,t){var r=I.length,e=n.constructor,u=m.isFunction(e)&&e.prototype||a,i="constructor";for(m.has(n,i)&&!m.contains(t,i)&&t.push(i);r--;)i=I[r],i in n&&n[i]!==u[i]&&!m.contains(t,i)&&t.push(i)}var u=this,i=u._,o=Array.prototype,a=Object.prototype,c=Function.prototype,f=o.push,l=o.slice,s=a.toString,p=a.hasOwnProperty,h=Array.isArray,v=Object.keys,g=c.bind,y=Object.create,d=function(){},m=function(n){return n instanceof m?n:this instanceof m?void(this._wrapped=n):new m(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=m),exports._=m):u._=m,m.VERSION="1.8.3";var b=function(n,t,r){if(t===void 0)return n;switch(null==r?3:r){case 1:return function(r){return n.call(t,r)};case 2:return function(r,e){return n.call(t,r,e)};case 3:return function(r,e,u){return n.call(t,r,e,u)};case 4:return function(r,e,u,i){return n.call(t,r,e,u,i)}}return function(){return n.apply(t,arguments)}},x=function(n,t,r){return null==n?m.identity:m.isFunction(n)?b(n,t,r):m.isObject(n)?m.matcher(n):m.property(n)};m.iteratee=function(n,t){return x(n,t,1/0)};var _=function(n,t){return function(r){var e=arguments.length;if(2>e||null==r)return r;for(var u=1;e>u;u++)for(var i=arguments[u],o=n(i),a=o.length,c=0;a>c;c++){var f=o[c];t&&r[f]!==void 0||(r[f]=i[f])}return r}},j=function(n){if(!m.isObject(n))return{};if(y)return y(n);d.prototype=n;var t=new d;return d.prototype=null,t},w=function(n){return function(t){return null==t?void 0:t[n]}},A=Math.pow(2,53)-1,O=w("length"),k=function(n){var t=O(n);return"number"==typeof t&&t>=0&&A>=t};m.each=m.forEach=function(n,t,r){t=b(t,r);var e,u;if(k(n))for(e=0,u=n.length;u>e;e++)t(n[e],e,n);else{var i=m.keys(n);for(e=0,u=i.length;u>e;e++)t(n[i[e]],i[e],n)}return n},m.map=m.collect=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=Array(u),o=0;u>o;o++){var a=e?e[o]:o;i[o]=t(n[a],a,n)}return i},m.reduce=m.foldl=m.inject=n(1),m.reduceRight=m.foldr=n(-1),m.find=m.detect=function(n,t,r){var e;return e=k(n)?m.findIndex(n,t,r):m.findKey(n,t,r),e!==void 0&&e!==-1?n[e]:void 0},m.filter=m.select=function(n,t,r){var e=[];return t=x(t,r),m.each(n,function(n,r,u){t(n,r,u)&&e.push(n)}),e},m.reject=function(n,t,r){return m.filter(n,m.negate(x(t)),r)},m.every=m.all=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(!t(n[o],o,n))return!1}return!0},m.some=m.any=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(t(n[o],o,n))return!0}return!1},m.contains=m.includes=m.include=function(n,t,r,e){return k(n)||(n=m.values(n)),("number"!=typeof r||e)&&(r=0),m.indexOf(n,t,r)>=0},m.invoke=function(n,t){var r=l.call(arguments,2),e=m.isFunction(t);return m.map(n,function(n){var u=e?t:n[t];return null==u?u:u.apply(n,r)})},m.pluck=function(n,t){return m.map(n,m.property(t))},m.where=function(n,t){return m.filter(n,m.matcher(t))},m.findWhere=function(n,t){return m.find(n,m.matcher(t))},m.max=function(n,t,r){var e,u,i=-1/0,o=-1/0;if(null==t&&null!=n){n=k(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],e>i&&(i=e)}else t=x(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(u>o||u===-1/0&&i===-1/0)&&(i=n,o=u)});return i},m.min=function(n,t,r){var e,u,i=1/0,o=1/0;if(null==t&&null!=n){n=k(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],i>e&&(i=e)}else t=x(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(o>u||1/0===u&&1/0===i)&&(i=n,o=u)});return i},m.shuffle=function(n){for(var t,r=k(n)?n:m.values(n),e=r.length,u=Array(e),i=0;e>i;i++)t=m.random(0,i),t!==i&&(u[i]=u[t]),u[t]=r[i];return u},m.sample=function(n,t,r){return null==t||r?(k(n)||(n=m.values(n)),n[m.random(n.length-1)]):m.shuffle(n).slice(0,Math.max(0,t))},m.sortBy=function(n,t,r){return t=x(t,r),m.pluck(m.map(n,function(n,r,e){return{value:n,index:r,criteria:t(n,r,e)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var F=function(n){return function(t,r,e){var u={};return r=x(r,e),m.each(t,function(e,i){var o=r(e,i,t);n(u,e,o)}),u}};m.groupBy=F(function(n,t,r){m.has(n,r)?n[r].push(t):n[r]=[t]}),m.indexBy=F(function(n,t,r){n[r]=t}),m.countBy=F(function(n,t,r){m.has(n,r)?n[r]++:n[r]=1}),m.toArray=function(n){return n?m.isArray(n)?l.call(n):k(n)?m.map(n,m.identity):m.values(n):[]},m.size=function(n){return null==n?0:k(n)?n.length:m.keys(n).length},m.partition=function(n,t,r){t=x(t,r);var e=[],u=[];return m.each(n,function(n,r,i){(t(n,r,i)?e:u).push(n)}),[e,u]},m.first=m.head=m.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:m.initial(n,n.length-t)},m.initial=function(n,t,r){return l.call(n,0,Math.max(0,n.length-(null==t||r?1:t)))},m.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:m.rest(n,Math.max(0,n.length-t))},m.rest=m.tail=m.drop=function(n,t,r){return l.call(n,null==t||r?1:t)},m.compact=function(n){return m.filter(n,m.identity)};var S=function(n,t,r,e){for(var u=[],i=0,o=e||0,a=O(n);a>o;o++){var c=n[o];if(k(c)&&(m.isArray(c)||m.isArguments(c))){t||(c=S(c,t,r));var f=0,l=c.length;for(u.length+=l;l>f;)u[i++]=c[f++]}else r||(u[i++]=c)}return u};m.flatten=function(n,t){return S(n,t,!1)},m.without=function(n){return m.difference(n,l.call(arguments,1))},m.uniq=m.unique=function(n,t,r,e){m.isBoolean(t)||(e=r,r=t,t=!1),null!=r&&(r=x(r,e));for(var u=[],i=[],o=0,a=O(n);a>o;o++){var c=n[o],f=r?r(c,o,n):c;t?(o&&i===f||u.push(c),i=f):r?m.contains(i,f)||(i.push(f),u.push(c)):m.contains(u,c)||u.push(c)}return u},m.union=function(){return m.uniq(S(arguments,!0,!0))},m.intersection=function(n){for(var t=[],r=arguments.length,e=0,u=O(n);u>e;e++){var i=n[e];if(!m.contains(t,i)){for(var o=1;r>o&&m.contains(arguments[o],i);o++);o===r&&t.push(i)}}return t},m.difference=function(n){var t=S(arguments,!0,!0,1);return m.filter(n,function(n){return!m.contains(t,n)})},m.zip=function(){return m.unzip(arguments)},m.unzip=function(n){for(var t=n&&m.max(n,O).length||0,r=Array(t),e=0;t>e;e++)r[e]=m.pluck(n,e);return r},m.object=function(n,t){for(var r={},e=0,u=O(n);u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},m.findIndex=t(1),m.findLastIndex=t(-1),m.sortedIndex=function(n,t,r,e){r=x(r,e,1);for(var u=r(t),i=0,o=O(n);o>i;){var a=Math.floor((i+o)/2);r(n[a])i;i++,n+=r)u[i]=n;return u};var E=function(n,t,r,e,u){if(!(e instanceof t))return n.apply(r,u);var i=j(n.prototype),o=n.apply(i,u);return m.isObject(o)?o:i};m.bind=function(n,t){if(g&&n.bind===g)return g.apply(n,l.call(arguments,1));if(!m.isFunction(n))throw new TypeError("Bind must be called on a function");var r=l.call(arguments,2),e=function(){return E(n,e,t,this,r.concat(l.call(arguments)))};return e},m.partial=function(n){var t=l.call(arguments,1),r=function(){for(var e=0,u=t.length,i=Array(u),o=0;u>o;o++)i[o]=t[o]===m?arguments[e++]:t[o];for(;e=e)throw new Error("bindAll must be passed function names");for(t=1;e>t;t++)r=arguments[t],n[r]=m.bind(n[r],n);return n},m.memoize=function(n,t){var r=function(e){var u=r.cache,i=""+(t?t.apply(this,arguments):e);return m.has(u,i)||(u[i]=n.apply(this,arguments)),u[i]};return r.cache={},r},m.delay=function(n,t){var r=l.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},m.defer=m.partial(m.delay,m,1),m.throttle=function(n,t,r){var e,u,i,o=null,a=0;r||(r={});var c=function(){a=r.leading===!1?0:m.now(),o=null,i=n.apply(e,u),o||(e=u=null)};return function(){var f=m.now();a||r.leading!==!1||(a=f);var l=t-(f-a);return e=this,u=arguments,0>=l||l>t?(o&&(clearTimeout(o),o=null),a=f,i=n.apply(e,u),o||(e=u=null)):o||r.trailing===!1||(o=setTimeout(c,l)),i}},m.debounce=function(n,t,r){var e,u,i,o,a,c=function(){var f=m.now()-o;t>f&&f>=0?e=setTimeout(c,t-f):(e=null,r||(a=n.apply(i,u),e||(i=u=null)))};return function(){i=this,u=arguments,o=m.now();var f=r&&!e;return e||(e=setTimeout(c,t)),f&&(a=n.apply(i,u),i=u=null),a}},m.wrap=function(n,t){return m.partial(t,n)},m.negate=function(n){return function(){return!n.apply(this,arguments)}},m.compose=function(){var n=arguments,t=n.length-1;return function(){for(var r=t,e=n[t].apply(this,arguments);r--;)e=n[r].call(this,e);return e}},m.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},m.before=function(n,t){var r;return function(){return--n>0&&(r=t.apply(this,arguments)),1>=n&&(t=null),r}},m.once=m.partial(m.before,2);var M=!{toString:null}.propertyIsEnumerable("toString"),I=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"];m.keys=function(n){if(!m.isObject(n))return[];if(v)return v(n);var t=[];for(var r in n)m.has(n,r)&&t.push(r);return M&&e(n,t),t},m.allKeys=function(n){if(!m.isObject(n))return[];var t=[];for(var r in n)t.push(r);return M&&e(n,t),t},m.values=function(n){for(var t=m.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},m.mapObject=function(n,t,r){t=x(t,r);for(var e,u=m.keys(n),i=u.length,o={},a=0;i>a;a++)e=u[a],o[e]=t(n[e],e,n);return o},m.pairs=function(n){for(var t=m.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},m.invert=function(n){for(var t={},r=m.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},m.functions=m.methods=function(n){var t=[];for(var r in n)m.isFunction(n[r])&&t.push(r);return t.sort()},m.extend=_(m.allKeys),m.extendOwn=m.assign=_(m.keys),m.findKey=function(n,t,r){t=x(t,r);for(var e,u=m.keys(n),i=0,o=u.length;o>i;i++)if(e=u[i],t(n[e],e,n))return e},m.pick=function(n,t,r){var e,u,i={},o=n;if(null==o)return i;m.isFunction(t)?(u=m.allKeys(o),e=b(t,r)):(u=S(arguments,!1,!1,1),e=function(n,t,r){return t in r},o=Object(o));for(var a=0,c=u.length;c>a;a++){var f=u[a],l=o[f];e(l,f,o)&&(i[f]=l)}return i},m.omit=function(n,t,r){if(m.isFunction(t))t=m.negate(t);else{var e=m.map(S(arguments,!1,!1,1),String);t=function(n,t){return!m.contains(e,t)}}return m.pick(n,t,r)},m.defaults=_(m.allKeys,!0),m.create=function(n,t){var r=j(n);return t&&m.extendOwn(r,t),r},m.clone=function(n){return m.isObject(n)?m.isArray(n)?n.slice():m.extend({},n):n},m.tap=function(n,t){return t(n),n},m.isMatch=function(n,t){var r=m.keys(t),e=r.length;if(null==n)return!e;for(var u=Object(n),i=0;e>i;i++){var o=r[i];if(t[o]!==u[o]||!(o in u))return!1}return!0};var N=function(n,t,r,e){if(n===t)return 0!==n||1/n===1/t;if(null==n||null==t)return n===t;n instanceof m&&(n=n._wrapped),t instanceof m&&(t=t._wrapped);var u=s.call(n);if(u!==s.call(t))return!1;switch(u){case"[object RegExp]":case"[object String]":return""+n==""+t;case"[object Number]":return+n!==+n?+t!==+t:0===+n?1/+n===1/t:+n===+t;case"[object Date]":case"[object Boolean]":return+n===+t}var i="[object Array]"===u;if(!i){if("object"!=typeof n||"object"!=typeof t)return!1;var o=n.constructor,a=t.constructor;if(o!==a&&!(m.isFunction(o)&&o instanceof o&&m.isFunction(a)&&a instanceof a)&&"constructor"in n&&"constructor"in t)return!1}r=r||[],e=e||[];for(var c=r.length;c--;)if(r[c]===n)return e[c]===t;if(r.push(n),e.push(t),i){if(c=n.length,c!==t.length)return!1;for(;c--;)if(!N(n[c],t[c],r,e))return!1}else{var f,l=m.keys(n);if(c=l.length,m.keys(t).length!==c)return!1;for(;c--;)if(f=l[c],!m.has(t,f)||!N(n[f],t[f],r,e))return!1}return r.pop(),e.pop(),!0};m.isEqual=function(n,t){return N(n,t)},m.isEmpty=function(n){return null==n?!0:k(n)&&(m.isArray(n)||m.isString(n)||m.isArguments(n))?0===n.length:0===m.keys(n).length},m.isElement=function(n){return!(!n||1!==n.nodeType)},m.isArray=h||function(n){return"[object Array]"===s.call(n)},m.isObject=function(n){var t=typeof n;return"function"===t||"object"===t&&!!n},m.each(["Arguments","Function","String","Number","Date","RegExp","Error"],function(n){m["is"+n]=function(t){return s.call(t)==="[object "+n+"]"}}),m.isArguments(arguments)||(m.isArguments=function(n){return m.has(n,"callee")}),"function"!=typeof/./&&"object"!=typeof Int8Array&&(m.isFunction=function(n){return"function"==typeof n||!1}),m.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},m.isNaN=function(n){return m.isNumber(n)&&n!==+n},m.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"===s.call(n)},m.isNull=function(n){return null===n},m.isUndefined=function(n){return n===void 0},m.has=function(n,t){return null!=n&&p.call(n,t)},m.noConflict=function(){return u._=i,this},m.identity=function(n){return n},m.constant=function(n){return function(){return n}},m.noop=function(){},m.property=w,m.propertyOf=function(n){return null==n?function(){}:function(t){return n[t]}},m.matcher=m.matches=function(n){return n=m.extendOwn({},n),function(t){return m.isMatch(t,n)}},m.times=function(n,t,r){var e=Array(Math.max(0,n));t=b(t,r,1);for(var u=0;n>u;u++)e[u]=t(u);return e},m.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},m.now=Date.now||function(){return(new Date).getTime()};var B={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},T=m.invert(B),R=function(n){var t=function(t){return n[t]},r="(?:"+m.keys(n).join("|")+")",e=RegExp(r),u=RegExp(r,"g");return function(n){return n=null==n?"":""+n,e.test(n)?n.replace(u,t):n}};m.escape=R(B),m.unescape=R(T),m.result=function(n,t,r){var e=null==n?void 0:n[t];return e===void 0&&(e=r),m.isFunction(e)?e.call(n):e};var q=0;m.uniqueId=function(n){var t=++q+"";return n?n+t:t},m.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var K=/(.)^/,z={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\u2028|\u2029/g,L=function(n){return"\\"+z[n]};m.template=function(n,t,r){!t&&r&&(t=r),t=m.defaults({},t,m.templateSettings);var e=RegExp([(t.escape||K).source,(t.interpolate||K).source,(t.evaluate||K).source].join("|")+"|$","g"),u=0,i="__p+='";n.replace(e,function(t,r,e,o,a){return i+=n.slice(u,a).replace(D,L),u=a+t.length,r?i+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'":e?i+="'+\n((__t=("+e+"))==null?'':__t)+\n'":o&&(i+="';\n"+o+"\n__p+='"),t}),i+="';\n",t.variable||(i="with(obj||{}){\n"+i+"}\n"),i="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+i+"return __p;\n";try{var o=new Function(t.variable||"obj","_",i)}catch(a){throw a.source=i,a}var c=function(n){return o.call(this,n,m)},f=t.variable||"obj";return c.source="function("+f+"){\n"+i+"}",c},m.chain=function(n){var t=m(n);return t._chain=!0,t};var P=function(n,t){return n._chain?m(t).chain():t};m.mixin=function(n){m.each(m.functions(n),function(t){var r=m[t]=n[t];m.prototype[t]=function(){var n=[this._wrapped];return f.apply(n,arguments),P(this,r.apply(m,n))}})},m.mixin(m),m.each(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=o[n];m.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!==n&&"splice"!==n||0!==r.length||delete r[0],P(this,r)}}),m.each(["concat","join","slice"],function(n){var t=o[n];m.prototype[n]=function(){return P(this,t.apply(this._wrapped,arguments))}}),m.prototype.value=function(){return this._wrapped},m.prototype.valueOf=m.prototype.toJSON=m.prototype.value,m.prototype.toString=function(){return""+this._wrapped},"function"==typeof define&&define.amd&&define("underscore",[],function(){return m})}).call(this);
+//# sourceMappingURL=underscore-min.map
\ No newline at end of file
diff --git a/examples/sprayPaintSpawner.js b/examples/sprayPaintSpawner.js
deleted file mode 100644
index 3b9cee6ef4..0000000000
--- a/examples/sprayPaintSpawner.js
+++ /dev/null
@@ -1,41 +0,0 @@
-// sprayPaintSpawner.js
-//
-// Created by Eric Levin on 9/3/15
-// Copyright 2015 High Fidelity, Inc.
-//
-// This is script spwans a spreay paint can model with the sprayPaintCan.js entity script attached
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-
-//Just temporarily using my own bucket here so others can test the entity. Once PR is tested and merged, then the entity script will appear in its proper place in S3, and I wil switch it
-// var scriptURL = "https://hifi-public.s3.amazonaws.com/eric/scripts/sprayPaintCan.js?=v6 ";
-var scriptURL = Script.resolvePath("entityScripts/sprayPaintCan.js?v2");
-var modelURL = "https://hifi-public.s3.amazonaws.com/eric/models/paintcan.fbx";
-
-var sprayCan = Entities.addEntity({
- type: "Model",
- name: "spraycan",
- modelURL: modelURL,
- position: {x: 549.12, y:495.55, z:503.77},
- rotation: {x: 0, y: 0, z: 0, w: 1},
- dimensions: {
- x: 0.07,
- y: 0.17,
- z: 0.07
- },
- collisionsWillMove: true,
- shapeType: 'box',
- script: scriptURL,
- gravity: {x: 0, y: -0.5, z: 0},
- velocity: {x: 0, y: -1, z: 0}
-});
-
-function cleanup() {
-
- // Uncomment the below line to delete sprayCan on script reload- for faster iteration during development
- // Entities.deleteEntity(sprayCan);
-}
-
-Script.scriptEnding.connect(cleanup);
-
diff --git a/examples/tests/dot.png b/examples/tests/dot.png
new file mode 100644
index 0000000000..4ccfdc80df
Binary files /dev/null and b/examples/tests/dot.png differ
diff --git a/examples/tests/overlayMouseTrackingTest.js b/examples/tests/overlayMouseTrackingTest.js
new file mode 100644
index 0000000000..d18fcfb599
--- /dev/null
+++ b/examples/tests/overlayMouseTrackingTest.js
@@ -0,0 +1,46 @@
+MouseTracker = function() {
+ this.WIDTH = 60;
+ this.HEIGHT = 60;
+
+ this.overlay = Overlays.addOverlay("image", {
+ imageURL: Script.resolvePath("dot.png"),
+ width: this.HEIGHT,
+ height: this.WIDTH,
+ x: 100,
+ y: 100,
+ visible: true
+ });
+
+ var that = this;
+ Script.scriptEnding.connect(function() {
+ that.onCleanup();
+ });
+
+ Controller.mousePressEvent.connect(function(event) {
+ that.onMousePress(event);
+ });
+
+ Controller.mouseMoveEvent.connect(function(event) {
+ that.onMouseMove(event);
+ });
+}
+
+MouseTracker.prototype.onCleanup = function() {
+ Overlays.deleteOverlay(this.overlay);
+}
+
+MouseTracker.prototype.onMousePress = function(event) {
+}
+
+MouseTracker.prototype.onMouseMove = function(event) {
+ var width = Overlays.width();
+ var height = Overlays.height();
+ var x = Math.max(event.x, 0);
+ x = Math.min(x, width);
+ var y = Math.max(event.y, 0);
+ y = Math.min(y, height);
+ Overlays.editOverlay(this.overlay, {x: x - this.WIDTH / 2.0, y: y - this.HEIGHT / 2.0});
+}
+
+
+new MouseTracker();
\ No newline at end of file
diff --git a/examples/toys/basketball_hoop/createHoop.js b/examples/toys/basketball_hoop/createHoop.js
new file mode 100644
index 0000000000..3887e0b421
--- /dev/null
+++ b/examples/toys/basketball_hoop/createHoop.js
@@ -0,0 +1,43 @@
+//
+// createHoop.js
+// examples/entityScripts
+//
+// Created by James B. Pollack on 9/29/2015
+// Copyright 2015 High Fidelity, Inc.
+//
+// This is a script that creates a persistent basketball hoop with a working collision hull. Feel free to move it.
+// Run basketball.js to make a basketball.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
+
+var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx";
+var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj";
+
+var hoopStartPosition =
+ Vec3.sum(MyAvatar.position,
+ Vec3.multiplyQbyV(MyAvatar.orientation, {
+ x: 0,
+ y: 0.0,
+ z: -2
+ }));
+
+var hoop = Entities.addEntity({
+ type: "Model",
+ modelURL: hoopURL,
+ position: hoopStartPosition,
+ shapeType: 'compound',
+ gravity: {
+ x: 0,
+ y: -9.8,
+ z: 0
+ },
+ dimensions: {
+ x: 1.89,
+ y: 3.99,
+ z: 3.79
+ },
+ compoundShapeURL: hoopCollisionHullURL
+});
+
diff --git a/examples/toys/cat.js b/examples/toys/cat.js
new file mode 100644
index 0000000000..103752bb1b
--- /dev/null
+++ b/examples/toys/cat.js
@@ -0,0 +1,52 @@
+//
+// cat.js
+// examples/toybox/entityScripts
+//
+// Created by Eric Levin on 9/21/15.
+// 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
+/*global Cat, print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
+
+
+(function() {
+
+ var _this;
+ Cat = function() {
+ _this = this;
+ this.meowSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Animals/cat_meow.wav");
+ };
+
+ Cat.prototype = {
+ isMeowing: false,
+ injector: null,
+ startTouch: function() {
+ if (this.isMeowing !== true) {
+ this.meow();
+ this.isMeowing = true;
+ }
+
+ },
+ meow: function() {
+ this.injector = Audio.playSound(this.meowSound, {
+ position: this.position,
+ volume: 0.1
+ });
+ Script.setTimeout(function() {
+ _this.isMeowing = false;
+ }, this.soundClipTime);
+ },
+
+ preload: function(entityID) {
+ this.entityID = entityID;
+ this.position = Entities.getEntityProperties(this.entityID, "position").position;
+ this.soundClipTime = 700;
+ },
+
+ };
+
+ // entity scripts always need to return a newly constructed object of our type
+ return new Cat();
+});
\ No newline at end of file
diff --git a/examples/toys/doll/doll.js b/examples/toys/doll/doll.js
index f97b6de378..19a7dacda9 100644
--- a/examples/toys/doll/doll.js
+++ b/examples/toys/doll/doll.js
@@ -15,21 +15,14 @@
(function() {
Script.include("../../utilities.js");
Script.include("../../libraries/utils.js");
-
+ var _this;
// this is the "constructor" for the entity as a JS object we don't do much here
var Doll = function() {
+ _this = this;
this.screamSounds = [SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/KenDoll_1%2303.wav")];
};
Doll.prototype = {
- startAnimationSetting: JSON.stringify({
- running: true,
- fps: 30,
- startFrame: 0,
- lastFrame: 128,
- startAutomatically: true
- }),
- stopAnimationSetting: JSON.stringify({running: false}),
audioInjector: null,
isGrabbed: false,
setLeftHand: function() {
@@ -41,26 +34,27 @@
},
startNearGrab: function() {
- if (this.isGrabbed === false) {
+ Entities.editEntity(this.entityID, {
+ animationURL: "https://hifi-public.s3.amazonaws.com/models/Bboys/zombie_scream.fbx",
+ animationFrameIndex: 0
+ });
- Entities.editEntity(this.entityID, {
- animationURL: "https://hifi-public.s3.amazonaws.com/models/Bboys/zombie_scream.fbx",
- animationSettings: this.startAnimationSetting
- });
+ Entities.editEntity(_this.entityID, {
+ animationIsPlaying: true
+ });
- var position = Entities.getEntityProperties(this.entityID, "position").position;
- this.audioInjector = Audio.playSound(this.screamSounds[randInt(0, this.screamSounds.length)], {
- position: position,
- volume: 0.1
- });
+ var position = Entities.getEntityProperties(this.entityID, "position").position;
+ this.audioInjector = Audio.playSound(this.screamSounds[randInt(0, this.screamSounds.length)], {
+ position: position,
+ volume: 0.1
+ });
- this.isGrabbed = true;
- this.initialHand = this.hand;
- }
+ this.isGrabbed = true;
+ this.initialHand = this.hand;
},
continueNearGrab: function() {
- var props = Entities.getEntityProperties(this.entityID, "position");
+ var props = Entities.getEntityProperties(this.entityID, ["position"]);
var audioOptions = {
position: props.position
};
@@ -69,24 +63,23 @@
releaseGrab: function() {
if (this.isGrabbed === true && this.hand === this.initialHand) {
-
this.audioInjector.stop();
+ Entities.editEntity(this.entityID, {
+ animationFrameIndex: 0
+ });
Entities.editEntity(this.entityID, {
- animationSettings: this.stopAnimationSetting,
- animationURL: "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx",
+ animationURL: "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx"
});
+
this.isGrabbed = false;
}
},
preload: function(entityID) {
- // preload() will be called when the entity has become visible (or known) to the interface
- // it gives us a chance to set our local JavaScript object up. In this case it means:
- // * remembering our entityID, so we can access it in cases where we're called without an entityID
this.entityID = entityID;
},
};
// entity scripts always need to return a newly constructed object of our type
return new Doll();
-});
\ No newline at end of file
+});
diff --git a/examples/toys/lightSwitchGarage.js b/examples/toys/lightSwitchGarage.js
new file mode 100644
index 0000000000..19d33117df
--- /dev/null
+++ b/examples/toys/lightSwitchGarage.js
@@ -0,0 +1,202 @@
+//
+// lightSwitchGarage.js.js
+// examples/entityScripts
+//
+// Created by Eric Levin on 9/21/15.
+// 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
+//
+
+(function() {
+
+ var _this;
+
+ // this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
+ // our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
+ LightSwitchGarage = function() {
+ _this = this;
+
+ this.lightStateKey = "lightStateKey";
+ this.resetKey = "resetMe";
+
+ this.switchSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/lamp_switch_2.wav");
+
+ };
+
+ LightSwitchGarage.prototype = {
+
+ clickReleaseOnEntity: function(entityID, mouseEvent) {
+ if (!mouseEvent.isLeftButton) {
+ return;
+ }
+ this.toggleLights();
+ },
+
+ startNearGrabNonColliding: function() {
+ this.toggleLights();
+ },
+
+ toggleLights: function() {
+ var defaultLightData = {
+ on: false
+ };
+ var lightState = getEntityCustomData(this.lightStateKey, this.entityID, defaultLightData);
+ if (lightState.on === true) {
+ this.clearLights();
+ } else if (lightState.on === false) {
+ this.createLights();
+ }
+
+ this.flipLights();
+
+ Audio.playSound(this.switchSound, {
+ volume: 0.5,
+ position: this.position
+ });
+
+ },
+
+ clearLights: function() {
+ var entities = Entities.findEntities(MyAvatar.position, 100);
+ var self = this;
+ entities.forEach(function(entity) {
+ var resetData = getEntityCustomData(self.resetKey, entity, {})
+ if (resetData.resetMe === true && resetData.lightType === "Sconce Light Garage") {
+ Entities.deleteEntity(entity);
+ }
+ });
+
+ setEntityCustomData(this.lightStateKey, this.entityID, {
+ on: false
+ });
+ },
+
+ createLights: function() {
+
+ var sconceLight3 = Entities.addEntity({
+ type: "Light",
+ position: {
+ x: 545.49468994140625,
+ y: 496.24026489257812,
+ z: 500.63516235351562
+ },
+
+ name: "Sconce 3 Light",
+ dimensions: {
+ x: 2.545,
+ y: 2.545,
+ z: 2.545
+ },
+ cutoff: 90,
+ color: {
+ red: 217,
+ green: 146,
+ blue: 24
+ }
+ });
+
+ setEntityCustomData(this.resetKey, sconceLight3, {
+ resetMe: true,
+ lightType: "Sconce Light Garage"
+ });
+
+ var sconceLight4 = Entities.addEntity({
+ type: "Light",
+ position: {
+ x: 550.90399169921875,
+ y: 496.24026489257812,
+ z: 507.90237426757812
+ },
+ name: "Sconce 4 Light",
+ dimensions: {
+ x: 2.545,
+ y: 2.545,
+ z: 2.545
+ },
+ cutoff: 90,
+ color: {
+ red: 217,
+ green: 146,
+ blue: 24
+ }
+ });
+
+ setEntityCustomData(this.resetKey, sconceLight4, {
+ resetMe: true,
+ lightType: "Sconce Light Garage"
+ });
+
+ var sconceLight5 = Entities.addEntity({
+ type: "Light",
+ position: {
+ x: 548.407958984375,
+ y: 496.24026489257812,
+ z: 509.5504150390625
+ },
+ name: "Sconce 5 Light",
+ dimensions: {
+ x: 2.545,
+ y: 2.545,
+ z: 2.545
+ },
+ cutoff: 90,
+ color: {
+ red: 217,
+ green: 146,
+ blue: 24
+ }
+ });
+
+ setEntityCustomData(this.resetKey, sconceLight5, {
+ resetMe: true,
+ lightType: "Sconce Light Garage"
+ });
+
+ setEntityCustomData(this.lightStateKey, this.entityID, {
+ on: true
+ });
+ },
+
+ flipLights: function() {
+ // flip model to give illusion of light switch being flicked
+ var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation;
+ var axis = {
+ x: 0,
+ y: 1,
+ z: 0
+ };
+ var dQ = Quat.angleAxis(180, axis);
+ rotation = Quat.multiply(rotation, dQ);
+
+
+ Entities.editEntity(this.entityID, {
+ rotation: rotation
+ });
+
+ },
+
+ preload: function(entityID) {
+ this.entityID = entityID;
+
+ //The light switch is static, so just cache its position once
+ this.position = Entities.getEntityProperties(this.entityID, "position").position;
+ var defaultLightData = {
+ on: false
+ };
+ var lightState = getEntityCustomData(this.lightStateKey, this.entityID, defaultLightData);
+
+ //If light is off, then we create two new lights- at the position of the sconces
+ if (lightState.on === false) {
+ this.createLights();
+ this.flipLights();
+ }
+ //If lights are on, do nothing!
+ },
+ };
+
+ // entity scripts always need to return a newly constructed object of our type
+ return new LightSwitchGarage();
+})
diff --git a/examples/toys/lightSwitchHall.js b/examples/toys/lightSwitchHall.js
new file mode 100644
index 0000000000..e1093311f9
--- /dev/null
+++ b/examples/toys/lightSwitchHall.js
@@ -0,0 +1,179 @@
+//
+// lightSwitchHall.js
+// examples/entityScripts
+//
+// Created by Eric Levin on 9/21/15.
+// 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
+//
+
+(function() {
+
+ var _this;
+
+
+ // this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
+ // our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
+ LightSwitchHall = function() {
+ _this = this;
+
+ this.lightStateKey = "lightStateKey";
+ this.resetKey = "resetMe";
+
+ this.switchSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/lamp_switch_2.wav");
+ };
+
+ LightSwitchHall.prototype = {
+
+ clickReleaseOnEntity: function(entityId, mouseEvent) {
+ if (!mouseEvent.isLeftButton) {
+ return;
+ }
+ this.toggleLights();
+ },
+
+ startNearGrabNonColliding: function() {
+ this.toggleLights();
+ },
+
+ toggleLights: function() {
+ var defaultLightData = {
+ on: false
+ };
+ var lightState = getEntityCustomData(this.lightStateKey, this.entityID, defaultLightData);
+ if (lightState.on === true) {
+ this.clearLights();
+ } else if (lightState.on === false) {
+ this.createLights();
+ }
+
+ // flip model to give illusion of light switch being flicked
+ this.flipLights();
+
+ Audio.playSound(this.switchSound, {
+ volume: 0.5,
+ position: this.position
+ });
+
+ },
+
+ clearLights: function() {
+ var entities = Entities.findEntities(MyAvatar.position, 100);
+ var self = this;
+ entities.forEach(function(entity) {
+ var resetData = getEntityCustomData(self.resetKey, entity, {})
+ if (resetData.resetMe === true && resetData.lightType === "Sconce Light Hall") {
+ Entities.deleteEntity(entity);
+ }
+ });
+
+ setEntityCustomData(this.lightStateKey, this.entityID, {
+ on: false
+ });
+ },
+
+ createLights: function() {
+ var sconceLight1 = Entities.addEntity({
+ type: "Light",
+ position: {
+ x: 543.75,
+ y: 496.24,
+ z: 511.13
+ },
+ name: "Sconce 1 Light",
+ dimensions: {
+ x: 2.545,
+ y: 2.545,
+ z: 2.545
+ },
+ cutoff: 90,
+ color: {
+ red: 217,
+ green: 146,
+ blue: 24
+ }
+ });
+
+ setEntityCustomData(this.resetKey, sconceLight1, {
+ resetMe: true,
+ lightType: "Sconce Light Hall"
+ });
+
+ var sconceLight2 = Entities.addEntity({
+ type: "Light",
+ position: {
+ x: 540.1,
+ y: 496.24,
+ z: 505.57
+ },
+ name: "Sconce 2 Light",
+ dimensions: {
+ x: 2.545,
+ y: 2.545,
+ z: 2.545
+ },
+ cutoff: 90,
+ color: {
+ red: 217,
+ green: 146,
+ blue: 24
+ }
+ });
+
+ setEntityCustomData(this.resetKey, sconceLight2, {
+ resetMe: true,
+ lightType: "Sconce Light Hall"
+ });
+
+ setEntityCustomData(this.lightStateKey, this.entityID, {
+ on: true
+ });
+
+ },
+
+ flipLights: function() {
+ // flip model to give illusion of light switch being flicked
+ var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation;
+ var axis = {
+ x: 0,
+ y: 1,
+ z: 0
+ };
+ var dQ = Quat.angleAxis(180, axis);
+ rotation = Quat.multiply(rotation, dQ);
+
+
+ Entities.editEntity(this.entityID, {
+ rotation: rotation
+ });
+
+ },
+
+ // preload() will be called when the entity has become visible (or known) to the interface
+ // it gives us a chance to set our local JavaScript object up. In this case it means:
+ preload: function(entityID) {
+ this.entityID = entityID;
+
+ //The light switch is static, so just cache its position once
+ this.position = Entities.getEntityProperties(this.entityID, "position").position;
+ var defaultLightData = {
+ on: false
+ };
+ var lightState = getEntityCustomData(this.lightStateKey, this.entityID, defaultLightData);
+
+ //If light is off, then we create two new lights- at the position of the sconces
+ if (lightState.on === false) {
+ this.createLights();
+ this.flipLights();
+
+ }
+ //If lights are on, do nothing!
+ },
+ };
+
+ // entity scripts always need to return a newly constructed object of our type
+ return new LightSwitchHall();
+})
diff --git a/examples/toys/ping_pong_gun/createPingPongGun.js b/examples/toys/ping_pong_gun/createPingPongGun.js
new file mode 100644
index 0000000000..4b7ed27643
--- /dev/null
+++ b/examples/toys/ping_pong_gun/createPingPongGun.js
@@ -0,0 +1,43 @@
+// createPingPongGun.js
+//
+// Script Type: Entity Spawner
+// Created by James B. Pollack on 9/30/2015
+// Copyright 2015 High Fidelity, Inc.
+//
+// This script creates a gun that shoots ping pong balls when you pull the trigger on a hand controller.
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
+Script.include("../../utilities.js");
+
+var scriptURL = Script.resolvePath('pingPongGun.js');
+
+var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx'
+var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj';
+
+var center = Vec3.sum(Vec3.sum(MyAvatar.position, {
+ x: 0,
+ y: 0.5,
+ z: 0
+}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
+
+var pingPongGun = Entities.addEntity({
+ type: "Model",
+ modelURL: MODEL_URL,
+ shapeType: 'compound',
+ compoundShapeURL: COLLISION_HULL_URL,
+ script: scriptURL,
+ position: center,
+ dimensions: {
+ x:0.67,
+ y: 0.14,
+ z: 0.09
+ },
+ collisionsWillMove: true,
+});
+
+function cleanUp() {
+ Entities.deleteEntity(pingPongGun);
+}
+Script.scriptEnding.connect(cleanUp);
diff --git a/examples/toys/ping_pong_gun/pingPongGun.js b/examples/toys/ping_pong_gun/pingPongGun.js
new file mode 100644
index 0000000000..a980fc1bd3
--- /dev/null
+++ b/examples/toys/ping_pong_gun/pingPongGun.js
@@ -0,0 +1,160 @@
+// pingPongGun.js
+//
+// Script Type: Entity
+// Created by James B. Pollack @imgntn on 9/21/2015
+// Copyright 2015 High Fidelity, Inc.
+//
+// This script shoots a ping pong ball.
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
+(function() {
+
+ Script.include("../../libraries/utils.js");
+
+ var SHOOTING_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/ping_pong_gun/pong_sound.wav';
+
+ function PingPongGun() {
+ return;
+ }
+
+ //if the trigger value goes below this value, reload the gun.
+ var RELOAD_THRESHOLD = 0.95;
+ var GUN_TIP_FWD_OFFSET = 0.45;
+ var GUN_TIP_UP_OFFSET = 0.040;
+ var GUN_FORCE = 15;
+ var BALL_RESTITUTION = 0.6;
+ var BALL_LINEAR_DAMPING = 0.4;
+ var BALL_GRAVITY = {
+ x: 0,
+ y: -9.8,
+ z: 0
+ };
+
+ var BALL_DIMENSIONS = {
+ x: 0.04,
+ y: 0.04,
+ z: 0.04
+ }
+
+
+ var BALL_COLOR = {
+ red: 255,
+ green: 255,
+ blue: 255
+ }
+
+ PingPongGun.prototype = {
+ hand: null,
+ whichHand: null,
+ gunTipPosition: null,
+ canShoot: false,
+ canShootTimeout: null,
+ setRightHand: function() {
+ this.hand = 'RIGHT';
+ },
+
+ setLeftHand: function() {
+ this.hand = 'LEFT';
+ },
+
+ startNearGrab: function() {
+ this.setWhichHand();
+ },
+
+ setWhichHand: function() {
+ this.whichHand = this.hand;
+ },
+
+ continueNearGrab: function() {
+
+ if (this.whichHand === null) {
+ //only set the active hand once -- if we always read the current hand, our 'holding' hand will get overwritten
+ this.setWhichHand();
+ } else {
+ if (this.canShootTimeout !== null) {
+ Script.clearTimeout(this.canShootTimeout);
+ }
+ this.checkTriggerPressure(this.whichHand);
+ }
+ },
+
+ releaseGrab: function() {
+ var _t = this;
+ this.canShootTimeout = Script.setTimeout(function() {
+ _t.canShoot = false;
+ }, 250)
+ },
+
+ checkTriggerPressure: function(gunHand) {
+ var handClickString = gunHand + "_HAND_CLICK";
+
+ var handClick = Controller.findAction(handClickString);
+
+ this.triggerValue = Controller.getActionValue(handClick);
+
+ if (this.triggerValue < RELOAD_THRESHOLD) {
+ // print('RELOAD');
+ this.canShoot = true;
+ } else if (this.triggerValue >= RELOAD_THRESHOLD && this.canShoot === true) {
+ var gunProperties = Entities.getEntityProperties(this.entityID, ["position", "rotation"]);
+ this.shootBall(gunProperties);
+ this.canShoot = false;
+ }
+ return;
+ },
+
+ shootBall: function(gunProperties) {
+ var forwardVec = Quat.getFront(Quat.multiply(gunProperties.rotation, Quat.fromPitchYawRollDegrees(0, -90, 0)));
+ forwardVec = Vec3.normalize(forwardVec);
+ forwardVec = Vec3.multiply(forwardVec, GUN_FORCE);
+ var properties = {
+ type: 'Sphere',
+ color: BALL_COLOR,
+ dimensions: BALL_DIMENSIONS,
+ linearDamping: BALL_LINEAR_DAMPING,
+ gravity: BALL_GRAVITY,
+ restitution: BALL_RESTITUTION,
+ collisionsWillMove: true,
+ rotation: gunProperties.rotation,
+ position: this.getGunTipPosition(gunProperties),
+ velocity: forwardVec,
+ lifetime: 10
+ };
+
+ Entities.addEntity(properties);
+
+ this.playSoundAtCurrentPosition(gunProperties.position);
+ },
+
+ playSoundAtCurrentPosition: function(position) {
+ var audioProperties = {
+ volume: 0.1,
+ position: position
+ };
+
+ Audio.playSound(this.SHOOTING_SOUND, audioProperties);
+ },
+
+ getGunTipPosition: function(properties) {
+ //the tip of the gun is going to be in a different place than the center, so we move in space relative to the model to find that position
+ var frontVector = Quat.getRight(properties.rotation);
+ var frontOffset = Vec3.multiply(frontVector, GUN_TIP_FWD_OFFSET);
+ var upVector = Quat.getRight(properties.rotation);
+ var upOffset = Vec3.multiply(upVector, GUN_TIP_UP_OFFSET);
+ var gunTipPosition = Vec3.sum(properties.position, frontOffset);
+ gunTipPosition = Vec3.sum(gunTipPosition, upOffset);
+ return gunTipPosition;
+ },
+
+ preload: function(entityID) {
+ this.entityID = entityID;
+ this.SHOOTING_SOUND = SoundCache.getSound(SHOOTING_SOUND_URL);
+ }
+
+ };
+
+ // entity scripts always need to return a newly constructed object of our type
+ return new PingPongGun();
+});
\ No newline at end of file
diff --git a/examples/toys/sprayPaintCan.js b/examples/toys/sprayPaintCan.js
new file mode 100644
index 0000000000..e0aeb19995
--- /dev/null
+++ b/examples/toys/sprayPaintCan.js
@@ -0,0 +1,162 @@
+//
+// sprayPaintCan.js
+// examples/entityScripts
+//
+// Created by Eric Levin on 9/21/15.
+// 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
+//
+
+
+(function() {
+ // Script.include("../libraries/utils.js");
+ //Need absolute path for now, for testing before PR merge and s3 cloning. Will change post-merge
+
+ Script.include("../libraries/utils.js");
+
+ this.spraySound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/sprayPaintSound.wav");
+
+ var TIP_OFFSET_Z = 0.02;
+ var TIP_OFFSET_Y = 0.08;
+
+ var ZERO_VEC = {
+ x: 0,
+ y: 0,
+ z: 0
+ };
+
+ // if the trigger value goes below this while held, the can will stop spraying. if it goes above, it will spray
+ var DISABLE_SPRAY_THRESHOLD = 0.5;
+
+ var MAX_POINTS_PER_LINE = 40;
+ var MIN_POINT_DISTANCE = 0.01;
+ var STROKE_WIDTH = 0.02;
+
+ this.setRightHand = function() {
+ this.hand = 'RIGHT';
+ }
+
+ this.setLeftHand = function() {
+ this.hand = 'LEFT';
+ }
+
+ this.startNearGrab = function() {
+ this.whichHand = this.hand;
+ }
+
+ this.toggleWithTriggerPressure = function() {
+ var handClickString = this.whichHand + "_HAND_CLICK";
+
+ var handClick = Controller.findAction(handClickString);
+
+ this.triggerValue = Controller.getActionValue(handClick);
+ if (this.triggerValue < DISABLE_SPRAY_THRESHOLD && this.spraying === true) {
+ this.spraying = false;
+ this.disableStream();
+ } else if (this.triggerValue >= DISABLE_SPRAY_THRESHOLD && this.spraying === false) {
+ this.spraying = true;
+ this.enableStream();
+ }
+ }
+
+ this.enableStream = function() {
+ var position = Entities.getEntityProperties(this.entityId, "position").position;
+ var animationSettings = JSON.stringify({
+ fps: 30,
+ loop: true,
+ firstFrame: 1,
+ lastFrame: 10000,
+ running: true
+ });
+ var PI = 3.141593;
+ var DEG_TO_RAD = PI / 180.0;
+
+ this.paintStream = Entities.addEntity({
+ type: "ParticleEffect",
+ name: "streamEffect",
+ animationSettings: animationSettings,
+ position: position,
+ textures: "https://raw.githubusercontent.com/ericrius1/SantasLair/santa/assets/smokeparticle.png",
+ emitSpeed: 3,
+ speedSpread: 0.02,
+ emitAcceleration: ZERO_VEC,
+ emitRate: 100,
+ particleRadius: 0.01,
+ radiusSpread: 0.005,
+ polarFinish: 0.05,
+ color: {
+ red: 170,
+ green: 20,
+ blue: 150
+ },
+ lifetime: 50, //probably wont be holding longer than this straight
+ });
+
+ setEntityCustomData(this.resetKey, this.paintStream, {
+ resetMe: true
+ });
+
+ this.sprayInjector = Audio.playSound(this.spraySound, {
+ position: position,
+ volume: this.sprayVolume,
+ loop: true
+ });
+
+ }
+
+ this.releaseGrab = function() {
+ this.disableStream();
+ }
+
+ this.disableStream = function() {
+ Entities.deleteEntity(this.paintStream);
+ this.paintStream = null;
+ this.spraying = false;
+ this.sprayInjector.stop();
+ }
+
+
+ this.continueNearGrab = function() {
+
+ this.toggleWithTriggerPressure();
+
+ if (this.spraying === false) {
+ return;
+ }
+
+ var props = Entities.getEntityProperties(this.entityId, ["position, rotation"]);
+ var forwardVec = Quat.getFront(Quat.multiply(props.rotation, Quat.fromPitchYawRollDegrees(0, 90, 0)));
+ forwardVec = Vec3.normalize(forwardVec);
+ var forwardQuat = orientationOf(forwardVec);
+ var upVec = Quat.getUp(props.rotation);
+ var position = Vec3.sum(props.position, Vec3.multiply(forwardVec, TIP_OFFSET_Z));
+ position = Vec3.sum(position, Vec3.multiply(upVec, TIP_OFFSET_Y))
+ Entities.editEntity(this.paintStream, {
+ position: position,
+ emitOrientation: forwardQuat,
+ });
+
+ this.sprayInjector.setOptions({
+ position: position,
+ volume: this.sprayVolume
+ });
+ }
+
+ this.preload = function(entityId) {
+ this.sprayVolume = 0.1;
+ this.spraying = false;
+ this.entityId = entityId;
+ this.resetKey = "resetMe";
+ }
+
+
+ this.unload = function() {
+ if (this.paintStream) {
+ Entities.deleteEntity(this.paintStream);
+ }
+ this.strokes.forEach(function(stroke) {
+ Entities.deleteEntity(stroke);
+ });
+ }
+});
diff --git a/examples/utilities/tools/currentAPI.js b/examples/utilities/tools/currentAPI.js
index 30b24910f9..cb9f152794 100644
--- a/examples/utilities/tools/currentAPI.js
+++ b/examples/utilities/tools/currentAPI.js
@@ -10,22 +10,21 @@
//
var array = [];
-var buffer = "\n\n\n\n\n======= JS API list =======";
function listKeys(string, object) {
- if (string == "listKeys" || string == "array" || string == "buffer" || string == "i") {
+ if (string === "listKeys" || string === "array" || string === "buffer" || string === "i") {
return;
}
- if (typeof(object) != "object") {
+ if (typeof(object) !== "object" || object === null) {
array.push(string + " " + typeof(object));
return;
}
var keys = Object.keys(object);
for (var i = 0; i < keys.length; ++i) {
- if (string == "") {
+ if (string === "") {
listKeys(keys[i], object[keys[i]]);
- } else {
+ } else if (keys[i] !== "parent") {
listKeys(string + "." + keys[i], object[keys[i]]);
}
}
@@ -34,9 +33,10 @@ function listKeys(string, object) {
listKeys("", this);
array.sort();
+var buffer = "\n======= JS API list =======";
for (var i = 0; i < array.length; ++i) {
- buffer = buffer + "\n" + array[i];
+ buffer += "\n" + array[i];
}
-buffer = buffer + "\n========= API END =========\n\n\n\n\n";
+buffer += "\n========= API END =========\n";
print(buffer);
diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json
index 72eefaf7e8..dceecddfe0 100644
--- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json
+++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json
@@ -26,13 +26,14 @@
{
"jointName": "Neck",
"positionVar": "neckPosition",
- "rotationVar": "neckRotation"
+ "rotationVar": "neckRotation",
+ "typeVar": "headAndNeckType"
},
{
"jointName": "Head",
"positionVar": "headPosition",
"rotationVar": "headRotation",
- "typeVar": "headType"
+ "typeVar": "headAndNeckType"
}
]
},
diff --git a/interface/resources/qml/VrMenu.qml b/interface/resources/qml/VrMenu.qml
index ef7ae852d4..14a4a449fd 100644
--- a/interface/resources/qml/VrMenu.qml
+++ b/interface/resources/qml/VrMenu.qml
@@ -196,7 +196,6 @@ Hifi.VrMenu {
function insertItem(menu, beforeItem, newMenuItem) {
for (var i = 0; i < menu.items.length; ++i) {
- console.log(menu.items[i]);
if (menu.items[i] === beforeItem) {
return menu.insertItem(i, newMenuItem);
}
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 2a8f7cf8b5..5f127988e9 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -1849,8 +1849,16 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
_entities.mouseMoveEvent(event, deviceID);
+ {
+ auto offscreenUi = DependencyManager::get();
+ QPointF transformedPos = offscreenUi->mapToVirtualScreen(event->localPos(), _glWidget);
+ QMouseEvent mappedEvent(event->type(),
+ transformedPos,
+ event->screenPos(), event->button(),
+ event->buttons(), event->modifiers());
+ _controllerScriptingInterface.emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts
+ }
- _controllerScriptingInterface.emitMouseMoveEvent(event, deviceID); // send events to any registered scripts
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isMouseCaptured()) {
return;
@@ -1865,12 +1873,19 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
// Inhibit the menu if the user is using alt-mouse dragging
_altPressed = false;
-
if (!_aboutToQuit) {
_entities.mousePressEvent(event, deviceID);
}
- _controllerScriptingInterface.emitMousePressEvent(event); // send events to any registered scripts
+ {
+ auto offscreenUi = DependencyManager::get();
+ QPointF transformedPos = offscreenUi->mapToVirtualScreen(event->localPos(), _glWidget);
+ QMouseEvent mappedEvent(event->type(),
+ transformedPos,
+ event->screenPos(), event->button(),
+ event->buttons(), event->modifiers());
+ _controllerScriptingInterface.emitMousePressEvent(&mappedEvent); // send events to any registered scripts
+ }
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isMouseCaptured()) {
@@ -1921,7 +1936,15 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
_entities.mouseReleaseEvent(event, deviceID);
}
- _controllerScriptingInterface.emitMouseReleaseEvent(event); // send events to any registered scripts
+ {
+ auto offscreenUi = DependencyManager::get();
+ QPointF transformedPos = offscreenUi->mapToVirtualScreen(event->localPos(), _glWidget);
+ QMouseEvent mappedEvent(event->type(),
+ transformedPos,
+ event->screenPos(), event->button(),
+ event->buttons(), event->modifiers());
+ _controllerScriptingInterface.emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts
+ }
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isMouseCaptured()) {
diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp
index 4af1a26612..6145529b52 100644
--- a/interface/src/Stars.cpp
+++ b/interface/src/Stars.cpp
@@ -141,6 +141,7 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
auto state = gpu::StatePointer(new gpu::State());
// enable decal blend
state->setDepthTest(gpu::State::DepthTest(false));
+ state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
_gridPipeline.reset(gpu::Pipeline::create(program, state));
}
@@ -152,6 +153,7 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
auto state = gpu::StatePointer(new gpu::State());
// enable decal blend
state->setDepthTest(gpu::State::DepthTest(false));
+ state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
state->setAntialiasedLineEnable(true); // line smoothing also smooth points
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
_starsPipeline.reset(gpu::Pipeline::create(program, state));
diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp
index b1f6e6d8d1..7c2cf43c07 100644
--- a/interface/src/avatar/SkeletonModel.cpp
+++ b/interface/src/avatar/SkeletonModel.cpp
@@ -320,7 +320,7 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position)
float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f;
_rig->applyJointRotationDelta(jointIndex,
rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector),
- true, PALM_PRIORITY);
+ PALM_PRIORITY);
}
void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp
index bc786f3f4c..db3360cbbf 100644
--- a/interface/src/ui/overlays/Overlays.cpp
+++ b/interface/src/ui/overlays/Overlays.cpp
@@ -10,7 +10,7 @@
#include "Overlays.h"
-#include
+#include
#include
@@ -31,6 +31,7 @@
#include "TextOverlay.h"
#include "Text3DOverlay.h"
#include "Web3DOverlay.h"
+#include
Overlays::Overlays() : _nextOverlayID(1) {
@@ -331,10 +332,6 @@ void Overlays::setParentPanel(unsigned int childId, unsigned int panelId) {
unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) {
glm::vec2 pointCopy = point;
- if (qApp->isHMDMode()) {
- pointCopy = qApp->getApplicationCompositor().screenToOverlay(point);
- }
-
QReadLocker lock(&_lock);
if (!_enabled) {
return 0;
@@ -607,3 +604,13 @@ void Overlays::deletePanel(unsigned int panelId) {
bool Overlays::isAddedOverlay(unsigned int id) {
return _overlaysHUD.contains(id) || _overlaysWorld.contains(id);
}
+
+float Overlays::width() const {
+ auto offscreenUi = DependencyManager::get();
+ return offscreenUi->getWindow()->size().width();
+}
+
+float Overlays::height() const {
+ auto offscreenUi = DependencyManager::get();
+ return offscreenUi->getWindow()->size().height();
+}
\ No newline at end of file
diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h
index 5bd8098150..62f00b8989 100644
--- a/interface/src/ui/overlays/Overlays.h
+++ b/interface/src/ui/overlays/Overlays.h
@@ -113,6 +113,10 @@ public slots:
/// overlay; in meters if it is a 3D text overlay
QSizeF textSize(unsigned int id, const QString& text) const;
+ // Return the size of the virtual screen
+ float width() const;
+ float height() const;
+
/// adds a panel that has already been created
unsigned int addPanel(OverlayPanel::Pointer panel);
diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp
index de226092f1..c0adba0ad6 100644
--- a/libraries/animation/src/AnimInverseKinematics.cpp
+++ b/libraries/animation/src/AnimInverseKinematics.cpp
@@ -89,7 +89,7 @@ static int findRootJointInSkeleton(AnimSkeleton::ConstPointer skeleton, int inde
return rootIndex;
}
-void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector& targets) {
+void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector& targets, const AnimPoseVec& underPoses) {
// build a list of valid targets from _targetVarVec and animVars
_maxTargetIndex = -1;
bool removeUnfoundJoints = false;
@@ -107,7 +107,7 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::
}
} else {
IKTarget target;
- AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, _relativePoses);
+ AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, underPoses);
target.pose.trans = animVars.lookup(targetVar.positionVar, defaultPose.trans);
target.pose.rot = animVars.lookup(targetVar.rotationVar, defaultPose.rot);
target.setType(animVars.lookup(targetVar.typeVar, QString("")));
@@ -154,7 +154,6 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector& targets);
+ void computeTargets(const AnimVariantMap& animVars, std::vector& targets, const AnimPoseVec& underPoses);
void solveWithCyclicCoordinateDescent(const std::vector& targets);
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton);
diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h
index 700a8b4121..cb886cd369 100644
--- a/libraries/animation/src/AnimVariant.h
+++ b/libraries/animation/src/AnimVariant.h
@@ -184,6 +184,8 @@ public:
case AnimVariant::Type::String:
qCDebug(animation) << " " << pair.first << "=" << pair.second.getString();
break;
+ default:
+ assert("AnimVariant::Type" == "valid");
}
}
}
diff --git a/libraries/animation/src/AnimationHandle.cpp b/libraries/animation/src/AnimationHandle.cpp
index f2d12b398e..93dd965e02 100644
--- a/libraries/animation/src/AnimationHandle.cpp
+++ b/libraries/animation/src/AnimationHandle.cpp
@@ -181,7 +181,6 @@ void AnimationHandle::applyFrame(float frameIndex) {
ceilFrame.rotations.at(i),
frameFraction),
_priority,
- false,
_mix);
}
}
diff --git a/libraries/animation/src/JointState.cpp b/libraries/animation/src/JointState.cpp
index 9597a46726..0746f65e84 100644
--- a/libraries/animation/src/JointState.cpp
+++ b/libraries/animation/src/JointState.cpp
@@ -13,16 +13,11 @@
#include
-#include
#include
#include "JointState.h"
JointState::~JointState() {
- if (_constraint) {
- delete _constraint;
- _constraint = NULL;
- }
}
void JointState::copyState(const JointState& other) {
@@ -35,18 +30,12 @@ void JointState::copyState(const JointState& other) {
_distanceToParent = other._distanceToParent;
_animationPriority = other._animationPriority;
- _visibleTransform = other._visibleTransform;
- _visibleRotation = extractRotation(_visibleTransform);
- _visibleRotationInConstrainedFrame = other._visibleRotationInConstrainedFrame;
- // DO NOT copy _constraint
_name = other._name;
_isFree = other._isFree;
_parentIndex = other._parentIndex;
_defaultRotation = other._defaultRotation;
_inverseDefaultRotation = other._inverseDefaultRotation;
_translation = other._translation;
- _rotationMin = other._rotationMin;
- _rotationMax = other._rotationMax;
_preRotation = other._preRotation;
_postRotation = other._postRotation;
_preTransform = other._preTransform;
@@ -61,8 +50,6 @@ JointState::JointState(const FBXJoint& joint) {
_translation = joint.translation;
_defaultRotation = joint.rotation;
_inverseDefaultRotation = joint.inverseDefaultRotation;
- _rotationMin = joint.rotationMin;
- _rotationMax = joint.rotationMax;
_preRotation = joint.preRotation;
_postRotation = joint.postRotation;
_preTransform = joint.preTransform;
@@ -71,15 +58,6 @@ JointState::JointState(const FBXJoint& joint) {
}
void JointState::buildConstraint() {
- if (_constraint) {
- delete _constraint;
- _constraint = NULL;
- }
- if (glm::distance2(glm::vec3(-PI), _rotationMin) > EPSILON ||
- glm::distance2(glm::vec3(PI), _rotationMax) > EPSILON ) {
- // this joint has rotation constraints
- _constraint = AngularConstraint::newAngularConstraint(_rotationMin, _rotationMax);
- }
}
glm::quat JointState::getRotation() const {
@@ -113,13 +91,6 @@ void JointState::computeTransform(const glm::mat4& parentTransform, bool parentT
}
}
-void JointState::computeVisibleTransform(const glm::mat4& parentTransform) {
- glm::quat rotationInParentFrame = _preRotation * _visibleRotationInConstrainedFrame * _postRotation;
- glm::mat4 transformInParentFrame = _preTransform * glm::mat4_cast(rotationInParentFrame) * _postTransform;
- _visibleTransform = parentTransform * glm::translate(_translation) * transformInParentFrame;
- _visibleRotation = extractRotation(_visibleTransform);
-}
-
glm::quat JointState::getRotationInBindFrame() const {
return getRotation() * _inverseBindRotation;
}
@@ -128,10 +99,6 @@ glm::quat JointState::getRotationInParentFrame() const {
return _preRotation * _rotationInConstrainedFrame * _postRotation;
}
-glm::quat JointState::getVisibleRotationInParentFrame() const {
- return _preRotation * _visibleRotationInConstrainedFrame * _postRotation;
-}
-
void JointState::restoreRotation(float fraction, float priority) {
if (priority == _animationPriority || _animationPriority == 0.0f) {
setRotationInConstrainedFrameInternal(safeMix(_rotationInConstrainedFrame, _defaultRotation, fraction));
@@ -139,19 +106,16 @@ void JointState::restoreRotation(float fraction, float priority) {
}
}
-void JointState::setRotationInBindFrame(const glm::quat& rotation, float priority, bool constrain) {
+void JointState::setRotationInBindFrame(const glm::quat& rotation, float priority) {
// rotation is from bind- to model-frame
if (priority >= _animationPriority) {
glm::quat targetRotation = _rotationInConstrainedFrame * glm::inverse(getRotation()) * rotation * glm::inverse(_inverseBindRotation);
- if (constrain && _constraint) {
- _constraint->softClamp(targetRotation, _rotationInConstrainedFrame, 0.5f);
- }
setRotationInConstrainedFrameInternal(targetRotation);
_animationPriority = priority;
}
}
-void JointState::setRotationInModelFrame(const glm::quat& rotationInModelFrame, float priority, bool constrain) {
+void JointState::setRotationInModelFrame(const glm::quat& rotationInModelFrame, float priority) {
// rotation is from bind- to model-frame
if (priority >= _animationPriority) {
glm::quat parentRotation = computeParentRotation();
@@ -160,9 +124,6 @@ void JointState::setRotationInModelFrame(const glm::quat& rotationInModelFrame,
// R' = Rp * Rpre * r' * Rpost
// r' = (Rp * Rpre)^ * R' * Rpost^
glm::quat targetRotation = glm::inverse(parentRotation * _preRotation) * rotationInModelFrame * glm::inverse(_postRotation);
- if (constrain && _constraint) {
- _constraint->softClamp(targetRotation, _rotationInConstrainedFrame, 0.5f);
- }
_rotationInConstrainedFrame = glm::normalize(targetRotation);
_transformChanged = true;
_animationPriority = priority;
@@ -174,26 +135,15 @@ void JointState::clearTransformTranslation() {
_transform[3][1] = 0.0f;
_transform[3][2] = 0.0f;
_transformChanged = true;
- _visibleTransform[3][0] = 0.0f;
- _visibleTransform[3][1] = 0.0f;
- _visibleTransform[3][2] = 0.0f;
}
-void JointState::applyRotationDelta(const glm::quat& delta, bool constrain, float priority) {
+void JointState::applyRotationDelta(const glm::quat& delta, float priority) {
// NOTE: delta is in model-frame
if (priority < _animationPriority || delta == glm::quat()) {
return;
}
_animationPriority = priority;
glm::quat targetRotation = _rotationInConstrainedFrame * glm::inverse(getRotation()) * delta * getRotation();
- if (!constrain || _constraint == NULL) {
- // no constraints
- _rotationInConstrainedFrame = targetRotation;
- _transformChanged = true;
-
- _rotation = delta * getRotation();
- return;
- }
setRotationInConstrainedFrameInternal(targetRotation);
}
@@ -209,36 +159,17 @@ void JointState::mixRotationDelta(const glm::quat& delta, float mixFactor, float
if (mixFactor > 0.0f && mixFactor <= 1.0f) {
targetRotation = safeMix(targetRotation, _defaultRotation, mixFactor);
}
- if (_constraint) {
- _constraint->softClamp(targetRotation, _rotationInConstrainedFrame, 0.5f);
- }
setRotationInConstrainedFrameInternal(targetRotation);
}
-void JointState::mixVisibleRotationDelta(const glm::quat& delta, float mixFactor) {
- // NOTE: delta is in model-frame
- glm::quat targetRotation = _visibleRotationInConstrainedFrame * glm::inverse(_visibleRotation) * delta * _visibleRotation;
- if (mixFactor > 0.0f && mixFactor <= 1.0f) {
- targetRotation = safeMix(targetRotation, _rotationInConstrainedFrame, mixFactor);
- }
- setVisibleRotationInConstrainedFrame(targetRotation);
-}
-
glm::quat JointState::computeParentRotation() const {
// R = Rp * Rpre * r * Rpost
// Rp = R * (Rpre * r * Rpost)^
return getRotation() * glm::inverse(_preRotation * _rotationInConstrainedFrame * _postRotation);
}
-glm::quat JointState::computeVisibleParentRotation() const {
- return _visibleRotation * glm::inverse(_preRotation * _visibleRotationInConstrainedFrame * _postRotation);
-}
-
-void JointState::setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain, float mix) {
+void JointState::setRotationInConstrainedFrame(glm::quat targetRotation, float priority, float mix) {
if (priority >= _animationPriority || _animationPriority == 0.0f) {
- if (constrain && _constraint) {
- _constraint->softClamp(targetRotation, _rotationInConstrainedFrame, 0.5f);
- }
auto rotation = (mix == 1.0f) ? targetRotation : safeMix(getRotationInConstrainedFrame(), targetRotation, mix);
setRotationInConstrainedFrameInternal(rotation);
_animationPriority = priority;
@@ -255,12 +186,6 @@ void JointState::setRotationInConstrainedFrameInternal(const glm::quat& targetRo
}
}
-void JointState::setVisibleRotationInConstrainedFrame(const glm::quat& targetRotation) {
- glm::quat parentRotation = computeVisibleParentRotation();
- _visibleRotationInConstrainedFrame = targetRotation;
- _visibleRotation = parentRotation * _preRotation * _visibleRotationInConstrainedFrame * _postRotation;
-}
-
bool JointState::rotationIsDefault(const glm::quat& rotation, float tolerance) const {
glm::quat defaultRotation = _defaultRotation;
return glm::abs(rotation.x - defaultRotation.x) < tolerance &&
@@ -277,9 +202,3 @@ glm::quat JointState::getDefaultRotationInParentFrame() const {
const glm::vec3& JointState::getDefaultTranslationInConstrainedFrame() const {
return _translation;
}
-
-void JointState::slaveVisibleTransform() {
- _visibleTransform = _transform;
- _visibleRotation = getRotation();
- _visibleRotationInConstrainedFrame = _rotationInConstrainedFrame;
-}
diff --git a/libraries/animation/src/JointState.h b/libraries/animation/src/JointState.h
index 07ed010104..eee8863e6f 100644
--- a/libraries/animation/src/JointState.h
+++ b/libraries/animation/src/JointState.h
@@ -22,8 +22,6 @@
const float DEFAULT_PRIORITY = 3.0f;
-class AngularConstraint;
-
class JointState {
public:
JointState() {}
@@ -39,11 +37,6 @@ public:
// but _rotation will be asynchronously extracted
void computeTransform(const glm::mat4& parentTransform, bool parentTransformChanged = true, bool synchronousRotationCompute = false);
- void computeVisibleTransform(const glm::mat4& parentTransform);
- const glm::mat4& getVisibleTransform() const { return _visibleTransform; }
- glm::quat getVisibleRotation() const { return _visibleRotation; }
- glm::vec3 getVisiblePosition() const { return extractTranslation(_visibleTransform); }
-
const glm::mat4& getTransform() const { return _transform; }
void resetTransformChanged() { _transformChanged = false; }
bool getTransformChanged() const { return _transformChanged; }
@@ -55,14 +48,13 @@ public:
glm::quat getRotationInBindFrame() const;
glm::quat getRotationInParentFrame() const;
- glm::quat getVisibleRotationInParentFrame() const;
const glm::vec3& getPositionInParentFrame() const { return _positionInParentFrame; }
float getDistanceToParent() const { return _distanceToParent; }
int getParentIndex() const { return _parentIndex; }
/// \param delta is in the model-frame
- void applyRotationDelta(const glm::quat& delta, bool constrain = true, float priority = 1.0f);
+ void applyRotationDelta(const glm::quat& delta, float priority = 1.0f);
/// Applies delta rotation to joint but mixes a little bit of the default pose as well.
/// This helps keep an IK solution stable.
@@ -70,7 +62,6 @@ public:
/// \param mixFactor fraction in range [0,1] of how much default pose to blend in (0 is none, 1 is all)
/// \param priority priority level of this animation blend
void mixRotationDelta(const glm::quat& delta, float mixFactor, float priority = 1.0f);
- void mixVisibleRotationDelta(const glm::quat& delta, float mixFactor);
/// Blends a fraciton of default pose into joint rotation.
/// \param fraction fraction in range [0,1] of how much default pose to blend in (0 is none, 1 is all)
@@ -80,17 +71,15 @@ public:
/// \param rotation is from bind- to model-frame
/// computes and sets new _rotationInConstrainedFrame
/// NOTE: the JointState's model-frame transform/rotation are NOT updated!
- void setRotationInBindFrame(const glm::quat& rotation, float priority, bool constrain = false);
+ void setRotationInBindFrame(const glm::quat& rotation, float priority);
/// \param rotationInModelRame is in model-frame
/// computes and sets new _rotationInConstrainedFrame to match rotationInModelFrame
/// NOTE: the JointState's model-frame transform/rotation are NOT updated!
- void setRotationInModelFrame(const glm::quat& rotationInModelFrame, float priority, bool constrain);
+ void setRotationInModelFrame(const glm::quat& rotationInModelFrame, float priority);
- void setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain = false, float mix = 1.0f);
- void setVisibleRotationInConstrainedFrame(const glm::quat& targetRotation);
+ void setRotationInConstrainedFrame(glm::quat targetRotation, float priority, float mix = 1.0f);
const glm::quat& getRotationInConstrainedFrame() const { return _rotationInConstrainedFrame; }
- const glm::quat& getVisibleRotationInConstrainedFrame() const { return _visibleRotationInConstrainedFrame; }
bool rotationIsDefault(const glm::quat& rotation, float tolerance = EPSILON) const;
@@ -100,15 +89,11 @@ public:
void clearTransformTranslation();
- void slaveVisibleTransform();
-
/// \return parent model-frame rotation
// (used to keep _rotation consistent when modifying _rotationInWorldFrame directly)
glm::quat computeParentRotation() const;
- glm::quat computeVisibleParentRotation() const;
void setTransform(const glm::mat4& transform) { _transform = transform; }
- void setVisibleTransform(const glm::mat4& transform) { _visibleTransform = transform; }
const glm::vec3& getTranslation() const { return _translation; }
const glm::mat4& getPreTransform() const { return _preTransform; }
@@ -132,24 +117,17 @@ private:
glm::vec3 _positionInParentFrame {0.0f}; // only changes when the Model is scaled
float _animationPriority {0.0f}; // the priority of the animation affecting this joint
float _distanceToParent {0.0f};
- AngularConstraint* _constraint{nullptr}; // JointState owns its AngularConstraint
glm::mat4 _transform; // joint- to model-frame
glm::quat _rotation; // joint- to model-frame
glm::quat _rotationInConstrainedFrame; // rotation in frame where angular constraints would be applied
- glm::mat4 _visibleTransform;
- glm::quat _visibleRotation;
- glm::quat _visibleRotationInConstrainedFrame;
-
glm::quat _defaultRotation; // Not necessarilly bind rotation. See FBXJoint transform/bindTransform
glm::quat _inverseDefaultRotation;
glm::vec3 _translation;
QString _name;
int _parentIndex;
bool _isFree;
- glm::vec3 _rotationMin;
- glm::vec3 _rotationMax;
glm::quat _preRotation;
glm::quat _postRotation;
glm::mat4 _preTransform;
diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp
index 1e512ee767..ac80f46836 100644
--- a/libraries/animation/src/Rig.cpp
+++ b/libraries/animation/src/Rig.cpp
@@ -224,14 +224,6 @@ void Rig::initJointStates(QVector states, glm::mat4 rootTransform,
_rightShoulderJointIndex = rightShoulderJointIndex;
initJointTransforms(rootTransform);
-
- int numStates = _jointStates.size();
- for (int i = 0; i < numStates; ++i) {
- _jointStates[i].buildConstraint();
- }
- for (int i = 0; i < _jointStates.size(); i++) {
- _jointStates[i].slaveVisibleTransform();
- }
}
// We could build and cache a dictionary, too....
@@ -293,15 +285,6 @@ bool Rig::getJointStateRotation(int index, glm::quat& rotation) const {
return !state.rotationIsDefault(rotation);
}
-bool Rig::getVisibleJointState(int index, glm::quat& rotation) const {
- if (index == -1 || index >= _jointStates.size()) {
- return false;
- }
- const JointState& state = _jointStates.at(index);
- rotation = state.getVisibleRotationInConstrainedFrame();
- return !state.rotationIsDefault(rotation);
-}
-
void Rig::clearJointState(int index) {
if (index != -1 && index < _jointStates.size()) {
JointState& state = _jointStates[index];
@@ -392,25 +375,6 @@ bool Rig::getJointCombinedRotation(int jointIndex, glm::quat& result, const glm:
return true;
}
-
-bool Rig::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position,
- glm::vec3 translation, glm::quat rotation) const {
- if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
- return false;
- }
- // position is in world-frame
- position = translation + rotation * _jointStates[jointIndex].getVisiblePosition();
- return true;
-}
-
-bool Rig::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& result, glm::quat rotation) const {
- if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
- return false;
- }
- result = rotation * _jointStates[jointIndex].getVisibleRotation();
- return true;
-}
-
glm::mat4 Rig::getJointTransform(int jointIndex) const {
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
return glm::mat4();
@@ -418,13 +382,6 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const {
return _jointStates[jointIndex].getTransform();
}
-glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const {
- if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
- return glm::mat4();
- }
- return _jointStates[jointIndex].getVisibleTransform();
-}
-
void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) {
glm::vec3 front = worldRotation * IDENTITY_FRONT;
@@ -596,7 +553,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
// copy poses into jointStates
const float PRIORITY = 1.0f;
for (size_t i = 0; i < poses.size(); i++) {
- setJointRotationInConstrainedFrame((int)i, glm::inverse(_animSkeleton->getRelativeBindPose(i).rot) * poses[i].rot, PRIORITY, false, 1.0f);
+ setJointRotationInConstrainedFrame((int)i, glm::inverse(_animSkeleton->getRelativeBindPose(i).rot) * poses[i].rot, PRIORITY, 1.0f);
}
} else {
@@ -719,7 +676,7 @@ bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm:
1.0f / (combinedWeight + 1.0f));
}
}
- state.applyRotationDelta(combinedDelta, true, priority);
+ state.applyRotationDelta(combinedDelta, priority);
glm::quat actualDelta = state.getRotation() * glm::inverse(oldCombinedRotation);
endPosition = actualDelta * jointVector + jointPosition;
if (useRotation) {
@@ -838,11 +795,9 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q
// Apply the rotation delta.
glm::quat oldNextRotation = nextState.getRotation();
- float mixFactor = 0.05f;
- nextState.applyRotationDelta(deltaRotation, mixFactor, priority);
+ nextState.applyRotationDelta(deltaRotation, priority);
- // measure the result of the rotation which may have been modified by
- // blending and constraints
+ // measure the result of the rotation which may have been modified by blending
glm::quat actualDelta = nextState.getRotation() * glm::inverse(oldNextRotation);
endPosition = pivot + actualDelta * leverArm;
}
@@ -861,7 +816,7 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q
} while (numIterations < MAX_ITERATION_COUNT && distanceToGo > ACCEPTABLE_IK_ERROR);
// set final rotation of the end joint
- endState.setRotationInModelFrame(targetRotation, priority, true);
+ endState.setRotationInModelFrame(targetRotation, priority);
}
bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority, const QVector& freeLineage) {
@@ -889,13 +844,13 @@ float Rig::getLimbLength(int jointIndex, const QVector& freeLineage,
return length;
}
-glm::quat Rig::setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain) {
+glm::quat Rig::setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority) {
glm::quat endRotation;
if (jointIndex == -1 || _jointStates.isEmpty()) {
return endRotation;
}
JointState& state = _jointStates[jointIndex];
- state.setRotationInBindFrame(rotation, priority, constrain);
+ state.setRotationInBindFrame(rotation, priority);
endRotation = state.getRotationInBindFrame();
return endRotation;
}
@@ -907,13 +862,13 @@ glm::vec3 Rig::getJointDefaultTranslationInConstrainedFrame(int jointIndex) {
return _jointStates[jointIndex].getDefaultTranslationInConstrainedFrame();
}
-glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, bool constrain, float mix) {
+glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, float mix) {
glm::quat endRotation;
if (jointIndex == -1 || _jointStates.isEmpty()) {
return endRotation;
}
JointState& state = _jointStates[jointIndex];
- state.setRotationInConstrainedFrame(targetRotation, priority, constrain, mix);
+ state.setRotationInConstrainedFrame(targetRotation, priority, mix);
endRotation = state.getRotationInConstrainedFrame();
return endRotation;
}
@@ -926,30 +881,17 @@ bool Rig::getJointRotationInConstrainedFrame(int jointIndex, glm::quat& quatOut)
return true;
}
-void Rig::updateVisibleJointStates() {
- for (int i = 0; i < _jointStates.size(); i++) {
- _jointStates[i].slaveVisibleTransform();
- }
-}
-
void Rig::clearJointStatePriorities() {
for (int i = 0; i < _jointStates.size(); i++) {
_jointStates[i].setAnimationPriority(0.0f);
}
}
-void Rig::setJointVisibleTransform(int jointIndex, glm::mat4 newTransform) {
- if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
- return;
- }
- _jointStates[jointIndex].setVisibleTransform(newTransform);
-}
-
-void Rig::applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority) {
+void Rig::applyJointRotationDelta(int jointIndex, const glm::quat& delta, float priority) {
if (jointIndex == -1 || _jointStates.isEmpty()) {
return;
}
- _jointStates[jointIndex].applyRotationDelta(delta, constrain, priority);
+ _jointStates[jointIndex].applyRotationDelta(delta, priority);
}
glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) {
@@ -1088,7 +1030,7 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) {
_animVars.set("headPosition", headPos);
_animVars.set("headRotation", headRot);
- _animVars.set("headType", QString("RotationAndPosition"));
+ _animVars.set("headAndNeckType", QString("RotationAndPosition"));
_animVars.set("neckPosition", neckPos);
_animVars.set("neckRotation", neckRot);
@@ -1101,7 +1043,7 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) {
_animVars.unset("headPosition");
_animVars.set("headRotation", realLocalHeadOrientation);
- _animVars.set("headType", QString("RotationOnly"));
+ _animVars.set("headAndNeckType", QString("RotationOnly"));
_animVars.unset("neckPosition");
_animVars.unset("neckRotation");
}
diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h
index 6dad58db87..8ddc0c9f6d 100644
--- a/libraries/animation/src/Rig.h
+++ b/libraries/animation/src/Rig.h
@@ -129,7 +129,7 @@ public:
void clearJointTransformTranslation(int jointIndex);
void reset(const QVector& fbxJoints);
bool getJointStateRotation(int index, glm::quat& rotation) const;
- void applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority);
+ void applyJointRotationDelta(int jointIndex, const glm::quat& delta, float priority);
JointState getJointState(int jointIndex) const; // XXX
bool getVisibleJointState(int index, glm::quat& rotation) const;
void clearJointState(int index);
@@ -165,10 +165,10 @@ public:
float getLimbLength(int jointIndex, const QVector& freeLineage,
const glm::vec3 scale, const QVector& fbxJoints) const;
- glm::quat setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority, bool constrain = false);
+ glm::quat setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority);
glm::vec3 getJointDefaultTranslationInConstrainedFrame(int jointIndex);
glm::quat setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation,
- float priority, bool constrain = false, float mix = 1.0f);
+ float priority, float mix = 1.0f);
bool getJointRotationInConstrainedFrame(int jointIndex, glm::quat& rotOut) const;
glm::quat getJointDefaultRotationInParentFrame(int jointIndex);
void updateVisibleJointStates();
diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp
index a1b191bc0c..eaa21fed12 100644
--- a/libraries/entities/src/EntityItemProperties.cpp
+++ b/libraries/entities/src/EntityItemProperties.cpp
@@ -1489,14 +1489,17 @@ void EntityItemProperties::markAllChanged() {
_accelerationSpreadChanged = true;
_particleRadiusChanged = true;
_radiusSpreadChanged = true;
- _radiusStartChanged = true;
- _radiusFinishChanged = true;
_colorSpreadChanged = true;
- _colorStartChanged = true;
- _colorFinishChanged = true;
_alphaSpreadChanged = true;
- _alphaStartChanged = true;
- _alphaFinishChanged = true;
+
+ // Only mark the following as changed if their values are specified in the properties when the particle is created. If their
+ // values are specified then they are marked as changed in getChangedProperties().
+ //_radiusStartChanged = true;
+ //_radiusFinishChanged = true;
+ //_colorStartChanged = true;
+ //_colorFinishChanged = true;
+ //_alphaStartChanged = true;
+ //_alphaFinishChanged = true;
_marketplaceIDChanged = true;
diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp
index 496d0d437f..e52d9d0b5d 100644
--- a/libraries/entities/src/ParticleEffectEntityItem.cpp
+++ b/libraries/entities/src/ParticleEffectEntityItem.cpp
@@ -610,25 +610,22 @@ QString ParticleEffectEntityItem::getAnimationSettings() const {
}
void ParticleEffectEntityItem::updateRadius(quint32 index, float age) {
- _particleRadiuses[index] = Interpolate::cubicInterpolate3Points(_radiusStarts[index], _radiusMiddles[index],
+ _particleRadiuses[index] = Interpolate::interpolate3Points(_radiusStarts[index], _radiusMiddles[index],
_radiusFinishes[index], age);
}
void ParticleEffectEntityItem::updateColor(quint32 index, float age) {
- _particleColors[index].red =
- (int)glm::clamp(Interpolate::cubicInterpolate3Points(_colorStarts[index].red, _colorMiddles[index].red,
- _colorFinishes[index].red, age), 0.0f, 255.0f);
- _particleColors[index].green =
- (int)glm::clamp(Interpolate::cubicInterpolate3Points(_colorStarts[index].green, _colorMiddles[index].green,
- _colorFinishes[index].green, age), 0.0f, 255.0f);
- _particleColors[index].blue =
- (int)glm::clamp(Interpolate::cubicInterpolate3Points(_colorStarts[index].blue, _colorMiddles[index].blue,
- _colorFinishes[index].blue, age), 0.0f, 255.0f);
+ _particleColors[index].red = (int)Interpolate::interpolate3Points(_colorStarts[index].red, _colorMiddles[index].red,
+ _colorFinishes[index].red, age);
+ _particleColors[index].green = (int)Interpolate::interpolate3Points(_colorStarts[index].green, _colorMiddles[index].green,
+ _colorFinishes[index].green, age);
+ _particleColors[index].blue = (int)Interpolate::interpolate3Points(_colorStarts[index].blue, _colorMiddles[index].blue,
+ _colorFinishes[index].blue, age);
}
void ParticleEffectEntityItem::updateAlpha(quint32 index, float age) {
- _particleAlphas[index] = glm::clamp(Interpolate::cubicInterpolate3Points(_alphaStarts[index], _alphaMiddles[index],
- _alphaFinishes[index], age), 0.0f, 1.0f);
+ _particleAlphas[index] = Interpolate::interpolate3Points(_alphaStarts[index], _alphaMiddles[index],
+ _alphaFinishes[index], age);
}
void ParticleEffectEntityItem::extendBounds(const glm::vec3& point) {
diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h
index 4e37165b55..cb2e80a1be 100644
--- a/libraries/entities/src/ParticleEffectEntityItem.h
+++ b/libraries/entities/src/ParticleEffectEntityItem.h
@@ -180,12 +180,12 @@ public:
float getParticleRadius() const { return _particleRadius; }
static const float DEFAULT_RADIUS_START;
- bool _isRadiusStartInitialized;
+ bool _isRadiusStartInitialized = false;
void setRadiusStart(float radiusStart) { _radiusStart = radiusStart; _isRadiusStartInitialized = true; }
float getRadiusStart() const { return _isRadiusStartInitialized ? _radiusStart : _particleRadius; }
static const float DEFAULT_RADIUS_FINISH;
- bool _isRadiusFinishInitialized;
+ bool _isRadiusFinishInitialized = false;
void setRadiusFinish(float radiusFinish) { _radiusFinish = radiusFinish; _isRadiusFinishInitialized = true; }
float getRadiusFinish() const { return _isRadiusFinishInitialized ? _radiusFinish : _particleRadius; }
diff --git a/libraries/fbx/src/FBXReader_Material.cpp b/libraries/fbx/src/FBXReader_Material.cpp
index 55a61c77fc..6da53dbede 100644
--- a/libraries/fbx/src/FBXReader_Material.cpp
+++ b/libraries/fbx/src/FBXReader_Material.cpp
@@ -144,13 +144,12 @@ void FBXReader::consolidateFBXMaterials() {
// FIXME: Do not use the Specular Factor yet as some FBX models have it set to 0
// metallic *= material.specularFactor;
material._material->setMetallic(metallic);
- material._material->setGloss(material.shininess);
+ material._material->setGloss(material.shininess);
if (material.opacity <= 0.0f) {
- material._material->setOpacity(1.0f);
+ material._material->setOpacity(1.0f);
} else {
- material._material->setOpacity(material.opacity);
+ material._material->setOpacity(material.opacity);
}
}
}
-
diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp
index ad30a38550..a9275e2d4a 100644
--- a/libraries/fbx/src/OBJReader.cpp
+++ b/libraries/fbx/src/OBJReader.cpp
@@ -539,14 +539,16 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping,
foreach (QString materialID, materials.keys()) {
OBJMaterial& objMaterial = materials[materialID];
- geometry.materials[materialID] = FBXMaterial(objMaterial.diffuseColor, // glm::vec3(1.0f, 1.0f, 1.0f)
- objMaterial.specularColor, // glm::vec3(1.0f)
- glm::vec3(), // glm::vec3()
- glm::vec2(0.f, 1.0f), // glm::vec2(0.f, 1.0f)
- objMaterial.shininess, // 96.0f
- objMaterial.opacity); // 1.0f
- FBXMaterial& material = geometry.materials[materialID];
- material._material = std::make_shared();
+ geometry.materials[materialID] = FBXMaterial(objMaterial.diffuseColor,
+ objMaterial.specularColor,
+ glm::vec3(0.0f),
+ glm::vec2(0.0f, 1.0f),
+ objMaterial.shininess,
+ objMaterial.opacity);
+ FBXMaterial& fbxMaterial = geometry.materials[materialID];
+ fbxMaterial.materialID = materialID;
+ fbxMaterial._material = std::make_shared();
+ model::MaterialPointer modelMaterial = fbxMaterial._material;
if (!objMaterial.diffuseTextureFilename.isEmpty()) {
FBXTexture texture;
@@ -554,21 +556,16 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping,
// TODO -- something to get textures working again
}
- material._material->setEmissive(material.emissiveColor);
- if (glm::all(glm::equal(material.diffuseColor, glm::vec3(0.0f)))) {
- material._material->setDiffuse(material.diffuseColor);
- } else {
- material._material->setDiffuse(material.diffuseColor);
- }
- material._material->setMetallic(glm::length(material.specularColor));
- material._material->setGloss(material.shininess);
+ modelMaterial->setEmissive(fbxMaterial.emissiveColor);
+ modelMaterial->setDiffuse(fbxMaterial.diffuseColor);
+ modelMaterial->setMetallic(glm::length(fbxMaterial.specularColor));
+ modelMaterial->setGloss(fbxMaterial.shininess);
- if (material.opacity <= 0.0f) {
- material._material->setOpacity(1.0f);
+ if (fbxMaterial.opacity <= 0.0f) {
+ modelMaterial->setOpacity(1.0f);
} else {
- material._material->setOpacity(material.opacity);
+ modelMaterial->setOpacity(fbxMaterial.opacity);
}
-
}
return geometryPtr;
diff --git a/libraries/gpu/src/gpu/DrawUnitQuadTexcoord.slv b/libraries/gpu/src/gpu/DrawUnitQuadTexcoord.slv
new file mode 100644
index 0000000000..60ab0bd7dd
--- /dev/null
+++ b/libraries/gpu/src/gpu/DrawUnitQuadTexcoord.slv
@@ -0,0 +1,28 @@
+<@include gpu/Config.slh@>
+<$VERSION_HEADER$>
+// Generated on <$_SCRIBE_DATE$>
+//
+// Draw the unit quad [-1,-1 -> 1,1] amd pass along the unit texcoords [0, 0 -> 1, 1]. Not transform used.
+// Simply draw a Triangle_strip of 2 triangles, no input buffers or index buffer needed
+//
+// Created by Sam Gateau on 6/22/2015
+// 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
+//
+out vec2 varTexCoord0;
+
+void main(void) {
+ const vec4 UNIT_QUAD[4] = vec4[4](
+ vec4(-1.0, -1.0, 0.0, 1.0),
+ vec4(1.0, -1.0, 0.0, 1.0),
+ vec4(-1.0, 1.0, 0.0, 1.0),
+ vec4(1.0, 1.0, 0.0, 1.0)
+ );
+ vec4 pos = UNIT_QUAD[gl_VertexID];
+
+ varTexCoord0 = (pos.xy + 1) * 0.5;
+
+ gl_Position = pos;
+}
diff --git a/libraries/gpu/src/gpu/Framebuffer.cpp b/libraries/gpu/src/gpu/Framebuffer.cpp
index 96bd3d3002..d570007b3e 100755
--- a/libraries/gpu/src/gpu/Framebuffer.cpp
+++ b/libraries/gpu/src/gpu/Framebuffer.cpp
@@ -247,7 +247,13 @@ bool Framebuffer::setDepthStencilBuffer(const TexturePointer& texture, const For
_bufferMask = ( _bufferMask & ~BUFFER_DEPTHSTENCIL);
if (texture) {
- _bufferMask |= BUFFER_DEPTHSTENCIL;
+ if (format.getSemantic() == gpu::DEPTH) {
+ _bufferMask |= BUFFER_DEPTH;
+ } else if (format.getSemantic() == gpu::STENCIL) {
+ _bufferMask |= BUFFER_STENCIL;
+ } else if (format.getSemantic() == gpu::DEPTH_STENCIL) {
+ _bufferMask |= BUFFER_DEPTHSTENCIL;
+ }
}
return true;
diff --git a/libraries/gpu/src/gpu/Framebuffer.h b/libraries/gpu/src/gpu/Framebuffer.h
index 310255af9f..83ff8fbb23 100755
--- a/libraries/gpu/src/gpu/Framebuffer.h
+++ b/libraries/gpu/src/gpu/Framebuffer.h
@@ -116,6 +116,8 @@ public:
bool isEmpty() const { return (_bufferMask == 0); }
bool hasColor() const { return (getBufferMask() & BUFFER_COLORS); }
bool hasDepthStencil() const { return (getBufferMask() & BUFFER_DEPTHSTENCIL); }
+ bool hasDepth() const { return (getBufferMask() & BUFFER_DEPTH); }
+ bool hasStencil() const { return (getBufferMask() & BUFFER_STENCIL); }
bool validateTargetCompatibility(const Texture& texture, uint32 subresource = 0) const;
diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp
index 250fc4aaac..b1e63a18bd 100644
--- a/libraries/gpu/src/gpu/GLBackend.cpp
+++ b/libraries/gpu/src/gpu/GLBackend.cpp
@@ -132,20 +132,26 @@ void GLBackend::renderPassTransfer(Batch& batch) {
const size_t numCommands = batch.getCommands().size();
const Batch::Commands::value_type* command = batch.getCommands().data();
const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data();
-
- for (auto& cached : batch._buffers._items) {
- if (cached._data) {
- syncGPUObject(*cached._data);
+
+ { // Sync all the buffers
+ PROFILE_RANGE("syncGPUBuffer");
+
+ for (auto& cached : batch._buffers._items) {
+ if (cached._data) {
+ syncGPUObject(*cached._data);
+ }
}
}
- // Reset the transform buffers
- _transform._cameras.resize(0);
- _transform._cameraOffsets.clear();
- _transform._objects.resize(0);
- _transform._objectOffsets.clear();
- for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) {
- switch (*command) {
+ { // Sync all the buffers
+ PROFILE_RANGE("syncCPUTransform");
+ _transform._cameras.resize(0);
+ _transform._cameraOffsets.clear();
+ _transform._objects.resize(0);
+ _transform._objectOffsets.clear();
+
+ for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) {
+ switch (*command) {
case Batch::COMMAND_draw:
case Batch::COMMAND_drawIndexed:
case Batch::COMMAND_drawInstanced:
@@ -164,11 +170,16 @@ void GLBackend::renderPassTransfer(Batch& batch) {
default:
break;
+ }
+ command++;
+ offset++;
}
- command++;
- offset++;
}
- _transform.transfer();
+
+ { // Sync the transform buffers
+ PROFILE_RANGE("syncGPUTransform");
+ _transform.transfer();
+ }
}
void GLBackend::renderPassDraw(Batch& batch) {
diff --git a/libraries/gpu/src/gpu/GLBackendOutput.cpp b/libraries/gpu/src/gpu/GLBackendOutput.cpp
index c3f61a67c3..70e4b18b0f 100755
--- a/libraries/gpu/src/gpu/GLBackendOutput.cpp
+++ b/libraries/gpu/src/gpu/GLBackendOutput.cpp
@@ -100,7 +100,18 @@ GLBackend::GLFramebuffer* GLBackend::syncGPUObject(const Framebuffer& framebuffe
if (surface) {
auto gltexture = GLBackend::syncGPUObject(*surface);
if (gltexture) {
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, gltexture->_texture, 0);
+ GLenum attachement = GL_DEPTH_STENCIL_ATTACHMENT;
+ if (!framebuffer.hasStencil()) {
+ attachement = GL_DEPTH_ATTACHMENT;
+ glFramebufferTexture2D(GL_FRAMEBUFFER, attachement, GL_TEXTURE_2D, gltexture->_texture, 0);
+ } else if (!framebuffer.hasDepth()) {
+ attachement = GL_STENCIL_ATTACHMENT;
+ glFramebufferTexture2D(GL_FRAMEBUFFER, attachement, GL_TEXTURE_2D, gltexture->_texture, 0);
+ } else {
+ attachement = GL_DEPTH_STENCIL_ATTACHMENT;
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachement, GL_RENDERBUFFER, gltexture->_texture);
+ }
+ (void) CHECK_GL_ERROR();
}
}
}
diff --git a/libraries/gpu/src/gpu/GLBackendState.cpp b/libraries/gpu/src/gpu/GLBackendState.cpp
index 9fdcbc0870..895d0a0027 100644
--- a/libraries/gpu/src/gpu/GLBackendState.cpp
+++ b/libraries/gpu/src/gpu/GLBackendState.cpp
@@ -642,8 +642,13 @@ void GLBackend::do_setStateStencil(State::StencilActivation activation, State::S
if (activation.isEnabled()) {
glEnable(GL_STENCIL_TEST);
- glStencilMaskSeparate(GL_FRONT, activation.getWriteMaskFront());
- glStencilMaskSeparate(GL_BACK, activation.getWriteMaskBack());
+
+ if (activation.getWriteMaskFront() != activation.getWriteMaskBack()) {
+ glStencilMaskSeparate(GL_FRONT, activation.getWriteMaskFront());
+ glStencilMaskSeparate(GL_BACK, activation.getWriteMaskBack());
+ } else {
+ glStencilMask(activation.getWriteMaskFront());
+ }
static GLenum STENCIL_OPS[] = {
GL_KEEP,
@@ -655,11 +660,16 @@ void GLBackend::do_setStateStencil(State::StencilActivation activation, State::S
GL_INCR,
GL_DECR };
- glStencilFuncSeparate(GL_FRONT, STENCIL_OPS[frontTest.getFailOp()], STENCIL_OPS[frontTest.getPassOp()], STENCIL_OPS[frontTest.getDepthFailOp()]);
- glStencilFuncSeparate(GL_FRONT, GL_COMPARISON_FUNCTIONS[frontTest.getFunction()], frontTest.getReference(), frontTest.getReadMask());
+ if (frontTest != backTest) {
+ glStencilOpSeparate(GL_FRONT, STENCIL_OPS[frontTest.getFailOp()], STENCIL_OPS[frontTest.getPassOp()], STENCIL_OPS[frontTest.getDepthFailOp()]);
+ glStencilFuncSeparate(GL_FRONT, GL_COMPARISON_FUNCTIONS[frontTest.getFunction()], frontTest.getReference(), frontTest.getReadMask());
- glStencilFuncSeparate(GL_BACK, STENCIL_OPS[backTest.getFailOp()], STENCIL_OPS[backTest.getPassOp()], STENCIL_OPS[backTest.getDepthFailOp()]);
- glStencilFuncSeparate(GL_BACK, GL_COMPARISON_FUNCTIONS[backTest.getFunction()], backTest.getReference(), backTest.getReadMask());
+ glStencilOpSeparate(GL_BACK, STENCIL_OPS[backTest.getFailOp()], STENCIL_OPS[backTest.getPassOp()], STENCIL_OPS[backTest.getDepthFailOp()]);
+ glStencilFuncSeparate(GL_BACK, GL_COMPARISON_FUNCTIONS[backTest.getFunction()], backTest.getReference(), backTest.getReadMask());
+ } else {
+ glStencilOp(STENCIL_OPS[frontTest.getFailOp()], STENCIL_OPS[frontTest.getPassOp()], STENCIL_OPS[frontTest.getDepthFailOp()]);
+ glStencilFunc(GL_COMPARISON_FUNCTIONS[frontTest.getFunction()], frontTest.getReference(), frontTest.getReadMask());
+ }
} else {
glDisable(GL_STENCIL_TEST);
}
diff --git a/libraries/gpu/src/gpu/GLBackendTexture.cpp b/libraries/gpu/src/gpu/GLBackendTexture.cpp
index dce5236868..ee5e1d3bc6 100755
--- a/libraries/gpu/src/gpu/GLBackendTexture.cpp
+++ b/libraries/gpu/src/gpu/GLBackendTexture.cpp
@@ -66,7 +66,9 @@ public:
texel.internalFormat = GL_RG;
break;
case gpu::DEPTH_STENCIL:
- texel.internalFormat = GL_DEPTH_STENCIL;
+ texel.type = GL_UNSIGNED_BYTE;
+ texel.format = GL_DEPTH_STENCIL;
+ texel.internalFormat = GL_DEPTH24_STENCIL8;
break;
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
@@ -197,7 +199,9 @@ public:
texel.internalFormat = GL_RG;
break;
case gpu::DEPTH_STENCIL:
- texel.internalFormat = GL_DEPTH_STENCIL;
+ texel.type = GL_UNSIGNED_BYTE;
+ texel.format = GL_DEPTH_STENCIL;
+ texel.internalFormat = GL_DEPTH24_STENCIL8;
break;
default:
qCDebug(gpulogging) << "Unknown combination of texel format";
@@ -334,22 +338,34 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) {
}
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat);
-
- glTexImage2D(GL_TEXTURE_2D, 0,
- texelFormat.internalFormat, texture.getWidth(), texture.getHeight(), 0,
- texelFormat.format, texelFormat.type, bytes);
- if (bytes && texture.isAutogenerateMips()) {
- glGenerateMipmap(GL_TEXTURE_2D);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
- }/* else {
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- }*/
-
- object->_target = GL_TEXTURE_2D;
+ auto semantic = texture.getTexelFormat().getSemantic();
- syncSampler(texture.getSampler(), texture.getType(), object);
+ if (semantic == gpu::DEPTH_STENCIL) {
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDeleteTextures(1, &object->_texture);
+
+ glGenRenderbuffers(1, &object->_texture);
+ glBindRenderbuffer(GL_RENDERBUFFER, object->_texture);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, texture.getWidth(), texture.getHeight());
+ // At this point the mip pixels have been loaded, we can notify
+ texture.notifyMipFaceGPULoaded(0, 0);
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+
+ } else {
+ glTexImage2D(GL_TEXTURE_2D, 0,
+ texelFormat.internalFormat, texture.getWidth(), texture.getHeight(), 0,
+ texelFormat.format, texelFormat.type, bytes);
+
+ if (bytes && texture.isAutogenerateMips()) {
+ glGenerateMipmap(GL_TEXTURE_2D);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ }
+
+ object->_target = GL_TEXTURE_2D;
+
+ syncSampler(texture.getSampler(), texture.getType(), object);
+ }
// At this point the mip pixels have been loaded, we can notify
texture.notifyMipFaceGPULoaded(0, 0);
diff --git a/libraries/gpu/src/gpu/GLBackendTransform.cpp b/libraries/gpu/src/gpu/GLBackendTransform.cpp
index 4a55155a86..5e16421c6a 100755
--- a/libraries/gpu/src/gpu/GLBackendTransform.cpp
+++ b/libraries/gpu/src/gpu/GLBackendTransform.cpp
@@ -130,19 +130,27 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo
void GLBackend::TransformStageState::transfer() const {
static QByteArray bufferData;
- glBindBuffer(GL_UNIFORM_BUFFER, _cameraBuffer);
- bufferData.resize(_cameraUboSize * _cameras.size());
- for (size_t i = 0; i < _cameras.size(); ++i) {
- memcpy(bufferData.data() + (_cameraUboSize * i), &_cameras[i], sizeof(TransformCamera));
+ if (!_cameras.empty()) {
+ glBindBuffer(GL_UNIFORM_BUFFER, _cameraBuffer);
+ bufferData.resize(_cameraUboSize * _cameras.size());
+ for (size_t i = 0; i < _cameras.size(); ++i) {
+ memcpy(bufferData.data() + (_cameraUboSize * i), &_cameras[i], sizeof(TransformCamera));
+ }
+ glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW);
}
- glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW);
- glBindBuffer(GL_UNIFORM_BUFFER, _objectBuffer);
- bufferData.resize(_objectUboSize * _objects.size());
- for (size_t i = 0; i < _objects.size(); ++i) {
- memcpy(bufferData.data() + (_objectUboSize * i), &_objects[i], sizeof(TransformObject));
+
+ if (!_objects.empty()) {
+ glBindBuffer(GL_UNIFORM_BUFFER, _objectBuffer);
+ bufferData.resize(_objectUboSize * _objects.size());
+ for (size_t i = 0; i < _objects.size(); ++i) {
+ memcpy(bufferData.data() + (_objectUboSize * i), &_objects[i], sizeof(TransformObject));
+ }
+ glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW);
+ }
+
+ if (!_cameras.empty() || !_objects.empty()) {
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
- glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW);
- glBindBuffer(GL_UNIFORM_BUFFER, 0);
CHECK_GL_ERROR();
}
diff --git a/libraries/gpu/src/gpu/StandardShaderLib.cpp b/libraries/gpu/src/gpu/StandardShaderLib.cpp
index 3f27a7fc35..864bff08c9 100755
--- a/libraries/gpu/src/gpu/StandardShaderLib.cpp
+++ b/libraries/gpu/src/gpu/StandardShaderLib.cpp
@@ -12,6 +12,7 @@
//
#include "StandardShaderLib.h"
+#include "DrawUnitQuadTexcoord_vert.h"
#include "DrawTransformUnitQuad_vert.h"
#include "DrawTexcoordRectTransformUnitQuad_vert.h"
#include "DrawViewportQuadTransformTexcoord_vert.h"
@@ -21,6 +22,7 @@
using namespace gpu;
+ShaderPointer StandardShaderLib::_drawUnitQuadTexcoordVS;
ShaderPointer StandardShaderLib::_drawTransformUnitQuadVS;
ShaderPointer StandardShaderLib::_drawTexcoordRectTransformUnitQuadVS;
ShaderPointer StandardShaderLib::_drawViewportQuadTransformTexcoordVS;
@@ -55,6 +57,12 @@ ShaderPointer StandardShaderLib::getProgram(GetShader getVS, GetShader getPS) {
}
+ShaderPointer StandardShaderLib::getDrawUnitQuadTexcoordVS() {
+ if (!_drawUnitQuadTexcoordVS) {
+ _drawUnitQuadTexcoordVS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(DrawUnitQuadTexcoord_vert)));
+ }
+ return _drawUnitQuadTexcoordVS;
+}
ShaderPointer StandardShaderLib::getDrawTransformUnitQuadVS() {
if (!_drawTransformUnitQuadVS) {
diff --git a/libraries/gpu/src/gpu/StandardShaderLib.h b/libraries/gpu/src/gpu/StandardShaderLib.h
index 2d9c168473..12ea9045c2 100755
--- a/libraries/gpu/src/gpu/StandardShaderLib.h
+++ b/libraries/gpu/src/gpu/StandardShaderLib.h
@@ -23,6 +23,9 @@ namespace gpu {
class StandardShaderLib {
public:
+ // Shader draws the unit quad in the full viewport clipPos = ([(-1,-1),(1,1)]) and the unit texcoord = [(0,0),(1,1)].
+ static ShaderPointer getDrawUnitQuadTexcoordVS();
+
// Shader draw the unit quad objectPos = ([(-1,-1),(1,1)]) and transform it by the full model transform stack (Model, View, Proj).
// A texcoord attribute is also generated texcoord = [(0,0),(1,1)]
static ShaderPointer getDrawTransformUnitQuadVS();
@@ -44,6 +47,7 @@ public:
protected:
+ static ShaderPointer _drawUnitQuadTexcoordVS;
static ShaderPointer _drawTransformUnitQuadVS;
static ShaderPointer _drawTexcoordRectTransformUnitQuadVS;
static ShaderPointer _drawViewportQuadTransformTexcoordVS;
diff --git a/libraries/gpu/src/gpu/State.h b/libraries/gpu/src/gpu/State.h
index 5500f20e06..7740506bce 100755
--- a/libraries/gpu/src/gpu/State.h
+++ b/libraries/gpu/src/gpu/State.h
@@ -143,11 +143,11 @@ public:
static const int PASS_OP_OFFSET = 12;
uint16 _functionAndOperations;
- uint8 _reference = 0;
+ int8 _reference = 0;
uint8 _readMask = 0xff;
public:
- StencilTest(uint8 reference = 0, uint8 readMask =0xFF, ComparisonFunction func = ALWAYS, StencilOp failOp = STENCIL_OP_KEEP, StencilOp depthFailOp = STENCIL_OP_KEEP, StencilOp passOp = STENCIL_OP_KEEP) :
+ StencilTest(int8 reference = 0, uint8 readMask =0xFF, ComparisonFunction func = ALWAYS, StencilOp failOp = STENCIL_OP_KEEP, StencilOp depthFailOp = STENCIL_OP_KEEP, StencilOp passOp = STENCIL_OP_KEEP) :
_functionAndOperations(func | (failOp << FAIL_OP_OFFSET) | (depthFailOp << DEPTH_FAIL_OP_OFFSET) | (passOp << PASS_OP_OFFSET)),
_reference(reference), _readMask(readMask)
{}
@@ -157,7 +157,7 @@ public:
StencilOp getDepthFailOp() const { return StencilOp((_functionAndOperations & DEPTH_FAIL_OP_MASK) >> DEPTH_FAIL_OP_OFFSET); }
StencilOp getPassOp() const { return StencilOp((_functionAndOperations & PASS_OP_MASK) >> PASS_OP_OFFSET); }
- uint8 getReference() const { return _reference; }
+ int8 getReference() const { return _reference; }
uint8 getReadMask() const { return _readMask; }
int32 getRaw() const { return *(reinterpret_cast(this)); }
diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp
index 9bc3a71fd5..aa04c49adb 100644
--- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp
+++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp
@@ -13,11 +13,12 @@
#include
-#include
+#include
#include
+#include
+#include
#include "NumericalConstants.h"
-#include
#include "SixenseManager.h"
#include "UserActivityLogger.h"
@@ -38,14 +39,9 @@ const unsigned int RIGHT_MASK = 1U << 1;
const int CALIBRATION_STATE_IDLE = 0;
const int CALIBRATION_STATE_X = 1;
-const int CALIBRATION_STATE_Y = 2;
-const int CALIBRATION_STATE_Z = 3;
-const int CALIBRATION_STATE_COMPLETE = 4;
+const int CALIBRATION_STATE_COMPLETE = 2;
-// default (expected) location of neck in sixense space
-const float NECK_X = 0.25f; // meters
-const float NECK_Y = 0.3f; // meters
-const float NECK_Z = 0.3f; // meters
+const glm::vec3 DEFAULT_AVATAR_POSITION(-0.25f, -0.35f, -0.3f); // in hydra frame
const float CONTROLLER_THRESHOLD = 0.35f;
@@ -92,9 +88,7 @@ bool SixenseManager::isSupported() const {
void SixenseManager::activate() {
#ifdef HAVE_SIXENSE
_calibrationState = CALIBRATION_STATE_IDLE;
- // By default we assume the _neckBase (in orb frame) is as high above the orb
- // as the "torso" is below it.
- _neckBase = glm::vec3(NECK_X, -NECK_Y, NECK_Z);
+ _avatarPosition = DEFAULT_AVATAR_POSITION;
CONTAINER->addMenu(MENU_PATH);
CONTAINER->addMenuItem(MENU_PATH, TOGGLE_SMOOTH,
@@ -258,11 +252,13 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) {
#ifdef HAVE_SIXENSE
// the calibration sequence is:
-// (1) press BUTTON_FWD on both hands
-// (2) reach arm straight out to the side (X)
-// (3) lift arms staight up above head (Y)
-// (4) move arms a bit forward (Z)
-// (5) release BUTTON_FWD on both hands
+// (1) reach arm straight out to the sides (xAxis is to the left)
+// (2) press BUTTON_FWD on both hands and hold for one second
+// (3) release both BUTTON_FWDs
+//
+// The code will:
+// (4) assume that the orb is on a flat surface (yAxis is UP)
+// (5) compute the forward direction (zAxis = xAxis cross yAxis)
const float MINIMUM_ARM_REACH = 0.3f; // meters
const float MAXIMUM_NOISE_LEVEL = 0.05f; // meters
@@ -279,21 +275,16 @@ void SixenseManager::updateCalibration(void* controllersX) {
return;
}
switch (_calibrationState) {
- case CALIBRATION_STATE_Y:
- case CALIBRATION_STATE_Z:
case CALIBRATION_STATE_COMPLETE:
{
// compute calibration results
- // ATM we only handle the case where the XAxis has been measured, and we assume the rest
- // (i.e. that the orb is on a level surface)
- // TODO: handle COMPLETE state where all three axes have been defined. This would allow us
- // to also handle the case where left and right controllers have been reversed.
- _neckBase = 0.5f * (_reachLeft + _reachRight); // neck is midway between right and left reaches
+ _avatarPosition = - 0.5f * (_reachLeft + _reachRight); // neck is midway between right and left hands
glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft);
- glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
- glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, yAxis));
- xAxis = glm::normalize(glm::cross(yAxis, zAxis));
- _orbRotation = glm::inverse(glm::quat_cast(glm::mat3(xAxis, yAxis, zAxis)));
+ glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, Vectors::UNIT_Y));
+ xAxis = glm::normalize(glm::cross(Vectors::UNIT_Y, zAxis));
+ _avatarRotation = glm::inverse(glm::quat_cast(glm::mat3(xAxis, Vectors::UNIT_Y, zAxis)));
+ const float Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR = -0.3f;
+ _avatarPosition.y += Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR;
qCDebug(inputplugins, "succeess: sixense calibration");
}
break;
@@ -349,54 +340,10 @@ void SixenseManager::updateCalibration(void* controllersX) {
_lockExpiry = now + LOCK_DURATION;
_lastDistance = 0.0f;
_reachUp = 0.5f * (_reachLeft + _reachRight);
- _calibrationState = CALIBRATION_STATE_Y;
+ _calibrationState = CALIBRATION_STATE_COMPLETE;
qCDebug(inputplugins, "success: sixense calibration: left");
}
}
- else if (_calibrationState == CALIBRATION_STATE_Y) {
- glm::vec3 torso = 0.5f * (_reachLeft + _reachRight);
- glm::vec3 averagePosition = 0.5f * (_averageLeft + _averageRight);
- float distance = (averagePosition - torso).y;
- if (fabsf(distance) > fabsf(_lastDistance) + MAXIMUM_NOISE_LEVEL) {
- // distance is increasing so acquire the data and push the expiry out
- _reachUp = averagePosition;
- _lastDistance = distance;
- _lockExpiry = now + LOCK_DURATION;
- } else if (now > _lockExpiry) {
- if (_lastDistance > MINIMUM_ARM_REACH) {
- // lock has expired so clamp the data and move on
- _reachForward = _reachUp;
- _lastDistance = 0.0f;
- _lockExpiry = now + LOCK_DURATION;
- _calibrationState = CALIBRATION_STATE_Z;
- qCDebug(inputplugins, "success: sixense calibration: up");
- }
- }
- }
- else if (_calibrationState == CALIBRATION_STATE_Z) {
- glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft);
- glm::vec3 torso = 0.5f * (_reachLeft + _reachRight);
- //glm::vec3 yAxis = glm::normalize(_reachUp - torso);
- glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
- glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, yAxis));
-
- glm::vec3 averagePosition = 0.5f * (_averageLeft + _averageRight);
- float distance = glm::dot((averagePosition - torso), zAxis);
- if (fabs(distance) > fabs(_lastDistance)) {
- // distance is increasing so acquire the data and push the expiry out
- _reachForward = averagePosition;
- _lastDistance = distance;
- _lockExpiry = now + LOCK_DURATION;
- } else if (now > _lockExpiry) {
- if (fabsf(_lastDistance) > 0.05f * MINIMUM_ARM_REACH) {
- // lock has expired so clamp the data and move on
- _calibrationState = CALIBRATION_STATE_COMPLETE;
- qCDebug(inputplugins, "success: sixense calibration: forward");
- // TODO: it is theoretically possible to detect that the controllers have been
- // accidentally switched (left hand is holding right controller) and to swap the order.
- }
- }
- }
}
#endif // HAVE_SIXENSE
@@ -456,12 +403,9 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int
// z
// Transform the measured position into body frame.
- glm::vec3 neck = _neckBase;
- // Set y component of the "neck" to raise the measured position a little bit.
- neck.y = 0.5f;
- position = _orbRotation * (position - neck);
+ position = _avatarRotation * (position + _avatarPosition);
- // From ABOVE the hand canonical axes looks like this:
+ // From ABOVE the hand canonical axes look like this:
//
// | | | | y | | | |
// | | | | | | | | |
@@ -480,28 +424,25 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int
//
// Qsh = angleAxis(PI, zAxis) * angleAxis(-PI/2, xAxis)
//
- const glm::vec3 xAxis = glm::vec3(1.0f, 0.0f, 0.0f);
- const glm::vec3 yAxis = glm::vec3(0.0f, 1.0f, 0.0f);
- const glm::vec3 zAxis = glm::vec3(0.0f, 0.0f, 1.0f);
- const glm::quat sixenseToHand = glm::angleAxis(PI, zAxis) * glm::angleAxis(-PI/2.0f, xAxis);
+ const glm::quat sixenseToHand = glm::angleAxis(PI, Vectors::UNIT_Z) * glm::angleAxis(-PI/2.0f, Vectors::UNIT_X);
// In addition to Qsh each hand has pre-offset introduced by the shape of the sixense controllers
// and how they fit into the hand in their relaxed state. This offset is a quarter turn about
// the sixense's z-axis, with its direction different for the two hands:
float sign = (index == 0) ? 1.0f : -1.0f;
- const glm::quat preOffset = glm::angleAxis(sign * PI / 2.0f, zAxis);
+ const glm::quat preOffset = glm::angleAxis(sign * PI / 2.0f, Vectors::UNIT_Z);
// Finally, there is a post-offset (same for both hands) to get the hand's rest orientation
// (fingers forward, palm down) aligned properly in the avatar's model-frame,
// and then a flip about the yAxis to get into model-frame.
- const glm::quat postOffset = glm::angleAxis(PI, yAxis) * glm::angleAxis(PI / 2.0f, xAxis);
+ const glm::quat postOffset = glm::angleAxis(PI, Vectors::UNIT_Y) * glm::angleAxis(PI / 2.0f, Vectors::UNIT_X);
// The total rotation of the hand uses the formula:
//
// rotation = postOffset * Qsh^ * (measuredRotation * preOffset) * Qsh
//
// TODO: find a shortcut with fewer rotations.
- rotation = postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand;
+ rotation = _avatarRotation * postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand;
_poseStateMap[makeInput(JointChannel(index)).getChannel()] = UserInputMapper::PoseValue(position, rotation);
#endif // HAVE_SIXENSE
diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h
index 03482287d9..22340cfc95 100644
--- a/libraries/input-plugins/src/input-plugins/SixenseManager.h
+++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h
@@ -97,8 +97,8 @@ private:
int _calibrationState;
// these are calibration results
- glm::vec3 _neckBase; // midpoint between controllers during X-axis calibration
- glm::quat _orbRotation; // rotates from orb frame into body frame
+ glm::vec3 _avatarPosition; // in hydra-frame
+ glm::quat _avatarRotation; // in hydra-frame
float _armLength;
// these are measured values used to compute the calibration results
diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/model/src/model/Skybox.cpp
index e27a0d25ce..21b40a54c8 100755
--- a/libraries/model/src/model/Skybox.cpp
+++ b/libraries/model/src/model/Skybox.cpp
@@ -21,6 +21,8 @@
using namespace model;
Skybox::Skybox() {
+ Data data;
+ _dataBuffer = gpu::BufferView(std::make_shared(sizeof(Data), (const gpu::Byte*) &data));
/* // PLease create a default engineer skybox
_cubemap.reset( gpu::Texture::createCube(gpu::Element::COLOR_RGBA_32, 1));
@@ -36,7 +38,7 @@ Skybox::Skybox() {
}
void Skybox::setColor(const Color& color) {
- _color = color;
+ _dataBuffer.edit()._color = color;
}
void Skybox::setCubemap(const gpu::TexturePointer& cubemap) {
@@ -44,12 +46,40 @@ void Skybox::setCubemap(const gpu::TexturePointer& cubemap) {
}
+void Skybox::updateDataBuffer() const {
+ auto blend = 0.0f;
+ if (getCubemap() && getCubemap()->isDefined()) {
+ blend = 1.0f;
+ // If pitch black neutralize the color
+ if (glm::all(glm::equal(getColor(), glm::vec3(0.0f)))) {
+ blend = 2.0f;
+ }
+ }
+
+ if (blend != _dataBuffer.get()._blend) {
+ _dataBuffer.edit()._blend = blend;
+ }
+}
+
+
+
+void Skybox::render(gpu::Batch& batch, const ViewFrustum& frustum) const {
+ updateDataBuffer();
+ Skybox::render(batch, frustum, (*this));
+}
+
+
void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Skybox& skybox) {
+ // Create the static shared elements used to render the skybox
static gpu::BufferPointer theBuffer;
static gpu::Stream::FormatPointer theFormat;
-
- if (skybox.getCubemap()) {
- if (!theBuffer) {
+ static gpu::BufferPointer theConstants;
+ static gpu::PipelinePointer thePipeline;
+ const int SKYBOX_SKYMAP_SLOT = 0;
+ const int SKYBOX_CONSTANTS_SLOT = 0;
+ static std::once_flag once;
+ std::call_once(once, [&] {
+ {
const float CLIP = 1.0f;
const glm::vec2 vertices[4] = { { -CLIP, -CLIP }, { CLIP, -CLIP }, { -CLIP, CLIP }, { CLIP, CLIP } };
theBuffer = std::make_shared(sizeof(vertices), (const gpu::Byte*) vertices);
@@ -57,62 +87,49 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
theFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ));
}
- glm::mat4 projMat;
- viewFrustum.evalProjectionMatrix(projMat);
+ {
+ auto skyVS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(Skybox_vert)));
+ auto skyFS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(Skybox_frag)));
+ auto skyShader = gpu::ShaderPointer(gpu::Shader::createProgram(skyVS, skyFS));
- Transform viewTransform;
- viewFrustum.evalViewTransform(viewTransform);
- batch.setProjectionTransform(projMat);
- batch.setViewTransform(viewTransform);
- batch.setModelTransform(Transform()); // only for Mac
- batch.setInputBuffer(gpu::Stream::POSITION, theBuffer, 0, 8);
- batch.setInputFormat(theFormat);
+ gpu::Shader::BindingSet bindings;
+ bindings.insert(gpu::Shader::Binding(std::string("cubeMap"), SKYBOX_SKYMAP_SLOT));
+ bindings.insert(gpu::Shader::Binding(std::string("skyboxBuffer"), SKYBOX_CONSTANTS_SLOT));
+ if (!gpu::Shader::makeProgram(*skyShader, bindings)) {
- if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) {
- static gpu::BufferPointer theConstants;
- static gpu::PipelinePointer thePipeline;
- static int SKYBOX_CONSTANTS_SLOT = 0; // need to be defined by the compilation of the shader
- if (!thePipeline) {
- auto skyVS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(Skybox_vert)));
- auto skyFS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(Skybox_frag)));
- auto skyShader = gpu::ShaderPointer(gpu::Shader::createProgram(skyVS, skyFS));
-
- gpu::Shader::BindingSet bindings;
- bindings.insert(gpu::Shader::Binding(std::string("cubeMap"), 0));
- if (!gpu::Shader::makeProgram(*skyShader, bindings)) {
-
- }
-
- SKYBOX_CONSTANTS_SLOT = skyShader->getBuffers().findLocation("skyboxBuffer");
- if (SKYBOX_CONSTANTS_SLOT == gpu::Shader::INVALID_LOCATION) {
- SKYBOX_CONSTANTS_SLOT = skyShader->getUniforms().findLocation("skyboxBuffer");
- }
-
- auto skyState = std::make_shared();
-
- thePipeline = gpu::PipelinePointer(gpu::Pipeline::create(skyShader, skyState));
-
- auto color = glm::vec4(1.0f);
- theConstants = std::make_shared(sizeof(color), (const gpu::Byte*) &color);
}
- if (glm::all(glm::equal(skybox.getColor(), glm::vec3(0.0f)))) {
- auto color = glm::vec4(1.0f);
- theConstants->setSubData(0, sizeof(color), (const gpu::Byte*) &color);
- } else {
- theConstants->setSubData(0, sizeof(Color), (const gpu::Byte*) &skybox.getColor());
- }
+ auto skyState = std::make_shared();
+ skyState->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
- batch.setPipeline(thePipeline);
- batch.setUniformBuffer(SKYBOX_CONSTANTS_SLOT, theConstants, 0, theConstants->getSize());
- batch.setResourceTexture(0, skybox.getCubemap());
- batch.draw(gpu::TRIANGLE_STRIP, 4);
+ thePipeline = gpu::PipelinePointer(gpu::Pipeline::create(skyShader, skyState));
}
+ });
- } else {
- // skybox has no cubemap, just clear the color buffer
- auto color = skybox.getColor();
- batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(color, 0.0f), 0.0f, 0, true);
+ // Render
+ glm::mat4 projMat;
+ viewFrustum.evalProjectionMatrix(projMat);
+
+ Transform viewTransform;
+ viewFrustum.evalViewTransform(viewTransform);
+ batch.setProjectionTransform(projMat);
+ batch.setViewTransform(viewTransform);
+ batch.setModelTransform(Transform()); // only for Mac
+ batch.setInputBuffer(gpu::Stream::POSITION, theBuffer, 0, 8);
+ batch.setInputFormat(theFormat);
+
+ gpu::TexturePointer skymap;
+ if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) {
+ skymap = skybox.getCubemap();
}
+
+ batch.setPipeline(thePipeline);
+ batch.setUniformBuffer(SKYBOX_CONSTANTS_SLOT, skybox._dataBuffer);
+ batch.setResourceTexture(SKYBOX_SKYMAP_SLOT, skymap);
+
+ batch.draw(gpu::TRIANGLE_STRIP, 4);
+
+ batch.setResourceTexture(SKYBOX_SKYMAP_SLOT, nullptr);
+
}
diff --git a/libraries/model/src/model/Skybox.h b/libraries/model/src/model/Skybox.h
index e9f95afa16..14ba9fa005 100755
--- a/libraries/model/src/model/Skybox.h
+++ b/libraries/model/src/model/Skybox.h
@@ -30,20 +30,28 @@ public:
virtual ~Skybox() {};
void setColor(const Color& color);
- const Color& getColor() const { return _color; }
+ const Color getColor() const { return _dataBuffer.get()._color; }
void setCubemap(const gpu::TexturePointer& cubemap);
const gpu::TexturePointer& getCubemap() const { return _cubemap; }
- virtual void render(gpu::Batch& batch, const ViewFrustum& frustum) const {
- render(batch, frustum, (*this));
- }
+ virtual void render(gpu::Batch& batch, const ViewFrustum& frustum) const;
+
static void render(gpu::Batch& batch, const ViewFrustum& frustum, const Skybox& skybox);
protected:
gpu::TexturePointer _cubemap;
- Color _color{1.0f, 1.0f, 1.0f};
+
+ class Data {
+ public:
+ glm::vec3 _color{ 1.0f, 1.0f, 1.0f };
+ float _blend = 1.0f;
+ };
+
+ mutable gpu::BufferView _dataBuffer;
+
+ void updateDataBuffer() const;
};
typedef std::shared_ptr< Skybox > SkyboxPointer;
diff --git a/libraries/model/src/model/Skybox.slf b/libraries/model/src/model/Skybox.slf
index 382801f52d..6246bbd9d3 100755
--- a/libraries/model/src/model/Skybox.slf
+++ b/libraries/model/src/model/Skybox.slf
@@ -40,8 +40,18 @@ void main(void) {
#else
vec3 coord = normalize(_normal);
- vec3 texel = texture(cubeMap, coord).rgb;
- vec3 color = texel * _skybox._color.rgb;
+
+ // Skybox color or blend with skymap
+ vec3 color = _skybox._color.rgb;
+ if (_skybox._color.a > 0.0) {
+ vec3 texel = texture(cubeMap, coord).rgb;
+ if (_skybox._color.a < 2.0) {
+ color *= texel;
+ } else {
+ color = texel;
+ }
+ }
+
vec3 pixel = pow(color, vec3(1.0/2.2)); // manual Gamma correction
_fragColor = vec4(pixel, 0.0);
diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp
index 5f0db9412c..0422c03297 100644
--- a/libraries/networking/src/ThreadedAssignment.cpp
+++ b/libraries/networking/src/ThreadedAssignment.cpp
@@ -49,7 +49,8 @@ void ThreadedAssignment::setFinished(bool isFinished) {
}
if (_statsTimer) {
- _statsTimer->stop();
+ _statsTimer->deleteLater();
+ _statsTimer = nullptr;
}
// call our virtual aboutToFinish method - this gives the ThreadedAssignment subclass a chance to cleanup
@@ -105,7 +106,7 @@ void ThreadedAssignment::sendStatsPacket() {
void ThreadedAssignment::startSendingStats() {
// send the stats packet every 1s
if (!_statsTimer) {
- _statsTimer = new QTimer();
+ _statsTimer = new QTimer;
connect(_statsTimer, &QTimer::timeout, this, &ThreadedAssignment::sendStatsPacket);
}
diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp
index 1bda840a6c..e8b22f6ab8 100644
--- a/libraries/networking/src/udt/Connection.cpp
+++ b/libraries/networking/src/udt/Connection.cpp
@@ -32,7 +32,7 @@ Connection::Connection(Socket* parentSocket, HifiSockAddr destination, std::uniq
_destination(destination),
_congestionControl(move(congestionControl))
{
- Q_ASSERT_X(socket, "Connection::Connection", "Must be called with a valid Socket*");
+ Q_ASSERT_X(parentSocket, "Connection::Connection", "Must be called with a valid Socket*");
Q_ASSERT_X(_congestionControl, "Connection::Connection", "Must be called with a valid CongestionControl object");
_congestionControl->init();
diff --git a/libraries/networking/src/udt/PacketList.h b/libraries/networking/src/udt/PacketList.h
index ae783dabe3..7978e77ad7 100644
--- a/libraries/networking/src/udt/PacketList.h
+++ b/libraries/networking/src/udt/PacketList.h
@@ -28,7 +28,8 @@ class Packet;
class PacketList : public QIODevice {
Q_OBJECT
public:
- static std::unique_ptr create(PacketType packetType, QByteArray extendedHeader = QByteArray(), bool isReliable = false, bool isOrdered = false);
+ static std::unique_ptr create(PacketType packetType, QByteArray extendedHeader = QByteArray(),
+ bool isReliable = false, bool isOrdered = false);
static std::unique_ptr fromReceivedPackets(std::list>&& packets);
bool isReliable() const { return _isReliable; }
diff --git a/libraries/physics/src/Constraint.h b/libraries/physics/src/Constraint.h
deleted file mode 100644
index ed97d6cc73..0000000000
--- a/libraries/physics/src/Constraint.h
+++ /dev/null
@@ -1,25 +0,0 @@
-//
-// Constraint.h
-// libraries/physics/src
-//
-// Created by Andrew Meadows 2014.07.24
-// Copyright 2014 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
-//
-
-#ifndef hifi_Constraint_h
-#define hifi_Constraint_h
-
-class Constraint {
-public:
- Constraint() {}
- virtual ~Constraint() {}
-
- /// Enforce contraint by moving relevant points.
- /// \return max distance of point movement
- virtual float enforce() = 0;
-};
-
-#endif // hifi_Constraint_h
diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp
index 8d34f0e7e5..1c7e7e457c 100644
--- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp
+++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp
@@ -32,7 +32,8 @@ void ProceduralSkybox::setProcedural(const ProceduralPointer& procedural) {
if (_procedural) {
_procedural->_vertexSource = ProceduralSkybox_vert;
_procedural->_fragmentSource = ProceduralSkybox_frag;
- // No pipeline state customization
+ // Adjust the pipeline state for background using the stencil test
+ _procedural->_state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
}
}
@@ -42,6 +43,7 @@ void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& frustum) con
void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const ProceduralSkybox& skybox) {
if (!(skybox._procedural)) {
+ skybox.updateDataBuffer();
Skybox::render(batch, viewFrustum, skybox);
}
diff --git a/libraries/render-utils/src/Environment.cpp b/libraries/render-utils/src/Environment.cpp
index 7fbd89acc1..b26d402fa3 100644
--- a/libraries/render-utils/src/Environment.cpp
+++ b/libraries/render-utils/src/Environment.cpp
@@ -62,7 +62,7 @@ void Environment::setupAtmosphereProgram(const char* vertSource, const char* fra
auto state = std::make_shared();
state->setCullMode(gpu::State::CULL_NONE);
- state->setDepthTest(false);
+ state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
state->setBlendFunction(true,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
diff --git a/libraries/render-utils/src/FramebufferCache.cpp b/libraries/render-utils/src/FramebufferCache.cpp
index d6ebd001d2..cd81a21f9a 100644
--- a/libraries/render-utils/src/FramebufferCache.cpp
+++ b/libraries/render-utils/src/FramebufferCache.cpp
@@ -35,7 +35,9 @@ void FramebufferCache::setFrameBufferSize(QSize frameBufferSize) {
_frameBufferSize = frameBufferSize;
_primaryFramebufferFull.reset();
_primaryFramebufferDepthColor.reset();
+ _primaryFramebufferStencilColor.reset();
_primaryDepthTexture.reset();
+ _primaryStencilTexture.reset();
_primaryColorTexture.reset();
_primaryNormalTexture.reset();
_primarySpecularTexture.reset();
@@ -47,6 +49,7 @@ void FramebufferCache::setFrameBufferSize(QSize frameBufferSize) {
void FramebufferCache::createPrimaryFramebuffer() {
_primaryFramebufferFull = gpu::FramebufferPointer(gpu::Framebuffer::create());
_primaryFramebufferDepthColor = gpu::FramebufferPointer(gpu::Framebuffer::create());
+ _primaryFramebufferStencilColor = gpu::FramebufferPointer(gpu::Framebuffer::create());
auto colorFormat = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
auto width = _frameBufferSize.width();
@@ -63,12 +66,19 @@ void FramebufferCache::createPrimaryFramebuffer() {
_primaryFramebufferDepthColor->setRenderBuffer(0, _primaryColorTexture);
+ _primaryFramebufferStencilColor->setRenderBuffer(0, _primaryColorTexture);
+
auto depthFormat = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH);
_primaryDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, width, height, defaultSampler));
+ auto stencilFormat = gpu::Element(gpu::VEC2, gpu::UINT32, gpu::DEPTH_STENCIL);
+ _primaryStencilTexture = gpu::TexturePointer(gpu::Texture::create2D(stencilFormat, width, height, defaultSampler));
+
_primaryFramebufferFull->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
_primaryFramebufferDepthColor->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
+
+ _primaryFramebufferStencilColor->setDepthStencilBuffer(_primaryStencilTexture, stencilFormat);
_selfieFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
auto tex = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width * 0.5, height * 0.5, defaultSampler));
@@ -89,6 +99,12 @@ gpu::FramebufferPointer FramebufferCache::getPrimaryFramebufferDepthColor() {
return _primaryFramebufferDepthColor;
}
+gpu::FramebufferPointer FramebufferCache::getPrimaryFramebufferStencilColor() {
+ if (!_primaryFramebufferStencilColor) {
+ createPrimaryFramebuffer();
+ }
+ return _primaryFramebufferStencilColor;
+}
gpu::TexturePointer FramebufferCache::getPrimaryDepthTexture() {
if (!_primaryDepthTexture) {
@@ -97,6 +113,13 @@ gpu::TexturePointer FramebufferCache::getPrimaryDepthTexture() {
return _primaryDepthTexture;
}
+gpu::TexturePointer FramebufferCache::getPrimaryStencilTexture() {
+ if (!_primaryStencilTexture) {
+ createPrimaryFramebuffer();
+ }
+ return _primaryStencilTexture;
+}
+
gpu::TexturePointer FramebufferCache::getPrimaryColorTexture() {
if (!_primaryColorTexture) {
createPrimaryFramebuffer();
diff --git a/libraries/render-utils/src/FramebufferCache.h b/libraries/render-utils/src/FramebufferCache.h
index c2274a77e8..8951ceee80 100644
--- a/libraries/render-utils/src/FramebufferCache.h
+++ b/libraries/render-utils/src/FramebufferCache.h
@@ -31,8 +31,10 @@ public:
/// used for scene rendering.
gpu::FramebufferPointer getPrimaryFramebuffer();
gpu::FramebufferPointer getPrimaryFramebufferDepthColor();
+ gpu::FramebufferPointer getPrimaryFramebufferStencilColor();
gpu::TexturePointer getPrimaryDepthTexture();
+ gpu::TexturePointer getPrimaryStencilTexture();
gpu::TexturePointer getPrimaryColorTexture();
gpu::TexturePointer getPrimaryNormalTexture();
gpu::TexturePointer getPrimarySpecularTexture();
@@ -58,7 +60,9 @@ private:
gpu::FramebufferPointer _primaryFramebufferFull;
gpu::FramebufferPointer _primaryFramebufferDepthColor;
+ gpu::FramebufferPointer _primaryFramebufferStencilColor;
gpu::TexturePointer _primaryDepthTexture;
+ gpu::TexturePointer _primaryStencilTexture;
gpu::TexturePointer _primaryColorTexture;
gpu::TexturePointer _primaryNormalTexture;
gpu::TexturePointer _primarySpecularTexture;
diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index 2f16259f89..90dbe6524c 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -68,7 +68,6 @@ Model::Model(RigPointer rig, QObject* parent) :
_scaledToFit(false),
_snapModelToRegistrationPoint(false),
_snappedToRegistrationPoint(false),
- _showTrueJointTransforms(true),
_cauterizeBones(false),
_pupilDilation(0.0f),
_url(HTTP_INVALID_COM),
@@ -998,10 +997,6 @@ bool Model::getJointState(int index, glm::quat& rotation) const {
return _rig->getJointStateRotation(index, rotation);
}
-bool Model::getVisibleJointState(int index, glm::quat& rotation) const {
- return _rig->getVisibleJointState(index, rotation);
-}
-
void Model::clearJointState(int index) {
_rig->clearJointState(index);
}
@@ -1083,14 +1078,6 @@ bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const
return _rig->getJointCombinedRotation(jointIndex, rotation, _rotation);
}
-bool Model::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const {
- return _rig->getVisibleJointPositionInWorldFrame(jointIndex, position, _translation, _rotation);
-}
-
-bool Model::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const {
- return _rig->getVisibleJointRotationInWorldFrame(jointIndex, rotation, _rotation);
-}
-
QStringList Model::getJointNames() const {
if (QThread::currentThread() != thread()) {
QStringList result;
@@ -1294,33 +1281,17 @@ void Model::updateClusterMatrices() {
for (int i = 0; i < _meshStates.size(); i++) {
MeshState& state = _meshStates[i];
const FBXMesh& mesh = geometry.meshes.at(i);
- if (_showTrueJointTransforms) {
- for (int j = 0; j < mesh.clusters.size(); j++) {
- const FBXCluster& cluster = mesh.clusters.at(j);
- auto jointMatrix = _rig->getJointTransform(cluster.jointIndex);
- state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix;
+ for (int j = 0; j < mesh.clusters.size(); j++) {
+ const FBXCluster& cluster = mesh.clusters.at(j);
+ auto jointMatrix = _rig->getJointTransform(cluster.jointIndex);
+ state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix;
- // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty.
- if (!_cauterizeBoneSet.empty()) {
- if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) {
- jointMatrix = cauterizeMatrix;
- }
- state.cauterizedClusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix;
- }
- }
- } else {
- for (int j = 0; j < mesh.clusters.size(); j++) {
- const FBXCluster& cluster = mesh.clusters.at(j);
- auto jointMatrix = _rig->getJointVisibleTransform(cluster.jointIndex); // differs from above only in using get...VisibleTransform
- state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix;
-
- // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty.
- if (!_cauterizeBoneSet.empty()) {
- if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) {
- jointMatrix = cauterizeMatrix;
- }
- state.cauterizedClusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix;
+ // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty.
+ if (!_cauterizeBoneSet.empty()) {
+ if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) {
+ jointMatrix = cauterizeMatrix;
}
+ state.cauterizedClusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix;
}
}
}
@@ -1533,13 +1504,6 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, int shape
pickPrograms(batch, mode, translucentMesh, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe,
args, locations);
- {
- if (!_showTrueJointTransforms) {
- PerformanceTimer perfTimer("_rig->updateVisibleJointStates()");
- _rig->updateVisibleJointStates();
- } // else no need to update visible transforms
- }
-
// if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown
// to false to rebuild out mesh groups.
if (meshIndex < 0 || meshIndex >= (int)networkMeshes.size() || meshIndex > geometry.meshes.size()) {
diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h
index e069b10954..2620310857 100644
--- a/libraries/render-utils/src/Model.h
+++ b/libraries/render-utils/src/Model.h
@@ -221,26 +221,17 @@ protected:
/// \return whether or not the joint state is "valid" (that is, non-default)
bool getJointState(int index, glm::quat& rotation) const;
- /// Fetches the visible joint state at the specified index.
- /// \return whether or not the joint state is "valid" (that is, non-default)
- bool getVisibleJointState(int index, glm::quat& rotation) const;
-
/// Clear the joint states
void clearJointState(int index);
/// Returns the index of the last free ancestor of the indexed joint, or -1 if not found.
int getLastFreeJointIndex(int jointIndex) const;
- bool getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const;
- bool getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const;
-
/// \param jointIndex index of joint in model structure
/// \param position[out] position of joint in model-frame
/// \return true if joint exists
bool getJointPosition(int jointIndex, glm::vec3& position) const;
- void setShowTrueJointTransforms(bool show) { _showTrueJointTransforms = show; }
-
QSharedPointer _geometry;
void setGeometry(const QSharedPointer& newGeometry);
@@ -259,8 +250,6 @@ protected:
bool _snappedToRegistrationPoint; /// are we currently snapped to a registration point
glm::vec3 _registrationPoint = glm::vec3(0.5f); /// the point in model space our center is snapped to
- bool _showTrueJointTransforms;
-
class MeshState {
public:
QVector clusterMatrices;
diff --git a/libraries/render-utils/src/OffscreenQmlSurface.cpp b/libraries/render-utils/src/OffscreenQmlSurface.cpp
index 9923849aab..3f940d8569 100644
--- a/libraries/render-utils/src/OffscreenQmlSurface.cpp
+++ b/libraries/render-utils/src/OffscreenQmlSurface.cpp
@@ -496,6 +496,13 @@ QPointF OffscreenQmlSurface::mapWindowToUi(const QPointF& sourcePosition, QObjec
return QPointF(offscreenPosition.x, offscreenPosition.y);
}
+QPointF OffscreenQmlSurface::mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget) {
+ QPointF transformedPos = _mouseTranslator(originalPoint);
+ transformedPos = mapWindowToUi(transformedPos, originalWidget);
+ return transformedPos;
+}
+
+
///////////////////////////////////////////////////////
//
// Event handling customization
@@ -541,8 +548,9 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even
case QEvent::Wheel: {
QWheelEvent* wheelEvent = static_cast(event);
+ QPointF transformedPos = mapToVirtualScreen(wheelEvent->pos(), originalDestination);
QWheelEvent mappedEvent(
- mapWindowToUi(wheelEvent->pos(), originalDestination),
+ transformedPos,
wheelEvent->delta(), wheelEvent->buttons(),
wheelEvent->modifiers(), wheelEvent->orientation());
mappedEvent.ignore();
@@ -558,9 +566,7 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even
case QEvent::MouseButtonRelease:
case QEvent::MouseMove: {
QMouseEvent* mouseEvent = static_cast(event);
- QPointF originalPos = mouseEvent->localPos();
- QPointF transformedPos = _mouseTranslator(originalPos);
- transformedPos = mapWindowToUi(transformedPos, originalDestination);
+ QPointF transformedPos = mapToVirtualScreen(mouseEvent->localPos(), originalDestination);
QMouseEvent mappedEvent(mouseEvent->type(),
transformedPos,
mouseEvent->screenPos(), mouseEvent->button(),
diff --git a/libraries/render-utils/src/OffscreenQmlSurface.h b/libraries/render-utils/src/OffscreenQmlSurface.h
index 13e467bccd..01dd2b88f9 100644
--- a/libraries/render-utils/src/OffscreenQmlSurface.h
+++ b/libraries/render-utils/src/OffscreenQmlSurface.h
@@ -61,6 +61,7 @@ public:
QQuickWindow* getWindow();
QObject* getEventHandler();
+ QPointF mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget);
virtual bool eventFilter(QObject* originalDestination, QEvent* event);
signals:
diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp
index 65f77689c3..0f79dc8b8d 100755
--- a/libraries/render-utils/src/RenderDeferredTask.cpp
+++ b/libraries/render-utils/src/RenderDeferredTask.cpp
@@ -15,6 +15,7 @@
#include
#include
#include
+#include
#include "FramebufferCache.h"
#include "DeferredLightingEffect.h"
@@ -28,21 +29,27 @@
#include "overlay3D_vert.h"
#include "overlay3D_frag.h"
+#include "drawOpaqueStencil_frag.h"
+
using namespace render;
void SetupDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
RenderArgs* args = renderContext->args;
gpu::doInBatch(args->_context, [=](gpu::Batch& batch) {
+ auto primaryFboStencil = DependencyManager::get()->getPrimaryFramebufferStencilColor();
auto primaryFbo = DependencyManager::get()->getPrimaryFramebufferDepthColor();
batch.enableStereo(false);
- batch.setFramebuffer(nullptr);
- batch.setFramebuffer(primaryFbo);
-
batch.setViewportTransform(args->_viewport);
batch.setStateScissorRect(args->_viewport);
+ batch.setFramebuffer(primaryFboStencil);
+ batch.clearFramebuffer(
+ gpu::Framebuffer::BUFFER_STENCIL,
+ vec4(vec3(0), 1), 1.0, 0.0, true);
+
+ batch.setFramebuffer(primaryFbo);
batch.clearFramebuffer(
gpu::Framebuffer::BUFFER_COLOR0 |
gpu::Framebuffer::BUFFER_DEPTH,
@@ -65,7 +72,6 @@ void ResolveDeferred::run(const SceneContextPointer& sceneContext, const RenderC
RenderDeferredTask::RenderDeferredTask() : Task() {
_jobs.push_back(Job(new SetupDeferred::JobModel("SetupFramebuffer")));
- _jobs.push_back(Job(new DrawBackground::JobModel("DrawBackground")));
_jobs.push_back(Job(new PrepareDeferred::JobModel("PrepareDeferred")));
_jobs.push_back(Job(new FetchItems::JobModel("FetchOpaque",
@@ -79,7 +85,10 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
_jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortOpaque", _jobs.back().getOutput())));
auto& renderedOpaques = _jobs.back().getOutput();
_jobs.push_back(Job(new DrawOpaqueDeferred::JobModel("DrawOpaqueDeferred", _jobs.back().getOutput())));
-
+
+ _jobs.push_back(Job(new DrawStencilDeferred::JobModel("DrawOpaqueStencil")));
+ _jobs.push_back(Job(new DrawBackgroundDeferred::JobModel("DrawBackgroundDeferred")));
+
_jobs.push_back(Job(new DrawLight::JobModel("DrawLight")));
_jobs.push_back(Job(new RenderDeferred::JobModel("RenderDeferred")));
_jobs.push_back(Job(new ResolveDeferred::JobModel("ResolveDeferred")));
@@ -226,7 +235,9 @@ const gpu::PipelinePointer& DrawOverlay3D::getOpaquePipeline() {
auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps));
auto state = std::make_shared();
- state->setDepthTest(true, true, gpu::LESS_EQUAL);
+ state->setDepthTest(false);
+ // additive blending
+ state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
_opaquePipeline.reset(gpu::Pipeline::create(program, state));
}
@@ -289,3 +300,95 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon
args->_whiteTexture.reset();
}
}
+
+gpu::PipelinePointer DrawStencilDeferred::_opaquePipeline;
+const gpu::PipelinePointer& DrawStencilDeferred::getOpaquePipeline() {
+ if (!_opaquePipeline) {
+ const gpu::int8 STENCIL_OPAQUE = 1;
+ auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
+ auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(drawOpaqueStencil_frag)));
+ auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps));
+
+
+ gpu::Shader::makeProgram((*program));
+
+ auto state = std::make_shared();
+ state->setStencilTest(true, 0xFF, gpu::State::StencilTest(STENCIL_OPAQUE, 0xFF, gpu::ALWAYS, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_REPLACE, gpu::State::STENCIL_OP_REPLACE));
+ state->setColorWriteMask(0);
+
+ _opaquePipeline.reset(gpu::Pipeline::create(program, state));
+ }
+ return _opaquePipeline;
+}
+
+void DrawStencilDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
+ assert(renderContext->args);
+ assert(renderContext->args->_viewFrustum);
+
+ // from the touched pixel generate the stencil buffer
+ RenderArgs* args = renderContext->args;
+ doInBatch(args->_context, [=](gpu::Batch& batch) {
+ args->_batch = &batch;
+
+ auto primaryFboColorDepthStencil = DependencyManager::get()->getPrimaryFramebufferStencilColor();
+ auto primaryDepth = DependencyManager::get()->getPrimaryDepthTexture();
+
+ batch.enableStereo(false);
+
+ batch.setFramebuffer(primaryFboColorDepthStencil);
+ batch.setViewportTransform(args->_viewport);
+ batch.setStateScissorRect(args->_viewport);
+
+ batch.setPipeline(getOpaquePipeline());
+ batch.setResourceTexture(0, primaryDepth);
+
+ batch.draw(gpu::TRIANGLE_STRIP, 4);
+ batch.setResourceTexture(0, nullptr);
+
+ });
+ args->_batch = nullptr;
+}
+
+void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
+ assert(renderContext->args);
+ assert(renderContext->args->_viewFrustum);
+
+ // render backgrounds
+ auto& scene = sceneContext->_scene;
+ auto& items = scene->getMasterBucket().at(ItemFilter::Builder::background());
+
+
+ ItemIDsBounds inItems;
+ inItems.reserve(items.size());
+ for (auto id : items) {
+ inItems.emplace_back(id);
+ }
+ RenderArgs* args = renderContext->args;
+ doInBatch(args->_context, [=](gpu::Batch& batch) {
+ args->_batch = &batch;
+
+ auto primaryFboColorStencil = DependencyManager::get()->getPrimaryFramebufferStencilColor();
+ auto primaryFboFull = DependencyManager::get()->getPrimaryFramebuffer();
+
+ batch.enableSkybox(true);
+
+ batch.setFramebuffer(primaryFboColorStencil);
+
+ batch.setViewportTransform(args->_viewport);
+ batch.setStateScissorRect(args->_viewport);
+
+ glm::mat4 projMat;
+ Transform viewMat;
+ args->_viewFrustum->evalProjectionMatrix(projMat);
+ args->_viewFrustum->evalViewTransform(viewMat);
+
+ batch.setProjectionTransform(projMat);
+ batch.setViewTransform(viewMat);
+
+ renderItems(sceneContext, renderContext, inItems);
+
+ batch.setFramebuffer(primaryFboFull);
+
+ });
+ args->_batch = nullptr;
+}
\ No newline at end of file
diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h
index 8366a2665d..04483fc037 100755
--- a/libraries/render-utils/src/RenderDeferredTask.h
+++ b/libraries/render-utils/src/RenderDeferredTask.h
@@ -59,6 +59,23 @@ public:
typedef render::Job::ModelI JobModel;
};
+class DrawStencilDeferred {
+ static gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable
+public:
+ static const gpu::PipelinePointer& getOpaquePipeline();
+
+ void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
+
+ typedef render::Job::Model JobModel;
+};
+
+class DrawBackgroundDeferred {
+public:
+ void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
+
+ typedef render::Job::Model JobModel;
+};
+
class DrawOverlay3D {
static gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable
public:
diff --git a/libraries/render-utils/src/drawOpaqueStencil.slf b/libraries/render-utils/src/drawOpaqueStencil.slf
new file mode 100644
index 0000000000..14feda21e9
--- /dev/null
+++ b/libraries/render-utils/src/drawOpaqueStencil.slf
@@ -0,0 +1,24 @@
+<@include gpu/Config.slh@>
+<$VERSION_HEADER$>
+// Generated on <$_SCRIBE_DATE$>
+//
+// drawOpaqueStencil.frag
+// fragment shader
+//
+// Created by Sam Gateau on 9/29/15.
+// 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
+//
+
+in vec2 varTexCoord0;
+
+uniform sampler2D depthTexture;
+
+void main(void) {
+ float depth = texture(depthTexture, varTexCoord0.xy).r;
+ if (depth >= 1.0) {
+ discard;
+ }
+}
diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h
index ec6656c0dc..b7a03b81e2 100755
--- a/libraries/render/src/render/DrawTask.h
+++ b/libraries/render/src/render/DrawTask.h
@@ -257,6 +257,7 @@ class DrawBackground {
public:
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
+
typedef Job::Model JobModel;
};
diff --git a/libraries/shared/src/AngularConstraint.cpp b/libraries/shared/src/AngularConstraint.cpp
deleted file mode 100644
index 62cdca67fd..0000000000
--- a/libraries/shared/src/AngularConstraint.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-//
-// AngularConstraint.cpp
-// interface/src/renderer
-//
-// Created by Andrew Meadows on 2014.05.30
-// Copyright 2014 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
-//
-
-#include
-
-#include "AngularConstraint.h"
-#include "GLMHelpers.h"
-#include "NumericalConstants.h"
-
-// helper function
-/// \param angle radian angle to be clamped within angleMin and angleMax
-/// \param angleMin minimum value
-/// \param angleMax maximum value
-/// \return value between minAngle and maxAngle closest to angle
-float clampAngle(float angle, float angleMin, float angleMax) {
- float minDistance = angle - angleMin;
- float maxDistance = angle - angleMax;
- if (maxDistance > 0.0f) {
- minDistance = glm::min(minDistance, angleMin + TWO_PI - angle);
- angle = (minDistance < maxDistance) ? angleMin : angleMax;
- } else if (minDistance < 0.0f) {
- maxDistance = glm::max(maxDistance, angleMax - TWO_PI - angle);
- angle = (minDistance > maxDistance) ? angleMin : angleMax;
- }
- return angle;
-}
-
-// static
-AngularConstraint* AngularConstraint::newAngularConstraint(const glm::vec3& minAngles, const glm::vec3& maxAngles) {
- float minDistance2 = glm::distance2(minAngles, glm::vec3(-PI, -PI, -PI));
- float maxDistance2 = glm::distance2(maxAngles, glm::vec3(PI, PI, PI));
- if (minDistance2 < EPSILON && maxDistance2 < EPSILON) {
- // no constraint
- return NULL;
- }
- // count the zero length elements
- glm::vec3 rangeAngles = maxAngles - minAngles;
- int pivotIndex = -1;
- int numZeroes = 0;
- for (int i = 0; i < 3; ++i) {
- if (rangeAngles[i] < EPSILON) {
- ++numZeroes;
- } else {
- pivotIndex = i;
- }
- }
- if (numZeroes == 2) {
- // this is a hinge
- int forwardIndex = (pivotIndex + 1) % 3;
- glm::vec3 forwardAxis(0.0f);
- forwardAxis[forwardIndex] = 1.0f;
- glm::vec3 rotationAxis(0.0f);
- rotationAxis[pivotIndex] = 1.0f;
- return new HingeConstraint(forwardAxis, rotationAxis, minAngles[pivotIndex], maxAngles[pivotIndex]);
- } else if (numZeroes == 0) {
- // approximate the angular limits with a cone roller
- // we assume the roll is about z
- glm::vec3 middleAngles = 0.5f * (maxAngles + minAngles);
- glm::quat yaw = glm::angleAxis(middleAngles[1], glm::vec3(0.0f, 1.0f, 0.0f));
- glm::quat pitch = glm::angleAxis(middleAngles[0], glm::vec3(1.0f, 0.0f, 0.0f));
- glm::vec3 coneAxis = pitch * yaw * glm::vec3(0.0f, 0.0f, 1.0f);
- // the coneAngle is half the average range of the two non-roll rotations
- glm::vec3 range = maxAngles - minAngles;
- float coneAngle = 0.25f * (range[0] + range[1]);
- return new ConeRollerConstraint(coneAngle, coneAxis, minAngles.z, maxAngles.z);
- }
- return NULL;
-}
-
-bool AngularConstraint::softClamp(glm::quat& targetRotation, const glm::quat& oldRotation, float mixFraction) {
- glm::quat clampedTarget = targetRotation;
- bool clamped = clamp(clampedTarget);
- if (clamped) {
- // check if oldRotation is also clamped
- glm::quat clampedOld = oldRotation;
- bool clamped2 = clamp(clampedOld);
- if (clamped2) {
- // oldRotation is already beyond the constraint
- // we clamp again midway between targetRotation and clamped oldPosition
- clampedTarget = glm::shortMix(clampedOld, targetRotation, mixFraction);
- // and then clamp that
- clamp(clampedTarget);
- }
- // finally we mix targetRotation with the clampedTarget
- targetRotation = glm::shortMix(clampedTarget, targetRotation, mixFraction);
- }
- return clamped;
-}
-
-HingeConstraint::HingeConstraint(const glm::vec3& forwardAxis, const glm::vec3& rotationAxis, float minAngle, float maxAngle)
- : _minAngle(minAngle), _maxAngle(maxAngle) {
- assert(_minAngle < _maxAngle);
- // we accept the rotationAxis direction
- assert(glm::length(rotationAxis) > EPSILON);
- _rotationAxis = glm::normalize(rotationAxis);
- // but we compute the final _forwardAxis
- glm::vec3 otherAxis = glm::cross(_rotationAxis, forwardAxis);
- assert(glm::length(otherAxis) > EPSILON);
- _forwardAxis = glm::normalize(glm::cross(otherAxis, _rotationAxis));
-}
-
-// virtual
-bool HingeConstraint::clamp(glm::quat& rotation) const {
- glm::vec3 forward = rotation * _forwardAxis;
- forward -= glm::dot(forward, _rotationAxis) * _rotationAxis;
- float length = glm::length(forward);
- if (length < EPSILON) {
- // infinite number of solutions ==> choose the middle of the contrained range
- rotation = glm::angleAxis(0.5f * (_minAngle + _maxAngle), _rotationAxis);
- return true;
- }
- forward /= length;
- float sign = (glm::dot(glm::cross(_forwardAxis, forward), _rotationAxis) > 0.0f ? 1.0f : -1.0f);
- //float angle = sign * acos(glm::dot(forward, _forwardAxis) / length);
- float angle = sign * acosf(glm::dot(forward, _forwardAxis));
- glm::quat newRotation = glm::angleAxis(clampAngle(angle, _minAngle, _maxAngle), _rotationAxis);
- if (fabsf(1.0f - glm::dot(newRotation, rotation)) > EPSILON * EPSILON) {
- rotation = newRotation;
- return true;
- }
- return false;
-}
-
-bool HingeConstraint::softClamp(glm::quat& targetRotation, const glm::quat& oldRotation, float mixFraction) {
- // the hinge works best without a soft clamp
- return clamp(targetRotation);
-}
-
-ConeRollerConstraint::ConeRollerConstraint(float coneAngle, const glm::vec3& coneAxis, float minRoll, float maxRoll)
- : _coneAngle(coneAngle), _minRoll(minRoll), _maxRoll(maxRoll) {
- assert(_maxRoll >= _minRoll);
- float axisLength = glm::length(coneAxis);
- assert(axisLength > EPSILON);
- _coneAxis = coneAxis / axisLength;
-}
-
-// virtual
-bool ConeRollerConstraint::clamp(glm::quat& rotation) const {
- bool applied = false;
- glm::vec3 rotatedAxis = rotation * _coneAxis;
- glm::vec3 perpAxis = glm::cross(rotatedAxis, _coneAxis);
- float perpAxisLength = glm::length(perpAxis);
- if (perpAxisLength > EPSILON) {
- perpAxis /= perpAxisLength;
- // enforce the cone
- float angle = acosf(glm::dot(rotatedAxis, _coneAxis));
- if (angle > _coneAngle) {
- rotation = glm::angleAxis(angle - _coneAngle, perpAxis) * rotation;
- rotatedAxis = rotation * _coneAxis;
- applied = true;
- }
- } else {
- // the rotation is 100% roll
- // there is no obvious perp axis so we must pick one
- perpAxis = rotatedAxis;
- // find the first non-zero element:
- float iValue = 0.0f;
- int i = 0;
- for (i = 0; i < 3; ++i) {
- if (fabsf(perpAxis[i]) > EPSILON) {
- iValue = perpAxis[i];
- break;
- }
- }
- assert(i != 3);
- // swap or negate the next element
- int j = (i + 1) % 3;
- float jValue = perpAxis[j];
- if (fabsf(jValue - iValue) > EPSILON) {
- perpAxis[i] = jValue;
- perpAxis[j] = iValue;
- } else {
- perpAxis[i] = -iValue;
- }
- perpAxis = glm::cross(perpAxis, rotatedAxis);
- perpAxisLength = glm::length(perpAxis);
- assert(perpAxisLength > EPSILON);
- perpAxis /= perpAxisLength;
- }
- // measure the roll
- // NOTE: perpAxis is perpendicular to both _coneAxis and rotatedConeAxis, so we can
- // rotate it again and we'll end up with an something that has only been rolled.
- glm::vec3 rolledPerpAxis = rotation * perpAxis;
- float sign = glm::dot(rotatedAxis, glm::cross(perpAxis, rolledPerpAxis)) > 0.0f ? 1.0f : -1.0f;
- float roll = sign * angleBetween(rolledPerpAxis, perpAxis);
- if (roll < _minRoll || roll > _maxRoll) {
- float clampedRoll = clampAngle(roll, _minRoll, _maxRoll);
- rotation = glm::normalize(glm::angleAxis(clampedRoll - roll, rotatedAxis) * rotation);
- applied = true;
- }
- return applied;
-}
-
-
diff --git a/libraries/shared/src/AngularConstraint.h b/libraries/shared/src/AngularConstraint.h
deleted file mode 100644
index 74d3fdb82b..0000000000
--- a/libraries/shared/src/AngularConstraint.h
+++ /dev/null
@@ -1,54 +0,0 @@
-//
-// AngularConstraint.h
-// interface/src/renderer
-//
-// Created by Andrew Meadows on 2014.05.30
-// Copyright 2013 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
-//
-
-#ifndef hifi_AngularConstraint_h
-#define hifi_AngularConstraint_h
-
-#include
-
-class AngularConstraint {
-public:
- /// \param minAngles minumum euler angles for the constraint
- /// \param maxAngles minumum euler angles for the constraint
- /// \return pointer to new AngularConstraint of the right type or NULL if none could be made
- static AngularConstraint* newAngularConstraint(const glm::vec3& minAngles, const glm::vec3& maxAngles);
-
- AngularConstraint() {}
- virtual ~AngularConstraint() {}
- virtual bool clamp(glm::quat& rotation) const = 0;
- virtual bool softClamp(glm::quat& targetRotation, const glm::quat& oldRotation, float mixFraction);
-protected:
-};
-
-class HingeConstraint : public AngularConstraint {
-public:
- HingeConstraint(const glm::vec3& forwardAxis, const glm::vec3& rotationAxis, float minAngle, float maxAngle);
- virtual bool clamp(glm::quat& rotation) const;
- virtual bool softClamp(glm::quat& targetRotation, const glm::quat& oldRotation, float mixFraction);
-protected:
- glm::vec3 _forwardAxis;
- glm::vec3 _rotationAxis;
- float _minAngle;
- float _maxAngle;
-};
-
-class ConeRollerConstraint : public AngularConstraint {
-public:
- ConeRollerConstraint(float coneAngle, const glm::vec3& coneAxis, float minRoll, float maxRoll);
- virtual bool clamp(glm::quat& rotation) const;
-private:
- float _coneAngle;
- glm::vec3 _coneAxis;
- float _minRoll;
- float _maxRoll;
-};
-
-#endif // hifi_AngularConstraint_h
diff --git a/libraries/shared/src/Interpolate.cpp b/libraries/shared/src/Interpolate.cpp
index 7c18236bd0..bef69c9a33 100644
--- a/libraries/shared/src/Interpolate.cpp
+++ b/libraries/shared/src/Interpolate.cpp
@@ -11,41 +11,50 @@
#include "Interpolate.h"
-float Interpolate::cubicInterpolate2Points(float y0, float y1, float y2, float y3, float u) {
- float a0, a1, a2, a3, uu, uuu;
+#include
+#include
- a0 = y3 - y2 - y0 + y1;
- a1 = y0 - y1 - a0;
- a2 = y2 - y0;
- a3 = y1;
-
- uu = u * u;
- uuu = uu * u;
-
- return (a0 * uuu + a1 * uu + a2 * u + a3);
+float Interpolate::bezierInterpolate(float y1, float y2, float y3, float u) {
+ // https://en.wikipedia.org/wiki/Bezier_curve
+ assert(0.0f <= u && u <= 1.0f);
+ return (1.0f - u) * (1.0f - u) * y1 + 2.0f * (1.0f - u) * u * y2 + u * u * y3;
}
-float Interpolate::cubicInterpolate3Points(float y1, float y2, float y3, float u) {
- float y0, y4;
+float Interpolate::interpolate3Points(float y1, float y2, float y3, float u) {
+ assert(0.0f <= u && u <= 1.0f);
- if (u <= 0.5f) {
- if (y1 == y2) {
- return y2;
+ if ((u <= 0.5f && y1 == y2) || (u >= 0.5f && y2 == y3)) {
+ // Flat line.
+ return y2;
+ }
+
+ if ((y2 >= y1 && y2 >= y3) || (y2 <= y1 && y2 <= y3)) {
+ // U or inverted-U shape.
+ // Make the slope at y2 = 0, which means that the control points half way between the value points have the value y2.
+ if (u <= 0.5f) {
+ return bezierInterpolate(y1, y2, y2, 2.0f * u);
+ } else {
+ return bezierInterpolate(y2, y2, y3, 2.0f * u - 1.0f);
}
- y0 = 2.0f * y1 - y2; // y0 is linear extension of line from y2 to y1.
- u = 2.0f * u;
-
- return Interpolate::cubicInterpolate2Points(y0, y1, y2, y3, u);
-
} else {
- if (y2 == y3) {
- return y2;
+ // L or inverted and/or mirrored L shape.
+ // Make the slope at y2 be the slope between y1 and y3, up to a maximum of double the minimum of the slopes between y1
+ // and y2, and y2 and y3. Use this slope to calculate the control points half way between the value points.
+ // Note: The maximum ensures that the control points and therefore the interpolated values stay between y1 and y3.
+ float slope = y3 - y1;
+ float slope12 = y2 - y1;
+ float slope23 = y3 - y2;
+ if (fabsf(slope) > fabsf(2.0f * slope12)) {
+ slope = 2.0f * slope12;
+ } else if (fabsf(slope) > fabsf(2.0f * slope23)) {
+ slope = 2.0f * slope23;
}
- y4 = 2.0f * y3 - y2; // y4 is linear extension of line from y2 to y3.
- u = 2.0f * u - 1.0f;
-
- return Interpolate::cubicInterpolate2Points(y1, y2, y3, y4, u);
+ if (u <= 0.5f) {
+ return bezierInterpolate(y1, y2 - slope / 2.0f, y2, 2.0f * u);
+ } else {
+ return bezierInterpolate(y2, y2 + slope / 2.0f, y3, 2.0f * u - 1.0f);
+ }
}
}
diff --git a/libraries/shared/src/Interpolate.h b/libraries/shared/src/Interpolate.h
index 919d075b15..316bee1339 100644
--- a/libraries/shared/src/Interpolate.h
+++ b/libraries/shared/src/Interpolate.h
@@ -15,11 +15,13 @@
class Interpolate {
public:
- // Cubic interpolation at position u [0.0 - 1.0] between values y1 and y2 with equidistant values y0 and y3 either side.
- static float cubicInterpolate2Points(float y0, float y1, float y2, float y3, float u);
+ // Bezier interpolate at position u [0.0 - 1.0] between y values equally spaced along the x-axis. The interpolated values
+ // pass through y1 and y3 but not y2; y2 is the Bezier control point.
+ static float bezierInterpolate(float y1, float y2, float y3, float u);
- // Cubic interpolation at position u [0.0 - 1.0] between values y1 and y3 with midpoint value y2.
- static float cubicInterpolate3Points(float y1, float y2, float y3, float u);
+ // Interpolate at position u [0.0 - 1.0] between y values equally spaced along the x-axis such that the interpolated values
+ // pass through all three y values. Return value lies wholly within the range of y values passed in.
+ static float interpolate3Points(float y1, float y2, float y3, float u);
};
#endif // hifi_Interpolate_h
diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js
new file mode 100644
index 0000000000..d6759e2b48
--- /dev/null
+++ b/unpublishedScripts/masterReset.js
@@ -0,0 +1,834 @@
+// masterReset.js
+// Created by Eric Levin on 9/23/2015
+// 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
+//
+
+/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */
+//per script
+/*global deleteAllToys, createAllToys, createGates, createPingPongBallGun, createFire, createPottedPlant, createCombinedArmChair, createBasketballHoop, createBasketBall, createSprayCan, createDoll, createWand, createDice, createCat, deleteAllToys, createFlashlight, createBlocks, createMagballs, createLightSwitches */
+var utilitiesScript = Script.resolvePath("../examples/libraries/utils.js");
+Script.include(utilitiesScript);
+
+var resetKey = "resetMe";
+var GRABBABLE_DATA_KEY = "grabbableKey";
+
+var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
+
+var shouldDeleteOnEndScript = false;
+
+//Before creating anything, first search a radius and delete all the things that should be deleted
+deleteAllToys();
+createAllToys();
+
+function createAllToys() {
+ createBlocks({
+ x: 548.3,
+ y: 495.55,
+ z: 504.4
+ });
+
+ createSprayCan({
+ x: 549.7,
+ y: 495.6,
+ z: 503.91
+ });
+
+ createBasketBall({
+ x: 547.73,
+ y: 495.5,
+ z: 505.47
+ });
+
+ createDoll({
+ x: 546.67,
+ y: 495.41,
+ z: 505.09
+ });
+
+ createWand({
+ x: 546.71,
+ y: 495.55,
+ z: 506.15
+ });
+
+ createDice();
+
+ createFlashlight({
+ x: 545.72,
+ y: 495.41,
+ z: 505.78
+ });
+
+ createCat({
+ x: 551.09,
+ y: 494.98,
+ z: 503.49
+ });
+
+
+ createCombinedArmChair({
+ x: 549.29,
+ y: 495.05,
+ z: 508.22
+ });
+
+ createPottedPlant({
+ x: 554.26,
+ y: 495.23,
+ z: 504.53
+ });
+
+ createPingPongBallGun();
+
+ createBasketballHoop();
+
+ createGates();
+
+ createFire();
+ // //Handles toggling of all sconce lights
+ createLightSwitches();
+
+
+
+}
+
+function deleteAllToys() {
+ var entities = Entities.findEntities(MyAvatar.position, 100);
+
+ entities.forEach(function (entity) {
+ //params: customKey, id, defaultValue
+ var shouldReset = getEntityCustomData(resetKey, entity, {}).resetMe;
+ if (shouldReset === true) {
+ Entities.deleteEntity(entity);
+ }
+ });
+}
+
+function createFire() {
+
+
+ var myOrientation = Quat.fromPitchYawRollDegrees(-90, 0, 0.0);
+
+ var animationSettings = JSON.stringify({
+ fps: 30,
+ running: true,
+ loop: true,
+ firstFrame: 1,
+ lastFrame: 10000
+ });
+
+
+ var fire = Entities.addEntity({
+ type: "ParticleEffect",
+ name: "fire",
+ animationSettings: animationSettings,
+ textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
+ position: {
+ x: 551.45,
+ y: 494.82,
+ z: 502.05
+ },
+ emitRate: 100,
+ colorStart: {
+ red: 70,
+ green: 70,
+ blue: 137
+ },
+ color: {
+ red: 200,
+ green: 99,
+ blue: 42
+ },
+ colorFinish: {
+ red: 255,
+ green: 99,
+ blue: 32
+ },
+ radiusSpread: 0.01,
+ radiusStart: 0.02,
+ radiusEnd: 0.001,
+ particleRadius: 0.05,
+ radiusFinish: 0.0,
+ emitOrientation: myOrientation,
+ emitSpeed: 0.3,
+ speedSpread: 0.1,
+ alphaStart: 0.05,
+ alpha: 0.1,
+ alphaFinish: 0.05,
+ emitDimensions: {
+ x: 1,
+ y: 1,
+ z: 0.1
+ },
+ polarFinish: 0.1,
+ emitAcceleration: {
+ x: 0.0,
+ y: 0.0,
+ z: 0.0
+ },
+ accelerationSpread: {
+ x: 0.1,
+ y: 0.01,
+ z: 0.1
+ },
+ lifespan: 1
+ });
+
+
+ setEntityCustomData(resetKey, fire, {
+ resetMe: true
+ });
+}
+
+
+function createCat(position) {
+ var scriptURL = Script.resolvePath("../examples/toys/cat.js");
+ var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/Dark_Cat.fbx";
+ var animationURL = "http://hifi-public.s3.amazonaws.com/ryan/sleeping.fbx";
+ var animationSettings = JSON.stringify({
+ running: true,
+ });
+ var cat = Entities.addEntity({
+ type: "Model",
+ modelURL: modelURL,
+ name: "cat",
+ script: scriptURL,
+ animationURL: animationURL,
+ animationSettings: animationSettings,
+ position: position,
+ rotation: {
+ w: 0.35020983219146729,
+ x: -4.57763671875e-05,
+ y: 0.93664455413818359,
+ z: -1.52587890625e-05
+ },
+ dimensions: {
+ x: 0.15723302960395813,
+ y: 0.50762706995010376,
+ z: 0.90716040134429932
+ },
+ });
+
+ setEntityCustomData(resetKey, cat, {
+ resetMe: true
+ });
+}
+
+function createFlashlight(position) {
+ var scriptURL = Script.resolvePath('../examples/toys/flashlight/flashlight.js');
+ var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx";
+
+ var flashlight = Entities.addEntity({
+ type: "Model",
+ modelURL: modelURL,
+ name: "flashlight",
+ script: scriptURL,
+ position: position,
+ dimensions: {
+ x: 0.08,
+ y: 0.30,
+ z: 0.08
+ },
+ collisionsWillMove: true,
+ gravity: {
+ x: 0,
+ y: -3.5,
+ z: 0
+ },
+ velocity: {
+ x: 0,
+ y: -0.01,
+ z: 0
+ },
+ shapeType: 'box',
+ });
+
+ setEntityCustomData(resetKey, flashlight, {
+ resetMe: true
+ });
+
+}
+
+function createLightSwitches() {
+ var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/lightswitch.fbx?v1";
+ var scriptURL = Script.resolvePath("../examples/toys/lightSwitchHall.js");
+
+ var lightSwitchHall = Entities.addEntity({
+ type: "Model",
+ modelURL: modelURL,
+ name: "Light Switch Hall",
+ script: scriptURL,
+ position: {
+ x: 543.27764892578125,
+ y: 495.67999267578125,
+ z: 511.00564575195312
+ },
+ rotation: {
+ w: 0.63280689716339111,
+ x: 0.63280689716339111,
+ y: -0.31551080942153931,
+ z: 0.31548023223876953
+ },
+ dimensions: {
+ x: 0.10546875,
+ y: 0.032372996211051941,
+ z: 0.16242524981498718
+ }
+ });
+
+ setEntityCustomData(resetKey, lightSwitchHall, {
+ resetMe: true
+ });
+
+ scriptURL = Script.resolvePath("../examples/toys/lightSwitchGarage.js");
+
+ var lightSwitchGarage = Entities.addEntity({
+ type: "Model",
+ modelURL: modelURL,
+ name: "Light Switch Garage",
+ script: scriptURL,
+ position: {
+ x: 545.62,
+ y: 495.68,
+ z: 500.21
+ },
+ rotation: {
+ w: 0.20082402229309082,
+ x: 0.20082402229309082,
+ y: -0.67800414562225342,
+ z: 0.67797362804412842
+ },
+ dimensions: {
+ x: 0.10546875,
+ y: 0.032372996211051941,
+ z: 0.16242524981498718
+ }
+ });
+
+ setEntityCustomData(resetKey, lightSwitchGarage, {
+ resetMe: true
+ });
+
+}
+
+function createDice() {
+ var diceProps = {
+ type: "Model",
+ modelURL: "http://s3.amazonaws.com/hifi-public/models/props/Dice/goldDie.fbx",
+ collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/dice/diceCollide.wav",
+ name: "dice",
+ position: {
+ x: 541,
+ y: 494.96,
+ z: 509.1
+ },
+ dimensions: {
+ x: 0.09,
+ y: 0.09,
+ z: 0.09
+ },
+ gravity: {
+ x: 0,
+ y: -3.5,
+ z: 0
+ },
+ velocity: {
+ x: 0,
+ y: -0.01,
+ z: 0
+ },
+ shapeType: "box",
+ collisionsWillMove: true
+ };
+ var dice1 = Entities.addEntity(diceProps);
+
+ diceProps.position = {
+ x: 541.05,
+ y: 494.96,
+ z: 509.0
+ };
+
+ var dice2 = Entities.addEntity(diceProps);
+
+ setEntityCustomData(resetKey, dice1, {
+ resetMe: true
+ });
+
+ setEntityCustomData(resetKey, dice2, {
+ resetMe: true
+ });
+}
+
+
+function createGates() {
+ var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/ryan/fence.fbx';
+
+ var rotation1 = Quat.fromPitchYawRollDegrees(0, 36, 0);
+
+ var gate1 = Entities.addEntity({
+ name: 'Back Door Gate',
+ type: 'Model',
+ shapeType: 'box',
+ modelURL: MODEL_URL,
+ position: {
+ x: 546.52,
+ y: 494.76,
+ z: 498.87
+ },
+ dimensions: {
+ x: 1.42,
+ y: 1.13,
+ z: 0.25
+ },
+ rotation: rotation1,
+ collisionsWillMove: true,
+ gravity: {
+ x: 0,
+ y: -50,
+ z: 0
+ },
+ linearDamping: 1,
+ angularDamping: 10,
+ mass: 10,
+
+ });
+
+ setEntityCustomData(resetKey, gate1, {
+ resetMe: true
+ });
+
+ setEntityCustomData(GRABBABLE_DATA_KEY, gate1, {
+ grabbable: false
+ });
+
+ var rotation2 = Quat.fromPitchYawRollDegrees(0, -16, 0);
+ var gate2 = Entities.addEntity({
+ name: 'Front Door Fence',
+ type: 'Model',
+ modelURL: MODEL_URL,
+ shapeType: 'box',
+ position: {
+ x: 531.15,
+ y: 495.11,
+ z: 520.20
+ },
+ dimensions: {
+ x: 1.42,
+ y: 1.13,
+ z: 0.2
+ },
+ rotation: rotation2,
+ collisionsWillMove: true,
+ gravity: {
+ x: 0,
+ y: -100,
+ z: 0
+ },
+ linearDamping: 1,
+ angularDamping: 10,
+ mass: 10,
+ });
+
+ setEntityCustomData(resetKey, gate2, {
+ resetMe: true
+ });
+
+ setEntityCustomData(GRABBABLE_DATA_KEY, gate2, {
+ grabbable: false
+ });
+}
+
+function createPingPongBallGun() {
+ var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx';
+ var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj';
+ var scriptURL = Script.resolvePath('../examples/toys/ping_pong_gun/pingPongGun.js');
+
+ var position = {
+ x: 548.6,
+ y: 495.4,
+ z: 503.39
+ };
+
+ var rotation = Quat.fromPitchYawRollDegrees(0, 36, 0);
+
+ var pingPongGun = Entities.addEntity({
+ type: "Model",
+ modelURL: MODEL_URL,
+ shapeType: 'compound',
+ compoundShapeURL: COLLISION_HULL_URL,
+ script: scriptURL,
+ position: position,
+ rotation: rotation,
+ gravity: {
+ x: 0,
+ y: -9.8,
+ z: 0
+ },
+ dimensions: {
+ x: 0.67,
+ y: 0.14,
+ z: 0.09
+ },
+ collisionsWillMove: true,
+ });
+
+ setEntityCustomData(resetKey, pingPongGun, {
+ resetMe: true
+ });
+
+
+}
+
+function createBasketballHoop() {
+ var position = {
+ x: 539.23,
+ y: 496.13,
+ z: 475.89
+ };
+ var rotation = Quat.fromPitchYawRollDegrees(0, 58.49, 0);
+
+ var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx";
+ var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj";
+
+ var hoop = Entities.addEntity({
+ type: "Model",
+ modelURL: hoopURL,
+ position: position,
+ rotation: rotation,
+ shapeType: 'compound',
+ gravity: {
+ x: 0,
+ y: -9.8,
+ z: 0
+ },
+ dimensions: {
+ x: 1.89,
+ y: 3.99,
+ z: 3.79
+ },
+ compoundShapeURL: hoopCollisionHullURL
+ });
+
+ setEntityCustomData(resetKey, hoop, {
+ resetMe: true
+ });
+
+ setEntityCustomData(GRABBABLE_DATA_KEY, hoop, {
+ grabbable: false
+ });
+}
+
+function createWand(position) {
+ var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx';
+ var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj';
+ var scriptURL = Script.resolvePath("../examples/toys/bubblewand/wand.js");
+
+ var entity = Entities.addEntity({
+ name: 'Bubble Wand',
+ type: "Model",
+ modelURL: WAND_MODEL,
+ position: position,
+ gravity: {
+ x: 0,
+ y: -9.8,
+ z: 0
+ },
+ dimensions: {
+ x: 0.05,
+ y: 0.25,
+ z: 0.05
+ },
+ //must be enabled to be grabbable in the physics engine
+ shapeType: 'compound',
+ collisionsWillMove: true,
+ compoundShapeURL: WAND_COLLISION_SHAPE,
+ //Look into why bubble wand is going through table when gravity is enabled
+ // gravity: {x: 0, y: -3.5, z: 0},
+ // velocity: {x: 0, y: -0.01, z:0},
+ script: scriptURL
+ });
+
+ setEntityCustomData(resetKey, entity, {
+ resetMe: true
+ });
+}
+
+function createBasketBall(position) {
+
+ var modelURL = "http://s3.amazonaws.com/hifi-public/models/content/basketball2.fbx";
+
+ var entity = Entities.addEntity({
+ type: "Model",
+ modelURL: modelURL,
+ position: position,
+ collisionsWillMove: true,
+ shapeType: "sphere",
+ name: "basketball",
+ dimensions: {
+ x: 0.25,
+ y: 0.26,
+ z: 0.25
+ },
+ gravity: {
+ x: 0,
+ y: -7,
+ z: 0
+ },
+ restitution: 10,
+ linearDamping: 0.0,
+ velocity: {
+ x: 0,
+ y: -0.01,
+ z: 0
+ },
+ collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/basketball/basketball.wav"
+ });
+
+ setEntityCustomData(resetKey, entity, {
+ resetMe: true
+ });
+}
+
+function createDoll(position) {
+ var modelURL = "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx";
+ var scriptURL = Script.resolvePath("../examples/toys/doll/doll.js");
+
+ var naturalDimensions = {
+ x: 1.63,
+ y: 1.67,
+ z: 0.26
+ };
+ var desiredDimensions = Vec3.multiply(naturalDimensions, 0.15);
+ var entity = Entities.addEntity({
+ type: "Model",
+ name: "doll",
+ modelURL: modelURL,
+ script: scriptURL,
+ position: position,
+ shapeType: 'box',
+ dimensions: desiredDimensions,
+ gravity: {
+ x: 0,
+ y: -5,
+ z: 0
+ },
+ velocity: {
+ x: 0,
+ y: -0.1,
+ z: 0
+ },
+ collisionsWillMove: true
+ });
+
+ setEntityCustomData(resetKey, entity, {
+ resetMe: true
+ });
+}
+
+function createSprayCan(position) {
+ var scriptURL = Script.resolvePath("../examples/toys/sprayPaintCan.js");
+ var modelURL = "https://hifi-public.s3.amazonaws.com/eric/models/paintcan.fbx";
+
+ var entity = Entities.addEntity({
+ type: "Model",
+ name: "spraycan",
+ modelURL: modelURL,
+ position: position,
+ dimensions: {
+ x: 0.07,
+ y: 0.17,
+ z: 0.07
+ },
+ collisionsWillMove: true,
+ shapeType: 'box',
+ script: scriptURL,
+ gravity: {
+ x: 0,
+ y: -0.5,
+ z: 0
+ },
+ velocity: {
+ x: 0,
+ y: -1,
+ z: 0
+ }
+ });
+
+ setEntityCustomData(resetKey, entity, {
+ resetMe: true
+ });
+
+}
+
+function createPottedPlant(position) {
+ var modelURL = "http://hifi-public.s3.amazonaws.com/models/potted_plant/potted_plant.fbx";
+
+ var entity = Entities.addEntity({
+ type: "Model",
+ name: "Potted Plant",
+ modelURL: modelURL,
+ position: position,
+ dimensions: {
+ x: 1.10,
+ y: 2.18,
+ z: 1.07
+ },
+ collisionsWillMove: true,
+ shapeType: 'box',
+ gravity: {
+ x: 0,
+ y: -9.8,
+ z: 0
+ },
+ velocity: {
+ x: 0,
+ y: 0,
+ z: 0
+ },
+ linearDamping: 0.4
+ });
+
+ setEntityCustomData(resetKey, entity, {
+ resetMe: true
+ });
+
+
+ setEntityCustomData(GRABBABLE_DATA_KEY, entity, {
+ grabbable: false
+ });
+}
+
+
+function createCombinedArmChair(position) {
+ var modelURL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/combined_chair.fbx";
+ var RED_ARM_CHAIR_COLLISION_HULL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/red_arm_chair_collision_hull.obj";
+
+ var rotation = Quat.fromPitchYawRollDegrees(0, -143, 0);
+
+ var entity = Entities.addEntity({
+ type: "Model",
+ name: "Red Arm Chair",
+ modelURL: modelURL,
+ shapeType: 'compound',
+ compoundShapeURL: RED_ARM_CHAIR_COLLISION_HULL,
+ position: position,
+ rotation: rotation,
+ dimensions: {
+ x: 1.26,
+ y: 1.56,
+ z: 1.35
+ },
+ collisionsWillMove: true,
+ gravity: {
+ x: 0,
+ y: -0.8,
+ z: 0
+ },
+ velocity: {
+ x: 0,
+ y: 0,
+ z: 0
+ },
+ linearDamping: 0.2
+ });
+
+ setEntityCustomData(resetKey, entity, {
+ resetMe: true
+ });
+
+ setEntityCustomData(GRABBABLE_DATA_KEY, entity, {
+ grabbable: false
+ });
+}
+
+function createBlocks(position) {
+ var baseURL = HIFI_PUBLIC_BUCKET + "models/content/planky/";
+ var collisionSoundURL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/ToyWoodBlock.L.wav";
+ var NUM_BLOCKS_PER_COLOR = 4;
+ var i, j;
+
+ var blockTypes = [{
+ url: "planky_blue.fbx",
+ dimensions: {
+ x: 0.05,
+ y: 0.05,
+ z: 0.25
+ }
+ }, {
+ url: "planky_green.fbx",
+ dimensions: {
+ x: 0.1,
+ y: 0.1,
+ z: 0.25
+ }
+ }, {
+ url: "planky_natural.fbx",
+ dimensions: {
+ x: 0.05,
+ y: 0.05,
+ z: 0.05
+ }
+ }, {
+ url: "planky_yellow.fbx",
+ dimensions: {
+ x: 0.03,
+ y: 0.05,
+ z: 0.25
+ }
+ }, {
+ url: "planky_red.fbx",
+ dimensions: {
+ x: 0.1,
+ y: 0.05,
+ z: 0.25
+ }
+ }, ];
+
+ var modelURL, entity;
+ for (i = 0; i < blockTypes.length; i++) {
+ for (j = 0; j < NUM_BLOCKS_PER_COLOR; j++) {
+ modelURL = baseURL + blockTypes[i].url;
+ entity = Entities.addEntity({
+ type: "Model",
+ modelURL: modelURL,
+ position: Vec3.sum(position, {
+ x: j / 10,
+ y: i / 10,
+ z: 0
+ }),
+ shapeType: 'box',
+ name: "block",
+ dimensions: blockTypes[i].dimensions,
+ collisionsWillMove: true,
+ collisionSoundURL: collisionSoundURL,
+ gravity: {
+ x: 0,
+ y: -2.5,
+ z: 0
+ },
+ velocity: {
+ x: 0,
+ y: -0.01,
+ z: 0
+ }
+ });
+
+ //customKey, id, data
+ setEntityCustomData(resetKey, entity, {
+ resetMe: true
+ });
+ }
+ }
+}
+
+function cleanup() {
+ deleteAllToys();
+}
+
+if (shouldDeleteOnEndScript) {
+
+ Script.scriptEnding.connect(cleanup);
+}