Added cart, cash, inspect and readme

This commit is contained in:
AlessandroSigna 2016-01-14 18:23:39 -08:00
parent 40279c8fc5
commit e4501e3a8f
9 changed files with 1741 additions and 0 deletions

View file

@ -0,0 +1,51 @@
Instructions for experiencing vrShop
Alessandro Signa and Edgar Pironti, 13 Jan 2016
To avoid weird issues we suggest to run this build https://github.com/highfidelity/hifi/pull/6786
To test the experience in the current release of interface that PR needs to be merged
and the scripts need to be updated to the new collisionMask system.
Go to the vrShop domain.
When you get closer to the shop (entering the Zone - GrabSwapper) you should notice that
shopItemGrab is in your running scripts. At this point the handControllerGrab should be disabled
(it will be re-enabled automatically when leaving the zone), but if you still see rays
coming out from your hands when you squeeze the triggers something went wrong so you probably
want to remove manually handControllerGrab
Once you're here you can do two different experiences: buyer and reviewr.
Both of them are designed to be done entirely using HMD and hand controllers,
so please put your Oculus on and equip your hydra.
BUYER
First of all you have to pass through the pick cart line, the Zone - CartSpawner is there.
Now you see a cart following you (look at your right).
Reach the shelf and grab an item (you can only near grab here).
An inspection area should now stay in front of you, drop the item inside it to inspect. Be sure
to see the overlay turning green before releasing the item.
In inspect mode you can't move or rotate your avatar but you can scale and rotate the item.
Interact with the UI buttons using your bumpers: you can change the color of the item,
see the reviews (looking at the review cabin) and you can also try some of the items on (but
this feature at the moment works well only with the Will avatar increasing its default size once).
If you are good with your item you can put it in the cart, otherwise just drop it around.
You can also pick again an item from your cart to inspect it or to discard it.
To empty the cart you can push the right primary button.
Then you can go to the cashier for the "payment": once you enter the Zone - CashDesk
a credit card will appear above the register and a bot will give you some informations.
Just drag that card to the register to confirm the purchase.
When you're ready to exit pass again through the leave cart line.
REVIEWER
Go and grab the item you want to review, this time don't pick the cart.
Enter into the review cabin holding the item in your hand , if all goes right the item should disappear
and a UI appears allowing you to rate the item.
Now you can follow the instrucions to record a review: both the voice and the animation of your avatar
will be recorded and stored into the DB of that item.

View file

@ -0,0 +1,324 @@
// shopCartEntityScript.js
//
// This script makes the cart follow the avatar who picks it and manage interations with items (store and delete them) and with cash register (send the item prices)
// Created by Alessandro Signa and Edgar Pironti on 01/13/2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
var utilitiesScript = Script.resolvePath("../../libraries/utils.js");
var overlayManagerScript = Script.resolvePath("../../libraries/overlayManager.js");
Script.include(utilitiesScript);
Script.include(overlayManagerScript);
var COMFORT_ARM_LENGTH = 0.5;
var CART_REGISTER_CHANNEL = "Hifi-vrShop-Register";
var PENETRATION_THRESHOLD = 0.2;
var _this;
var cartIsMine = false;
var originalY = 0;
var itemsID = [];
var scaleFactor = 0.7; //TODO: The scale factor will dipend on the number of items in the cart. We would resize even the items already present.
var cartTargetPosition;
var singlePrices = [];
var singlePriceTagsAreShowing = false;
var collidedItemID = null;
// 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)
ShopCart = function() {
_this = this;
};
function update(deltaTime) {
_this.followAvatar();
if (Controller.getValue(Controller.Standard.RightPrimaryThumb)) {
_this.resetCart();
_this.computeAndSendTotalPrice();
}
};
function receivingMessage(channel, message, senderID) {
if (senderID === MyAvatar.sessionUUID && channel == CART_REGISTER_CHANNEL) {
var messageObj = JSON.parse(message);
if (messageObj.senderEntity != _this.entityID) {
print("--------------- cart received message");
//Receiving this message means that the register wants the total price
_this.computeAndSendTotalPrice();
}
}
};
ShopCart.prototype = {
// 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
// * connecting to the update signal so we can check our grabbed state
preload: function(entityID) {
this.entityID = entityID;
//get the owner ID from user data and compare to the mine
//so the update will be connected just for the owner
var ownerObj = getEntityCustomData('ownerKey', this.entityID, null);
if (ownerObj.ownerID === MyAvatar.sessionUUID) {
cartIsMine = true;
cartTargetPosition = Entities.getEntityProperties(_this.entityID).position; //useful if the entity script is assigned manually
Script.update.connect(update);
Messages.subscribe(CART_REGISTER_CHANNEL);
Messages.messageReceived.connect(receivingMessage);
}
},
//update cart's target position. It will be at the right of the avatar as long as he moves
followAvatar: function() {
if (Vec3.length(MyAvatar.getVelocity()) > 0.1) {
var radius = (Entities.getEntityProperties(_this.entityID).dimensions.x) / 2 + COMFORT_ARM_LENGTH;
var properY = MyAvatar.position.y + ((MyAvatar.getHeadPosition().y - MyAvatar.position.y) / 2);
var targetPositionPrecomputing = {x: MyAvatar.position.x, y: properY, z: MyAvatar.position.z};
cartTargetPosition = Vec3.sum(targetPositionPrecomputing, Vec3.multiply(Quat.getRight(MyAvatar.orientation), radius));
}
var cartPosition = Entities.getEntityProperties(_this.entityID).position;
var positionDifference = Vec3.subtract(cartTargetPosition, cartPosition);
if (Vec3.length(positionDifference) > 0.1) {
//give to the cart the proper velocity and make it ignore for collision
Entities.editEntity(_this.entityID, { velocity: positionDifference });
Entities.editEntity(_this.entityID, { ignoreForCollisions: true });
if (collidedItemID != null) {
Entities.callEntityMethod(collidedItemID, 'setCartOverlayNotVisible', null);
collidedItemID = null;
}
} else if (Vec3.length(positionDifference) > 0.01) {
//give to the cart the proper velocity and make it NOT ignore for collision
Entities.editEntity(_this.entityID, { velocity: positionDifference });
Entities.editEntity(_this.entityID, { ignoreForCollisions: false });
} else if (Vec3.length(positionDifference) > 0) {
//set the position to be at the right of MyAvatar and make it NOT ignore for collision
Entities.editEntity(_this.entityID, { position: cartTargetPosition });
positionDifference = Vec3.subtract(cartTargetPosition, cartPosition);
Entities.editEntity(_this.entityID, { velocity: positionDifference });
Entities.editEntity(_this.entityID, { ignoreForCollisions: false });
}
},
// delete all items stored into the cart
resetCart: function () {
//print("RESET CART - USER DATA: " + Entities.getEntityProperties(_this.entityID).userData);
if (itemsID.length != 0) {
if (singlePriceTagsAreShowing) {
_this.singlePriceOff();
}
for (var i=0; i < itemsID.length; i++) {
Entities.deleteEntity(itemsID[i]);
}
// Clear the userData field for the cart
Entities.editEntity(this.entityID, { userData: ""});
setEntityCustomData('ownerKey', this.entityID, {
ownerID: MyAvatar.sessionUUID
});
setEntityCustomData('grabbableKey', this.entityID, {
grabbable: false
});
itemsID = [];
}
},
//delete the item pointed by dataArray (data.id) from the cart because it's been grabbed from there
refreshCartContent: function (entityID, dataArray) {
var data = JSON.parse(dataArray[0]);
for (var i=0; i < itemsID.length; i++) {
if(itemsID[i] == data.id) {
itemsID.splice(i, 1);
//if the price tags are showing we have to remove also the proper tag
if (singlePriceTagsAreShowing) {
singlePrices[i].destroy();
singlePrices.splice(i, 1);
}
}
}
_this.computeAndSendTotalPrice();
},
//show the prices on each item into the cart
singlePriceOn: function () {
//create an array of text3D which follows the structure of the itemsID array. Each text3D is like the 'Store the item!' one
var i = 0;
itemsID.forEach( function(itemID) {
singlePrices[i] = new OverlayPanel({
anchorPositionBinding: { entity: itemID },
offsetPosition: { x: 0, y: 0.15, z: 0 },
isFacingAvatar: true,
});
var textPrice = new Text3DOverlay({
text: "" + getEntityCustomData('infoKey', itemID, null).price + " $",
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: true,
dimensions: { x: 0, y: 0 },
backgroundColor: { red: 255, green: 255, blue: 255 },
color: { red: 0, green: 0, blue: 0 },
topMargin: 0.00625,
leftMargin: 0.00625,
bottomMargin: 0.1,
rightMargin: 0.00625,
lineHeight: 0.02,
alpha: 1,
backgroundAlpha: 0.3,
visible: true
});
singlePrices[i].addChild(textPrice);
i++;
});
singlePriceTagsAreShowing = true;
},
singlePriceOff: function () {
//destroy or make invisible the text3D, or both
singlePrices.forEach(function(panel) {
panel.destroy();
});
singlePrices = [];
singlePriceTagsAreShowing = false;
},
//Send to the register the total price for all the items in the cart
computeAndSendTotalPrice: function () {
var totalPrice = 0;
itemsID.forEach( function(itemID) {
var infoObj = getEntityCustomData('infoKey', itemID, null);
if(infoObj != null) {
totalPrice += infoObj.price;
}
});
var messageObj = {senderEntity: _this.entityID, totalPrice: totalPrice};
Messages.sendMessage(CART_REGISTER_CHANNEL, JSON.stringify(messageObj));
},
//dataArray stores the ID of the item which has to be stored into the cart
//this entity method is invoked by shopItemEntityScript.js
doSomething: function (entityID, dataArray) {
collidedItemID = null;
var data = JSON.parse(dataArray[0]);
var itemOwnerObj = getEntityCustomData('ownerKey', data.id, null);
var cartOwnerObj = getEntityCustomData('ownerKey', this.entityID, null);
if (cartOwnerObj == null) {
//print("The cart doesn't have a owner.");
Entities.deleteEntity(data.id);
}
if (itemOwnerObj.ownerID === cartOwnerObj.ownerID) {
// TODO if itemsQuantity == fullCart resize all the items present in the cart and change the scaleFactor for this and next insert
print("Going to put item in the cart!");
var itemsQuantity = itemsID.length;
itemsID[itemsQuantity] = data.id;
var oldDimension = Entities.getEntityProperties(data.id).dimensions;
Entities.editEntity(data.id, { dimensions: Vec3.multiply(oldDimension, scaleFactor) });
Entities.editEntity(data.id, { velocity: {x: 0.0, y: 0.0, z: 0.0} });
// parent item to the cart
Entities.editEntity(data.id, { parentID: this.entityID });
itemsQuantity = itemsID.length;
setEntityCustomData('statusKey', data.id, {
status: "inCart"
});
_this.computeAndSendTotalPrice();
//if the single price tags are showing we have to put a tag also in the new item
if (singlePriceTagsAreShowing) {
singlePrices[itemsQuantity-1] = new OverlayPanel({
anchorPositionBinding: { entity: data.id },
offsetPosition: { x: 0, y: 0.15, z: 0 },
isFacingAvatar: true,
});
var textPrice = new Text3DOverlay({
text: "" + getEntityCustomData('infoKey', data.id, null).price + " $",
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: true,
dimensions: { x: 0, y: 0 },
backgroundColor: { red: 255, green: 255, blue: 255 },
color: { red: 0, green: 0, blue: 0 },
topMargin: 0.00625,
leftMargin: 0.00625,
bottomMargin: 0.1,
rightMargin: 0.00625,
lineHeight: 0.02,
alpha: 1,
backgroundAlpha: 0.3,
visible: true
});
singlePrices[itemsQuantity-1].addChild(textPrice);
}
} else {
print("Not your cart!");
Entities.deleteEntity(data.id);
}
},
//detect when the item enters or leave the cart while it's grabbed
collisionWithEntity: function(myID, otherID, collisionInfo) {
var penetrationValue = Vec3.length(collisionInfo.penetration);
var cartOwnerObj = getEntityCustomData('ownerKey', myID, null);
var itemOwnerObj = getEntityCustomData('ownerKey', otherID, null);
if (penetrationValue > PENETRATION_THRESHOLD && collidedItemID === null) {
if (itemOwnerObj != null && itemOwnerObj.ownerID === cartOwnerObj.ownerID) {
Entities.callEntityMethod(otherID, 'setCartOverlayVisible', null);
collidedItemID = otherID;
}
} else if (penetrationValue < PENETRATION_THRESHOLD && collidedItemID !== null) {
if (itemOwnerObj != null && itemOwnerObj.ownerID === cartOwnerObj.ownerID) {
Entities.callEntityMethod(otherID, 'setCartOverlayNotVisible', null);
collidedItemID = null;
}
}
},
unload: function (entityID) {
print("UNLOAD CART");
if(cartIsMine){
Script.update.disconnect(update);
_this.resetCart(); //useful if the script is reloaded manually
//Entities.deleteEntity(_this.entityID); //comment for manual reload
Messages.unsubscribe(CART_REGISTER_CHANNEL);
Messages.messageReceived.disconnect(receivingMessage);
}
}
};
// entity scripts always need to return a newly constructed object of our type
return new ShopCart();
})

View file

@ -0,0 +1,91 @@
// shopCartSpawnEntityScript.js
//
// If an avatar doesn't own a cart and enters the zone, a cart is added.
// Otherwise if it already has a cart, this will be destroyed
// Created by Alessandro Signa and Edgar Pironti on 01/13/2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function () {
var CART_MASTER_NAME = "ShopCartZero";
var CART_SCRIPT_URL = Script.resolvePath("shopCartEntityScript.js");
var SHOP_GRAB_SCRIPT_URL = Script.resolvePath("../item/shopItemGrab.js");
var _this;
var isOwningACart = false;
var cartMasterID = null;
var myCartID = null;
function SpawnCartZone() {
_this = this;
return;
};
SpawnCartZone.prototype = {
preload: function (entityID) {
this.entityID = entityID;
// Look for the ShopCartZero. Every cart created by this script is a copy of it
var ids = Entities.findEntities(Entities.getEntityProperties(this.entityID).position, 50);
ids.forEach(function(id) {
var properties = Entities.getEntityProperties(id);
if (properties.name == CART_MASTER_NAME) {
cartMasterID = id;
print("Master Cart found");
Entities.editEntity(_this.entityID, { collisionMask: "static,dynamic,otherAvatar" });
return;
}
});
},
enterEntity: function (entityID) {
print("entering in the spawn cart area");
if (myCartID) {
Entities.callEntityMethod(myCartID, "resetCart");
Entities.deleteEntity (myCartID);
myCartID = null;
} else {
var entityProperties = Entities.getEntityProperties(cartMasterID);
myCartID = Entities.addEntity({
type: entityProperties.type,
name: "Shopping cart",
ignoreForCollisions: false,
collisionsWillMove: false,
position: entityProperties.position,
dimensions: entityProperties.dimensions,
modelURL: entityProperties.modelURL,
shapeType: entityProperties.shapeType,
originalTextures: entityProperties.originalTextures,
script: CART_SCRIPT_URL,
userData: JSON.stringify({
ownerKey: {
ownerID: MyAvatar.sessionUUID
},
grabbableKey: {
grabbable: false
}
})
});
}
},
leaveEntity: function (entityID) {
},
unload: function (entityID) {
}
}
return new SpawnCartZone();
});

View file

@ -0,0 +1,39 @@
// shopCartZeroEntityScript.js
//
// Created by Alessandro Signa and Edgar Pironti on 01/13/2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
BALL_ANGULAR_VELOCITY = {x:0, y:5, z:0}
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)
ShopCartZero = function() {
_this = this;
};
ShopCartZero.prototype = {
// 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
// * connecting to the update signal so we can check our grabbed state
preload: function(entityID) {
this.entityID = entityID;
Entities.editEntity(_this.entityID, { angularVelocity: BALL_ANGULAR_VELOCITY });
Entities.editEntity(_this.entityID, { angularDamping: 0 });
},
};
// entity scripts always need to return a newly constructed object of our type
return new ShopCartZero();
})

View file

@ -0,0 +1,94 @@
// shopCashEntityScript.js
//
// When an avatar enters the zone the cashier bot (agent) is activated and the cash register as well.
// A credit card model will be added to the scene for make the payment
// Created by Alessandro Signa and Edgar Pironti on 01/13/2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function () {
var AGENT_PLAYBACK_CHANNEL = "playbackChannel";
var PLAY_MESSAGE = "Play";
var REGISTER_NAME = "CashRegister";
var CARD_ANGULAR_VELOCITY = {x: 0, y: 2, z: 0};
var CARD_POSITION_OFFSET = {x: 0, y: 0.5, z: 0};
var CARD_INITIAL_ORIENTATION = {x: 0, y: 0, z: 40};
var CARD_DIMENSIONS = {x: 0.02, y: 0.09, z: 0.15};
var SCRIPT_URL = Script.resolvePath("shopCreditCardEntityScript.js");
var _this;
var cashRegisterID = null;
var cardID = null;
function CashZone() {
_this = this;
return;
};
CashZone.prototype = {
preload: function (entityID) {
},
enterEntity: function (entityID) {
print("entering in the cash area");
Messages.sendMessage(AGENT_PLAYBACK_CHANNEL, PLAY_MESSAGE);
print("Play sent.");
// Look for the register
var entitiesInZone = Entities.findEntities(Entities.getEntityProperties(entityID).position, (Entities.getEntityProperties(entityID).dimensions.x)/2);
entitiesInZone.forEach( function(e) {
if (Entities.getEntityProperties(e).name == REGISTER_NAME) {
cashRegisterID = Entities.getEntityProperties(e).id;
print(cashRegisterID);
}
});
// create a credit card above register position
var cardPosition = Vec3.sum(Entities.getEntityProperties(cashRegisterID).position, CARD_POSITION_OFFSET);
var cardOrientationQuat = Quat.fromVec3Degrees(CARD_INITIAL_ORIENTATION);
cardID = Entities.addEntity({
type: "Model",
name: "CreditCard",
position: cardPosition,
rotation: cardOrientationQuat,
dimensions: CARD_DIMENSIONS,
collisionsWillMove: false,
ignoreForCollisions: false,
angularVelocity: CARD_ANGULAR_VELOCITY,
angularDamping: 0,
script: Script.resolvePath(SCRIPT_URL),
// We have to put the ownerID in the card, and check this property when grabbing the card. Otherwise it cannot be grabbed - I can only grab my card
userData: JSON.stringify({
ownerKey: {
ownerID: MyAvatar.sessionUUID
}
}),
modelURL: "https://dl.dropboxusercontent.com/u/14127429/FBX/VRshop/CreditCard.fbx",
shapeType: "box"
});
Entities.callEntityMethod(cashRegisterID, 'cashRegisterOn', null);
},
leaveEntity: function (entityID) {
// destroy card
Entities.deleteEntity(cardID);
cardID = null;
Entities.callEntityMethod(cashRegisterID, 'cashRegisterOff', null);
},
unload: function (entityID) {
}
}
return new CashZone();
});

View file

@ -0,0 +1,166 @@
// shopCashRegisterEntityScript.js
//
// The register manages the total price overlay and the payment through collision with credit card
// Created by Alessandro Signa and Edgar Pironti on 01/13/2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function () {
var overlayManagerScript = Script.resolvePath("../../libraries/overlayManager.js");
Script.include(overlayManagerScript);
var SHOPPING_CART_NAME = "Shopping cart";
var CART_REGISTER_CHANNEL = "Hifi-vrShop-Register";
var CREDIT_CARD_NAME = "CreditCard";
var _this;
var cartID = null;
var registerPanel = null;
var priceText = null;
var payingAvatarID = null;
var totalPrice = 0;
function CashRegister() {
_this = this;
return;
};
function receivingMessage(channel, message, senderID) {
if (senderID === MyAvatar.sessionUUID && channel == CART_REGISTER_CHANNEL) {
var messageObj = JSON.parse(message);
if (messageObj.senderEntity != _this.entityID) {
//This message means that the cart sent the total price: create or update the Overlay on the register
var price = messageObj.totalPrice.toFixed(2);
_this.cashRegisterOverlayOn("" + price + " $");
totalPrice = messageObj.totalPrice;
}
}
};
CashRegister.prototype = {
preload: function (entityID) {
this.entityID = entityID;
},
//This method is called by the cashZone when an avatar comes in it
//It has to find the cart belonging to that avatar and ask it the total price of the items
cashRegisterOn: function() {
print("cashRegister ON");
Messages.subscribe(CART_REGISTER_CHANNEL);
Messages.messageReceived.connect(receivingMessage);
var cashRegisterPosition = Entities.getEntityProperties(_this.entityID).position;
var foundEntities = Entities.findEntities(cashRegisterPosition, 50);
foundEntities.forEach( function (foundEntityID) {
var entityName = Entities.getEntityProperties(foundEntityID).name;
if (entityName == SHOPPING_CART_NAME) {
var cartOwnerID = getEntityCustomData('ownerKey', foundEntityID, null).ownerID;
if (cartOwnerID == MyAvatar.sessionUUID) {
cartID = foundEntityID;
}
}
});
if (cartID != null) {
payingAvatarID = MyAvatar.sessionUUID;
Messages.sendMessage(CART_REGISTER_CHANNEL, JSON.stringify({senderEntity: _this.entityID})); //with this message the cart know that it has to compute and send back the total price of the items
Entities.callEntityMethod(cartID, 'singlePriceOn', null);
//sometimes the cart is unable to receive the message. I think it's a message mixer problem
} else {
payingAvatarID = null;
// Show anyway the overlay with the price 0$
_this.cashRegisterOverlayOn("0 $");
}
},
cashRegisterOff: function() {
print("cashRegister OFF");
Messages.unsubscribe(CART_REGISTER_CHANNEL);
Messages.messageReceived.disconnect(receivingMessage);
priceText.visible = false;
if (cartID != null) {
Entities.callEntityMethod(cartID, 'singlePriceOff', null);
}
},
cashRegisterOverlayOn: function (string) {
print("cashRegister OVERLAY ON");
var stringOffset = string.length * 0.018;
if (priceText == null) {
registerPanel = new OverlayPanel({
anchorPositionBinding: { entity: _this.entityID },
offsetPosition: { x: 0, y: 0.21, z: -0.14 },
isFacingAvatar: false,
});
priceText = new Text3DOverlay({
text: string,
isFacingAvatar: false,
ignoreRayIntersection: true,
dimensions: { x: 0, y: 0 },
offsetPosition: {
x: -stringOffset,
y: 0,
z: 0
},
backgroundColor: { red: 255, green: 255, blue: 255 },
color: { red: 0, green: 255, blue: 0 },
topMargin: 0.00625,
leftMargin: 0.00625,
bottomMargin: 0.1,
rightMargin: 0.00625,
lineHeight: 0.06,
alpha: 1,
backgroundAlpha: 0.3,
visible: true
});
registerPanel.addChild(priceText);
} else {
priceText.text = string;
priceText.visible = true;
priceText.offsetPosition = {
x: -stringOffset,
y: 0,
z: 0
};
}
},
//Manage the collision with credit card
collisionWithEntity: function (myID, otherID, collisionInfo) {
var entityName = Entities.getEntityProperties(otherID).name;
var entityOwnerID = getEntityCustomData('ownerKey', otherID, null).ownerID;
if (entityName == CREDIT_CARD_NAME && entityOwnerID == payingAvatarID) {
//The register collided with the right credit card - CHECKOUT
Entities.deleteEntity(otherID);
Entities.callEntityMethod(cartID, 'resetCart', null);
_this.cashRegisterOverlayOn("THANK YOU!");
_this.clean();
}
},
//clean all the variable related to the cart
clean: function () {
cartID = null;
payingAvatarID = null;
totalPrice = 0;
},
unload: function (entityID) {
_this.clean();
if (registerPanel != null) {
registerPanel.destroy();
}
registerPanel = priceText = null;
}
}
return new CashRegister();
});

View file

@ -0,0 +1,73 @@
// shopCashierAC.js
//
// Created by Alessandro Signa and Edgar Pironti on 01/13/2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// Set the following variable to the values needed
var clip_url = "atp:6865d9c89472d58b18929aff0ac779026bc129190f7536f71d3835f7e2629c93.hfr"; // This url is working in VRshop
var PLAYBACK_CHANNEL = "playbackChannel";
var playFromCurrentLocation = false;
var useDisplayName = true;
var useAttachments = true;
var useAvatarModel = true;
var totalTime = 0;
var subscribed = false;
var WAIT_FOR_AUDIO_MIXER = 1;
var PLAY = "Play";
function getAction(channel, message, senderID) {
if(subscribed) {
print("I'm the agent and I received this: " + message);
switch(message) {
case PLAY:
print("Play");
if (!Recording.isPlaying()) {
Recording.setPlayerTime(0.0);
Recording.startPlaying();
}
break;
default:
print("Unknown action: " + action);
break;
}
}
}
function update(deltaTime) {
totalTime += deltaTime;
if (totalTime > WAIT_FOR_AUDIO_MIXER) {
if (!subscribed) {
Messages.subscribe(PLAYBACK_CHANNEL);
subscribed = true;
Recording.loadRecording(clip_url);
Recording.setPlayFromCurrentLocation(playFromCurrentLocation);
Recording.setPlayerUseDisplayName(useDisplayName);
Recording.setPlayerUseAttachments(useAttachments);
Recording.setPlayerUseHeadModel(false);
Recording.setPlayerUseSkeletonModel(useAvatarModel);
Agent.isAvatar = true;
}
}
}
Messages.messageReceived.connect(function (channel, message, senderID) {
if (channel == PLAYBACK_CHANNEL) {
getAction(channel, message, senderID);
}
});
Script.update.connect(update);

View file

@ -0,0 +1,45 @@
// shopCreditCardEntityScript.js
//
// Created by Alessandro Signa and Edgar Pironti on 01/13/2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
var _this;
var entityProperties = null;
// 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)
CreditCard = function() {
_this = this;
};
CreditCard.prototype = {
preload: function(entityID) {
this.entityID = entityID;
var ownerObj = getEntityCustomData('ownerKey', this.entityID, null);
if (ownerObj.ownerID === MyAvatar.sessionUUID) {
myCard = true;
entityProperties = Entities.getEntityProperties(this.entityID);
}
},
releaseGrab: function () {
//reset the card to its original properties (position, angular velocity, ecc)
Entities.editEntity(_this.entityID, entityProperties);
},
unload: function (entityID) {
Entities.deleteEntity(this.entityID);
}
};
// entity scripts always need to return a newly constructed object of our type
return new CreditCard();
})

View file

@ -0,0 +1,858 @@
// shopInspectEntityScript.js
//
// The inspection entity which runs this entity script will be in fron of the avatar while he's grabbing an item.
// This script gives some information to the avatar using interactive 3DOverlays:
// - Drive the customer to put he item in the correct zone to inspect it
// - Create the UI while inspecting with text and buttons and manages the clicks on them modifying the item and communicating with agents
// And also it creates the MyController which are in charge to render the rays from the hands during inspecting and analysing their intersection with other overlays
// Created by Alessandro Signa and Edgar Pironti on 01/13/2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
var utilitiesScript = Script.resolvePath("../../libraries/utils.js");
var overlayManagerScript = Script.resolvePath("../../libraries/overlayManager.js");
Script.include(utilitiesScript);
Script.include(overlayManagerScript);
var AGENT_REVIEW_CHANNEL = "reviewChannel";
var NO_REVIEWS_AVAILABLE = "No reviews available";
var SEPARATOR = "Separator";
var CAMERA_REVIEW = "CameraReview";
var ZERO_STAR_URL = "https://dl.dropboxusercontent.com/u/14127429/FBX/VRshop/0Star.png";
var ONE_STAR_URL = "https://dl.dropboxusercontent.com/u/14127429/FBX/VRshop/1Star.png";
var TWO_STAR_URL = "https://dl.dropboxusercontent.com/u/14127429/FBX/VRshop/2Star.png";
var THREE_STAR_URL = "https://dl.dropboxusercontent.com/u/14127429/FBX/VRshop/3Star.png";
var POINTER_ICON_URL = "https://dl.dropboxusercontent.com/u/14127429/FBX/VRshop/Pointer.png";
var TRY_ON_ICON = "https://dl.dropboxusercontent.com/u/14127429/FBX/VRshop/TryOn.png"
var MIN_DIMENSION_THRESHOLD = null;
var IN_HAND_STATUS = "inHand";
var IN_INSPECT_STATUS = "inInspect";
var RIGHT_HAND = 1;
var LEFT_HAND = 0;
var LINE_LENGTH = 100;
var COLOR = {
red: 165,
green: 199,
blue: 218
};
var COMFORT_ARM_LENGTH = 0.4;
var PENETRATION_THRESHOLD = 0.2;
var _this;
var inspecting = false;
var inspectingMyItem = false;
var inspectedEntityID = null;
var isUIWorking = false;
var wantToStopTrying = false;
var rightController = null;
var leftController = null;
var workingHand = null;
var collidedItemID = null;
var tempTryEntity = null;
var tryingOnAvatar = false;
var itemOriginalDimensions = null;
var mainPanel = null;
var mirrorPanel = null;
var buttons = [];
var tryOnAvatarButton = null;
var playButton = null;
var nextButton = null;
var textReviewerName = null;
var modelURLsArray = [];
var previewURLsArray = [];
var starURL = null;
var reviewIndex = 0;
var reviewsNumber = 0;
var dbMatrix = null;
var separator = null;
var cameraReview = null;
var pointer = new Image3DOverlay({ //maybe we want to use one pointer for each hand ?
url: POINTER_ICON_URL,
dimensions: {
x: 0.015,
y: 0.015
},
alpha: 1,
emissive: true,
isFacingAvatar: false,
ignoreRayIntersection: true,
})
// 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)
InspectEntity = function() {
_this = this;
};
function MyController(hand) {
this.hand = hand;
if (this.hand === RIGHT_HAND) {
this.getHandPosition = MyAvatar.getRightPalmPosition;
this.getHandRotation = MyAvatar.getRightPalmRotation;
this.bumper = Controller.Standard.RB;
} else {
this.getHandPosition = MyAvatar.getLeftPalmPosition;
this.getHandRotation = MyAvatar.getLeftPalmRotation;
this.bumper = Controller.Standard.LB;
}
this.pickRay = null; // ray object
this.overlayLine = null; // id of line overlay
this.waitingForBumpReleased = false;
this.overlayLineOn = function(closePoint, farPoint, color) {
if (this.overlayLine == null) {
var lineProperties = {
lineWidth: 5,
start: closePoint,
end: farPoint,
color: color,
ignoreRayIntersection: true,
visible: true,
alpha: 1
};
this.overlayLine = new Line3DOverlay(lineProperties);
} else {
this.overlayLine.start = closePoint;
this.overlayLine.end = farPoint;
}
},
this.updateRay = function() {
//update the ray object
this.pickRay = {
origin: this.getHandPosition(),
direction: Quat.getUp(this.getHandRotation())
};
//update the ray overlay and the pointer
var rayPickResult = OverlayManager.findRayIntersection(this.pickRay);
if (rayPickResult.intersects) {
var normal = Vec3.multiply(Quat.getFront(Camera.getOrientation()), -1);
var offset = Vec3.multiply(normal, 0.001);
pointer.position = Vec3.sum(rayPickResult.intersection, offset); //pointer is a global Image3DOverlay
pointer.rotation = Camera.getOrientation();
pointer.visible = true;
} else {
pointer.visible = false;
}
var farPoint = rayPickResult.intersects ? rayPickResult.intersection : Vec3.sum(this.pickRay.origin, Vec3.multiply(this.pickRay.direction, LINE_LENGTH));
this.overlayLineOn(this.pickRay.origin, farPoint, COLOR);
},
//the update of each hand has to update the ray belonging to that hand and handle the bumper event
this.updateHand = function() {
//detect the bumper event
var bumperPressed = Controller.getValue(this.bumper);
if (bumperPressed && this != workingHand) {
workingHand.clean();
workingHand = this;
} else if (this != workingHand) {
return;
}
this.updateRay();
//manage event on UI
if (bumperPressed && !this.waitingForBumpReleased) {
this.waitingForBumpReleased = true; //to avoid looping on the button while keep pressing the bumper
var triggeredButton = OverlayManager.findOnRay(this.pickRay);
if (triggeredButton != null) {
//search the index of the UI element triggered
for (var i = 0; i < buttons.length; i++) {
if (buttons[i] == triggeredButton) {
_this.changeModel(i);
}
}
//the nextButton moves to the next customer review, changing the agent accordingly
if (nextButton == triggeredButton) {
reviewIndex ++;
if (reviewIndex == reviewsNumber) {
reviewIndex = 0;
}
var message = {
command: "Show",
clip_url: dbMatrix[reviewIndex].clip_url
};
Messages.sendMessage(AGENT_REVIEW_CHANNEL, JSON.stringify(message));
print("Show sent to agent");
// update UI
textReviewerName.text = dbMatrix[reviewIndex].name;
reviewerScore.url = starConverter(dbMatrix[reviewIndex].score);
print("UI updated");
}
//the playButton sends the play command to the agent
if (playButton == triggeredButton) {
var message = {
command: "Play",
clip_url: dbMatrix[reviewIndex].clip_url
};
Messages.sendMessage(AGENT_REVIEW_CHANNEL, JSON.stringify(message));
print("Play sent to agent");
}
//the tryOnAvatarButton change the camera mode and puts t a copy of the inspected item in a proper position on the the avatar
if (tryOnAvatarButton == triggeredButton) {
print("tryOnAvatar pressed!");
var itemPositionWhileTrying = null;
//All the offset here are good just for Will avatar increasing its size by one
switch (Entities.getEntityProperties(inspectedEntityID).name) {
case "Item_Sunglasses":
itemPositionWhileTrying = {x: 0, y: 0.04, z: 0.05};
break;
case "Item_Hat":
itemPositionWhileTrying = {x: 0, y: 0.16, z: 0.025};
break;
default:
//there isn't any position specified for that item, use a default one
itemPositionWhileTrying = {x: 0, y: 0.16, z: 0.025};
break;
}
//Code for the overlay text for the mirror.
mirrorPanel = new OverlayPanel({
anchorPositionBinding: { avatar: "MyAvatar" },
anchorRotationBinding: { avatar: "MyAvatar" },
offsetPosition: {
x: 0.5,
y: 0.9,
z: 0
},
offsetRotation: Quat.fromVec3Degrees({x: 0, y: 180, z: 0}),
isFacingAvatar: false
});
var mirrorText = new Text3DOverlay({
text: "Press the bumper to go back in inspection",
isFacingAvatar: false,
ignoreRayIntersection: true,
dimensions: { x: 0, y: 0 },
backgroundColor: { red: 255, green: 255, blue: 255 },
color: { red: 200, green: 0, blue: 0 },
topMargin: 0.00625,
leftMargin: 0.00625,
bottomMargin: 0.1,
rightMargin: 0.00625,
lineHeight: 0.05,
alpha: 1,
backgroundAlpha: 0.3,
visible: true
});
mirrorPanel.addChild(mirrorText);
tryingOnAvatar = true;
//Clean inspect Overlays and related stuff
workingHand.clean();
mainPanel.destroy();
mainPanel = null;
isUIWorking = false;
Entities.editEntity(inspectedEntityID, { visible: false }); //the inspected item becomes invisible
Camera.mode = "entity";
Camera.cameraEntity = _this.entityID;
var entityProperties = Entities.getEntityProperties(inspectedEntityID);
tempTryEntity = Entities.addEntity({
type: entityProperties.type,
name: entityProperties.name,
localPosition: itemPositionWhileTrying,
dimensions: itemOriginalDimensions,
collisionsWillMove: false,
ignoreForCollisions: true,
modelURL: entityProperties.modelURL,
shapeType: entityProperties.shapeType,
originalTextures: entityProperties.originalTextures,
parentID: MyAvatar.sessionUUID,
parentJointIndex: MyAvatar.getJointIndex("Head")
});
}
}
} else if (!bumperPressed && this.waitingForBumpReleased) {
this.waitingForBumpReleased = false;
}
},
this.clean = function() {
this.pickRay = null;
this.overlayLine.destroy();
this.overlayLine = null;
pointer.visible = false;
}
};
function update(deltaTime) {
if (tryingOnAvatar) {
// if trying the item on avatar, wait for a bumper being pressed to exit this mode
//the bumper is already pressed when we get here because we triggered the button pressing the bumper so we have to wait it's released
if(Controller.getValue(workingHand.bumper) && wantToStopTrying) {
Camera.cameraEntity = null;
Camera.mode = "first person";
mirrorPanel.destroy();
mirrorPanel = null;
Entities.deleteEntity(tempTryEntity);
tempTryEntity = null;
Entities.editEntity(inspectedEntityID, { visible: true });
_this.createInspectUI();
tryingOnAvatar = false;
wantToStopTrying = false;
} else if (!Controller.getValue(workingHand.bumper) && !wantToStopTrying) {
//no bumper is pressed
wantToStopTrying = true;
}
return;
} else if (inspecting) {
//update the rays from both hands
leftController.updateHand();
rightController.updateHand();
//check the item status for consistency
var entityStatus = getEntityCustomData('statusKey', inspectedEntityID, null).status;
if (entityStatus == IN_HAND_STATUS) {
//the inspection is over
inspecting = false;
inspectedEntityID = null;
}
} else if (isUIWorking) {
//getting here when the inspect phase end, so we want to clean the UI
// Destroy rays
workingHand.clean();
// Destroy overlay
mainPanel.destroy();
isUIWorking = false;
var message = {
command: "Hide",
clip_url: ""
};
Messages.sendMessage(AGENT_REVIEW_CHANNEL, JSON.stringify(message));
print("Hide sent to agent");
if (separator != null || cameraReview != null) {
Entities.editEntity(separator, { visible: true });
Entities.editEntity(separator, { locked: true });
Entities.editEntity(cameraReview, { visible: true });
Entities.editEntity(cameraReview, { locked: true });
}
}
_this.positionRotationUpdate();
};
function starConverter(value) {
var starURL = ZERO_STAR_URL;
switch(value) {
case 0:
starURL = ZERO_STAR_URL;
break;
case 1:
starURL = ONE_STAR_URL;
break;
case 2:
starURL = TWO_STAR_URL;
break;
case 3:
starURL = THREE_STAR_URL;
break;
default:
starURL = ZERO_STAR_URL;
break;
}
return starURL;
};
// look for the database entity relative to the inspected item
function findItemDataBase(entityID, item) {
var dataBaseID = null;
var databaseEntityName = item + "DB";
var entitiesInZone = Entities.findEntities(Entities.getEntityProperties(entityID).position, (Entities.getEntityProperties(entityID).dimensions.x)*100);
for (var i = 0; i < entitiesInZone.length && dataBaseID == null; i++) {
if (Entities.getEntityProperties(entitiesInZone[i]).name == databaseEntityName) {
dataBaseID = entitiesInZone[i];
print("Database found! " + entitiesInZone[i]);
return dataBaseID;
}
}
print("No database for this item.");
return null;
};
function findItemByName(searchingPointEntityID, itemName) {
print("Looking for item: " + itemName);
var entitiesInZone = Entities.findEntities(Entities.getEntityProperties(searchingPointEntityID).position, (Entities.getEntityProperties(searchingPointEntityID).dimensions.x)*100);
for (var i = 0; i < entitiesInZone.length; i++) {
if (Entities.getEntityProperties(entitiesInZone[i]).name == itemName) {
print(itemName + " found! " + entitiesInZone[i]);
return entitiesInZone[i];
}
}
print("Item " + itemName + " not found");
return null;
};
InspectEntity.prototype = {
preload: function(entityID) {
this.entityID = entityID;
print("PRELOAD INSPECT ENTITY");
//get the owner ID from user data and compare to the mine
//the update will be connected just for the owner
var ownerObj = getEntityCustomData('ownerKey', this.entityID, null);
if (ownerObj.ownerID === MyAvatar.sessionUUID) {
rightController = new MyController(RIGHT_HAND); //rightController and leftController are two objects
leftController = new MyController(LEFT_HAND);
workingHand = rightController;
inspectingMyItem = true;
inspectRadius = (Entities.getEntityProperties(_this.entityID).dimensions.x) / 2 + COMFORT_ARM_LENGTH;
Script.update.connect(update);
}
},
//Put the item which calls this method in inspect mode if it belongs to the owner of the inspect zone
doSomething: function (entityID, dataArray) {
var data = JSON.parse(dataArray[0]);
var itemOwnerObj = getEntityCustomData('ownerKey', data.id, null);
var inspectOwnerObj = getEntityCustomData('ownerKey', this.entityID, null);
if (inspectOwnerObj == null) {
Entities.deleteEntity(data.id);
}
if (itemOwnerObj.ownerID === inspectOwnerObj.ownerID) {
//setup the things for inspecting the item
inspecting = true;
inspectedEntityID = data.id; //store the ID of the inspected entity
setEntityCustomData('statusKey', data.id, {
status: IN_INSPECT_STATUS
});
_this.createInspectUI();
itemOriginalDimensions = Entities.getEntityProperties(inspectedEntityID).dimensions;
// find separator and camera and hide
separator = findItemByName(inspectedEntityID, SEPARATOR);
Entities.editEntity(separator, { locked: false });
Entities.editEntity(separator, { visible: false });
print("Got here! Entity to hide: " + separator);
cameraReview = findItemByName(inspectedEntityID, CAMERA_REVIEW);
Entities.editEntity(cameraReview, { locked: false });
Entities.editEntity(cameraReview, { visible: false });
print("Got here! Entity to hide: " + cameraReview);
} else {
Entities.deleteEntity(data.id);
}
},
//make the inspection entity stay at the proper position with respect to the avatar and camera positions
positionRotationUpdate: function() {
var newRotation;
if (tryingOnAvatar) {
newRotation = Vec3.sum(Quat.safeEulerAngles(MyAvatar.orientation), {x:0, y: 180, z: 0}); //neccessary to set properly the camera in entity mode when trying on avatar
Entities.editEntity(_this.entityID, { rotation: Quat.fromVec3Degrees(newRotation) });
} else {
var newPosition = Vec3.sum(Camera.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), inspectRadius));
Entities.editEntity(_this.entityID, { position: newPosition });
newRotation = MyAvatar.orientation;
Entities.editEntity(_this.entityID, { rotation: newRotation });
}
newPosition = Vec3.sum(newPosition, Vec3.multiply(Quat.getRight(newRotation), 0.34));
},
createInspectUI : function() {
var infoObj = getEntityCustomData('infoKey', inspectedEntityID, null);
var itemDescriptionString = null;
var priceNumber = -1;
var availabilityNumber = -1;
var wearable = false;
if(infoObj != null) {
//var modelURLsLoop = infoObj.modelURLs; ??
var rootURLString = infoObj.rootURL;
for (var i = 0; i < infoObj.modelURLs.length; i++) {
modelURLsArray[i] = rootURLString + infoObj.modelURLs[i];
previewURLsArray[i] = rootURLString + infoObj.previewURLs[i];
}
itemDescriptionString = infoObj.description;
priceNumber = infoObj.price;
availabilityNumber = infoObj.availability;
wearable = infoObj.wearable;
infoObj = null;
}
//Looking for the item DB, this entity has in its userData the info of the customer reviews for the item
var DBID = findItemDataBase(_this.entityID, Entities.getEntityProperties(inspectedEntityID).name);
if (DBID != null) {
infoObj = getEntityCustomData('infoKey', DBID, null);
var scoreAverage = null;
var reviewerName = null;
if(infoObj != null) {
dbMatrix = infoObj.dbKey;
reviewsNumber = infoObj.dbKey.length;
//print("DB matrix is " + dbMatrix + " with element number: " + reviewsNumber);
var scoreSum = null;
for (var i = 0; i < dbMatrix.length; i++) {
scoreSum += dbMatrix[i].score;
}
if (dbMatrix.length) {
scoreAverage = Math.round(scoreSum / dbMatrix.length);
reviewerName = dbMatrix[reviewIndex].name;
var message = {
command: "Show",
clip_url: dbMatrix[reviewIndex].clip_url
};
Messages.sendMessage(AGENT_REVIEW_CHANNEL, JSON.stringify(message));
print("Show sent to agent");
} else {
//some default value if the DB is empty
scoreAverage = 0;
reviewerName = NO_REVIEWS_AVAILABLE;
}
}
print ("Creating inspect UI");
//set the main panel to follow the inspect entity
mainPanel = new OverlayPanel({
anchorPositionBinding: { entity: _this.entityID },
anchorRotationBinding: { entity: _this.entityID },
isFacingAvatar: false
});
var offsetPositionY = 0.2;
var offsetPositionX = -0.4;
//these buttons are the 3 previews of the item
for (var i = 0; i < previewURLsArray.length; i++) {
buttons[i] = new Image3DOverlay({
url: previewURLsArray[i],
dimensions: {
x: 0.15,
y: 0.15
},
isFacingAvatar: false,
alpha: 0.8,
ignoreRayIntersection: false,
offsetPosition: {
x: offsetPositionX,
y: offsetPositionY - (i * offsetPositionY),
z: 0
},
emissive: true,
});
mainPanel.addChild(buttons[i]);
}
//aggregateScore is the average between those given by the reviewers
var aggregateScore = new Image3DOverlay({
url: starConverter(scoreAverage),
dimensions: {
x: 0.25,
y: 0.25
},
isFacingAvatar: false,
alpha: 1,
ignoreRayIntersection: true,
offsetPosition: {
x: 0,
y: 0.27,
z: 0
},
emissive: true,
});
mainPanel.addChild(aggregateScore);
//if any review is available create buttons to manage them
if (dbMatrix.length) {
playButton = new Image3DOverlay({
url: "https://dl.dropboxusercontent.com/u/14127429/FBX/VRshop/Play.png",
dimensions: {
x: 0.08,
y: 0.08
},
isFacingAvatar: false,
alpha: 1,
ignoreRayIntersection: false,
offsetPosition: {
x: 0.42,
y: 0.27,
z: 0
},
emissive: true,
});
mainPanel.addChild(playButton);
reviewerScore = new Image3DOverlay({
url: starConverter(dbMatrix[reviewIndex].score),
dimensions: {
x: 0.15,
y: 0.15
},
isFacingAvatar: false,
alpha: 1,
ignoreRayIntersection: true,
offsetPosition: {
x: 0.31,
y: 0.26,
z: 0
},
emissive: true,
});
mainPanel.addChild(reviewerScore);
nextButton = new Image3DOverlay({
url: "https://dl.dropboxusercontent.com/u/14127429/FBX/VRshop/Next.png",
dimensions: {
x: 0.2,
y: 0.2
},
isFacingAvatar: false,
alpha: 1,
ignoreRayIntersection: false,
offsetPosition: {
x: 0.36,
y: 0.18,
z: 0
},
emissive: true,
});
mainPanel.addChild(nextButton);
}
textReviewerName = new Text3DOverlay({
text: reviewerName,
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: true,
offsetPosition: {
x: 0.23,
y: 0.31,
z: 0
},
dimensions: { x: 0, y: 0 },
backgroundColor: { red: 255, green: 255, blue: 255 },
color: { red: 0, green: 0, blue: 0 },
topMargin: 0.00625,
leftMargin: 0.00625,
bottomMargin: 0.1,
rightMargin: 0.00625,
lineHeight: 0.02,
alpha: 1,
backgroundAlpha: 0.3
});
mainPanel.addChild(textReviewerName);
//if the item is wearable create a tryOnAvatarButton
if (wearable) {
tryOnAvatarButton = new Image3DOverlay({
url: TRY_ON_ICON,
dimensions: {
x: 0.2,
y: 0.2
},
isFacingAvatar: false,
alpha: 1,
ignoreRayIntersection: false,
offsetPosition: {
x: 0.35,
y: -0.22,
z: 0
},
emissive: true,
});
mainPanel.addChild(tryOnAvatarButton);
}
var textQuantityString = new Text3DOverlay({
text: "Quantity: ",
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: true,
offsetPosition: {
x: 0.25,
y: -0.3,
z: 0
},
dimensions: { x: 0, y: 0 },
backgroundColor: { red: 255, green: 255, blue: 255 },
color: { red: 0, green: 0, blue: 0 },
topMargin: 0.00625,
leftMargin: 0.00625,
bottomMargin: 0.1,
rightMargin: 0.00625,
lineHeight: 0.02,
alpha: 1,
backgroundAlpha: 0.3
});
mainPanel.addChild(textQuantityString);
var textQuantityNumber = new Text3DOverlay({
text: availabilityNumber,
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: true,
offsetPosition: {
x: 0.28,
y: -0.32,
z: 0
},
dimensions: { x: 0, y: 0 },
backgroundColor: { red: 255, green: 255, blue: 255 },
color: { red: 0, green: 0, blue: 0 },
topMargin: 0.00625,
leftMargin: 0.00625,
bottomMargin: 0.1,
rightMargin: 0.00625,
lineHeight: 0.06,
alpha: 1,
backgroundAlpha: 0.3
});
mainPanel.addChild(textQuantityNumber);
if (itemDescriptionString != null) {
var textDescription = new Text3DOverlay({
text: "Price: " + priceNumber + "\nAdditional information: \n" + itemDescriptionString,
isFacingAvatar: false,
alpha: 1.0,
ignoreRayIntersection: true,
offsetPosition: {
x: -0.2,
y: -0.3,
z: 0
},
dimensions: { x: 0, y: 0 },
backgroundColor: { red: 255, green: 255, blue: 255 },
color: { red: 0, green: 0, blue: 0 },
topMargin: 0.00625,
leftMargin: 0.00625,
bottomMargin: 0.1,
rightMargin: 0.00625,
lineHeight: 0.02,
alpha: 1,
backgroundAlpha: 0.3
});
mainPanel.addChild(textDescription);
}
print ("GOT HERE: Descrition " + itemDescriptionString + " Availability " + availabilityNumber);
isUIWorking = true;
}
},
//Manage the collisions and tell to the item if it is into the this inspection area
collisionWithEntity: function(myID, otherID, collisionInfo) {
var itemObj = getEntityCustomData('itemKey', _this.entityID, null);
if (itemObj != null) {
if (itemObj.itemID == otherID) { //verify that the inspect area is colliding with the actual item which created it
var penetrationValue = Vec3.length(collisionInfo.penetration);
if (penetrationValue > PENETRATION_THRESHOLD && collidedItemID === null) {
collidedItemID = otherID;
print("Start collision with: " + Entities.getEntityProperties(collidedItemID).name);
Entities.callEntityMethod(otherID, 'changeOverlayColor', null);
} else if (penetrationValue < PENETRATION_THRESHOLD && collidedItemID !== null) {
print("End collision with: " + Entities.getEntityProperties(collidedItemID).name);
collidedItemID = null;
Entities.callEntityMethod(otherID, 'changeOverlayColor', null);
}
}
}
},
changeModel: function(index) {
var entityProperties = Entities.getEntityProperties(inspectedEntityID);
if (entityProperties.modelURL != modelURLsArray[index]) {
Entities.editEntity(inspectedEntityID, { modelURL: modelURLsArray[index] });
}
},
unload: function (entityID) {
if(inspectingMyItem){
print("UNLOAD INSPECT ENTITY");
Script.update.disconnect(update);
// clean UI
Entities.deleteEntity(_this.entityID);
}
}
};
return new InspectEntity();
})