This commit is contained in:
Philip Rosedale 2014-09-12 13:59:33 -07:00
commit 089a17b5b1
34 changed files with 1545 additions and 923 deletions

View file

@ -878,10 +878,10 @@ void OctreeServer::setupDatagramProcessingThread() {
// we do not want this event loop to be the handler for UDP datagrams, so disconnect // we do not want this event loop to be the handler for UDP datagrams, so disconnect
disconnect(&nodeList->getNodeSocket(), 0, this, 0); disconnect(&nodeList->getNodeSocket(), 0, this, 0);
// setup a QThread with us as parent that will house the AudioMixerDatagramProcessor // setup a QThread with us as parent that will house the OctreeServerDatagramProcessor
_datagramProcessingThread = new QThread(this); _datagramProcessingThread = new QThread(this);
// create an AudioMixerDatagramProcessor and move it to that thread // create an OctreeServerDatagramProcessor and move it to that thread
OctreeServerDatagramProcessor* datagramProcessor = new OctreeServerDatagramProcessor(nodeList->getNodeSocket(), thread()); OctreeServerDatagramProcessor* datagramProcessor = new OctreeServerDatagramProcessor(nodeList->getNodeSocket(), thread());
datagramProcessor->moveToThread(_datagramProcessingThread); datagramProcessor->moveToThread(_datagramProcessingThread);

View file

@ -37,6 +37,7 @@ function vInterpolate(a, b, fraction) {
var startTimeInSeconds = new Date().getTime() / 1000; var startTimeInSeconds = new Date().getTime() / 1000;
var NATURAL_SIZE_OF_BUTTERFLY = { x: 9.512, y: 4.427, z: 1.169 };
var lifeTime = 600; // lifetime of the butterflies in seconds var lifeTime = 600; // lifetime of the butterflies in seconds
var range = 1.0; // Over what distance in meters do you want the flock to fly around var range = 1.0; // Over what distance in meters do you want the flock to fly around
var frame = 0; var frame = 0;
@ -78,8 +79,14 @@ function addButterfly() {
var color = { red: 100, green: 100, blue: 100 }; var color = { red: 100, green: 100, blue: 100 };
var size = 0; var size = 0;
var minSize = 0.06;
var randomSize = 0.2;
var maxSize = minSize + randomSize;
size = 0.06 + Math.random() * 0.2; size = 0.06 + Math.random() * 0.2;
var dimensions = Vec3.multiply(NATURAL_SIZE_OF_BUTTERFLY, (size / maxSize));
flockPosition = Vec3.sum(MyAvatar.position,Vec3.sum( flockPosition = Vec3.sum(MyAvatar.position,Vec3.sum(
Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_ABOVE_ME), Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_ABOVE_ME),
Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_IN_FRONT_OF_ME))); Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_IN_FRONT_OF_ME)));
@ -91,7 +98,7 @@ function addButterfly() {
velocity: { x: 0, y: 0.0, z: 0 }, velocity: { x: 0, y: 0.0, z: 0 },
gravity: { x: 0, y: 1.0, z: 0 }, gravity: { x: 0, y: 1.0, z: 0 },
damping: 0.1, damping: 0.1,
radius : size, dimensions: dimensions,
color: color, color: color,
rotation: rotation, rotation: rotation,
animationURL: "http://business.ozblog.me/objects/butterfly/newButterfly2.fbx", animationURL: "http://business.ozblog.me/objects/butterfly/newButterfly2.fbx",
@ -203,7 +210,8 @@ function updateButterflies(deltaTime) {
// If we are near the target, we should get a new target // If we are near the target, we should get a new target
if (Vec3.length(Vec3.subtract(properties.position, butterflies[i].targetPosition)) < (properties.radius / 1.0)) { var halfLargestDimension = Vec3.length(properties.dimensions) / 2.0;
if (Vec3.length(Vec3.subtract(properties.position, butterflies[i].targetPosition)) < (halfLargestDimension)) {
butterflies[i].moving = false; butterflies[i].moving = false;
} }
@ -214,7 +222,7 @@ function updateButterflies(deltaTime) {
} }
// Use a cosine wave offset to make it look like its flapping. // Use a cosine wave offset to make it look like its flapping.
var offset = Math.cos(nowTimeInSeconds * BUTTERFLY_FLAP_SPEED) * (properties.radius); var offset = Math.cos(nowTimeInSeconds * BUTTERFLY_FLAP_SPEED) * (halfLargestDimension);
properties.position.y = properties.position.y + (offset - butterflies[i].previousFlapOffset); properties.position.y = properties.position.y + (offset - butterflies[i].previousFlapOffset);
// Change position relative to previous offset. // Change position relative to previous offset.
butterflies[i].previousFlapOffset = offset; butterflies[i].previousFlapOffset = offset;

View file

@ -40,7 +40,7 @@ var LEFT = 0;
var RIGHT = 1; var RIGHT = 1;
var SPAWN_DISTANCE = 1; var SPAWN_DISTANCE = 1;
var DEFAULT_RADIUS = 0.10; var DEFAULT_DIMENSION = 0.20;
var modelURLs = [ var modelURLs = [
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX", "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX",
@ -1220,7 +1220,7 @@ var toolBar = (function () {
Entities.addEntity({ Entities.addEntity({
type: "Model", type: "Model",
position: position, position: position,
radius: DEFAULT_RADIUS, dimensions: { x: DEFAULT_DIMENSION, y: DEFAULT_DIMENSION, z: DEFAULT_DIMENSION },
modelURL: url modelURL: url
}); });
print("Model added: " + url); print("Model added: " + url);
@ -1311,8 +1311,9 @@ var toolBar = (function () {
Entities.addEntity({ Entities.addEntity({
type: "Box", type: "Box",
position: position, position: position,
radius: DEFAULT_RADIUS, dimensions: { x: DEFAULT_DIMENSION, y: DEFAULT_DIMENSION, z: DEFAULT_DIMENSION },
color: { red: 255, green: 0, blue: 0 } color: { red: 255, green: 0, blue: 0 }
}); });
} else { } else {
print("Can't create box: Box would be out of bounds."); print("Can't create box: Box would be out of bounds.");
@ -1327,7 +1328,7 @@ var toolBar = (function () {
Entities.addEntity({ Entities.addEntity({
type: "Sphere", type: "Sphere",
position: position, position: position,
radius: DEFAULT_RADIUS, dimensions: { x: DEFAULT_DIMENSION, y: DEFAULT_DIMENSION, z: DEFAULT_DIMENSION },
color: { red: 255, green: 0, blue: 0 } color: { red: 255, green: 0, blue: 0 }
}); });
} else { } else {
@ -1804,7 +1805,7 @@ function controller(wichSide) {
this.modelURL = ""; this.modelURL = "";
this.oldModelRotation; this.oldModelRotation;
this.oldModelPosition; this.oldModelPosition;
this.oldModelRadius; this.oldModelHalfDiagonal;
this.positionAtGrab; this.positionAtGrab;
this.rotationAtGrab; this.rotationAtGrab;
@ -1864,7 +1865,7 @@ function controller(wichSide) {
this.oldModelPosition = properties.position; this.oldModelPosition = properties.position;
this.oldModelRotation = properties.rotation; this.oldModelRotation = properties.rotation;
this.oldModelRadius = properties.radius; this.oldModelHalfDiagonal = Vec3.length(properties.dimensions) / 2.0;
this.positionAtGrab = this.palmPosition; this.positionAtGrab = this.palmPosition;
this.rotationAtGrab = this.rotation; this.rotationAtGrab = this.rotation;
@ -1873,7 +1874,7 @@ function controller(wichSide) {
this.jointsIntersectingFromStart = []; this.jointsIntersectingFromStart = [];
for (var i = 0; i < jointList.length; i++) { for (var i = 0; i < jointList.length; i++) {
var distance = Vec3.distance(MyAvatar.getJointPosition(jointList[i]), this.oldModelPosition); var distance = Vec3.distance(MyAvatar.getJointPosition(jointList[i]), this.oldModelPosition);
if (distance < this.oldModelRadius) { if (distance < this.oldModelHalfDiagonal) {
this.jointsIntersectingFromStart.push(i); this.jointsIntersectingFromStart.push(i);
} }
} }
@ -1897,10 +1898,10 @@ function controller(wichSide) {
if (closestJointIndex != -1) { if (closestJointIndex != -1) {
print("closestJoint: " + jointList[closestJointIndex]); print("closestJoint: " + jointList[closestJointIndex]);
print("closestJointDistance (attach max distance): " + closestJointDistance + " (" + this.oldModelRadius + ")"); print("closestJointDistance (attach max distance): " + closestJointDistance + " (" + this.oldModelHalfDiagonal + ")");
} }
if (closestJointDistance < this.oldModelRadius) { if (closestJointDistance < this.oldModelHalfDiagonal) {
if (this.jointsIntersectingFromStart.indexOf(closestJointIndex) != -1 || if (this.jointsIntersectingFromStart.indexOf(closestJointIndex) != -1 ||
(leftController.grabbing && rightController.grabbing && (leftController.grabbing && rightController.grabbing &&
@ -1916,7 +1917,7 @@ function controller(wichSide) {
var attachmentRotation = Quat.multiply(Quat.inverse(jointRotation), this.oldModelRotation); var attachmentRotation = Quat.multiply(Quat.inverse(jointRotation), this.oldModelRotation);
MyAvatar.attach(this.modelURL, jointList[closestJointIndex], MyAvatar.attach(this.modelURL, jointList[closestJointIndex],
attachmentOffset, attachmentRotation, 2.0 * this.oldModelRadius, attachmentOffset, attachmentRotation, 2.0 * this.oldModelHalfDiagonal,
true, false); true, false);
Entities.deleteEntity(this.entityID); Entities.deleteEntity(this.entityID);
} }
@ -1970,11 +1971,12 @@ function controller(wichSide) {
var z = Vec3.dot(Vec3.subtract(P, A), this.right); var z = Vec3.dot(Vec3.subtract(P, A), this.right);
var X = Vec3.sum(A, Vec3.multiply(B, x)); var X = Vec3.sum(A, Vec3.multiply(B, x));
var d = Vec3.length(Vec3.subtract(P, X)); var d = Vec3.length(Vec3.subtract(P, X));
var halfDiagonal = Vec3.length(properties.dimensions) / 2.0;
var angularSize = 2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14; var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14;
if (0 < x && angularSize > MIN_ANGULAR_SIZE) { if (0 < x && angularSize > MIN_ANGULAR_SIZE) {
if (angularSize > MAX_ANGULAR_SIZE) { if (angularSize > MAX_ANGULAR_SIZE) {
print("Angular size too big: " + 2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14); print("Angular size too big: " + angularSize);
return { valid: false }; return { valid: false };
} }
@ -2021,7 +2023,10 @@ function controller(wichSide) {
origin: this.palmPosition, origin: this.palmPosition,
direction: this.front direction: this.front
}); });
var angularSize = 2 * Math.atan(intersection.properties.radius / Vec3.distance(Camera.getPosition(), intersection.properties.position)) * 180 / 3.14;
var halfDiagonal = Vec3.length(intersection.properties.dimensions) / 2.0;
var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), intersection.properties.position)) * 180 / 3.14;
if (intersection.accurate && intersection.entityID.isKnownID && angularSize > MIN_ANGULAR_SIZE && angularSize < MAX_ANGULAR_SIZE) { if (intersection.accurate && intersection.entityID.isKnownID && angularSize > MIN_ANGULAR_SIZE && angularSize < MAX_ANGULAR_SIZE) {
this.glowedIntersectingModel = intersection.entityID; this.glowedIntersectingModel = intersection.entityID;
Entities.editEntity(this.glowedIntersectingModel, { glowLevel: 0.25 }); Entities.editEntity(this.glowedIntersectingModel, { glowLevel: 0.25 });
@ -2099,7 +2104,7 @@ function controller(wichSide) {
var indicesToRemove = []; var indicesToRemove = [];
for (var i = 0; i < this.jointsIntersectingFromStart.length; ++i) { for (var i = 0; i < this.jointsIntersectingFromStart.length; ++i) {
var distance = Vec3.distance(MyAvatar.getJointPosition(this.jointsIntersectingFromStart[i]), this.oldModelPosition); var distance = Vec3.distance(MyAvatar.getJointPosition(this.jointsIntersectingFromStart[i]), this.oldModelPosition);
if (distance >= this.oldModelRadius) { if (distance >= this.oldModelHalfDiagonal) {
indicesToRemove.push(this.jointsIntersectingFromStart[i]); indicesToRemove.push(this.jointsIntersectingFromStart[i]);
} }
@ -2192,7 +2197,12 @@ function controller(wichSide) {
Vec3.multiplyQbyV(MyAvatar.getJointCombinedRotation(attachments[attachmentIndex].jointName), attachments[attachmentIndex].translation)), Vec3.multiplyQbyV(MyAvatar.getJointCombinedRotation(attachments[attachmentIndex].jointName), attachments[attachmentIndex].translation)),
rotation: Quat.multiply(MyAvatar.getJointCombinedRotation(attachments[attachmentIndex].jointName), rotation: Quat.multiply(MyAvatar.getJointCombinedRotation(attachments[attachmentIndex].jointName),
attachments[attachmentIndex].rotation), attachments[attachmentIndex].rotation),
radius: attachments[attachmentIndex].scale / 2.0,
// TODO: how do we know the correct dimensions for detachment???
dimensions: { x: attachments[attachmentIndex].scale / 2.0,
y: attachments[attachmentIndex].scale / 2.0,
z: attachments[attachmentIndex].scale / 2.0 },
modelURL: attachments[attachmentIndex].modelURL modelURL: attachments[attachmentIndex].modelURL
}; };
@ -2310,15 +2320,21 @@ function moveEntities() {
Entities.editEntity(leftController.entityID, { Entities.editEntity(leftController.entityID, {
position: newPosition, position: newPosition,
rotation: rotation, rotation: rotation,
radius: leftController.oldModelRadius * ratio // TODO: how do we know the correct dimensions for detachment???
//radius: leftController.oldModelHalfDiagonal * ratio
dimensions: { x: leftController.oldModelHalfDiagonal * ratio,
y: leftController.oldModelHalfDiagonal * ratio,
z: leftController.oldModelHalfDiagonal * ratio }
}); });
leftController.oldModelPosition = newPosition; leftController.oldModelPosition = newPosition;
leftController.oldModelRotation = rotation; leftController.oldModelRotation = rotation;
leftController.oldModelRadius *= ratio; leftController.oldModelHalfDiagonal *= ratio;
rightController.oldModelPosition = newPosition; rightController.oldModelPosition = newPosition;
rightController.oldModelRotation = rotation; rightController.oldModelRotation = rotation;
rightController.oldModelRadius *= ratio; rightController.oldModelHalfDiagonal *= ratio;
return; return;
} }
leftController.moveEntity(); leftController.moveEntity();
@ -2379,7 +2395,7 @@ function Tooltip() {
this.x = 285; this.x = 285;
this.y = 115; this.y = 115;
this.width = 500; this.width = 500;
this.height = 145; this.height = 180; // 145;
this.margin = 5; this.margin = 5;
this.decimals = 3; this.decimals = 3;
@ -2407,7 +2423,14 @@ function Tooltip() {
text += "Pitch: " + angles.x.toFixed(this.decimals) + "\n" text += "Pitch: " + angles.x.toFixed(this.decimals) + "\n"
text += "Yaw: " + angles.y.toFixed(this.decimals) + "\n" text += "Yaw: " + angles.y.toFixed(this.decimals) + "\n"
text += "Roll: " + angles.z.toFixed(this.decimals) + "\n" text += "Roll: " + angles.z.toFixed(this.decimals) + "\n"
text += "Scale: " + 2 * properties.radius.toFixed(this.decimals) + "\n" text += "Dimensions: " + properties.dimensions.x.toFixed(this.decimals) + ", "
+ properties.dimensions.y.toFixed(this.decimals) + ", "
+ properties.dimensions.z.toFixed(this.decimals) + "\n";
text += "Natural Dimensions: " + properties.naturalDimensions.x.toFixed(this.decimals) + ", "
+ properties.naturalDimensions.y.toFixed(this.decimals) + ", "
+ properties.naturalDimensions.z.toFixed(this.decimals) + "\n";
text += "ID: " + properties.id + "\n" text += "ID: " + properties.id + "\n"
if (properties.type == "Model") { if (properties.type == "Model") {
text += "Model URL: " + properties.modelURL + "\n" text += "Model URL: " + properties.modelURL + "\n"
@ -2426,6 +2449,7 @@ function Tooltip() {
text += "Lifetime: " + properties.lifetime + "\n" text += "Lifetime: " + properties.lifetime + "\n"
} }
text += "Age: " + properties.ageAsText + "\n" text += "Age: " + properties.ageAsText + "\n"
text += "Script: " + properties.script + "\n"
Overlays.editOverlay(this.textOverlay, { text: text }); Overlays.editOverlay(this.textOverlay, { text: text });
@ -2477,7 +2501,9 @@ function mousePressEvent(event) {
if (isLocked(properties)) { if (isLocked(properties)) {
print("Model locked " + properties.id); print("Model locked " + properties.id);
} else { } else {
print("Checking properties: " + properties.id + " " + properties.isKnownID); var halfDiagonal = Vec3.length(properties.dimensions) / 2.0;
print("Checking properties: " + properties.id + " " + properties.isKnownID + " - Half Diagonal:" + halfDiagonal);
// P P - Model // P P - Model
// /| A - Palm // /| A - Palm
// / | d B - unit vector toward tip // / | d B - unit vector toward tip
@ -2496,8 +2522,9 @@ function mousePressEvent(event) {
var x = Vec3.dot(Vec3.subtract(P, A), B); var x = Vec3.dot(Vec3.subtract(P, A), B);
var X = Vec3.sum(A, Vec3.multiply(B, x)); var X = Vec3.sum(A, Vec3.multiply(B, x));
var d = Vec3.length(Vec3.subtract(P, X)); var d = Vec3.length(Vec3.subtract(P, X));
var halfDiagonal = Vec3.length(properties.dimensions) / 2.0;
var angularSize = 2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14; var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14;
if (0 < x && angularSize > MIN_ANGULAR_SIZE) { if (0 < x && angularSize > MIN_ANGULAR_SIZE) {
if (angularSize < MAX_ANGULAR_SIZE) { if (angularSize < MAX_ANGULAR_SIZE) {
entitySelected = true; entitySelected = true;
@ -2506,13 +2533,13 @@ function mousePressEvent(event) {
orientation = MyAvatar.orientation; orientation = MyAvatar.orientation;
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation)); intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
} else { } else {
print("Angular size too big: " + 2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14); print("Angular size too big: " + angularSize);
} }
} }
} }
} }
if (entitySelected) { if (entitySelected) {
selectedEntityProperties.oldRadius = selectedEntityProperties.radius; selectedEntityProperties.oldDimensions = selectedEntityProperties.dimensions;
selectedEntityProperties.oldPosition = { selectedEntityProperties.oldPosition = {
x: selectedEntityProperties.position.x, x: selectedEntityProperties.position.x,
y: selectedEntityProperties.position.y, y: selectedEntityProperties.position.y,
@ -2551,7 +2578,11 @@ function mouseMoveEvent(event) {
glowedEntityID.isKnownID = false; glowedEntityID.isKnownID = false;
} }
var angularSize = 2 * Math.atan(entityIntersection.properties.radius / Vec3.distance(Camera.getPosition(), entityIntersection.properties.position)) * 180 / 3.14; var halfDiagonal = Vec3.length(entityIntersection.properties.dimensions) / 2.0;
var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(),
entityIntersection.properties.position)) * 180 / 3.14;
if (entityIntersection.entityID.isKnownID && angularSize > MIN_ANGULAR_SIZE && angularSize < MAX_ANGULAR_SIZE) { if (entityIntersection.entityID.isKnownID && angularSize > MIN_ANGULAR_SIZE && angularSize < MAX_ANGULAR_SIZE) {
Entities.editEntity(entityIntersection.entityID, { glowLevel: 0.25 }); Entities.editEntity(entityIntersection.entityID, { glowLevel: 0.25 });
glowedEntityID = entityIntersection.entityID; glowedEntityID = entityIntersection.entityID;
@ -2573,7 +2604,7 @@ function mouseMoveEvent(event) {
} }
pickRay = Camera.computePickRay(event.x, event.y); pickRay = Camera.computePickRay(event.x, event.y);
if (wasShifted != event.isShifted || modifier != oldModifier) { if (wasShifted != event.isShifted || modifier != oldModifier) {
selectedEntityProperties.oldRadius = selectedEntityProperties.radius; selectedEntityProperties.oldDimensions = selectedEntityProperties.dimensions;
selectedEntityProperties.oldPosition = { selectedEntityProperties.oldPosition = {
x: selectedEntityProperties.position.x, x: selectedEntityProperties.position.x,
@ -2603,9 +2634,12 @@ function mouseMoveEvent(event) {
return; return;
case 1: case 1:
// Let's Scale // Let's Scale
selectedEntityProperties.radius = (selectedEntityProperties.oldRadius * selectedEntityProperties.dimensions = Vec3.multiply(selectedEntityProperties.dimensions,
(1.0 + (mouseLastPosition.y - event.y) / SCALE_FACTOR)); (1.0 + (mouseLastPosition.y - event.y) / SCALE_FACTOR));
if (selectedEntityProperties.radius < 0.01) {
var halfDiagonal = Vec3.length(selectedEntityProperties.dimensions) / 2.0;
if (halfDiagonal < 0.01) {
print("Scale too small ... bailling."); print("Scale too small ... bailling.");
return; return;
} }
@ -2753,6 +2787,13 @@ Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
setupModelMenus(); setupModelMenus();
var propertiesForEditedEntity;
var editEntityFormArray;
var editModelID = -1;
var dimensionX;
var dimensionY;
var dimensionZ;
function handeMenuEvent(menuItem) { function handeMenuEvent(menuItem) {
print("menuItemEvent() in JS... menuItem=" + menuItem); print("menuItemEvent() in JS... menuItem=" + menuItem);
if (menuItem == "Delete") { if (menuItem == "Delete") {
@ -2781,7 +2822,7 @@ function handeMenuEvent(menuItem) {
print(" Delete Entity.... not holding..."); print(" Delete Entity.... not holding...");
} }
} else if (menuItem == "Edit Properties...") { } else if (menuItem == "Edit Properties...") {
var editModelID = -1; editModelID = -1;
if (leftController.grabbing) { if (leftController.grabbing) {
print(" Edit Properties.... leftController.entityID="+ leftController.entityID); print(" Edit Properties.... leftController.entityID="+ leftController.entityID);
editModelID = leftController.entityID; editModelID = leftController.entityID;
@ -2797,79 +2838,111 @@ function handeMenuEvent(menuItem) {
if (editModelID != -1) { if (editModelID != -1) {
print(" Edit Properties.... about to edit properties..."); print(" Edit Properties.... about to edit properties...");
var properties = Entities.getEntityProperties(editModelID); propertiesForEditedEntity = Entities.getEntityProperties(editModelID);
var properties = propertiesForEditedEntity;
var array = new Array(); var array = new Array();
var index = 0;
var decimals = 3; var decimals = 3;
if (properties.type == "Model") { if (properties.type == "Model") {
array.push({ label: "Model URL:", value: properties.modelURL }); array.push({ label: "Model URL:", value: properties.modelURL });
index++;
array.push({ label: "Animation URL:", value: properties.animationURL }); array.push({ label: "Animation URL:", value: properties.animationURL });
index++;
array.push({ label: "Animation is playing:", value: properties.animationIsPlaying }); array.push({ label: "Animation is playing:", value: properties.animationIsPlaying });
index++;
array.push({ label: "Animation FPS:", value: properties.animationFPS }); array.push({ label: "Animation FPS:", value: properties.animationFPS });
index++;
array.push({ label: "Animation Frame:", value: properties.animationFrameIndex }); array.push({ label: "Animation Frame:", value: properties.animationFrameIndex });
index++;
} }
array.push({ label: "Position:", type: "header" });
index++;
array.push({ label: "X:", value: properties.position.x.toFixed(decimals) }); array.push({ label: "X:", value: properties.position.x.toFixed(decimals) });
index++;
array.push({ label: "Y:", value: properties.position.y.toFixed(decimals) }); array.push({ label: "Y:", value: properties.position.y.toFixed(decimals) });
index++;
array.push({ label: "Z:", value: properties.position.z.toFixed(decimals) }); array.push({ label: "Z:", value: properties.position.z.toFixed(decimals) });
index++;
array.push({ label: "Registration X:", value: properties.registrationPoint.x.toFixed(decimals) });
index++;
array.push({ label: "Registration Y:", value: properties.registrationPoint.y.toFixed(decimals) });
index++;
array.push({ label: "Registration Z:", value: properties.registrationPoint.z.toFixed(decimals) });
index++;
array.push({ label: "Rotation:", type: "header" });
index++;
var angles = Quat.safeEulerAngles(properties.rotation); var angles = Quat.safeEulerAngles(properties.rotation);
array.push({ label: "Pitch:", value: angles.x.toFixed(decimals) }); array.push({ label: "Pitch:", value: angles.x.toFixed(decimals) });
index++;
array.push({ label: "Yaw:", value: angles.y.toFixed(decimals) }); array.push({ label: "Yaw:", value: angles.y.toFixed(decimals) });
index++;
array.push({ label: "Roll:", value: angles.z.toFixed(decimals) }); array.push({ label: "Roll:", value: angles.z.toFixed(decimals) });
array.push({ label: "Scale:", value: 2 * properties.radius.toFixed(decimals) }); index++;
array.push({ label: "Velocity X:", value: properties.velocity.x.toFixed(decimals) }); array.push({ label: "Dimensions:", type: "header" });
array.push({ label: "Velocity Y:", value: properties.velocity.y.toFixed(decimals) }); index++;
array.push({ label: "Velocity Z:", value: properties.velocity.z.toFixed(decimals) }); array.push({ label: "Width:", value: properties.dimensions.x.toFixed(decimals) });
array.push({ label: "Damping:", value: properties.damping.toFixed(decimals) }); dimensionX = index;
index++;
array.push({ label: "Height:", value: properties.dimensions.y.toFixed(decimals) });
dimensionY = index;
index++;
array.push({ label: "Depth:", value: properties.dimensions.z.toFixed(decimals) });
dimensionZ = index;
index++;
array.push({ label: "", type: "inlineButton", buttonLabel: "Reset to Natural Dimensions", name: "resetDimensions" });
index++;
array.push({ label: "Velocity:", type: "header" });
index++;
array.push({ label: "Linear X:", value: properties.velocity.x.toFixed(decimals) });
index++;
array.push({ label: "Linear Y:", value: properties.velocity.y.toFixed(decimals) });
index++;
array.push({ label: "Linear Z:", value: properties.velocity.z.toFixed(decimals) });
index++;
array.push({ label: "Linear Damping:", value: properties.damping.toFixed(decimals) });
index++;
array.push({ label: "Angular Pitch:", value: properties.angularVelocity.x.toFixed(decimals) });
index++;
array.push({ label: "Angular Yaw:", value: properties.angularVelocity.y.toFixed(decimals) });
index++;
array.push({ label: "Angular Roll:", value: properties.angularVelocity.z.toFixed(decimals) });
index++;
array.push({ label: "Angular Damping:", value: properties.angularDamping.toFixed(decimals) });
index++;
array.push({ label: "Gravity X:", value: properties.gravity.x.toFixed(decimals) }); array.push({ label: "Gravity X:", value: properties.gravity.x.toFixed(decimals) });
index++;
array.push({ label: "Gravity Y:", value: properties.gravity.y.toFixed(decimals) }); array.push({ label: "Gravity Y:", value: properties.gravity.y.toFixed(decimals) });
index++;
array.push({ label: "Gravity Z:", value: properties.gravity.z.toFixed(decimals) }); array.push({ label: "Gravity Z:", value: properties.gravity.z.toFixed(decimals) });
index++;
array.push({ label: "Lifetime:", value: properties.lifetime.toFixed(decimals) }); array.push({ label: "Lifetime:", value: properties.lifetime.toFixed(decimals) });
index++;
array.push({ label: "Visible:", value: properties.visible });
index++;
if (properties.type == "Box" || properties.type == "Sphere") { if (properties.type == "Box" || properties.type == "Sphere") {
array.push({ label: "Color:", type: "header" });
index++;
array.push({ label: "Red:", value: properties.color.red }); array.push({ label: "Red:", value: properties.color.red });
index++;
array.push({ label: "Green:", value: properties.color.green }); array.push({ label: "Green:", value: properties.color.green });
index++;
array.push({ label: "Blue:", value: properties.color.blue }); array.push({ label: "Blue:", value: properties.color.blue });
index++;
} }
array.push({ button: "Cancel" }); array.push({ button: "Cancel" });
index++;
if (Window.form("Edit Properties", array)) { editEntityFormArray = array;
var index = 0; Window.nonBlockingForm("Edit Properties", array);
if (properties.type == "Model") {
properties.modelURL = array[index++].value;
properties.animationURL = array[index++].value;
properties.animationIsPlaying = array[index++].value;
properties.animationFPS = array[index++].value;
properties.animationFrameIndex = array[index++].value;
}
properties.position.x = array[index++].value;
properties.position.y = array[index++].value;
properties.position.z = array[index++].value;
angles.x = array[index++].value;
angles.y = array[index++].value;
angles.z = array[index++].value;
properties.rotation = Quat.fromVec3Degrees(angles);
properties.radius = array[index++].value / 2;
properties.velocity.x = array[index++].value;
properties.velocity.y = array[index++].value;
properties.velocity.z = array[index++].value;
properties.damping = array[index++].value;
properties.gravity.x = array[index++].value;
properties.gravity.y = array[index++].value;
properties.gravity.z = array[index++].value;
properties.lifetime = array[index++].value; // give ourselves that many more seconds
if (properties.type == "Box" || properties.type == "Sphere") {
properties.color.red = array[index++].value;
properties.color.green = array[index++].value;
properties.color.blue = array[index++].value;
}
Entities.editEntity(editModelID, properties);
}
modelSelected = false;
} }
} else if (menuItem == "Paste Models") { } else if (menuItem == "Paste Models") {
modelImporter.paste(); modelImporter.paste();
@ -2930,3 +3003,76 @@ Controller.keyReleaseEvent.connect(function (event) {
handeMenuEvent("Delete"); handeMenuEvent("Delete");
} }
}); });
Window.inlineButtonClicked.connect(function (name) {
if (name == "resetDimensions") {
var decimals = 3;
Window.reloadNonBlockingForm([
{ value: propertiesForEditedEntity.naturalDimensions.x.toFixed(decimals), oldIndex: dimensionX },
{ value: propertiesForEditedEntity.naturalDimensions.y.toFixed(decimals), oldIndex: dimensionY },
{ value: propertiesForEditedEntity.naturalDimensions.z.toFixed(decimals), oldIndex: dimensionZ }
]);
}
});
Window.nonBlockingFormClosed.connect(function() {
array = editEntityFormArray;
if (Window.getNonBlockingFormResult(array)) {
var properties = propertiesForEditedEntity;
var index = 0;
if (properties.type == "Model") {
properties.modelURL = array[index++].value;
properties.animationURL = array[index++].value;
properties.animationIsPlaying = array[index++].value;
properties.animationFPS = array[index++].value;
properties.animationFrameIndex = array[index++].value;
}
index++; // skip header
properties.position.x = array[index++].value;
properties.position.y = array[index++].value;
properties.position.z = array[index++].value;
properties.registrationPoint.x = array[index++].value;
properties.registrationPoint.y = array[index++].value;
properties.registrationPoint.z = array[index++].value;
index++; // skip header
var angles = Quat.safeEulerAngles(properties.rotation);
angles.x = array[index++].value;
angles.y = array[index++].value;
angles.z = array[index++].value;
properties.rotation = Quat.fromVec3Degrees(angles);
index++; // skip header
properties.dimensions.x = array[index++].value;
properties.dimensions.y = array[index++].value;
properties.dimensions.z = array[index++].value;
index++; // skip reset button
index++; // skip header
properties.velocity.x = array[index++].value;
properties.velocity.y = array[index++].value;
properties.velocity.z = array[index++].value;
properties.damping = array[index++].value;
properties.angularVelocity.x = array[index++].value;
properties.angularVelocity.y = array[index++].value;
properties.angularVelocity.z = array[index++].value;
properties.angularDamping = array[index++].value;
properties.gravity.x = array[index++].value;
properties.gravity.y = array[index++].value;
properties.gravity.z = array[index++].value;
properties.lifetime = array[index++].value;
properties.visible = array[index++].value;
if (properties.type == "Box" || properties.type == "Sphere") {
index++; // skip header
properties.color.red = array[index++].value;
properties.color.green = array[index++].value;
properties.color.blue = array[index++].value;
}
Entities.editEntity(editModelID, properties);
}
modelSelected = false;
});

View file

@ -33,7 +33,7 @@ ModelReferential::ModelReferential(Referential* referential, EntityTree* tree, A
const EntityItem* item = _tree->findEntityByID(_entityID); const EntityItem* item = _tree->findEntityByID(_entityID);
if (item != NULL) { if (item != NULL) {
_refScale = item->getRadius(); _refScale = item->getLargestDimension();
_refRotation = item->getRotation(); _refRotation = item->getRotation();
_refPosition = item->getPosition() * (float)TREE_SCALE; _refPosition = item->getPosition() * (float)TREE_SCALE;
update(); update();
@ -52,7 +52,7 @@ ModelReferential::ModelReferential(const QUuid& entityID, EntityTree* tree, Avat
return; return;
} }
_refScale = item->getRadius(); _refScale = item->getLargestDimension();
_refRotation = item->getRotation(); _refRotation = item->getRotation();
_refPosition = item->getPosition() * (float)TREE_SCALE; _refPosition = item->getPosition() * (float)TREE_SCALE;
@ -69,8 +69,8 @@ void ModelReferential::update() {
} }
bool somethingChanged = false; bool somethingChanged = false;
if (item->getRadius() != _refScale) { if (item->getLargestDimension() != _refScale) {
_refScale = item->getRadius(); _refScale = item->getLargestDimension();
_avatar->setTargetScale(_refScale * _scale, true); _avatar->setTargetScale(_refScale * _scale, true);
somethingChanged = true; somethingChanged = true;
} }
@ -109,7 +109,7 @@ JointReferential::JointReferential(Referential* referential, EntityTree* tree, A
const EntityItem* item = _tree->findEntityByID(_entityID); const EntityItem* item = _tree->findEntityByID(_entityID);
const Model* model = getModel(item); const Model* model = getModel(item);
if (!isValid() || model == NULL || _jointIndex >= (uint32_t)(model->getJointStateCount())) { if (!isValid() || model == NULL || _jointIndex >= (uint32_t)(model->getJointStateCount())) {
_refScale = item->getRadius(); _refScale = item->getLargestDimension();
model->getJointRotationInWorldFrame(_jointIndex, _refRotation); model->getJointRotationInWorldFrame(_jointIndex, _refRotation);
model->getJointPositionInWorldFrame(_jointIndex, _refPosition); model->getJointPositionInWorldFrame(_jointIndex, _refPosition);
} }
@ -129,7 +129,7 @@ JointReferential::JointReferential(uint32_t jointIndex, const QUuid& entityID, E
return; return;
} }
_refScale = item->getRadius(); _refScale = item->getLargestDimension();
model->getJointRotationInWorldFrame(_jointIndex, _refRotation); model->getJointRotationInWorldFrame(_jointIndex, _refRotation);
model->getJointPositionInWorldFrame(_jointIndex, _refPosition); model->getJointPositionInWorldFrame(_jointIndex, _refPosition);
@ -147,8 +147,8 @@ void JointReferential::update() {
} }
bool somethingChanged = false; bool somethingChanged = false;
if (item->getRadius() != _refScale) { if (item->getLargestDimension() != _refScale) {
_refScale = item->getRadius(); _refScale = item->getLargestDimension();
_avatar->setTargetScale(_refScale * _scale, true); _avatar->setTargetScale(_refScale * _scale, true);
somethingChanged = true; somethingChanged = true;
} }

View file

@ -192,7 +192,71 @@ bool EntityTreeRenderer::shouldRenderEntity(float largestDimension, float distan
return (distanceToCamera <= visibleDistanceAtScale); return (distanceToCamera <= visibleDistanceAtScale);
} }
void EntityTreeRenderer::renderProxies(const EntityItem* entity, RenderArgs* args) {
bool isShadowMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE;
bool displayModelBounds = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelBounds);
if (!isShadowMode && displayModelBounds) {
PerformanceTimer perfTimer("renderProxies");
AACube maxCube = entity->getMaximumAACube();
AACube minCube = entity->getMinimumAACube();
AABox entityBox = entity->getAABox();
maxCube.scale((float)TREE_SCALE);
minCube.scale((float)TREE_SCALE);
entityBox.scale((float)TREE_SCALE);
glm::vec3 maxCenter = maxCube.calcCenter();
glm::vec3 minCenter = minCube.calcCenter();
glm::vec3 entityBoxCenter = entityBox.calcCenter();
glm::vec3 entityBoxScale = entityBox.getScale();
// draw the max bounding cube
glColor4f(1.0f, 1.0f, 0.0f, 1.0f);
glPushMatrix();
glTranslatef(maxCenter.x, maxCenter.y, maxCenter.z);
glutWireCube(maxCube.getScale());
glPopMatrix();
// draw the min bounding cube
glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
glPushMatrix();
glTranslatef(minCenter.x, minCenter.y, minCenter.z);
glutWireCube(minCube.getScale());
glPopMatrix();
// draw the entityBox bounding box
glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
glPushMatrix();
glTranslatef(entityBoxCenter.x, entityBoxCenter.y, entityBoxCenter.z);
glScalef(entityBoxScale.x, entityBoxScale.y, entityBoxScale.z);
glutWireCube(1.0f);
glPopMatrix();
glm::vec3 position = entity->getPosition() * (float)TREE_SCALE;
glm::vec3 center = entity->getCenter() * (float)TREE_SCALE;
glm::vec3 dimensions = entity->getDimensions() * (float)TREE_SCALE;
glm::quat rotation = entity->getRotation();
glColor4f(1.0f, 0.0f, 1.0f, 1.0f);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glPushMatrix();
glm::vec3 positionToCenter = center - position;
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
glScalef(dimensions.x, dimensions.y, dimensions.z);
glutWireCube(1.0f);
glPopMatrix();
glPopMatrix();
}
}
void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) { void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) {
bool wantDebug = false;
args->_elementsTouched++; args->_elementsTouched++;
// actually render it here... // actually render it here...
// we need to iterate the actual entityItems of the element // we need to iterate the actual entityItems of the element
@ -214,26 +278,48 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
for (uint16_t i = 0; i < numberOfEntities; i++) { for (uint16_t i = 0; i < numberOfEntities; i++) {
EntityItem* entityItem = entityItems[i]; EntityItem* entityItem = entityItems[i];
// render entityItem
AACube entityCube = entityItem->getAACube();
entityCube.scale(TREE_SCALE);
// TODO: some entity types (like lights) might want to be rendered even if (entityItem->isVisible()) {
// when they are outside of the view frustum... // render entityItem
float distance = distanceToCamera(entityCube.calcCenter(), *args->_viewFrustum); AABox entityBox = entityItem->getAABox();
if (shouldRenderEntity(entityCube.getLargestDimension(), distance) &&
args->_viewFrustum->cubeInFrustum(entityCube) != ViewFrustum::OUTSIDE) {
Glower* glower = NULL; entityBox.scale(TREE_SCALE);
if (entityItem->getGlowLevel() > 0.0f) {
glower = new Glower(entityItem->getGlowLevel()); // TODO: some entity types (like lights) might want to be rendered even
// when they are outside of the view frustum...
float distance = distanceToCamera(entityBox.calcCenter(), *args->_viewFrustum);
if (wantDebug) {
qDebug() << "------- renderElement() ----------";
qDebug() << " type:" << EntityTypes::getEntityTypeName(entityItem->getType());
if (entityItem->getType() == EntityTypes::Model) {
ModelEntityItem* modelEntity = static_cast<ModelEntityItem*>(entityItem);
qDebug() << " url:" << modelEntity->getModelURL();
}
qDebug() << " entityBox:" << entityBox;
qDebug() << " dimensions:" << entityItem->getDimensionsInMeters() << "in meters";
qDebug() << " largestDimension:" << entityBox.getLargestDimension() << "in meters";
qDebug() << " shouldRender:" << shouldRenderEntity(entityBox.getLargestDimension(), distance);
qDebug() << " in frustum:" << (args->_viewFrustum->boxInFrustum(entityBox) != ViewFrustum::OUTSIDE);
} }
entityItem->render(args);
if (glower) { if (shouldRenderEntity(entityBox.getLargestDimension(), distance) &&
delete glower; args->_viewFrustum->boxInFrustum(entityBox) != ViewFrustum::OUTSIDE) {
renderProxies(entityItem, args);
Glower* glower = NULL;
if (entityItem->getGlowLevel() > 0.0f) {
glower = new Glower(entityItem->getGlowLevel());
}
entityItem->render(args);
if (glower) {
delete glower;
}
} else {
args->_itemsOutOfView++;
} }
} else {
args->_itemsOutOfView++;
} }
} }
} }

View file

@ -79,6 +79,7 @@ private:
float distanceToCamera(const glm::vec3& center, const ViewFrustum& viewFrustum) const; float distanceToCamera(const glm::vec3& center, const ViewFrustum& viewFrustum) const;
bool shouldRenderEntity(float largestDimension, float distanceToCamera) const; bool shouldRenderEntity(float largestDimension, float distanceToCamera) const;
void renderProxies(const EntityItem* entity, RenderArgs* args);
}; };

View file

@ -32,11 +32,12 @@ EntityItem* RenderableBoxEntityItem::factory(const EntityItemID& entityID, const
void RenderableBoxEntityItem::render(RenderArgs* args) { void RenderableBoxEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableBoxEntityItem::render"); PerformanceTimer perfTimer("RenderableBoxEntityItem::render");
assert(getType() == EntityTypes::Box); assert(getType() == EntityTypes::Box);
glm::vec3 position = getPosition() * (float)TREE_SCALE; glm::vec3 position = getPositionInMeters();
float size = getSize() * (float)TREE_SCALE; glm::vec3 center = getCenter() * (float)TREE_SCALE;
glm::vec3 dimensions = getDimensions() * (float)TREE_SCALE;
glm::vec3 halfDimensions = dimensions / 2.0f;
glm::quat rotation = getRotation(); glm::quat rotation = getRotation();
const bool useGlutCube = true; const bool useGlutCube = true;
if (useGlutCube) { if (useGlutCube) {
@ -45,10 +46,14 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
glTranslatef(position.x, position.y, position.z); glTranslatef(position.x, position.y, position.z);
glm::vec3 axis = glm::axis(rotation); glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glutSolidCube(size); glPushMatrix();
glm::vec3 positionToCenter = center - position;
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
glScalef(dimensions.x, dimensions.y, dimensions.z);
glutSolidCube(1.0f);
glPopMatrix();
glPopMatrix(); glPopMatrix();
} else { } else {
static GLfloat vertices[] = { 1, 1, 1, -1, 1, 1, -1,-1, 1, 1,-1, 1, // v0,v1,v2,v3 (front) static GLfloat vertices[] = { 1, 1, 1, -1, 1, 1, -1,-1, 1, 1,-1, 1, // v0,v1,v2,v3 (front)
1, 1, 1, 1,-1, 1, 1,-1,-1, 1, 1,-1, // v0,v3,v4,v5 (right) 1, 1, 1, 1,-1, 1, 1,-1,-1, 1, 1,-1, // v0,v3,v4,v5 (right)
1, 1, 1, 1, 1,-1, -1, 1,-1, -1, 1, 1, // v0,v5,v6,v1 (top) 1, 1, 1, 1, 1,-1, -1, 1,-1, -1, 1, 1, // v0,v5,v6,v1 (top)
@ -79,20 +84,19 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
glNormalPointer(GL_FLOAT, 0, normals); glNormalPointer(GL_FLOAT, 0, normals);
glVertexPointer(3, GL_FLOAT, 0, vertices); glVertexPointer(3, GL_FLOAT, 0, vertices);
//glEnable(GL_BLEND);
glColor3ub(getColor()[RED_INDEX], getColor()[GREEN_INDEX], getColor()[BLUE_INDEX]); glColor3ub(getColor()[RED_INDEX], getColor()[GREEN_INDEX], getColor()[BLUE_INDEX]);
glPushMatrix(); glPushMatrix();
glTranslatef(position.x, position.y, position.z); glTranslatef(position.x, position.y, position.z);
glm::vec3 axis = glm::axis(rotation); glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glPushMatrix();
// we need to do half the size because the geometry in the VBOs are from -1,-1,-1 to 1,1,1 glm::vec3 positionToCenter = center - position;
float halfSize = size/2.0f; glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
// we need to do half the size because the geometry in the VBOs are from -1,-1,-1 to 1,1,1
glScalef(halfSize, halfSize, halfSize); glScalef(halfDimensions.x, halfDimensions.y, halfDimensions.z);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, indices); glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, indices);
glPopMatrix();
glPopMatrix(); glPopMatrix();
glDisableClientState(GL_VERTEX_ARRAY); // disable vertex arrays glDisableClientState(GL_VERTEX_ARRAY); // disable vertex arrays

View file

@ -65,8 +65,8 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
bool drawAsModel = hasModel(); bool drawAsModel = hasModel();
glm::vec3 position = getPosition() * (float)TREE_SCALE; glm::vec3 position = getPosition() * (float)TREE_SCALE;
float radius = getRadius() * (float)TREE_SCALE;
float size = getSize() * (float)TREE_SCALE; float size = getSize() * (float)TREE_SCALE;
glm::vec3 dimensions = getDimensions() * (float)TREE_SCALE;
if (drawAsModel) { if (drawAsModel) {
glPushMatrix(); glPushMatrix();
@ -98,8 +98,8 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
glm::quat rotation = getRotation(); glm::quat rotation = getRotation();
if (needsSimulation() && _model->isActive()) { if (needsSimulation() && _model->isActive()) {
_model->setScaleToFit(true, radius * 2.0f); _model->setScaleToFit(true, dimensions);
_model->setSnapModelToCenter(true); _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint());
_model->setRotation(rotation); _model->setRotation(rotation);
_model->setTranslation(position); _model->setTranslation(position);
@ -128,52 +128,6 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
glutWireCube(size); glutWireCube(size);
glPopMatrix(); glPopMatrix();
} }
bool isShadowMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE;
bool displayModelBounds = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelBounds);
if (!isShadowMode && displayModelBounds) {
PerformanceTimer perfTimer("displayModelBounds");
glm::vec3 unRotatedMinimum = _model->getUnscaledMeshExtents().minimum;
glm::vec3 unRotatedMaximum = _model->getUnscaledMeshExtents().maximum;
glm::vec3 unRotatedExtents = unRotatedMaximum - unRotatedMinimum;
float width = unRotatedExtents.x;
float height = unRotatedExtents.y;
float depth = unRotatedExtents.z;
Extents rotatedExtents = _model->getUnscaledMeshExtents();
rotatedExtents.rotate(rotation);
glm::vec3 rotatedSize = rotatedExtents.maximum - rotatedExtents.minimum;
const glm::vec3& modelScale = _model->getScale();
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
// draw the orignal bounding cube
glColor4f(1.0f, 1.0f, 0.0f, 1.0f);
glutWireCube(size);
// draw the rotated bounding cube
glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
glPushMatrix();
glScalef(rotatedSize.x * modelScale.x, rotatedSize.y * modelScale.y, rotatedSize.z * modelScale.z);
glutWireCube(1.0);
glPopMatrix();
// draw the model relative bounding box
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glScalef(width * modelScale.x, height * modelScale.y, depth * modelScale.z);
glColor3f(0.0f, 1.0f, 0.0f);
glutWireCube(1.0);
glPopMatrix();
}
} else { } else {
// if we couldn't get a model, then just draw a cube // if we couldn't get a model, then just draw a cube
glColor3ub(getColor()[RED_INDEX],getColor()[GREEN_INDEX],getColor()[BLUE_INDEX]); glColor3ub(getColor()[RED_INDEX],getColor()[GREEN_INDEX],getColor()[BLUE_INDEX]);

View file

@ -31,12 +31,24 @@ EntityItem* RenderableSphereEntityItem::factory(const EntityItemID& entityID, co
void RenderableSphereEntityItem::render(RenderArgs* args) { void RenderableSphereEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableSphereEntityItem::render"); PerformanceTimer perfTimer("RenderableSphereEntityItem::render");
assert(getType() == EntityTypes::Sphere); assert(getType() == EntityTypes::Sphere);
glm::vec3 position = getPosition() * (float)TREE_SCALE; glm::vec3 position = getPositionInMeters();
float radius = getRadius() * (float)TREE_SCALE; glm::vec3 center = getCenterInMeters();
glm::vec3 dimensions = getDimensions() * (float)TREE_SCALE;
glm::quat rotation = getRotation();
glColor3ub(getColor()[RED_INDEX], getColor()[GREEN_INDEX], getColor()[BLUE_INDEX]); glColor3ub(getColor()[RED_INDEX], getColor()[GREEN_INDEX], getColor()[BLUE_INDEX]);
glPushMatrix(); glPushMatrix();
glTranslatef(position.x, position.y, position.z); glTranslatef(position.x, position.y, position.z);
glutSolidSphere(radius, 15, 15); glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glPushMatrix();
glm::vec3 positionToCenter = center - position;
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
glScalef(dimensions.x, dimensions.y, dimensions.z);
glutSolidSphere(0.5f, 15, 15);
glPopMatrix();
glPopMatrix(); glPopMatrix();
}; };

View file

@ -54,10 +54,10 @@ Model::Model(QObject* parent) :
QObject(parent), QObject(parent),
_scale(1.0f, 1.0f, 1.0f), _scale(1.0f, 1.0f, 1.0f),
_scaleToFit(false), _scaleToFit(false),
_scaleToFitLargestDimension(0.0f), _scaleToFitDimensions(0.0f),
_scaledToFit(false), _scaledToFit(false),
_snapModelToCenter(false), _snapModelToRegistrationPoint(false),
_snappedToCenter(false), _snappedToRegistrationPoint(false),
_showTrueJointTransforms(true), _showTrueJointTransforms(true),
_lodDistance(0.0f), _lodDistance(0.0f),
_pupilDilation(0.0f), _pupilDilation(0.0f),
@ -157,8 +157,8 @@ void Model::setOffset(const glm::vec3& offset) {
_offset = offset; _offset = offset;
// if someone manually sets our offset, then we are no longer snapped to center // if someone manually sets our offset, then we are no longer snapped to center
_snapModelToCenter = false; _snapModelToRegistrationPoint = false;
_snappedToCenter = false; _snappedToRegistrationPoint = false;
} }
void Model::initProgram(ProgramObject& program, Model::Locations& locations, void Model::initProgram(ProgramObject& program, Model::Locations& locations,
@ -882,50 +882,57 @@ void Blender::run() {
Q_ARG(const QVector<glm::vec3>&, vertices), Q_ARG(const QVector<glm::vec3>&, normals)); Q_ARG(const QVector<glm::vec3>&, vertices), Q_ARG(const QVector<glm::vec3>&, normals));
} }
void Model::setScaleToFit(bool scaleToFit, float largestDimension) { void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions) {
if (_scaleToFit != scaleToFit || _scaleToFitLargestDimension != largestDimension) { if (_scaleToFit != scaleToFit || _scaleToFitDimensions != dimensions) {
_scaleToFit = scaleToFit; _scaleToFit = scaleToFit;
_scaleToFitLargestDimension = largestDimension; _scaleToFitDimensions = dimensions;
_scaledToFit = false; // force rescaling _scaledToFit = false; // force rescaling
} }
} }
void Model::setScaleToFit(bool scaleToFit, float largestDimension) {
setScaleToFit(scaleToFit, glm::vec3(largestDimension, largestDimension, largestDimension));
}
void Model::scaleToFit() { void Model::scaleToFit() {
Extents modelMeshExtents = getUnscaledMeshExtents(); Extents modelMeshExtents = getUnscaledMeshExtents();
// size is our "target size in world space" // size is our "target size in world space"
// we need to set our model scale so that the extents of the mesh, fit in a cube that size... // we need to set our model scale so that the extents of the mesh, fit in a cube that size...
float maxDimension = glm::distance(modelMeshExtents.maximum, modelMeshExtents.minimum); glm::vec3 meshDimensions = modelMeshExtents.maximum - modelMeshExtents.minimum;
float maxScale = _scaleToFitLargestDimension / maxDimension; glm::vec3 rescaleDimensions = _scaleToFitDimensions / meshDimensions;
glm::vec3 scale(maxScale, maxScale, maxScale); setScaleInternal(rescaleDimensions);
setScaleInternal(scale);
_scaledToFit = true; _scaledToFit = true;
} }
void Model::setSnapModelToCenter(bool snapModelToCenter) { void Model::setSnapModelToRegistrationPoint(bool snapModelToRegistrationPoint, const glm::vec3& registrationPoint) {
if (_snapModelToCenter != snapModelToCenter) { glm::vec3 clampedRegistrationPoint = glm::clamp(registrationPoint, 0.0f, 1.0f);
_snapModelToCenter = snapModelToCenter; if (_snapModelToRegistrationPoint != snapModelToRegistrationPoint || _registrationPoint != clampedRegistrationPoint) {
_snappedToCenter = false; // force re-centering _snapModelToRegistrationPoint = snapModelToRegistrationPoint;
_registrationPoint = clampedRegistrationPoint;
_snappedToRegistrationPoint = false; // force re-centering
} }
} }
void Model::snapToCenter() { void Model::snapToRegistrationPoint() {
Extents modelMeshExtents = getUnscaledMeshExtents(); Extents modelMeshExtents = getUnscaledMeshExtents();
glm::vec3 halfDimensions = (modelMeshExtents.maximum - modelMeshExtents.minimum) * 0.5f; glm::vec3 dimensions = (modelMeshExtents.maximum - modelMeshExtents.minimum);
glm::vec3 offset = -modelMeshExtents.minimum - halfDimensions; glm::vec3 offset = -modelMeshExtents.minimum - (dimensions * _registrationPoint);
_offset = offset; _offset = offset;
_snappedToCenter = true; _snappedToRegistrationPoint = true;
} }
void Model::simulate(float deltaTime, bool fullUpdate) { void Model::simulate(float deltaTime, bool fullUpdate) {
fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit) || (_snapModelToCenter && !_snappedToCenter); fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit)
|| (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint);
if (isActive() && fullUpdate) { if (isActive() && fullUpdate) {
// check for scale to fit // check for scale to fit
if (_scaleToFit && !_scaledToFit) { if (_scaleToFit && !_scaledToFit) {
scaleToFit(); scaleToFit();
} }
if (_snapModelToCenter && !_snappedToCenter) { if (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint) {
snapToCenter(); snapToRegistrationPoint();
} }
simulateInternal(deltaTime); simulateInternal(deltaTime);
} }

View file

@ -52,10 +52,18 @@ public:
void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f); void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f);
bool getScaleToFit() const { return _scaleToFit; } /// is scale to fit enabled bool getScaleToFit() const { return _scaleToFit; } /// is scale to fit enabled
bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit
bool getScaleToFitDimension() const { return _scaleToFitLargestDimension; } /// the dimension model is scaled to const glm::vec3& getScaleToFitDimensions() const { return _scaleToFitDimensions; } /// the dimensions model is scaled to
void setScaleToFit(bool scaleToFit, const glm::vec3& dimensions);
void setSnapModelToCenter(bool snapModelToCenter); void setSnapModelToCenter(bool snapModelToCenter) {
bool getSnapModelToCenter() { return _snapModelToCenter; } setSnapModelToRegistrationPoint(snapModelToCenter, glm::vec3(0.5f,0.5f,0.5f));
};
bool getSnapModelToCenter() {
return _snapModelToRegistrationPoint && _registrationPoint == glm::vec3(0.5f,0.5f,0.5f);
}
void setSnapModelToRegistrationPoint(bool snapModelToRegistrationPoint, const glm::vec3& registrationPoint);
bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; }
void setScale(const glm::vec3& scale); void setScale(const glm::vec3& scale);
const glm::vec3& getScale() const { return _scale; } const glm::vec3& getScale() const { return _scale; }
@ -181,11 +189,13 @@ protected:
glm::vec3 _offset; glm::vec3 _offset;
bool _scaleToFit; /// If you set scaleToFit, we will calculate scale based on MeshExtents bool _scaleToFit; /// If you set scaleToFit, we will calculate scale based on MeshExtents
float _scaleToFitLargestDimension; /// this is the dimension that scale to fit will use glm::vec3 _scaleToFitDimensions; /// this is the dimensions that scale to fit will use
bool _scaledToFit; /// have we scaled to fit bool _scaledToFit; /// have we scaled to fit
bool _snapModelToCenter; /// is the model's offset automatically adjusted to center around 0,0,0 in model space bool _snapModelToRegistrationPoint; /// is the model's offset automatically adjusted to a registration point in model space
bool _snappedToCenter; /// are we currently snapped to center bool _snappedToRegistrationPoint; /// are we currently snapped to a registration point
glm::vec3 _registrationPoint; /// the point in model space our center is snapped to
bool _showTrueJointTransforms; bool _showTrueJointTransforms;
QVector<LocalLight> _localLights; QVector<LocalLight> _localLights;
@ -206,7 +216,7 @@ protected:
void setScaleInternal(const glm::vec3& scale); void setScaleInternal(const glm::vec3& scale);
void scaleToFit(); void scaleToFit();
void snapToCenter(); void snapToRegistrationPoint();
void simulateInternal(float deltaTime); void simulateInternal(float deltaTime);

View file

@ -25,8 +25,7 @@ AddEntityOperator::AddEntityOperator(EntityTree* tree,
{ {
// caller must have verified existence of newEntity // caller must have verified existence of newEntity
assert(_newEntity); assert(_newEntity);
_newEntityBox = _newEntity->getMaximumAACube().clamp(0.0f, 1.0f);
_newEntityBox = _newEntity->getAACube().clamp(0.0f, 1.0f);
} }
bool AddEntityOperator::preRecursion(OctreeElement* element) { bool AddEntityOperator::preRecursion(OctreeElement* element) {

View file

@ -20,13 +20,15 @@
EntityItem* BoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItem* BoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
return new BoxEntityItem(entityID, properties); EntityItem* result = new BoxEntityItem(entityID, properties);
return result;
} }
BoxEntityItem::BoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : BoxEntityItem::BoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
EntityItem(entityItemID, properties) EntityItem(entityItemID)
{ {
_type = EntityTypes::Box; _type = EntityTypes::Box;
_created = properties.getCreated();
setProperties(properties, true); setProperties(properties, true);
} }
@ -44,18 +46,9 @@ EntityItemProperties BoxEntityItem::getProperties() const {
bool BoxEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) { bool BoxEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) {
bool somethingChanged = false; bool somethingChanged = false;
somethingChanged = EntityItem::setProperties(properties, forceCopy); // set the properties in our base class somethingChanged = EntityItem::setProperties(properties, forceCopy); // set the properties in our base class
if (properties._colorChanged || forceCopy) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
setColor(properties._color);
somethingChanged = true;
}
if (properties._glowLevelChanged || forceCopy) {
setGlowLevel(properties._glowLevel);
somethingChanged = true;
}
if (somethingChanged) { if (somethingChanged) {
bool wantDebug = false; bool wantDebug = false;

View file

@ -27,14 +27,21 @@ const float EntityItem::IMMORTAL = -1.0f; /// special lifetime which means the e
const float EntityItem::DEFAULT_GLOW_LEVEL = 0.0f; const float EntityItem::DEFAULT_GLOW_LEVEL = 0.0f;
const float EntityItem::DEFAULT_MASS = 1.0f; const float EntityItem::DEFAULT_MASS = 1.0f;
const float EntityItem::DEFAULT_LIFETIME = EntityItem::IMMORTAL; const float EntityItem::DEFAULT_LIFETIME = EntityItem::IMMORTAL;
const float EntityItem::DEFAULT_DAMPING = 0.99f; const float EntityItem::DEFAULT_DAMPING = 0.5f;
const glm::vec3 EntityItem::NO_VELOCITY = glm::vec3(0, 0, 0); const glm::vec3 EntityItem::NO_VELOCITY = glm::vec3(0, 0, 0);
const float EntityItem::EPSILON_VELOCITY_LENGTH = (1.0f / 10000.0f) / (float)TREE_SCALE; // really small const float EntityItem::EPSILON_VELOCITY_LENGTH = (1.0f / 1000.0f) / (float)TREE_SCALE; // really small: 1mm/second
const glm::vec3 EntityItem::DEFAULT_VELOCITY = EntityItem::NO_VELOCITY; const glm::vec3 EntityItem::DEFAULT_VELOCITY = EntityItem::NO_VELOCITY;
const glm::vec3 EntityItem::NO_GRAVITY = glm::vec3(0, 0, 0); const glm::vec3 EntityItem::NO_GRAVITY = glm::vec3(0, 0, 0);
const glm::vec3 EntityItem::REGULAR_GRAVITY = glm::vec3(0, (-9.8f / TREE_SCALE), 0); const glm::vec3 EntityItem::REGULAR_GRAVITY = glm::vec3(0, (-9.8f / TREE_SCALE), 0);
const glm::vec3 EntityItem::DEFAULT_GRAVITY = EntityItem::NO_GRAVITY; const glm::vec3 EntityItem::DEFAULT_GRAVITY = EntityItem::NO_GRAVITY;
const QString EntityItem::DEFAULT_SCRIPT = QString(""); const QString EntityItem::DEFAULT_SCRIPT = QString("");
const glm::quat EntityItem::DEFAULT_ROTATION;
const glm::vec3 EntityItem::DEFAULT_DIMENSIONS = glm::vec3(0.1f, 0.1f, 0.1f);
const glm::vec3 EntityItem::DEFAULT_REGISTRATION_POINT = glm::vec3(0.5f, 0.5f, 0.5f); // center
const glm::vec3 EntityItem::NO_ANGULAR_VELOCITY = glm::vec3(0.0f, 0.0f, 0.0f);
const glm::vec3 EntityItem::DEFAULT_ANGULAR_VELOCITY = NO_ANGULAR_VELOCITY;
const float EntityItem::DEFAULT_ANGULAR_DAMPING = 0.5f;
const bool EntityItem::DEFAULT_VISIBLE = true;
void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) { void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) {
_id = entityItemID.id; _id = entityItemID.id;
@ -50,8 +57,8 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) {
_created = 0; // TODO: when do we actually want to make this "now" _created = 0; // TODO: when do we actually want to make this "now"
_position = glm::vec3(0,0,0); _position = glm::vec3(0,0,0);
_radius = 0; _rotation = DEFAULT_ROTATION;
_rotation = ENTITY_DEFAULT_ROTATION; _dimensions = DEFAULT_DIMENSIONS;
_glowLevel = DEFAULT_GLOW_LEVEL; _glowLevel = DEFAULT_GLOW_LEVEL;
_mass = DEFAULT_MASS; _mass = DEFAULT_MASS;
@ -59,6 +66,20 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) {
_gravity = DEFAULT_GRAVITY; _gravity = DEFAULT_GRAVITY;
_damping = DEFAULT_DAMPING; _damping = DEFAULT_DAMPING;
_lifetime = DEFAULT_LIFETIME; _lifetime = DEFAULT_LIFETIME;
_registrationPoint = DEFAULT_REGISTRATION_POINT;
_angularVelocity = DEFAULT_ANGULAR_VELOCITY;
_angularDamping = DEFAULT_ANGULAR_DAMPING;
_visible = DEFAULT_VISIBLE;
}
EntityItem::EntityItem(const EntityItemID& entityItemID) {
_type = EntityTypes::Unknown;
_lastEdited = 0;
_lastEditedFromRemote = 0;
_lastEditedFromRemoteInRemoteTime = 0;
_lastUpdated = 0;
_created = 0;
initFromEntityItemID(entityItemID);
} }
EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) { EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) {
@ -76,7 +97,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
EntityPropertyFlags requestedProperties; EntityPropertyFlags requestedProperties;
requestedProperties += PROP_POSITION; requestedProperties += PROP_POSITION;
requestedProperties += PROP_RADIUS; requestedProperties += PROP_DIMENSIONS; // NOTE: PROP_RADIUS obsolete
requestedProperties += PROP_ROTATION; requestedProperties += PROP_ROTATION;
requestedProperties += PROP_MASS; requestedProperties += PROP_MASS;
requestedProperties += PROP_VELOCITY; requestedProperties += PROP_VELOCITY;
@ -84,6 +105,10 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
requestedProperties += PROP_DAMPING; requestedProperties += PROP_DAMPING;
requestedProperties += PROP_LIFETIME; requestedProperties += PROP_LIFETIME;
requestedProperties += PROP_SCRIPT; requestedProperties += PROP_SCRIPT;
requestedProperties += PROP_REGISTRATION_POINT;
requestedProperties += PROP_ANGULAR_VELOCITY;
requestedProperties += PROP_ANGULAR_DAMPING;
requestedProperties += PROP_VISIBLE;
return requestedProperties; return requestedProperties;
} }
@ -178,10 +203,14 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
// These items would go here once supported.... // These items would go here once supported....
// PROP_PAGED_PROPERTY, // PROP_PAGED_PROPERTY,
// PROP_CUSTOM_PROPERTIES_INCLUDED, // PROP_CUSTOM_PROPERTIES_INCLUDED,
// PROP_VISIBLE,
APPEND_ENTITY_PROPERTY(PROP_POSITION, appendPosition, getPosition()); APPEND_ENTITY_PROPERTY(PROP_POSITION, appendPosition, getPosition());
APPEND_ENTITY_PROPERTY(PROP_RADIUS, appendValue, getRadius()); APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, appendValue, getDimensions()); // NOTE: PROP_RADIUS obsolete
if (wantDebug) {
qDebug() << " APPEND_ENTITY_PROPERTY() PROP_DIMENSIONS:" << getDimensions();
}
APPEND_ENTITY_PROPERTY(PROP_ROTATION, appendValue, getRotation()); APPEND_ENTITY_PROPERTY(PROP_ROTATION, appendValue, getRotation());
APPEND_ENTITY_PROPERTY(PROP_MASS, appendValue, getMass()); APPEND_ENTITY_PROPERTY(PROP_MASS, appendValue, getMass());
APPEND_ENTITY_PROPERTY(PROP_VELOCITY, appendValue, getVelocity()); APPEND_ENTITY_PROPERTY(PROP_VELOCITY, appendValue, getVelocity());
@ -189,6 +218,10 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
APPEND_ENTITY_PROPERTY(PROP_DAMPING, appendValue, getDamping()); APPEND_ENTITY_PROPERTY(PROP_DAMPING, appendValue, getDamping());
APPEND_ENTITY_PROPERTY(PROP_LIFETIME, appendValue, getLifetime()); APPEND_ENTITY_PROPERTY(PROP_LIFETIME, appendValue, getLifetime());
APPEND_ENTITY_PROPERTY(PROP_SCRIPT, appendValue, getScript()); APPEND_ENTITY_PROPERTY(PROP_SCRIPT, appendValue, getScript());
APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, appendValue, getRegistrationPoint());
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, appendValue, getAngularVelocity());
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, appendValue, getAngularDamping());
APPEND_ENTITY_PROPERTY(PROP_VISIBLE, appendValue, getVisible());
appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, appendSubclassData(packetData, params, entityTreeElementExtraEncodeData,
requestedProperties, requestedProperties,
@ -410,7 +443,34 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
bytesRead += propertyFlags.getEncodedLength(); bytesRead += propertyFlags.getEncodedLength();
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, _position); READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, _position);
READ_ENTITY_PROPERTY(PROP_RADIUS, float, _radius);
// Old bitstreams had PROP_RADIUS, new bitstreams have PROP_DIMENSIONS
if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_DIMENSIONS) {
if (propertyFlags.getHasProperty(PROP_RADIUS)) {
float fromBuffer;
memcpy(&fromBuffer, dataAt, sizeof(fromBuffer));
dataAt += sizeof(fromBuffer);
bytesRead += sizeof(fromBuffer);
if (overwriteLocalData) {
setRadius(fromBuffer);
}
if (wantDebug) {
qDebug() << " readEntityDataFromBuffer() OLD FORMAT... found PROP_RADIUS";
}
}
} else {
READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, _dimensions);
if (wantDebug) {
qDebug() << " readEntityDataFromBuffer() NEW FORMAT... look for PROP_DIMENSIONS";
}
}
if (wantDebug) {
qDebug() << " readEntityDataFromBuffer() _dimensions:" << getDimensionsInMeters() << " in meters";
}
READ_ENTITY_PROPERTY_QUAT(PROP_ROTATION, _rotation); READ_ENTITY_PROPERTY_QUAT(PROP_ROTATION, _rotation);
READ_ENTITY_PROPERTY(PROP_MASS, float, _mass); READ_ENTITY_PROPERTY(PROP_MASS, float, _mass);
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, _velocity); READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, _velocity);
@ -418,6 +478,15 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
READ_ENTITY_PROPERTY(PROP_DAMPING, float, _damping); READ_ENTITY_PROPERTY(PROP_DAMPING, float, _damping);
READ_ENTITY_PROPERTY(PROP_LIFETIME, float, _lifetime); READ_ENTITY_PROPERTY(PROP_LIFETIME, float, _lifetime);
READ_ENTITY_PROPERTY_STRING(PROP_SCRIPT,setScript); READ_ENTITY_PROPERTY_STRING(PROP_SCRIPT,setScript);
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, _registrationPoint);
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, _angularVelocity);
READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, _angularDamping);
READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, _visible);
if (wantDebug) {
qDebug() << " readEntityDataFromBuffer() _registrationPoint:" << _registrationPoint;
qDebug() << " readEntityDataFromBuffer() _visible:" << _visible;
}
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData); bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData);
@ -429,7 +498,7 @@ void EntityItem::debugDump() const {
qDebug() << "EntityItem id:" << getEntityItemID(); qDebug() << "EntityItem id:" << getEntityItemID();
qDebug(" edited ago:%f", getEditedAgo()); qDebug(" edited ago:%f", getEditedAgo());
qDebug(" position:%f,%f,%f", _position.x, _position.y, _position.z); qDebug(" position:%f,%f,%f", _position.x, _position.y, _position.z);
qDebug(" radius:%f", getRadius()); qDebug() << " dimensions:" << _dimensions;
} }
// adjust any internal timestamps to fix clock skew for this server // adjust any internal timestamps to fix clock skew for this server
@ -453,9 +522,14 @@ void EntityItem::adjustEditPacketForClockSkew(unsigned char* editPacketBuffer, s
} }
} }
// TODO: we probably want to change this to make "down" be the direction of the entity's gravity vector
// for now, this is always true DOWN even if entity has non-down gravity.
// TODO: the old code had "&& _velocity.y >= -EPSILON && _velocity.y <= EPSILON" --- what was I thinking?
bool EntityItem::isRestingOnSurface() const { bool EntityItem::isRestingOnSurface() const {
return _position.y <= _radius glm::vec3 downwardVelocity = glm::vec3(0.0f, _velocity.y, 0.0f);
&& _velocity.y >= -EPSILON && _velocity.y <= EPSILON
return _position.y <= getDistanceToBottomOfEntity()
&& (glm::length(downwardVelocity) <= EPSILON_VELOCITY_LENGTH)
&& _gravity.y < 0.0f; && _gravity.y < 0.0f;
} }
@ -466,9 +540,39 @@ void EntityItem::update(const quint64& updateTime) {
if (wantDebug) { if (wantDebug) {
qDebug() << "********** EntityItem::update()"; qDebug() << "********** EntityItem::update()";
qDebug() << " entity ID=" << getEntityItemID();
qDebug() << " updateTime=" << updateTime; qDebug() << " updateTime=" << updateTime;
qDebug() << " _lastUpdated=" << _lastUpdated; qDebug() << " _lastUpdated=" << _lastUpdated;
qDebug() << " timeElapsed=" << timeElapsed; qDebug() << " timeElapsed=" << timeElapsed;
qDebug() << " hasVelocity=" << hasVelocity();
qDebug() << " hasGravity=" << hasGravity();
qDebug() << " isRestingOnSurface=" << isRestingOnSurface();
qDebug() << " hasAngularVelocity=" << hasAngularVelocity();
qDebug() << " getAngularVelocity=" << getAngularVelocity();
qDebug() << " isMortal=" << isMortal();
qDebug() << " getAge()=" << getAge();
qDebug() << " getLifetime()=" << getLifetime();
if (hasVelocity() || (hasGravity() && !isRestingOnSurface())) {
qDebug() << " MOVING...=";
qDebug() << " hasVelocity=" << hasVelocity();
qDebug() << " hasGravity=" << hasGravity();
qDebug() << " isRestingOnSurface=" << isRestingOnSurface();
qDebug() << " hasAngularVelocity=" << hasAngularVelocity();
qDebug() << " getAngularVelocity=" << getAngularVelocity();
}
if (hasAngularVelocity()) {
qDebug() << " CHANGING...=";
qDebug() << " hasAngularVelocity=" << hasAngularVelocity();
qDebug() << " getAngularVelocity=" << getAngularVelocity();
}
if (isMortal()) {
qDebug() << " MORTAL...=";
qDebug() << " isMortal=" << isMortal();
qDebug() << " getAge()=" << getAge();
qDebug() << " getLifetime()=" << getLifetime();
}
} }
_lastUpdated = updateTime; _lastUpdated = updateTime;
@ -477,22 +581,54 @@ void EntityItem::update(const quint64& updateTime) {
qDebug() << "********** EntityItem::update() .... SETTING _lastUpdated=" << _lastUpdated; qDebug() << "********** EntityItem::update() .... SETTING _lastUpdated=" << _lastUpdated;
} }
if (hasAngularVelocity()) {
glm::quat rotation = getRotation();
glm::vec3 angularVelocity = glm::radians(getAngularVelocity());
float angularSpeed = glm::length(angularVelocity);
if (angularSpeed < EPSILON_VELOCITY_LENGTH) {
setAngularVelocity(NO_ANGULAR_VELOCITY);
} else {
float angle = timeElapsed * angularSpeed;
glm::quat dQ = glm::angleAxis(angle, glm::normalize(angularVelocity));
rotation = dQ * rotation;
setRotation(rotation);
// handle damping for angular velocity
if (getAngularDamping() > 0.0f) {
glm::vec3 dampingResistance = getAngularVelocity() * getAngularDamping();
glm::vec3 newAngularVelocity = getAngularVelocity() - (dampingResistance * timeElapsed);
setAngularVelocity(newAngularVelocity);
if (wantDebug) {
qDebug() << " getDamping():" << getDamping();
qDebug() << " dampingResistance:" << dampingResistance;
qDebug() << " newAngularVelocity:" << newAngularVelocity;
}
}
}
}
if (hasVelocity() || hasGravity()) { if (hasVelocity() || hasGravity()) {
glm::vec3 position = getPosition(); glm::vec3 position = getPosition();
glm::vec3 velocity = getVelocity(); glm::vec3 velocity = getVelocity();
glm::vec3 newPosition = position + (velocity * timeElapsed);
if (wantDebug) { if (wantDebug) {
qDebug() << "EntityItem::update()...."; qDebug() << "EntityItem::update()....";
qDebug() << " timeElapsed:" << timeElapsed; qDebug() << " timeElapsed:" << timeElapsed;
qDebug() << " old AACube:" << getAACube(); qDebug() << " old AACube:" << getMaximumAACube();
qDebug() << " old position:" << position; qDebug() << " old position:" << position;
qDebug() << " old velocity:" << velocity; qDebug() << " old velocity:" << velocity;
qDebug() << " old getAABox:" << getAABox();
qDebug() << " getDistanceToBottomOfEntity():" << getDistanceToBottomOfEntity() * (float)TREE_SCALE << " in meters";
qDebug() << " newPosition:" << newPosition;
qDebug() << " glm::distance(newPosition, position):" << glm::distance(newPosition, position);
} }
position += velocity * timeElapsed; position = newPosition;
// handle bounces off the ground... We bounce at the height of our radius... // handle bounces off the ground... We bounce at the distance to the bottom of our entity
if (position.y <= _radius) { if (position.y <= getDistanceToBottomOfEntity()) {
velocity = velocity * glm::vec3(1,-1,1); velocity = velocity * glm::vec3(1,-1,1);
// if we've slowed considerably, then just stop moving // if we've slowed considerably, then just stop moving
@ -500,7 +636,7 @@ void EntityItem::update(const quint64& updateTime) {
velocity = NO_VELOCITY; velocity = NO_VELOCITY;
} }
position.y = _radius; position.y = getDistanceToBottomOfEntity();
} }
// handle gravity.... // handle gravity....
@ -512,10 +648,10 @@ void EntityItem::update(const quint64& updateTime) {
// "ground" plane of the domain, but for now it // "ground" plane of the domain, but for now it
if (hasGravity() && isRestingOnSurface()) { if (hasGravity() && isRestingOnSurface()) {
velocity.y = 0.0f; velocity.y = 0.0f;
position.y = _radius; position.y = getDistanceToBottomOfEntity();
} }
// handle damping // handle damping for velocity
glm::vec3 dampingResistance = velocity * getDamping(); glm::vec3 dampingResistance = velocity * getDamping();
if (wantDebug) { if (wantDebug) {
qDebug() << " getDamping():" << getDamping(); qDebug() << " getDamping():" << getDamping();
@ -526,6 +662,8 @@ void EntityItem::update(const quint64& updateTime) {
if (wantDebug) { if (wantDebug) {
qDebug() << " velocity AFTER dampingResistance:" << velocity; qDebug() << " velocity AFTER dampingResistance:" << velocity;
qDebug() << " glm::length(velocity):" << glm::length(velocity);
qDebug() << " EPSILON_VELOCITY_LENGTH:" << EPSILON_VELOCITY_LENGTH;
} }
// round velocity to zero if it's close enough... // round velocity to zero if it's close enough...
@ -533,14 +671,14 @@ void EntityItem::update(const quint64& updateTime) {
velocity = NO_VELOCITY; velocity = NO_VELOCITY;
} }
setPosition(position);
setVelocity(velocity);
if (wantDebug) { if (wantDebug) {
qDebug() << " new position:" << position; qDebug() << " new position:" << position;
qDebug() << " new velocity:" << velocity; qDebug() << " new velocity:" << velocity;
} qDebug() << " new AACube:" << getMaximumAACube();
setPosition(position); qDebug() << " old getAABox:" << getAABox();
setVelocity(velocity);
if (wantDebug) {
qDebug() << " new AACube:" << getAACube();
} }
} }
} }
@ -549,6 +687,9 @@ EntityItem::SimulationState EntityItem::getSimulationState() const {
if (hasVelocity() || (hasGravity() && !isRestingOnSurface())) { if (hasVelocity() || (hasGravity() && !isRestingOnSurface())) {
return EntityItem::Moving; return EntityItem::Moving;
} }
if (hasAngularVelocity()) {
return EntityItem::Changing;
}
if (isMortal()) { if (isMortal()) {
return EntityItem::Mortal; return EntityItem::Mortal;
} }
@ -566,33 +707,26 @@ void EntityItem::copyChangedProperties(const EntityItem& other) {
EntityItemProperties EntityItem::getProperties() const { EntityItemProperties EntityItem::getProperties() const {
EntityItemProperties properties; EntityItemProperties properties;
properties._id = getID(); properties._id = getID();
properties._idSet = true; properties._idSet = true;
properties._created = _created; properties._created = _created;
properties._type = getType(); properties._type = getType();
properties._position = getPosition() * (float) TREE_SCALE; COPY_ENTITY_PROPERTY_TO_PROPERTIES(position, getPositionInMeters);
properties._radius = getRadius() * (float) TREE_SCALE; COPY_ENTITY_PROPERTY_TO_PROPERTIES(dimensions, getDimensionsInMeters); // NOTE: radius is obsolete
properties._rotation = getRotation(); COPY_ENTITY_PROPERTY_TO_PROPERTIES(rotation, getRotation);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(mass, getMass);
properties._mass = getMass(); COPY_ENTITY_PROPERTY_TO_PROPERTIES(velocity, getVelocityInMeters);
properties._velocity = getVelocity() * (float) TREE_SCALE; COPY_ENTITY_PROPERTY_TO_PROPERTIES(gravity, getGravityInMeters);
properties._gravity = getGravity() * (float) TREE_SCALE; COPY_ENTITY_PROPERTY_TO_PROPERTIES(damping, getDamping);
properties._damping = getDamping(); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lifetime, getLifetime);
properties._lifetime = getLifetime(); COPY_ENTITY_PROPERTY_TO_PROPERTIES(script, getScript);
properties._script = getScript(); COPY_ENTITY_PROPERTY_TO_PROPERTIES(registrationPoint, getRegistrationPoint);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(angularVelocity, getAngularVelocity);
properties._positionChanged = false; COPY_ENTITY_PROPERTY_TO_PROPERTIES(angularDamping, getAngularDamping);
properties._radiusChanged = false; COPY_ENTITY_PROPERTY_TO_PROPERTIES(glowLevel, getGlowLevel);
properties._rotationChanged = false; COPY_ENTITY_PROPERTY_TO_PROPERTIES(visible, getVisible);
properties._massChanged = false;
properties._velocityChanged = false;
properties._gravityChanged = false;
properties._dampingChanged = false;
properties._lifetimeChanged = false;
properties._scriptChanged = false;
properties._defaultSettings = false; properties._defaultSettings = false;
@ -611,54 +745,20 @@ bool EntityItem::setProperties(const EntityItemProperties& properties, bool forc
} }
} }
if (properties._positionChanged || forceCopy) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, setPositionInMeters);
// clamp positions to the domain to prevent someone from moving an entity out of the domain SET_ENTITY_PROPERTY_FROM_PROPERTIES(dimensions, setDimensionsInMeters); // NOTE: radius is obsolete
setPosition(glm::clamp(properties._position / (float) TREE_SCALE, 0.0f, 1.0f)); SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, setRotation);
somethingChanged = true; SET_ENTITY_PROPERTY_FROM_PROPERTIES(mass, setMass);
} SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, setVelocityInMeters);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(gravity, setGravityInMeters);
if (properties._radiusChanged || forceCopy) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(damping, setDamping);
setRadius(properties._radius / (float) TREE_SCALE); SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, setLifetime);
somethingChanged = true; SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript);
} SET_ENTITY_PROPERTY_FROM_PROPERTIES(registrationPoint, setRegistrationPoint);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularVelocity, setAngularVelocity);
if (properties._rotationChanged || forceCopy) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularDamping, setAngularDamping);
setRotation(properties._rotation); SET_ENTITY_PROPERTY_FROM_PROPERTIES(glowLevel, setGlowLevel);
somethingChanged = true; SET_ENTITY_PROPERTY_FROM_PROPERTIES(visible, setVisible);
}
if (properties._massChanged || forceCopy) {
setMass(properties._mass);
somethingChanged = true;
}
if (properties._velocityChanged || forceCopy) {
setVelocity(properties._velocity / (float) TREE_SCALE);
somethingChanged = true;
}
if (properties._massChanged || forceCopy) {
setMass(properties._mass);
somethingChanged = true;
}
if (properties._gravityChanged || forceCopy) {
setGravity(properties._gravity / (float) TREE_SCALE);
somethingChanged = true;
}
if (properties._dampingChanged || forceCopy) {
setDamping(properties._damping);
somethingChanged = true;
}
if (properties._lifetimeChanged || forceCopy) {
setLifetime(properties._lifetime);
somethingChanged = true;
}
if (properties._scriptChanged || forceCopy) {
setScript(properties._script);
somethingChanged = true;
}
if (somethingChanged) { if (somethingChanged) {
somethingChangedNotification(); // notify derived classes that something has changed somethingChangedNotification(); // notify derived classes that something has changed
@ -675,3 +775,127 @@ bool EntityItem::setProperties(const EntityItemProperties& properties, bool forc
return somethingChanged; return somethingChanged;
} }
// TODO: is this really correct? how do we use size, does it need to handle rotation?
float EntityItem::getSize() const {
return glm::length(_dimensions);
}
float EntityItem::getDistanceToBottomOfEntity() const {
glm::vec3 minimumPoint = getAABox().getMinimumPoint();
return getPosition().y - minimumPoint.y;
}
// TODO: doesn't this need to handle rotation?
glm::vec3 EntityItem::getCenter() const {
return _position + (_dimensions * (glm::vec3(0.5f,0.5f,0.5f) - _registrationPoint));
}
/// The maximum bounding cube for the entity, independent of it's rotation.
/// This accounts for the registration point (upon which rotation occurs around).
///
AACube EntityItem::getMaximumAACube() const {
// * we know that the position is the center of rotation
glm::vec3 centerOfRotation = _position; // also where _registration point is
// * we know that the registration point is the center of rotation
// * we can calculate the length of the furthest extent from the registration point
// as the dimensions * max (registrationPoint, (1.0,1.0,1.0) - registrationPoint)
glm::vec3 registrationPoint = (_dimensions * _registrationPoint);
glm::vec3 registrationRemainder = (_dimensions * (glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint));
glm::vec3 furthestExtentFromRegistration = glm::max(registrationPoint, registrationRemainder);
// * we know that if you rotate in any direction you would create a sphere
// that has a radius of the length of furthest extent from registration point
float radius = glm::length(furthestExtentFromRegistration);
// * we know that the minimum bounding cube of this maximum possible sphere is
// (center - radius) to (center + radius)
glm::vec3 minimumCorner = centerOfRotation - glm::vec3(radius, radius, radius);
AACube boundingCube(minimumCorner, radius * 2.0f);
return boundingCube;
}
/// The minimum bounding cube for the entity accounting for it's rotation.
/// This accounts for the registration point (upon which rotation occurs around).
///
AACube EntityItem::getMinimumAACube() const {
// _position represents the position of the registration point.
glm::vec3 registrationRemainder = glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint;
glm::vec3 unrotatedMinRelativeToEntity = glm::vec3(0.0f, 0.0f, 0.0f) - (_dimensions * _registrationPoint);
glm::vec3 unrotatedMaxRelativeToEntity = _dimensions * registrationRemainder;
Extents unrotatedExtentsRelativeToRegistrationPoint = { unrotatedMinRelativeToEntity, unrotatedMaxRelativeToEntity };
Extents rotatedExtentsRelativeToRegistrationPoint = unrotatedExtentsRelativeToRegistrationPoint.getRotated(getRotation());
// shift the extents to be relative to the position/registration point
rotatedExtentsRelativeToRegistrationPoint.shiftBy(_position);
// the cube that best encompasses extents is...
AABox box(rotatedExtentsRelativeToRegistrationPoint);
glm::vec3 centerOfBox = box.calcCenter();
float longestSide = box.getLargestDimension();
float halfLongestSide = longestSide / 2.0f;
glm::vec3 cornerOfCube = centerOfBox - glm::vec3(halfLongestSide, halfLongestSide, halfLongestSide);
// old implementation... not correct!!!
return AACube(cornerOfCube, longestSide);
}
AABox EntityItem::getAABox() const {
// _position represents the position of the registration point.
glm::vec3 registrationRemainder = glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint;
glm::vec3 unrotatedMinRelativeToEntity = glm::vec3(0.0f, 0.0f, 0.0f) - (_dimensions * _registrationPoint);
glm::vec3 unrotatedMaxRelativeToEntity = _dimensions * registrationRemainder;
Extents unrotatedExtentsRelativeToRegistrationPoint = { unrotatedMinRelativeToEntity, unrotatedMaxRelativeToEntity };
Extents rotatedExtentsRelativeToRegistrationPoint = unrotatedExtentsRelativeToRegistrationPoint.getRotated(getRotation());
// shift the extents to be relative to the position/registration point
rotatedExtentsRelativeToRegistrationPoint.shiftBy(_position);
return AABox(rotatedExtentsRelativeToRegistrationPoint);
}
// NOTE: This should only be used in cases of old bitstreams which only contain radius data
// 0,0,0 --> maxDimension,maxDimension,maxDimension
// ... has a corner to corner distance of glm::length(maxDimension,maxDimension,maxDimension)
// ... radius = cornerToCornerLength / 2.0f
// ... radius * 2.0f = cornerToCornerLength
// ... cornerToCornerLength = sqrt(3 x maxDimension ^ 2)
// ... cornerToCornerLength = sqrt(3 x maxDimension ^ 2)
// ... radius * 2.0f = sqrt(3 x maxDimension ^ 2)
// ... (radius * 2.0f) ^2 = 3 x maxDimension ^ 2
// ... ((radius * 2.0f) ^2) / 3 = maxDimension ^ 2
// ... sqrt(((radius * 2.0f) ^2) / 3) = maxDimension
// ... sqrt((diameter ^2) / 3) = maxDimension
//
void EntityItem::setRadius(float value) {
float diameter = value * 2.0f;
float maxDimension = sqrt((diameter * diameter) / 3.0f);
_dimensions = glm::vec3(maxDimension, maxDimension, maxDimension);
bool wantDebug = false;
if (wantDebug) {
qDebug() << "EntityItem::setRadius()...";
qDebug() << " radius:" << value;
qDebug() << " diameter:" << diameter;
qDebug() << " maxDimension:" << maxDimension;
qDebug() << " _dimensions:" << _dimensions;
}
}
// TODO: get rid of all users of this function...
// ... radius = cornerToCornerLength / 2.0f
// ... cornerToCornerLength = sqrt(3 x maxDimension ^ 2)
// ... radius = sqrt(3 x maxDimension ^ 2) / 2.0f;
float EntityItem::getRadius() const {
float length = glm::length(_dimensions);
float radius = length / 2.0f;
return radius;
}

View file

@ -25,6 +25,7 @@
#include "EntityItemProperties.h" #include "EntityItemProperties.h"
#include "EntityTypes.h" #include "EntityTypes.h"
class EntityTreeElement;
class EntityTreeElementExtraEncodeData; class EntityTreeElementExtraEncodeData;
#define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0; #define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0;
@ -39,6 +40,7 @@ class EntityItem {
public: public:
DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly
EntityItem(const EntityItemID& entityItemID);
EntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); EntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
virtual ~EntityItem() { } virtual ~EntityItem() { }
@ -119,11 +121,27 @@ public:
// attributes applicable to all entity types // attributes applicable to all entity types
EntityTypes::EntityType getType() const { return _type; } EntityTypes::EntityType getType() const { return _type; }
const glm::vec3& getPosition() const { return _position; } /// get position in domain scale units (0.0 - 1.0) const glm::vec3& getPosition() const { return _position; } /// get position in domain scale units (0.0 - 1.0)
glm::vec3 getPositionInMeters() const { return _position * (float) TREE_SCALE; } /// get position in meters
void setPosition(const glm::vec3& value) { _position = value; } /// set position in domain scale units (0.0 - 1.0) void setPosition(const glm::vec3& value) { _position = value; } /// set position in domain scale units (0.0 - 1.0)
void setPositionInMeters(const glm::vec3& value) /// set position in meter units (0.0 - TREE_SCALE)
{ setPosition(glm::clamp(value / (float) TREE_SCALE, 0.0f, 1.0f)); }
float getRadius() const { return _radius; } /// get radius in domain scale units (0.0 - 1.0) glm::vec3 getCenter() const; /// calculates center of the entity in domain scale units (0.0 - 1.0)
void setRadius(float value) { _radius = value; } /// set radius in domain scale units (0.0 - 1.0) glm::vec3 getCenterInMeters() const { return getCenter() * (float) TREE_SCALE; }
static const glm::vec3 DEFAULT_DIMENSIONS;
const glm::vec3& getDimensions() const { return _dimensions; } /// get dimensions in domain scale units (0.0 - 1.0)
glm::vec3 getDimensionsInMeters() const { return _dimensions * (float) TREE_SCALE; } /// get dimensions in meters
float getDistanceToBottomOfEntity() const; /// get the distance from the position of the entity to its "bottom" in y axis
float getLargestDimension() const { return glm::length(_dimensions); } /// get the largest possible dimension
/// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately
void setDimensions(const glm::vec3& value) { _dimensions = value; }
/// set dimensions in meter units (0.0 - TREE_SCALE) this will also reset radius appropriately
void setDimensionsInMeters(const glm::vec3& value) { setDimensions(value / (float) TREE_SCALE); }
static const glm::quat DEFAULT_ROTATION;
const glm::quat& getRotation() const { return _rotation; } const glm::quat& getRotation() const { return _rotation; }
void setRotation(const glm::quat& rotation) { _rotation = rotation; } void setRotation(const glm::quat& rotation) { _rotation = rotation; }
@ -138,15 +156,19 @@ public:
static const glm::vec3 DEFAULT_VELOCITY; static const glm::vec3 DEFAULT_VELOCITY;
static const glm::vec3 NO_VELOCITY; static const glm::vec3 NO_VELOCITY;
static const float EPSILON_VELOCITY_LENGTH; static const float EPSILON_VELOCITY_LENGTH;
const glm::vec3& getVelocity() const { return _velocity; } /// velocity in domain scale units (0.0-1.0) per second const glm::vec3 getVelocity() const { return _velocity; } /// velocity in domain scale units (0.0-1.0) per second
glm::vec3 getVelocityInMeters() const { return _velocity * (float) TREE_SCALE; } /// get velocity in meters
void setVelocity(const glm::vec3& value) { _velocity = value; } /// velocity in domain scale units (0.0-1.0) per second void setVelocity(const glm::vec3& value) { _velocity = value; } /// velocity in domain scale units (0.0-1.0) per second
void setVelocityInMeters(const glm::vec3& value) { _velocity = value / (float) TREE_SCALE; } /// velocity in meters
bool hasVelocity() const { return _velocity != NO_VELOCITY; } bool hasVelocity() const { return _velocity != NO_VELOCITY; }
static const glm::vec3 DEFAULT_GRAVITY; static const glm::vec3 DEFAULT_GRAVITY;
static const glm::vec3 REGULAR_GRAVITY; static const glm::vec3 REGULAR_GRAVITY;
static const glm::vec3 NO_GRAVITY; static const glm::vec3 NO_GRAVITY;
const glm::vec3& getGravity() const { return _gravity; } /// gravity in domain scale units (0.0-1.0) per second squared const glm::vec3& getGravity() const { return _gravity; } /// gravity in domain scale units (0.0-1.0) per second squared
glm::vec3 getGravityInMeters() const { return _gravity * (float) TREE_SCALE; } /// get gravity in meters
void setGravity(const glm::vec3& value) { _gravity = value; } /// gravity in domain scale units (0.0-1.0) per second squared void setGravity(const glm::vec3& value) { _gravity = value; } /// gravity in domain scale units (0.0-1.0) per second squared
void setGravityInMeters(const glm::vec3& value) { _gravity = value / (float) TREE_SCALE; } /// gravity in meters
bool hasGravity() const { return _gravity != NO_GRAVITY; } bool hasGravity() const { return _gravity != NO_GRAVITY; }
// TODO: this should eventually be updated to support resting on collisions with other surfaces // TODO: this should eventually be updated to support resting on collisions with other surfaces
@ -173,15 +195,39 @@ public:
bool lifetimeHasExpired() const; bool lifetimeHasExpired() const;
// position, size, and bounds related helpers // position, size, and bounds related helpers
float getSize() const { return _radius * 2.0f; } /// get maximum dimension in domain scale units (0.0 - 1.0) float getSize() const; /// get maximum dimension in domain scale units (0.0 - 1.0)
glm::vec3 getMinimumPoint() const { return _position - glm::vec3(_radius, _radius, _radius); } AACube getMaximumAACube() const;
glm::vec3 getMaximumPoint() const { return _position + glm::vec3(_radius, _radius, _radius); } AACube getMinimumAACube() const;
AACube getAACube() const { return AACube(getMinimumPoint(), getSize()); } /// AACube in domain scale units (0.0 - 1.0) AABox getAABox() const; /// axis aligned bounding box in domain scale units (0.0 - 1.0)
static const QString DEFAULT_SCRIPT; static const QString DEFAULT_SCRIPT;
const QString& getScript() const { return _script; } const QString& getScript() const { return _script; }
void setScript(const QString& value) { _script = value; } void setScript(const QString& value) { _script = value; }
static const glm::vec3 DEFAULT_REGISTRATION_POINT;
const glm::vec3& getRegistrationPoint() const { return _registrationPoint; } /// registration point as ratio of entity
void setRegistrationPoint(const glm::vec3& value) { _registrationPoint = glm::clamp(value, 0.0f, 1.0f); } /// registration point as ratio of entity
static const glm::vec3 NO_ANGULAR_VELOCITY;
static const glm::vec3 DEFAULT_ANGULAR_VELOCITY;
const glm::vec3& getAngularVelocity() const { return _angularVelocity; }
void setAngularVelocity(const glm::vec3& value) { _angularVelocity = value; }
bool hasAngularVelocity() const { return _angularVelocity != NO_ANGULAR_VELOCITY; }
static const float DEFAULT_ANGULAR_DAMPING;
float getAngularDamping() const { return _angularDamping; }
void setAngularDamping(float value) { _angularDamping = value; }
static const bool DEFAULT_VISIBLE;
bool getVisible() const { return _visible; }
void setVisible(bool value) { _visible = value; }
bool isVisible() const { return _visible; }
bool isInvisible() const { return !_visible; }
// TODO: We need to get rid of these users of getRadius()...
float getRadius() const;
protected: protected:
virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init
@ -196,7 +242,7 @@ protected:
quint64 _created; quint64 _created;
glm::vec3 _position; glm::vec3 _position;
float _radius; glm::vec3 _dimensions;
glm::quat _rotation; glm::quat _rotation;
float _glowLevel; float _glowLevel;
float _mass; float _mass;
@ -205,6 +251,17 @@ protected:
float _damping; float _damping;
float _lifetime; float _lifetime;
QString _script; QString _script;
glm::vec3 _registrationPoint;
glm::vec3 _angularVelocity;
float _angularDamping;
bool _visible;
// NOTE: Radius support is obsolete, but these private helper functions are available for this class to
// parse old data streams
/// set radius in domain scale units (0.0 - 1.0) this will also reset dimensions to be equal for each axis
void setRadius(float value);
}; };

View file

@ -18,6 +18,7 @@
#include "EntityItem.h" #include "EntityItem.h"
#include "EntityItemProperties.h" #include "EntityItemProperties.h"
#include "ModelEntityItem.h"
EntityItemProperties::EntityItemProperties() : EntityItemProperties::EntityItemProperties() :
@ -28,17 +29,21 @@ EntityItemProperties::EntityItemProperties() :
_type(EntityTypes::Unknown), _type(EntityTypes::Unknown),
_position(0), _position(0),
_radius(ENTITY_DEFAULT_RADIUS), _dimensions(EntityItem::DEFAULT_DIMENSIONS),
_rotation(ENTITY_DEFAULT_ROTATION), _rotation(EntityItem::DEFAULT_ROTATION),
_mass(EntityItem::DEFAULT_MASS), _mass(EntityItem::DEFAULT_MASS),
_velocity(EntityItem::DEFAULT_VELOCITY), _velocity(EntityItem::DEFAULT_VELOCITY),
_gravity(EntityItem::DEFAULT_GRAVITY), _gravity(EntityItem::DEFAULT_GRAVITY),
_damping(EntityItem::DEFAULT_DAMPING), _damping(EntityItem::DEFAULT_DAMPING),
_lifetime(EntityItem::DEFAULT_LIFETIME), _lifetime(EntityItem::DEFAULT_LIFETIME),
_script(EntityItem::DEFAULT_SCRIPT), _script(EntityItem::DEFAULT_SCRIPT),
_registrationPoint(EntityItem::DEFAULT_REGISTRATION_POINT),
_angularVelocity(EntityItem::DEFAULT_ANGULAR_VELOCITY),
_angularDamping(EntityItem::DEFAULT_ANGULAR_DAMPING),
_visible(EntityItem::DEFAULT_VISIBLE),
_positionChanged(false), _positionChanged(false),
_radiusChanged(false), _dimensionsChanged(false),
_rotationChanged(false), _rotationChanged(false),
_massChanged(false), _massChanged(false),
_velocityChanged(false), _velocityChanged(false),
@ -46,15 +51,20 @@ EntityItemProperties::EntityItemProperties() :
_dampingChanged(false), _dampingChanged(false),
_lifetimeChanged(false), _lifetimeChanged(false),
_scriptChanged(false), _scriptChanged(false),
_registrationPointChanged(false),
_angularVelocityChanged(false),
_angularDampingChanged(false),
_visibleChanged(false),
_color(), _color(),
_modelURL(""), _modelURL(""),
_animationURL(""), _animationURL(""),
_animationIsPlaying(false), _animationIsPlaying(ModelEntityItem::DEFAULT_ANIMATION_IS_PLAYING),
_animationFrameIndex(0.0), _animationFrameIndex(ModelEntityItem::DEFAULT_ANIMATION_FRAME_INDEX),
_animationFPS(ENTITY_DEFAULT_ANIMATION_FPS), _animationFPS(ModelEntityItem::DEFAULT_ANIMATION_FPS),
_glowLevel(0.0f), _glowLevel(0.0f),
_naturalDimensions(1.0f, 1.0f, 1.0f),
_colorChanged(false), _colorChanged(false),
_modelURLChanged(false), _modelURLChanged(false),
_animationURLChanged(false), _animationURLChanged(false),
@ -73,7 +83,7 @@ void EntityItemProperties::debugDump() const {
qDebug() << " _id=" << _id; qDebug() << " _id=" << _id;
qDebug() << " _idSet=" << _idSet; qDebug() << " _idSet=" << _idSet;
qDebug() << " _position=" << _position.x << "," << _position.y << "," << _position.z; qDebug() << " _position=" << _position.x << "," << _position.y << "," << _position.z;
qDebug() << " _radius=" << _radius; qDebug() << " _dimensions=" << getDimensions();
qDebug() << " _modelURL=" << _modelURL; qDebug() << " _modelURL=" << _modelURL;
qDebug() << " changed properties..."; qDebug() << " changed properties...";
EntityPropertyFlags props = getChangedProperties(); EntityPropertyFlags props = getChangedProperties();
@ -82,65 +92,26 @@ void EntityItemProperties::debugDump() const {
EntityPropertyFlags EntityItemProperties::getChangedProperties() const { EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
EntityPropertyFlags changedProperties; EntityPropertyFlags changedProperties;
if (_radiusChanged) {
changedProperties += PROP_RADIUS;
}
if (_positionChanged) { CHECK_PROPERTY_CHANGE(PROP_DIMENSIONS, dimensions);
changedProperties += PROP_POSITION; CHECK_PROPERTY_CHANGE(PROP_POSITION, position);
} CHECK_PROPERTY_CHANGE(PROP_ROTATION, rotation);
CHECK_PROPERTY_CHANGE(PROP_MASS, mass);
if (_rotationChanged) { CHECK_PROPERTY_CHANGE(PROP_VELOCITY, velocity);
changedProperties += PROP_ROTATION; CHECK_PROPERTY_CHANGE(PROP_GRAVITY, gravity);
} CHECK_PROPERTY_CHANGE(PROP_DAMPING, damping);
CHECK_PROPERTY_CHANGE(PROP_LIFETIME, lifetime);
if (_massChanged) { CHECK_PROPERTY_CHANGE(PROP_SCRIPT, script);
changedProperties += PROP_MASS; CHECK_PROPERTY_CHANGE(PROP_COLOR, color);
} CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_URL, animationURL);
if (_velocityChanged) { CHECK_PROPERTY_CHANGE(PROP_ANIMATION_PLAYING, animationIsPlaying);
changedProperties += PROP_VELOCITY; CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FRAME_INDEX, animationFrameIndex);
} CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FPS, animationFPS);
CHECK_PROPERTY_CHANGE(PROP_VISIBLE, visible);
if (_gravityChanged) { CHECK_PROPERTY_CHANGE(PROP_REGISTRATION_POINT, registrationPoint);
changedProperties += PROP_GRAVITY; CHECK_PROPERTY_CHANGE(PROP_ANGULAR_VELOCITY, angularVelocity);
} CHECK_PROPERTY_CHANGE(PROP_ANGULAR_DAMPING, angularDamping);
if (_dampingChanged) {
changedProperties += PROP_DAMPING;
}
if (_lifetimeChanged) {
changedProperties += PROP_LIFETIME;
}
if (_scriptChanged) {
changedProperties += PROP_SCRIPT;
}
if (_colorChanged) {
changedProperties += PROP_COLOR;
}
if (_modelURLChanged) {
changedProperties += PROP_MODEL_URL;
}
if (_animationURLChanged) {
changedProperties += PROP_ANIMATION_URL;
}
if (_animationIsPlayingChanged) {
changedProperties += PROP_ANIMATION_PLAYING;
}
if (_animationFrameIndexChanged) {
changedProperties += PROP_ANIMATION_FRAME_INDEX;
}
if (_animationFPSChanged) {
changedProperties += PROP_ANIMATION_FPS;
}
return changedProperties; return changedProperties;
} }
@ -149,38 +120,35 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
QScriptValue properties = engine->newObject(); QScriptValue properties = engine->newObject();
if (_idSet) { if (_idSet) {
properties.setProperty("id", _id.toString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(id, _id.toString());
bool isKnownID = (_id != UNKNOWN_ENTITY_ID); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(isKnownID, (_id != UNKNOWN_ENTITY_ID));
properties.setProperty("isKnownID", isKnownID); } else {
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(isKnownID, false);
} }
properties.setProperty("type", EntityTypes::getEntityTypeName(_type)); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(type, EntityTypes::getEntityTypeName(_type));
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(position);
QScriptValue position = vec3toScriptValue(engine, _position); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(dimensions);
properties.setProperty("position", position); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(naturalDimensions); // gettable, but not settable
properties.setProperty("radius", _radius); COPY_PROPERTY_TO_QSCRIPTVALUE_QUAT(rotation);
QScriptValue rotation = quatToScriptValue(engine, _rotation); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(velocity);
properties.setProperty("rotation", rotation); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(gravity);
properties.setProperty("mass", _mass); COPY_PROPERTY_TO_QSCRIPTVALUE(damping);
QScriptValue velocity = vec3toScriptValue(engine, _velocity); COPY_PROPERTY_TO_QSCRIPTVALUE(lifetime);
properties.setProperty("velocity", velocity); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(age, getAge()); // gettable, but not settable
QScriptValue gravity = vec3toScriptValue(engine, _gravity); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(ageAsText, formatSecondsElapsed(getAge())); // gettable, but not settable
properties.setProperty("gravity", gravity); COPY_PROPERTY_TO_QSCRIPTVALUE(script);
properties.setProperty("damping", _damping); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(registrationPoint);
properties.setProperty("lifetime", _lifetime); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(angularVelocity);
properties.setProperty("age", getAge()); // gettable, but not settable COPY_PROPERTY_TO_QSCRIPTVALUE(angularDamping);
properties.setProperty("ageAsText", formatSecondsElapsed(getAge())); // gettable, but not settable COPY_PROPERTY_TO_QSCRIPTVALUE(visible);
properties.setProperty("script", _script); COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR(color);
COPY_PROPERTY_TO_QSCRIPTVALUE(modelURL);
QScriptValue color = xColorToScriptValue(engine, _color); COPY_PROPERTY_TO_QSCRIPTVALUE(animationURL);
properties.setProperty("color", color); COPY_PROPERTY_TO_QSCRIPTVALUE(animationIsPlaying);
properties.setProperty("modelURL", _modelURL); COPY_PROPERTY_TO_QSCRIPTVALUE(animationFrameIndex);
COPY_PROPERTY_TO_QSCRIPTVALUE(animationFPS);
properties.setProperty("animationURL", _animationURL); COPY_PROPERTY_TO_QSCRIPTVALUE(glowLevel);
properties.setProperty("animationIsPlaying", _animationIsPlaying);
properties.setProperty("animationFrameIndex", _animationFrameIndex);
properties.setProperty("animationFPS", _animationFPS);
properties.setProperty("glowLevel", _glowLevel);
// Sitting properties support // Sitting properties support
QScriptValue sittingPoints = engine->newObject(); QScriptValue sittingPoints = engine->newObject();
@ -192,217 +160,39 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
sittingPoints.setProperty(i, sittingPoint); sittingPoints.setProperty(i, sittingPoint);
} }
sittingPoints.setProperty("length", _sittingPoints.size()); sittingPoints.setProperty("length", _sittingPoints.size());
properties.setProperty("sittingPoints", sittingPoints); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(sittingPoints, sittingPoints); // gettable, but not settable
return properties; return properties;
} }
void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) { void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
QScriptValue typeScriptValue = object.property("type"); QScriptValue typeScriptValue = object.property("type");
if (typeScriptValue.isValid()) { if (typeScriptValue.isValid()) {
QString typeName; setType(typeScriptValue.toVariant().toString());
typeName = typeScriptValue.toVariant().toString();
_type = EntityTypes::getEntityTypeFromName(typeName);
} }
QScriptValue position = object.property("position"); COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(position, setPosition);
if (position.isValid()) { COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(dimensions, setDimensions);
QScriptValue x = position.property("x"); COPY_PROPERTY_FROM_QSCRIPTVALUE_QUAT(rotation, setRotation);
QScriptValue y = position.property("y"); COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(mass, setMass);
QScriptValue z = position.property("z"); COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(velocity, setVelocity);
if (x.isValid() && y.isValid() && z.isValid()) { COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(gravity, setGravity);
glm::vec3 newPosition; COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(damping, setDamping);
newPosition.x = x.toVariant().toFloat(); COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(lifetime, setLifetime);
newPosition.y = y.toVariant().toFloat(); COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(script, setScript);
newPosition.z = z.toVariant().toFloat(); COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(registrationPoint, setRegistrationPoint);
if (_defaultSettings || newPosition != _position) { COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(angularVelocity, setAngularVelocity);
setPosition(newPosition); // gives us automatic clamping COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(angularDamping, setAngularDamping);
} COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(visible, setVisible);
} COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(color, setColor);
} COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(modelURL, setModelURL);
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(animationURL, setAnimationURL);
QScriptValue radius = object.property("radius"); COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(animationIsPlaying, setAnimationIsPlaying);
if (radius.isValid()) { COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(animationFPS, setAnimationFPS);
float newRadius; COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(animationFrameIndex, setAnimationFrameIndex);
newRadius = radius.toVariant().toFloat(); COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(glowLevel, setGlowLevel);
if (_defaultSettings || newRadius != _radius) {
_radius = newRadius;
_radiusChanged = true;
}
}
QScriptValue rotation = object.property("rotation");
if (rotation.isValid()) {
QScriptValue x = rotation.property("x");
QScriptValue y = rotation.property("y");
QScriptValue z = rotation.property("z");
QScriptValue w = rotation.property("w");
if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) {
glm::quat newRotation;
newRotation.x = x.toVariant().toFloat();
newRotation.y = y.toVariant().toFloat();
newRotation.z = z.toVariant().toFloat();
newRotation.w = w.toVariant().toFloat();
if (_defaultSettings || newRotation != _rotation) {
_rotation = newRotation;
_rotationChanged = true;
}
}
}
QScriptValue mass = object.property("mass");
if (mass.isValid()) {
float newValue;
newValue = mass.toVariant().toFloat();
if (_defaultSettings || newValue != _mass) {
_mass = newValue;
_massChanged = true;
}
}
QScriptValue velocity = object.property("velocity");
if (velocity.isValid()) {
QScriptValue x = velocity.property("x");
QScriptValue y = velocity.property("y");
QScriptValue z = velocity.property("z");
if (x.isValid() && y.isValid() && z.isValid()) {
glm::vec3 newValue;
newValue.x = x.toVariant().toFloat();
newValue.y = y.toVariant().toFloat();
newValue.z = z.toVariant().toFloat();
if (_defaultSettings || newValue != _velocity) {
_velocity = newValue;
_velocityChanged = true;
}
}
}
QScriptValue gravity = object.property("gravity");
if (gravity.isValid()) {
QScriptValue x = gravity.property("x");
QScriptValue y = gravity.property("y");
QScriptValue z = gravity.property("z");
if (x.isValid() && y.isValid() && z.isValid()) {
glm::vec3 newValue;
newValue.x = x.toVariant().toFloat();
newValue.y = y.toVariant().toFloat();
newValue.z = z.toVariant().toFloat();
if (_defaultSettings || newValue != _gravity) {
_gravity = newValue;
_gravityChanged = true;
}
}
}
QScriptValue damping = object.property("damping");
if (damping.isValid()) {
float newValue;
newValue = damping.toVariant().toFloat();
if (_defaultSettings || newValue != _damping) {
_damping = newValue;
_dampingChanged = true;
}
}
QScriptValue lifetime = object.property("lifetime");
if (lifetime.isValid()) {
float newValue;
newValue = lifetime.toVariant().toFloat();
if (_defaultSettings || newValue != _lifetime) {
_lifetime = newValue;
_lifetimeChanged = true;
}
}
QScriptValue script = object.property("script");
if (script.isValid()) {
QString newValue;
newValue = script.toVariant().toString();
if (_defaultSettings || newValue != _script) {
_script = newValue;
_scriptChanged = true;
}
}
QScriptValue color = object.property("color");
if (color.isValid()) {
QScriptValue red = color.property("red");
QScriptValue green = color.property("green");
QScriptValue blue = color.property("blue");
if (red.isValid() && green.isValid() && blue.isValid()) {
xColor newColor;
newColor.red = red.toVariant().toInt();
newColor.green = green.toVariant().toInt();
newColor.blue = blue.toVariant().toInt();
if (_defaultSettings || (newColor.red != _color.red ||
newColor.green != _color.green ||
newColor.blue != _color.blue)) {
_color = newColor;
_colorChanged = true;
}
}
}
QScriptValue modelURL = object.property("modelURL");
if (modelURL.isValid()) {
QString newModelURL;
newModelURL = modelURL.toVariant().toString();
if (_defaultSettings || newModelURL != _modelURL) {
_modelURL = newModelURL;
_modelURLChanged = true;
}
}
QScriptValue animationURL = object.property("animationURL");
if (animationURL.isValid()) {
QString newAnimationURL;
newAnimationURL = animationURL.toVariant().toString();
if (_defaultSettings || newAnimationURL != _animationURL) {
_animationURL = newAnimationURL;
_animationURLChanged = true;
}
}
QScriptValue animationIsPlaying = object.property("animationIsPlaying");
if (animationIsPlaying.isValid()) {
bool newIsAnimationPlaying;
newIsAnimationPlaying = animationIsPlaying.toVariant().toBool();
if (_defaultSettings || newIsAnimationPlaying != _animationIsPlaying) {
_animationIsPlaying = newIsAnimationPlaying;
_animationIsPlayingChanged = true;
}
}
QScriptValue animationFrameIndex = object.property("animationFrameIndex");
if (animationFrameIndex.isValid()) {
float newFrameIndex;
newFrameIndex = animationFrameIndex.toVariant().toFloat();
if (_defaultSettings || newFrameIndex != _animationFrameIndex) {
_animationFrameIndex = newFrameIndex;
_animationFrameIndexChanged = true;
}
}
QScriptValue animationFPS = object.property("animationFPS");
if (animationFPS.isValid()) {
float newFPS;
newFPS = animationFPS.toVariant().toFloat();
if (_defaultSettings || newFPS != _animationFPS) {
_animationFPS = newFPS;
_animationFPSChanged = true;
}
}
QScriptValue glowLevel = object.property("glowLevel");
if (glowLevel.isValid()) {
float newGlowLevel;
newGlowLevel = glowLevel.toVariant().toFloat();
if (_defaultSettings || newGlowLevel != _glowLevel) {
_glowLevel = newGlowLevel;
_glowLevelChanged = true;
}
}
_lastEdited = usecTimestampNow(); _lastEdited = usecTimestampNow();
} }
@ -432,7 +222,6 @@ void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemP
// //
bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties, bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties,
unsigned char* bufferOut, int sizeIn, int& sizeOut) { unsigned char* bufferOut, int sizeIn, int& sizeOut) {
OctreePacketData ourDataPacket(false, sizeIn); // create a packetData object to add out packet details too. OctreePacketData ourDataPacket(false, sizeIn); // create a packetData object to add out packet details too.
OctreePacketData* packetData = &ourDataPacket; // we want a pointer to this so we can use our APPEND_ENTITY_PROPERTY macro OctreePacketData* packetData = &ourDataPacket; // we want a pointer to this so we can use our APPEND_ENTITY_PROPERTY macro
@ -532,23 +321,26 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
// These items would go here once supported.... // These items would go here once supported....
// PROP_PAGED_PROPERTY, // PROP_PAGED_PROPERTY,
// PROP_CUSTOM_PROPERTIES_INCLUDED, // PROP_CUSTOM_PROPERTIES_INCLUDED,
// PROP_VISIBLE,
APPEND_ENTITY_PROPERTY(PROP_POSITION, appendPosition, properties.getPosition()); APPEND_ENTITY_PROPERTY(PROP_POSITION, appendPosition, properties.getPosition());
APPEND_ENTITY_PROPERTY(PROP_RADIUS, appendValue, properties.getRadius()); APPEND_ENTITY_PROPERTY(PROP_DIMENSIONS, appendValue, properties.getDimensions()); // NOTE: PROP_RADIUS obsolete
APPEND_ENTITY_PROPERTY(PROP_ROTATION, appendValue, properties.getRotation()); APPEND_ENTITY_PROPERTY(PROP_ROTATION, appendValue, properties.getRotation());
APPEND_ENTITY_PROPERTY(PROP_MASS, appendValue, properties.getMass()); APPEND_ENTITY_PROPERTY(PROP_MASS, appendValue, properties.getMass());
APPEND_ENTITY_PROPERTY(PROP_VELOCITY, appendValue, properties.getVelocity()); APPEND_ENTITY_PROPERTY(PROP_VELOCITY, appendValue, properties.getVelocity());
APPEND_ENTITY_PROPERTY(PROP_GRAVITY, appendValue, properties.getGravity()); APPEND_ENTITY_PROPERTY(PROP_GRAVITY, appendValue, properties.getGravity());
APPEND_ENTITY_PROPERTY(PROP_DAMPING, appendValue, properties.getDamping()); APPEND_ENTITY_PROPERTY(PROP_DAMPING, appendValue, properties.getDamping());
APPEND_ENTITY_PROPERTY(PROP_LIFETIME, appendValue, properties.getLifetime()); APPEND_ENTITY_PROPERTY(PROP_LIFETIME, appendValue, properties.getLifetime());
//APPEND_ENTITY_PROPERTY(PROP_SCRIPT, appendValue, properties.getScript()); // not supported by edit messages APPEND_ENTITY_PROPERTY(PROP_SCRIPT, appendValue, properties.getScript());
APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, properties.getColor()); APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, properties.getColor());
APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, appendValue, properties.getModelURL()); APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, appendValue, properties.getModelURL());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, appendValue, properties.getAnimationURL()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, appendValue, properties.getAnimationURL());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, appendValue, properties.getAnimationFPS()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, appendValue, properties.getAnimationFPS());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, appendValue, properties.getAnimationFrameIndex()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, appendValue, properties.getAnimationFrameIndex());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, appendValue, properties.getAnimationIsPlaying()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, appendValue, properties.getAnimationIsPlaying());
APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, appendValue, properties.getRegistrationPoint());
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, appendValue, properties.getAngularVelocity());
APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, appendValue, properties.getAngularDamping());
APPEND_ENTITY_PROPERTY(PROP_VISIBLE, appendValue, properties.getVisible());
} }
if (propertyCount > 0) { if (propertyCount > 0) {
int endOfEntityItemData = packetData->getUncompressedByteOffset(); int endOfEntityItemData = packetData->getUncompressedByteOffset();
@ -727,20 +519,24 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
processedBytes += propertyFlags.getEncodedLength(); processedBytes += propertyFlags.getEncodedLength();
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, glm::vec3, setPosition); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, glm::vec3, setPosition);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RADIUS, float, setRadius); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DIMENSIONS, glm::vec3, setDimensions); // NOTE: PROP_RADIUS obsolete
READ_ENTITY_PROPERTY_QUAT_TO_PROPERTIES(PROP_ROTATION, setRotation); READ_ENTITY_PROPERTY_QUAT_TO_PROPERTIES(PROP_ROTATION, setRotation);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MASS, float, setMass); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MASS, float, setMass);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VELOCITY, glm::vec3, setVelocity); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VELOCITY, glm::vec3, setVelocity);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GRAVITY, glm::vec3, setGravity); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GRAVITY, glm::vec3, setGravity);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DAMPING, float, setDamping); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DAMPING, float, setDamping);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LIFETIME, float, setLifetime); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LIFETIME, float, setLifetime);
//READ_ENTITY_PROPERTY_STRING(PROP_SCRIPT,setScript); // not yet supported by edit messages... READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_SCRIPT,setScript);
READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_COLOR, setColor); READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_COLOR, setColor);
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_MODEL_URL, setModelURL); READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_MODEL_URL, setModelURL);
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_ANIMATION_URL, setAnimationURL); READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_ANIMATION_URL, setAnimationURL);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FPS, float, setAnimationFPS); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FPS, float, setAnimationFPS);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FRAME_INDEX, float, setAnimationFrameIndex); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FRAME_INDEX, float, setAnimationFrameIndex);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_PLAYING, bool, setAnimationIsPlaying); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_PLAYING, bool, setAnimationIsPlaying);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_VELOCITY, glm::vec3, setAngularVelocity);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_DAMPING, float, setAngularDamping);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VISIBLE, bool, setVisible);
return valid; return valid;
} }
@ -774,10 +570,8 @@ bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityIt
return true; return true;
} }
void EntityItemProperties::markAllChanged() { void EntityItemProperties::markAllChanged() {
_positionChanged = true; _positionChanged = true;
_radiusChanged = true;
_rotationChanged = true; _rotationChanged = true;
_massChanged = true; _massChanged = true;
_velocityChanged = true; _velocityChanged = true;
@ -785,7 +579,10 @@ void EntityItemProperties::markAllChanged() {
_dampingChanged = true; _dampingChanged = true;
_lifetimeChanged = true; _lifetimeChanged = true;
_scriptChanged = true; _scriptChanged = true;
_registrationPointChanged = true;
_angularVelocityChanged = true;
_angularDampingChanged = true;
_visibleChanged = true;
_colorChanged = true; _colorChanged = true;
_modelURLChanged = true; _modelURLChanged = true;
_animationURLChanged = true; _animationURLChanged = true;
@ -793,5 +590,36 @@ void EntityItemProperties::markAllChanged() {
_animationFrameIndexChanged = true; _animationFrameIndexChanged = true;
_animationFPSChanged = true; _animationFPSChanged = true;
_glowLevelChanged = true; _glowLevelChanged = true;
}
AACube EntityItemProperties::getMaximumAACubeInTreeUnits() const {
AACube maxCube = getMaximumAACubeInMeters();
maxCube.scale(1 / (float)TREE_SCALE);
return maxCube;
}
/// The maximum bounding cube for the entity, independent of it's rotation.
/// This accounts for the registration point (upon which rotation occurs around).
///
AACube EntityItemProperties::getMaximumAACubeInMeters() const {
// * we know that the position is the center of rotation
glm::vec3 centerOfRotation = _position; // also where _registration point is
// * we know that the registration point is the center of rotation
// * we can calculate the length of the furthest extent from the registration point
// as the dimensions * max (registrationPoint, (1.0,1.0,1.0) - registrationPoint)
glm::vec3 registrationPoint = (_dimensions * _registrationPoint);
glm::vec3 registrationRemainder = (_dimensions * (glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint));
glm::vec3 furthestExtentFromRegistration = glm::max(registrationPoint, registrationRemainder);
// * we know that if you rotate in any direction you would create a sphere
// that has a radius of the length of furthest extent from registration point
float radius = glm::length(furthestExtentFromRegistration);
// * we know that the minimum bounding cube of this maximum possible sphere is
// (center - radius) to (center + radius)
glm::vec3 minimumCorner = centerOfRotation - glm::vec3(radius, radius, radius);
float diameter = radius * 2.0f;
return AACube(minimumCorner, diameter);
} }

View file

@ -15,6 +15,7 @@
#include <stdint.h> #include <stdint.h>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <glm/gtx/extented_min_max.hpp>
#include <QtScript/QScriptEngine> #include <QtScript/QScriptEngine>
#include <QtCore/QObject> #include <QtCore/QObject>
@ -28,20 +29,9 @@
#include "EntityItemID.h" #include "EntityItemID.h"
#include "EntityItemPropertiesMacros.h"
#include "EntityTypes.h" #include "EntityTypes.h"
// TODO: should these be static members of EntityItem or EntityItemProperties?
const float ENTITY_DEFAULT_RADIUS = 0.1f / TREE_SCALE;
const float ENTITY_MINIMUM_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container
const QString ENTITY_DEFAULT_MODEL_URL("");
const glm::quat ENTITY_DEFAULT_ROTATION;
const QString ENTITY_DEFAULT_ANIMATION_URL("");
const float ENTITY_DEFAULT_ANIMATION_FPS = 30.0f;
const quint64 UNKNOWN_CREATED_TIME = (quint64)(-1);
const quint64 USE_EXISTING_CREATED_TIME = (quint64)(-2);
// PropertyFlags support // PropertyFlags support
enum EntityPropertyList { enum EntityPropertyList {
PROP_PAGED_PROPERTY, PROP_PAGED_PROPERTY,
@ -50,7 +40,8 @@ enum EntityPropertyList {
// these properties are supported by the EntityItem base class // these properties are supported by the EntityItem base class
PROP_VISIBLE, PROP_VISIBLE,
PROP_POSITION, PROP_POSITION,
PROP_RADIUS, PROP_RADIUS, // NOTE: PROP_RADIUS is obsolete and only included in old format streams
PROP_DIMENSIONS = PROP_RADIUS,
PROP_ROTATION, PROP_ROTATION,
PROP_MASS, PROP_MASS,
PROP_VELOCITY, PROP_VELOCITY,
@ -67,16 +58,24 @@ enum EntityPropertyList {
PROP_ANIMATION_FRAME_INDEX, PROP_ANIMATION_FRAME_INDEX,
PROP_ANIMATION_PLAYING, PROP_ANIMATION_PLAYING,
PROP_LAST_ITEM = PROP_ANIMATION_PLAYING // these properties are supported by the EntityItem base class
PROP_REGISTRATION_POINT,
PROP_ANGULAR_VELOCITY,
PROP_ANGULAR_DAMPING,
PROP_LAST_ITEM = PROP_ANGULAR_DAMPING
}; };
typedef PropertyFlags<EntityPropertyList> EntityPropertyFlags; typedef PropertyFlags<EntityPropertyList> EntityPropertyFlags;
const quint64 UNKNOWN_CREATED_TIME = (quint64)(-1);
const quint64 USE_EXISTING_CREATED_TIME = (quint64)(-2);
/// A collection of properties of an entity item used in the scripting API. Translates between the actual properties of an /// A collection of properties of an entity item used in the scripting API. Translates between the actual properties of an
/// entity and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete /// entity and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete
/// set of entity item properties via JavaScript hashes/QScriptValues /// set of entity item properties via JavaScript hashes/QScriptValues
/// all units for position, radius, etc are in meter units /// all units for position, dimensions, etc are in meter units
class EntityItemProperties { class EntityItemProperties {
friend class EntityItem; // TODO: consider removing this friend relationship and use public methods friend class EntityItem; // TODO: consider removing this friend relationship and use public methods
friend class ModelEntityItem; // TODO: consider removing this friend relationship and use public methods friend class ModelEntityItem; // TODO: consider removing this friend relationship and use public methods
@ -96,31 +95,27 @@ public:
/// used by EntityScriptingInterface to return EntityItemProperties for unknown models /// used by EntityScriptingInterface to return EntityItemProperties for unknown models
void setIsUnknownID() { _id = UNKNOWN_ENTITY_ID; _idSet = true; } void setIsUnknownID() { _id = UNKNOWN_ENTITY_ID; _idSet = true; }
glm::vec3 getMinimumPointMeters() const { return _position - glm::vec3(_radius, _radius, _radius); } AACube getMaximumAACubeInTreeUnits() const;
glm::vec3 getMaximumPointMeters() const { return _position + glm::vec3(_radius, _radius, _radius); } AACube getMaximumAACubeInMeters() const;
AACube getAACubeMeters() const { return AACube(getMinimumPointMeters(), getMaxDimension()); } /// AACube in meter units
glm::vec3 getMinimumPointTreeUnits() const { return getMinimumPointMeters() / (float)TREE_SCALE; }
glm::vec3 getMaximumPointTreeUnits() const { return getMaximumPointMeters() / (float)TREE_SCALE; }
/// AACube in domain scale units (0.0 - 1.0)
AACube getAACubeTreeUnits() const {
return AACube(getMinimumPointMeters() / (float)TREE_SCALE, getMaxDimension() / (float)TREE_SCALE);
}
void debugDump() const; void debugDump() const;
// properties of all entities // properties of all entities
EntityTypes::EntityType getType() const { return _type; } EntityTypes::EntityType getType() const { return _type; }
const glm::vec3& getPosition() const { return _position; }
float getRadius() const { return _radius; }
float getMaxDimension() const { return _radius * 2.0f; }
glm::vec3 getDimensions() const { return glm::vec3(_radius, _radius, _radius) * 2.0f; }
const glm::quat& getRotation() const { return _rotation; }
void setType(EntityTypes::EntityType type) { _type = type; } void setType(EntityTypes::EntityType type) { _type = type; }
const glm::vec3& getPosition() const { return _position; }
/// set position in meter units, will be clamped to domain bounds /// set position in meter units, will be clamped to domain bounds
void setPosition(const glm::vec3& value) { _position = glm::clamp(value, 0.0f, (float)TREE_SCALE); _positionChanged = true; } void setPosition(const glm::vec3& value) { _position = glm::clamp(value, 0.0f, (float)TREE_SCALE); _positionChanged = true; }
void setRadius(float value) { _radius = value; _radiusChanged = true; }
const glm::vec3& getDimensions() const { return _dimensions; }
void setDimensions(const glm::vec3& value) { _dimensions = value; _dimensionsChanged = true; }
float getMaxDimension() const { return glm::max(_dimensions.x, _dimensions.y, _dimensions.z); }
const glm::quat& getRotation() const { return _rotation; }
void setRotation(const glm::quat& rotation) { _rotation = rotation; _rotationChanged = true; } void setRotation(const glm::quat& rotation) { _rotation = rotation; _rotationChanged = true; }
float getMass() const { return _mass; } float getMass() const { return _mass; }
@ -148,9 +143,9 @@ public:
// NOTE: how do we handle _defaultSettings??? // NOTE: how do we handle _defaultSettings???
bool containsBoundsProperties() const { return (_positionChanged || _radiusChanged); } bool containsBoundsProperties() const { return (_positionChanged || _dimensionsChanged); }
bool containsPositionChange() const { return _positionChanged; } bool containsPositionChange() const { return _positionChanged; }
bool containsRadiusChange() const { return _radiusChanged; } bool containsDimensionsChange() const { return _dimensionsChanged; }
// TODO: this need to be more generic. for now, we're going to have the properties class support these as // TODO: this need to be more generic. for now, we're going to have the properties class support these as
// named getter/setters, but we want to move them to generic types... // named getter/setters, but we want to move them to generic types...
@ -162,6 +157,7 @@ public:
bool getAnimationIsPlaying() const { return _animationIsPlaying; } bool getAnimationIsPlaying() const { return _animationIsPlaying; }
float getAnimationFPS() const { return _animationFPS; } float getAnimationFPS() const { return _animationFPS; }
float getGlowLevel() const { return _glowLevel; } float getGlowLevel() const { return _glowLevel; }
const QString& getScript() const { return _script; }
// model related properties // model related properties
void setColor(const xColor& value) { _color = value; _colorChanged = true; } void setColor(const xColor& value) { _color = value; _colorChanged = true; }
@ -171,6 +167,7 @@ public:
void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; _animationIsPlayingChanged = true; } void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; _animationIsPlayingChanged = true; }
void setAnimationFPS(float value) { _animationFPS = value; _animationFPSChanged = true; } void setAnimationFPS(float value) { _animationFPS = value; _animationFPSChanged = true; }
void setGlowLevel(float value) { _glowLevel = value; _glowLevelChanged = true; } void setGlowLevel(float value) { _glowLevel = value; _glowLevelChanged = true; }
void setScript(const QString& value) { _script = value; _scriptChanged = true; }
static bool encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties, static bool encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties,
@ -184,7 +181,6 @@ public:
EntityItemID& entityID, EntityItemProperties& properties); EntityItemID& entityID, EntityItemProperties& properties);
bool positionChanged() const { return _positionChanged; } bool positionChanged() const { return _positionChanged; }
bool radiusChanged() const { return _radiusChanged; }
bool rotationChanged() const { return _rotationChanged; } bool rotationChanged() const { return _rotationChanged; }
bool massChanged() const { return _massChanged; } bool massChanged() const { return _massChanged; }
bool velocityChanged() const { return _velocityChanged; } bool velocityChanged() const { return _velocityChanged; }
@ -192,6 +188,8 @@ public:
bool dampingChanged() const { return _dampingChanged; } bool dampingChanged() const { return _dampingChanged; }
bool lifetimeChanged() const { return _lifetimeChanged; } bool lifetimeChanged() const { return _lifetimeChanged; }
bool scriptChanged() const { return _scriptChanged; } bool scriptChanged() const { return _scriptChanged; }
bool dimensionsChanged() const { return _dimensionsChanged; }
bool registrationPointChanged() const { return _registrationPointChanged; }
bool colorChanged() const { return _colorChanged; } bool colorChanged() const { return _colorChanged; }
bool modelURLChanged() const { return _modelURLChanged; } bool modelURLChanged() const { return _modelURLChanged; }
bool animationURLChanged() const { return _animationURLChanged; } bool animationURLChanged() const { return _animationURLChanged; }
@ -203,6 +201,24 @@ public:
void clearID() { _id = UNKNOWN_ENTITY_ID; _idSet = false; } void clearID() { _id = UNKNOWN_ENTITY_ID; _idSet = false; }
void markAllChanged(); void markAllChanged();
QVector<SittingPoint> getSittingPoints() const { return _sittingPoints; }
void setSittingPoints(QVector<SittingPoint> sittingPoints) { _sittingPoints = sittingPoints; }
const glm::vec3& getNaturalDimensions() const { return _naturalDimensions; }
void setNaturalDimensions(const glm::vec3& value) { _naturalDimensions = value; }
const glm::vec3& getRegistrationPoint() const { return _registrationPoint; }
void setRegistrationPoint(const glm::vec3& value) { _registrationPoint = value; _registrationPointChanged = true; }
const glm::vec3& getAngularVelocity() const { return _angularVelocity; }
void setAngularVelocity(const glm::vec3& value) { _angularVelocity = value; _angularVelocityChanged = true; }
float getAngularDamping() const { return _angularDamping; }
void setAngularDamping(float value) { _angularDamping = value; _angularDampingChanged = true; }
bool getVisible() const { return _visible; }
void setVisible(bool value) { _visible = value; _visibleChanged = true; }
private: private:
void setLastEdited(quint64 usecTime) { _lastEdited = usecTime; } void setLastEdited(quint64 usecTime) { _lastEdited = usecTime; }
@ -212,8 +228,11 @@ private:
quint64 _created; quint64 _created;
EntityTypes::EntityType _type; EntityTypes::EntityType _type;
void setType(const QString& typeName) { _type = EntityTypes::getEntityTypeFromName(typeName); }
glm::vec3 _position; glm::vec3 _position;
float _radius; glm::vec3 _dimensions;
glm::quat _rotation; glm::quat _rotation;
float _mass; float _mass;
glm::vec3 _velocity; glm::vec3 _velocity;
@ -221,9 +240,13 @@ private:
float _damping; float _damping;
float _lifetime; float _lifetime;
QString _script; QString _script;
glm::vec3 _registrationPoint;
glm::vec3 _angularVelocity;
float _angularDamping;
bool _visible;
bool _positionChanged; bool _positionChanged;
bool _radiusChanged; bool _dimensionsChanged;
bool _rotationChanged; bool _rotationChanged;
bool _massChanged; bool _massChanged;
bool _velocityChanged; bool _velocityChanged;
@ -231,6 +254,10 @@ private:
bool _dampingChanged; bool _dampingChanged;
bool _lifetimeChanged; bool _lifetimeChanged;
bool _scriptChanged; bool _scriptChanged;
bool _registrationPointChanged;
bool _angularVelocityChanged;
bool _angularDampingChanged;
bool _visibleChanged;
// TODO: this need to be more generic. for now, we're going to have the properties class support these as // TODO: this need to be more generic. for now, we're going to have the properties class support these as
// named getter/setters, but we want to move them to generic types... // named getter/setters, but we want to move them to generic types...
@ -242,6 +269,7 @@ private:
float _animationFPS; float _animationFPS;
float _glowLevel; float _glowLevel;
QVector<SittingPoint> _sittingPoints; QVector<SittingPoint> _sittingPoints;
glm::vec3 _naturalDimensions;
bool _colorChanged; bool _colorChanged;
bool _modelURLChanged; bool _modelURLChanged;
@ -257,107 +285,4 @@ Q_DECLARE_METATYPE(EntityItemProperties);
QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties); QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties);
void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemProperties& properties); void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemProperties& properties);
#define APPEND_ENTITY_PROPERTY(P,O,V) \
if (requestedProperties.getHasProperty(P)) { \
LevelDetails propertyLevel = packetData->startLevel(); \
successPropertyFits = packetData->O(V); \
if (successPropertyFits) { \
propertyFlags |= P; \
propertiesDidntFit -= P; \
propertyCount++; \
packetData->endLevel(propertyLevel); \
} else { \
packetData->discardLevel(propertyLevel); \
appendState = OctreeElement::PARTIAL; \
} \
} else { \
propertiesDidntFit -= P; \
}
#define READ_ENTITY_PROPERTY(P,T,M) \
if (propertyFlags.getHasProperty(P)) { \
T fromBuffer; \
memcpy(&fromBuffer, dataAt, sizeof(fromBuffer)); \
dataAt += sizeof(fromBuffer); \
bytesRead += sizeof(fromBuffer); \
if (overwriteLocalData) { \
M = fromBuffer; \
} \
}
#define READ_ENTITY_PROPERTY_QUAT(P,M) \
if (propertyFlags.getHasProperty(P)) { \
glm::quat fromBuffer; \
int bytes = unpackOrientationQuatFromBytes(dataAt, fromBuffer); \
dataAt += bytes; \
bytesRead += bytes; \
if (overwriteLocalData) { \
M = fromBuffer; \
} \
}
#define READ_ENTITY_PROPERTY_STRING(P,O) \
if (propertyFlags.getHasProperty(P)) { \
uint16_t length; \
memcpy(&length, dataAt, sizeof(length)); \
dataAt += sizeof(length); \
bytesRead += sizeof(length); \
QString value((const char*)dataAt); \
dataAt += length; \
bytesRead += length; \
if (overwriteLocalData) { \
O(value); \
} \
}
#define READ_ENTITY_PROPERTY_COLOR(P,M) \
if (propertyFlags.getHasProperty(P)) { \
if (overwriteLocalData) { \
memcpy(M, dataAt, sizeof(M)); \
} \
dataAt += sizeof(rgbColor); \
bytesRead += sizeof(rgbColor); \
}
#define READ_ENTITY_PROPERTY_TO_PROPERTIES(P,T,O) \
if (propertyFlags.getHasProperty(P)) { \
T fromBuffer; \
memcpy(&fromBuffer, dataAt, sizeof(fromBuffer)); \
dataAt += sizeof(fromBuffer); \
processedBytes += sizeof(fromBuffer); \
properties.O(fromBuffer); \
}
#define READ_ENTITY_PROPERTY_QUAT_TO_PROPERTIES(P,O) \
if (propertyFlags.getHasProperty(P)) { \
glm::quat fromBuffer; \
int bytes = unpackOrientationQuatFromBytes(dataAt, fromBuffer); \
dataAt += bytes; \
processedBytes += bytes; \
properties.O(fromBuffer); \
}
#define READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(P,O) \
if (propertyFlags.getHasProperty(P)) { \
uint16_t length; \
memcpy(&length, dataAt, sizeof(length)); \
dataAt += sizeof(length); \
processedBytes += sizeof(length); \
QString value((const char*)dataAt); \
dataAt += length; \
processedBytes += length; \
properties.O(value); \
}
#define READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(P,O) \
if (propertyFlags.getHasProperty(P)) { \
xColor color; \
memcpy(&color, dataAt, sizeof(color)); \
dataAt += sizeof(color); \
processedBytes += sizeof(color); \
properties.O(color); \
}
#endif // hifi_EntityItemProperties_h #endif // hifi_EntityItemProperties_h

View file

@ -0,0 +1,241 @@
//
// EntityItemPropertiesMacros.h
// libraries/entities/src
//
// Created by Brad Hefta-Gaub on 9/10/14.
// 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_EntityItemPropertiesMacros_h
#define hifi_EntityItemPropertiesMacros_h
#define APPEND_ENTITY_PROPERTY(P,O,V) \
if (requestedProperties.getHasProperty(P)) { \
LevelDetails propertyLevel = packetData->startLevel(); \
successPropertyFits = packetData->O(V); \
if (successPropertyFits) { \
propertyFlags |= P; \
propertiesDidntFit -= P; \
propertyCount++; \
packetData->endLevel(propertyLevel); \
} else { \
packetData->discardLevel(propertyLevel); \
appendState = OctreeElement::PARTIAL; \
} \
} else { \
propertiesDidntFit -= P; \
}
#define READ_ENTITY_PROPERTY(P,T,M) \
if (propertyFlags.getHasProperty(P)) { \
T fromBuffer; \
memcpy(&fromBuffer, dataAt, sizeof(fromBuffer)); \
dataAt += sizeof(fromBuffer); \
bytesRead += sizeof(fromBuffer); \
if (overwriteLocalData) { \
M = fromBuffer; \
} \
}
#define READ_ENTITY_PROPERTY_QUAT(P,M) \
if (propertyFlags.getHasProperty(P)) { \
glm::quat fromBuffer; \
int bytes = unpackOrientationQuatFromBytes(dataAt, fromBuffer); \
dataAt += bytes; \
bytesRead += bytes; \
if (overwriteLocalData) { \
M = fromBuffer; \
} \
}
#define READ_ENTITY_PROPERTY_STRING(P,O) \
if (propertyFlags.getHasProperty(P)) { \
uint16_t length; \
memcpy(&length, dataAt, sizeof(length)); \
dataAt += sizeof(length); \
bytesRead += sizeof(length); \
QString value((const char*)dataAt); \
dataAt += length; \
bytesRead += length; \
if (overwriteLocalData) { \
O(value); \
} \
}
#define READ_ENTITY_PROPERTY_COLOR(P,M) \
if (propertyFlags.getHasProperty(P)) { \
if (overwriteLocalData) { \
memcpy(M, dataAt, sizeof(M)); \
} \
dataAt += sizeof(rgbColor); \
bytesRead += sizeof(rgbColor); \
}
#define READ_ENTITY_PROPERTY_TO_PROPERTIES(P,T,O) \
if (propertyFlags.getHasProperty(P)) { \
T fromBuffer; \
memcpy(&fromBuffer, dataAt, sizeof(fromBuffer)); \
dataAt += sizeof(fromBuffer); \
processedBytes += sizeof(fromBuffer); \
properties.O(fromBuffer); \
}
#define READ_ENTITY_PROPERTY_QUAT_TO_PROPERTIES(P,O) \
if (propertyFlags.getHasProperty(P)) { \
glm::quat fromBuffer; \
int bytes = unpackOrientationQuatFromBytes(dataAt, fromBuffer); \
dataAt += bytes; \
processedBytes += bytes; \
properties.O(fromBuffer); \
}
#define READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(P,O) \
if (propertyFlags.getHasProperty(P)) { \
uint16_t length; \
memcpy(&length, dataAt, sizeof(length)); \
dataAt += sizeof(length); \
processedBytes += sizeof(length); \
QString value((const char*)dataAt); \
dataAt += length; \
processedBytes += length; \
properties.O(value); \
}
#define READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(P,O) \
if (propertyFlags.getHasProperty(P)) { \
xColor color; \
memcpy(&color, dataAt, sizeof(color)); \
dataAt += sizeof(color); \
processedBytes += sizeof(color); \
properties.O(color); \
}
#define SET_ENTITY_PROPERTY_FROM_PROPERTIES(P,M) \
if (properties._##P##Changed || forceCopy) { \
M(properties._##P); \
somethingChanged = true; \
}
#define SET_ENTITY_PROPERTY_FROM_PROPERTIES_GETTER(C,G,S) \
if (properties.C() || forceCopy) { \
S(properties.G()); \
somethingChanged = true; \
}
#define COPY_ENTITY_PROPERTY_TO_PROPERTIES(M,G) \
properties._##M = G(); \
properties._##M##Changed = false;
#define CHECK_PROPERTY_CHANGE(P,M) \
if (_##M##Changed) { \
changedProperties += P; \
}
#define COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(P) \
QScriptValue P = vec3toScriptValue(engine, _##P); \
properties.setProperty(#P, P);
#define COPY_PROPERTY_TO_QSCRIPTVALUE_QUAT(P) \
QScriptValue P = quatToScriptValue(engine, _##P); \
properties.setProperty(#P, P);
#define COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR(P) \
QScriptValue P = xColorToScriptValue(engine, _##P); \
properties.setProperty(#P, P);
#define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(P, G) \
properties.setProperty(#P, G);
#define COPY_PROPERTY_TO_QSCRIPTVALUE(P) \
properties.setProperty(#P, _##P);
#define COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(P, S) \
QScriptValue P = object.property(#P); \
if (P.isValid()) { \
float newValue = P.toVariant().toFloat(); \
if (_defaultSettings || newValue != _##P) { \
S(newValue); \
} \
}
#define COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(P, S) \
QScriptValue P = object.property(#P); \
if (P.isValid()) { \
bool newValue = P.toVariant().toBool(); \
if (_defaultSettings || newValue != _##P) { \
S(newValue); \
} \
}
#define COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(P, S)\
QScriptValue P = object.property(#P); \
if (P.isValid()) { \
QString newValue = P.toVariant().toString();\
if (_defaultSettings || newValue != _##P) { \
S(newValue); \
} \
}
#define COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(P, S) \
QScriptValue P = object.property(#P); \
if (P.isValid()) { \
QScriptValue x = P.property("x"); \
QScriptValue y = P.property("y"); \
QScriptValue z = P.property("z"); \
if (x.isValid() && y.isValid() && z.isValid()) {\
glm::vec3 newValue; \
newValue.x = x.toVariant().toFloat(); \
newValue.y = y.toVariant().toFloat(); \
newValue.z = z.toVariant().toFloat(); \
if (_defaultSettings || newValue != _##P) { \
S(newValue); \
} \
} \
}
#define COPY_PROPERTY_FROM_QSCRIPTVALUE_QUAT(P, S) \
QScriptValue P = object.property(#P); \
if (P.isValid()) { \
QScriptValue x = P.property("x"); \
QScriptValue y = P.property("y"); \
QScriptValue z = P.property("z"); \
QScriptValue w = P.property("w"); \
if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { \
glm::quat newValue; \
newValue.x = x.toVariant().toFloat(); \
newValue.y = y.toVariant().toFloat(); \
newValue.z = z.toVariant().toFloat(); \
newValue.w = w.toVariant().toFloat(); \
if (_defaultSettings || newValue != _##P) { \
S(newValue); \
} \
} \
}
#define COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(P, S) \
QScriptValue P = object.property(#P); \
if (P.isValid()) { \
QScriptValue r = P.property("red"); \
QScriptValue g = P.property("green"); \
QScriptValue b = P.property("blue"); \
if (r.isValid() && g.isValid() && b.isValid()) {\
xColor newColor; \
newColor.red = r.toVariant().toInt(); \
newColor.green = g.toVariant().toInt(); \
newColor.blue = b.toVariant().toInt(); \
if (_defaultSettings || \
(newColor.red != _color.red || \
newColor.green != _color.green || \
newColor.blue != _color.blue)) { \
S(newColor); \
} \
} \
}
#endif // hifi_EntityItemPropertiesMacros_h

View file

@ -31,9 +31,6 @@ EntityItemID EntityScriptingInterface::addEntity(const EntityItemProperties& pro
EntityItemID id(NEW_ENTITY, creatorTokenID, false ); EntityItemID id(NEW_ENTITY, creatorTokenID, false );
// queue the packet
queueEntityMessage(PacketTypeEntityAddOrEdit, id, properties);
// If we have a local entity tree set, then also update it. // If we have a local entity tree set, then also update it.
if (_entityTree) { if (_entityTree) {
_entityTree->lockForWrite(); _entityTree->lockForWrite();
@ -41,6 +38,9 @@ EntityItemID EntityScriptingInterface::addEntity(const EntityItemProperties& pro
_entityTree->unlock(); _entityTree->unlock();
} }
// queue the packet
queueEntityMessage(PacketTypeEntityAddOrEdit, id, properties);
return id; return id;
} }
@ -68,18 +68,20 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(EntityItemID
EntityItem* entity = const_cast<EntityItem*>(_entityTree->findEntityByEntityItemID(identity)); EntityItem* entity = const_cast<EntityItem*>(_entityTree->findEntityByEntityItemID(identity));
if (entity) { if (entity) {
results = entity->getProperties();
// TODO: improve sitting points in the future, for now we've included the old model behavior for entity // TODO: improve sitting points and naturalDimensions in the future,
// types that are models // for now we've included the old sitting points model behavior for entity types that are models
// we've also added this hack for setting natural dimensions of models
if (entity->getType() == EntityTypes::Model) { if (entity->getType() == EntityTypes::Model) {
ModelEntityItem* model = dynamic_cast<ModelEntityItem*>(entity);
const FBXGeometry* geometry = _entityTree->getGeometryForEntity(entity); const FBXGeometry* geometry = _entityTree->getGeometryForEntity(entity);
if (geometry) { if (geometry) {
model->setSittingPoints(geometry->sittingPoints); results.setSittingPoints(geometry->sittingPoints);
Extents meshExtents = geometry->getUnscaledMeshExtents();
results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum);
} }
} }
results = entity->getProperties();
} else { } else {
results.setIsUnknownID(); results.setIsUnknownID();
} }
@ -95,13 +97,10 @@ EntityItemID EntityScriptingInterface::editEntity(EntityItemID entityID, const E
// if the entity is unknown, attempt to look it up // if the entity is unknown, attempt to look it up
if (!entityID.isKnownID) { if (!entityID.isKnownID) {
actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID); actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID);
} if (actualID.id != UNKNOWN_ENTITY_ID) {
entityID.id = actualID.id;
// if at this point, we know the id, send the update to the entity server entityID.isKnownID = true;
if (actualID.id != UNKNOWN_ENTITY_ID) { }
entityID.id = actualID.id;
entityID.isKnownID = true;
queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, properties);
} }
// If we have a local entity tree set, then also update it. We can do this even if we don't know // If we have a local entity tree set, then also update it. We can do this even if we don't know
@ -111,6 +110,12 @@ EntityItemID EntityScriptingInterface::editEntity(EntityItemID entityID, const E
_entityTree->updateEntity(entityID, properties); _entityTree->updateEntity(entityID, properties);
_entityTree->unlock(); _entityTree->unlock();
} }
// if at this point, we know the id, send the update to the entity server
if (entityID.isKnownID) {
queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, properties);
}
return entityID; return entityID;
} }
@ -121,13 +126,10 @@ void EntityScriptingInterface::deleteEntity(EntityItemID entityID) {
// if the entity is unknown, attempt to look it up // if the entity is unknown, attempt to look it up
if (!entityID.isKnownID) { if (!entityID.isKnownID) {
actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID); actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID);
} if (actualID.id != UNKNOWN_ENTITY_ID) {
entityID.id = actualID.id;
// if at this point, we know the id, send the update to the entity server entityID.isKnownID = true;
if (actualID.id != UNKNOWN_ENTITY_ID) { }
entityID.id = actualID.id;
entityID.isKnownID = true;
getEntityPacketSender()->queueEraseEntityMessage(entityID);
} }
// If we have a local entity tree set, then also update it. // If we have a local entity tree set, then also update it.
@ -136,6 +138,11 @@ void EntityScriptingInterface::deleteEntity(EntityItemID entityID) {
_entityTree->deleteEntity(entityID); _entityTree->deleteEntity(entityID);
_entityTree->unlock(); _entityTree->unlock();
} }
// if at this point, we know the id, send the update to the entity server
if (entityID.isKnownID) {
getEntityPacketSender()->queueEraseEntityMessage(entityID);
}
} }
EntityItemID EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float radius) const { EntityItemID EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float radius) const {

View file

@ -652,13 +652,14 @@ void EntityTree::updateMovingEntities(quint64 now, QSet<EntityItemID>& entitiesT
entitiesToDelete << thisEntity->getEntityItemID(); entitiesToDelete << thisEntity->getEntityItemID();
entitiesBecomingStatic << thisEntity; entitiesBecomingStatic << thisEntity;
} else { } else {
AACube oldCube = thisEntity->getAACube(); AACube oldCube = thisEntity->getMaximumAACube();
thisEntity->update(now); thisEntity->update(now);
AACube newCube = thisEntity->getAACube(); AACube newCube = thisEntity->getMaximumAACube();
// check to see if this movement has sent the entity outside of the domain. // check to see if this movement has sent the entity outside of the domain.
AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), 1.0f); AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), 1.0f);
if (!domainBounds.touches(newCube)) { if (!domainBounds.touches(newCube)) {
qDebug() << "Entity " << thisEntity->getEntityItemID() << " moved out of domain bounds.";
entitiesToDelete << thisEntity->getEntityItemID(); entitiesToDelete << thisEntity->getEntityItemID();
entitiesBecomingStatic << thisEntity; entitiesBecomingStatic << thisEntity;
} else { } else {

View file

@ -122,6 +122,9 @@ public:
const FBXGeometry* getGeometryForEntity(const EntityItem* entityItem) { const FBXGeometry* getGeometryForEntity(const EntityItem* entityItem) {
return _fbxService ? _fbxService->getGeometryForEntity(entityItem) : NULL; return _fbxService ? _fbxService->getGeometryForEntity(entityItem) : NULL;
} }
const Model* getModelForEntityItem(const EntityItem* entityItem) {
return _fbxService ? _fbxService->getModelForEntityItem(entityItem) : NULL;
}
EntityTreeElement* getContainingElement(const EntityItemID& entityItemID) /*const*/; EntityTreeElement* getContainingElement(const EntityItemID& entityItemID) /*const*/;
void setContainingElement(const EntityItemID& entityItemID, EntityTreeElement* element); void setContainingElement(const EntityItemID& entityItemID, EntityTreeElement* element);

View file

@ -296,7 +296,12 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
} }
if (includeThisEntity && params.viewFrustum) { if (includeThisEntity && params.viewFrustum) {
AACube entityCube = entity->getAACube();
// we want to use the maximum possible box for this, so that we don't have to worry about the nuance of
// simulation changing what's visible. consider the case where the entity contains an angular velocity
// the entity may not be in view and then in view a frame later, let the client side handle it's view
// frustum culling on rendering.
AACube entityCube = entity->getMaximumAACube();
entityCube.scale(TREE_SCALE); entityCube.scale(TREE_SCALE);
if (params.viewFrustum->cubeInFrustum(entityCube) == ViewFrustum::OUTSIDE) { if (params.viewFrustum->cubeInFrustum(entityCube) == ViewFrustum::OUTSIDE) {
includeThisEntity = false; // out of view, don't include it includeThisEntity = false; // out of view, don't include it
@ -405,19 +410,19 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
} }
bool EntityTreeElement::containsEntityBounds(const EntityItem* entity) const { bool EntityTreeElement::containsEntityBounds(const EntityItem* entity) const {
return containsBounds(entity->getMinimumPoint(), entity->getMaximumPoint()); return containsBounds(entity->getMaximumAACube());
} }
bool EntityTreeElement::bestFitEntityBounds(const EntityItem* entity) const { bool EntityTreeElement::bestFitEntityBounds(const EntityItem* entity) const {
return bestFitBounds(entity->getMinimumPoint(), entity->getMaximumPoint()); return bestFitBounds(entity->getMaximumAACube());
} }
bool EntityTreeElement::containsBounds(const EntityItemProperties& properties) const { bool EntityTreeElement::containsBounds(const EntityItemProperties& properties) const {
return containsBounds(properties.getMinimumPointTreeUnits(), properties.getMaximumPointTreeUnits()); return containsBounds(properties.getMaximumAACubeInTreeUnits());
} }
bool EntityTreeElement::bestFitBounds(const EntityItemProperties& properties) const { bool EntityTreeElement::bestFitBounds(const EntityItemProperties& properties) const {
return bestFitBounds(properties.getMinimumPointTreeUnits(), properties.getMaximumPointTreeUnits()); return bestFitBounds(properties.getMaximumAACubeInTreeUnits());
} }
bool EntityTreeElement::containsBounds(const AACube& bounds) const { bool EntityTreeElement::containsBounds(const AACube& bounds) const {
@ -477,76 +482,37 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con
while(entityItr != entityEnd) { while(entityItr != entityEnd) {
EntityItem* entity = (*entityItr); EntityItem* entity = (*entityItr);
AACube entityCube = entity->getAACube(); AABox entityBox = entity->getAABox();
float localDistance; float localDistance;
BoxFace localFace; BoxFace localFace;
// if the ray doesn't intersect with our cube, we can stop searching! // if the ray doesn't intersect with our cube, we can stop searching!
if (entityCube.findRayIntersection(origin, direction, localDistance, localFace)) { if (entityBox.findRayIntersection(origin, direction, localDistance, localFace)) {
const FBXGeometry* fbxGeometry = _myTree->getGeometryForEntity(entity);
if (fbxGeometry && fbxGeometry->meshExtents.isValid()) {
Extents extents = fbxGeometry->meshExtents;
// NOTE: If the entity has a bad mesh, then extents will be 0,0,0 & 0,0,0 // extents is the entity relative, scaled, centered extents of the entity
if (extents.minimum == extents.maximum && extents.minimum == glm::vec3(0,0,0)) { glm::mat4 rotation = glm::mat4_cast(entity->getRotation());
extents.maximum = glm::vec3(1.0f,1.0f,1.0f); // in this case we will simulate the unit cube glm::mat4 translation = glm::translate(entity->getPosition());
glm::mat4 entityToWorldMatrix = translation * rotation;
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
glm::vec3 dimensions = entity->getDimensions();
glm::vec3 registrationPoint = entity->getRegistrationPoint();
glm::vec3 corner = -(dimensions * registrationPoint);
AABox entityFrameBox(corner, dimensions);
glm::vec3 entityFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f));
glm::vec3 entityFrameDirection = glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f));
// we can use the AABox's ray intersection by mapping our origin and direction into the entity frame
// and testing intersection there.
if (entityFrameBox.findRayIntersection(entityFrameOrigin, entityFrameDirection, localDistance, localFace)) {
if (localDistance < distance) {
distance = localDistance;
face = localFace;
*intersectedObject = (void*)entity;
somethingIntersected = true;
} }
// NOTE: these extents are entity space, so we need to scale and center them accordingly
// size is our "target size in world space"
// we need to set our entity scale so that the extents of the mesh, fit in a cube that size...
float maxDimension = glm::distance(extents.maximum, extents.minimum);
float scale = entity->getSize() / maxDimension;
glm::vec3 halfDimensions = (extents.maximum - extents.minimum) * 0.5f;
glm::vec3 offset = -extents.minimum - halfDimensions;
extents.minimum += offset;
extents.maximum += offset;
extents.minimum *= scale;
extents.maximum *= scale;
Extents rotatedExtents = extents;
rotatedExtents.rotate(entity->getRotation());
rotatedExtents.minimum += entity->getPosition();
rotatedExtents.maximum += entity->getPosition();
AABox rotatedExtentsBox(rotatedExtents.minimum, (rotatedExtents.maximum - rotatedExtents.minimum));
// if it's in our AABOX for our rotated extents, then check to see if it's in our non-AABox
if (rotatedExtentsBox.findRayIntersection(origin, direction, localDistance, localFace)) {
// extents is the entity relative, scaled, centered extents of the entity
glm::mat4 rotation = glm::mat4_cast(entity->getRotation());
glm::mat4 translation = glm::translate(entity->getPosition());
glm::mat4 entityToWorldMatrix = translation * rotation;
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
AABox entityFrameBox(extents.minimum, (extents.maximum - extents.minimum));
glm::vec3 entityFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f));
glm::vec3 entityFrameDirection = glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f));
// we can use the AABox's ray intersection by mapping our origin and direction into the entity frame
// and testing intersection there.
if (entityFrameBox.findRayIntersection(entityFrameOrigin, entityFrameDirection, localDistance, localFace)) {
if (localDistance < distance) {
distance = localDistance;
face = localFace;
*intersectedObject = (void*)entity;
somethingIntersected = true;
}
}
}
} else if (localDistance < distance) {
distance = localDistance;
face = localFace;
*intersectedObject = (void*)entity;
somethingIntersected = true;
} }
} }
@ -555,6 +521,7 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con
return somethingIntersected; return somethingIntersected;
} }
// TODO: change this to use better bounding shape for entity than sphere
bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float radius, bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float radius,
glm::vec3& penetration, void** penetratedObject) const { glm::vec3& penetration, void** penetratedObject) const {
QList<EntityItem*>::iterator entityItr = _entityItems->begin(); QList<EntityItem*>::iterator entityItr = _entityItems->begin();
@ -605,6 +572,7 @@ const EntityItem* EntityTreeElement::getClosestEntity(glm::vec3 position) const
return closestEntity; return closestEntity;
} }
// TODO: change this to use better bounding shape for entity than sphere
void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searchRadius, QVector<const EntityItem*>& foundEntities) const { void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searchRadius, QVector<const EntityItem*>& foundEntities) const {
uint16_t numberOfEntities = _entityItems->size(); uint16_t numberOfEntities = _entityItems->size();
for (uint16_t i = 0; i < numberOfEntities; i++) { for (uint16_t i = 0; i < numberOfEntities; i++) {
@ -616,6 +584,7 @@ void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searc
} }
} }
// TODO: change this to use better bounding shape for entity than sphere
void EntityTreeElement::getEntities(const AACube& box, QVector<EntityItem*>& foundEntities) { void EntityTreeElement::getEntities(const AACube& box, QVector<EntityItem*>& foundEntities) {
QList<EntityItem*>::iterator entityItr = _entityItems->begin(); QList<EntityItem*>::iterator entityItr = _entityItems->begin();
QList<EntityItem*>::iterator entityEnd = _entityItems->end(); QList<EntityItem*>::iterator entityEnd = _entityItems->end();

View file

@ -17,6 +17,13 @@
#include "ModelEntityItem.h" #include "ModelEntityItem.h"
const QString ModelEntityItem::DEFAULT_MODEL_URL = QString("");
const QString ModelEntityItem::DEFAULT_ANIMATION_URL = QString("");
const float ModelEntityItem::DEFAULT_ANIMATION_FRAME_INDEX = 0.0f;
const bool ModelEntityItem::DEFAULT_ANIMATION_IS_PLAYING = false;
const float ModelEntityItem::DEFAULT_ANIMATION_FPS = 30.0f;
EntityItem* ModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItem* ModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
return new ModelEntityItem(entityID, properties); return new ModelEntityItem(entityID, properties);
} }
@ -40,7 +47,6 @@ EntityItemProperties ModelEntityItem::getProperties() const {
properties._animationIsPlaying = getAnimationIsPlaying(); properties._animationIsPlaying = getAnimationIsPlaying();
properties._animationFrameIndex = getAnimationFrameIndex(); properties._animationFrameIndex = getAnimationFrameIndex();
properties._animationFPS = getAnimationFPS(); properties._animationFPS = getAnimationFPS();
properties._sittingPoints = getSittingPoints(); // sitting support
properties._colorChanged = false; properties._colorChanged = false;
properties._modelURLChanged = false; properties._modelURLChanged = false;
properties._animationURLChanged = false; properties._animationURLChanged = false;
@ -55,40 +61,13 @@ EntityItemProperties ModelEntityItem::getProperties() const {
bool ModelEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) { bool ModelEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) {
bool somethingChanged = false; bool somethingChanged = false;
somethingChanged = EntityItem::setProperties(properties, forceCopy); // set the properties in our base class somethingChanged = EntityItem::setProperties(properties, forceCopy); // set the properties in our base class
if (properties._colorChanged || forceCopy) {
setColor(properties._color);
somethingChanged = true;
}
if (properties._modelURLChanged || forceCopy) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
setModelURL(properties._modelURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(modelURL, setModelURL);
somethingChanged = true; SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationURL, setAnimationURL);
} SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationIsPlaying, setAnimationIsPlaying);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationFrameIndex, setAnimationFrameIndex);
if (properties._animationURLChanged || forceCopy) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationFPS, setAnimationFPS);
setAnimationURL(properties._animationURL);
somethingChanged = true;
}
if (properties._animationIsPlayingChanged || forceCopy) {
setAnimationIsPlaying(properties._animationIsPlaying);
somethingChanged = true;
}
if (properties._animationFrameIndexChanged || forceCopy) {
setAnimationFrameIndex(properties._animationFrameIndex);
somethingChanged = true;
}
if (properties._animationFPSChanged || forceCopy) {
setAnimationFPS(properties._animationFPS);
somethingChanged = true;
}
if (properties._glowLevelChanged || forceCopy) {
setGlowLevel(properties._glowLevel);
somethingChanged = true;
}
if (somethingChanged) { if (somethingChanged) {
bool wantDebug = false; bool wantDebug = false;
@ -169,9 +148,11 @@ int ModelEntityItem::oldVersionReadEntityDataFromBuffer(const unsigned char* dat
<< "old ID=" << oldID << "new ID=" << _id; << "old ID=" << oldID << "new ID=" << _id;
// radius // radius
memcpy(&_radius, dataAt, sizeof(_radius)); float radius;
dataAt += sizeof(_radius); memcpy(&radius, dataAt, sizeof(radius));
bytesRead += sizeof(_radius); dataAt += sizeof(radius);
bytesRead += sizeof(radius);
setRadius(radius);
// position // position
memcpy(&_position, dataAt, sizeof(_position)); memcpy(&_position, dataAt, sizeof(_position));
@ -393,7 +374,7 @@ void ModelEntityItem::debugDump() const {
qDebug() << "ModelEntityItem id:" << getEntityItemID(); qDebug() << "ModelEntityItem id:" << getEntityItemID();
qDebug() << " edited ago:" << getEditedAgo(); qDebug() << " edited ago:" << getEditedAgo();
qDebug() << " position:" << getPosition() * (float)TREE_SCALE; qDebug() << " position:" << getPosition() * (float)TREE_SCALE;
qDebug() << " radius:" << getRadius() * (float)TREE_SCALE; qDebug() << " dimensions:" << getDimensions() * (float)TREE_SCALE;
qDebug() << " model URL:" << getModelURL(); qDebug() << " model URL:" << getModelURL();
} }

View file

@ -54,10 +54,13 @@ public:
const rgbColor& getColor() const { return _color; } const rgbColor& getColor() const { return _color; }
xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; } xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; }
bool hasModel() const { return !_modelURL.isEmpty(); } bool hasModel() const { return !_modelURL.isEmpty(); }
static const QString DEFAULT_MODEL_URL;
const QString& getModelURL() const { return _modelURL; } const QString& getModelURL() const { return _modelURL; }
bool hasAnimation() const { return !_animationURL.isEmpty(); } bool hasAnimation() const { return !_animationURL.isEmpty(); }
static const QString DEFAULT_ANIMATION_URL;
const QString& getAnimationURL() const { return _animationURL; } const QString& getAnimationURL() const { return _animationURL; }
QVector<SittingPoint> getSittingPoints() const { return _sittingPoints; }
void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); } void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); }
void setColor(const xColor& value) { void setColor(const xColor& value) {
@ -69,10 +72,14 @@ public:
// model related properties // model related properties
void setModelURL(const QString& url) { _modelURL = url; } void setModelURL(const QString& url) { _modelURL = url; }
void setAnimationURL(const QString& url) { _animationURL = url; } void setAnimationURL(const QString& url) { _animationURL = url; }
static const float DEFAULT_ANIMATION_FRAME_INDEX;
void setAnimationFrameIndex(float value) { _animationFrameIndex = value; } void setAnimationFrameIndex(float value) { _animationFrameIndex = value; }
static const bool DEFAULT_ANIMATION_IS_PLAYING;
void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; } void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; }
static const float DEFAULT_ANIMATION_FPS;
void setAnimationFPS(float value) { _animationFPS = value; } void setAnimationFPS(float value) { _animationFPS = value; }
void setSittingPoints(QVector<SittingPoint> sittingPoints) { _sittingPoints = sittingPoints; }
void mapJoints(const QStringList& modelJointNames); void mapJoints(const QStringList& modelJointNames);
QVector<glm::quat> getAnimationFrame(); QVector<glm::quat> getAnimationFrame();
@ -92,7 +99,6 @@ protected:
rgbColor _color; rgbColor _color;
QString _modelURL; QString _modelURL;
QVector<SittingPoint> _sittingPoints;
quint64 _lastAnimated; quint64 _lastAnimated;
QString _animationURL; QString _animationURL;

View file

@ -20,21 +20,53 @@ MovingEntitiesOperator::MovingEntitiesOperator(EntityTree* tree) :
_changeTime(usecTimestampNow()), _changeTime(usecTimestampNow()),
_foundOldCount(0), _foundOldCount(0),
_foundNewCount(0), _foundNewCount(0),
_lookingCount(0) _lookingCount(0),
_wantDebug(false)
{ {
} }
MovingEntitiesOperator::~MovingEntitiesOperator() { MovingEntitiesOperator::~MovingEntitiesOperator() {
if (_wantDebug) {
bool stopExecution = false;
qDebug() << "MovingEntitiesOperator::~MovingEntitiesOperator() -----------------------------";
qDebug() << " _lookingCount:" << _lookingCount;
qDebug() << " _foundOldCount:" << _foundOldCount;
qDebug() << " _foundNewCount:" << _foundNewCount;
if (_foundOldCount < _lookingCount) {
qDebug() << " FAILURE: **** _foundOldCount < _lookingCount ******";
stopExecution = true;
}
if (_foundNewCount < _lookingCount) {
qDebug() << " FAILURE: **** _foundNewCount < _lookingCount ******";
stopExecution = true;
}
qDebug() << "--------------------------------------------------------------------------";
if(stopExecution) {
debug();
assert(false);
}
}
} }
void MovingEntitiesOperator::addEntityToMoveList(EntityItem* entity, const AACube& oldCube, const AACube& newCube) { void MovingEntitiesOperator::addEntityToMoveList(EntityItem* entity, const AACube& oldCube, const AACube& newCube) {
EntityTreeElement* oldContainingElement = _tree->getContainingElement(entity->getEntityItemID()); EntityTreeElement* oldContainingElement = _tree->getContainingElement(entity->getEntityItemID());
AABox newBox = newCube.clamp(0.0f, 1.0f); AABox newCubeClamped = newCube.clamp(0.0f, 1.0f);
AABox oldCubeClamped = oldCube.clamp(0.0f, 1.0f);
if (_wantDebug) {
qDebug() << "MovingEntitiesOperator::addEntityToMoveList() -----------------------------";
qDebug() << " oldCube:" << oldCube;
qDebug() << " newCube:" << newCube;
qDebug() << " oldCubeClamped:" << oldCubeClamped;
qDebug() << " newCubeClamped:" << newCubeClamped;
qDebug() << " oldContainingElement:" << oldContainingElement->getAACube();
qDebug() << " oldContainingElement->bestFitBounds(newCubeClamped):" << oldContainingElement->bestFitBounds(newCubeClamped);
}
// If the original containing element is the best fit for the requested newCube locations then // If the original containing element is the best fit for the requested newCube locations then
// we don't actually need to add the entity for moving and we can short circuit all this work // we don't actually need to add the entity for moving and we can short circuit all this work
if (!oldContainingElement->bestFitBounds(newBox)) { if (!oldContainingElement->bestFitBounds(newCubeClamped)) {
// check our tree, to determine if this entity is known // check our tree, to determine if this entity is known
EntityToMoveDetails details; EntityToMoveDetails details;
details.oldContainingElement = oldContainingElement; details.oldContainingElement = oldContainingElement;
@ -44,9 +76,29 @@ void MovingEntitiesOperator::addEntityToMoveList(EntityItem* entity, const AACub
details.newFound = false; details.newFound = false;
details.oldCube = oldCube; details.oldCube = oldCube;
details.newCube = newCube; details.newCube = newCube;
details.newBox = newBox; details.oldCubeClamped = oldCubeClamped;
details.newCubeClamped = newCubeClamped;
_entitiesToMove << details; _entitiesToMove << details;
_lookingCount++; _lookingCount++;
if (_wantDebug) {
qDebug() << "MovingEntitiesOperator::addEntityToMoveList() -----------------------------";
qDebug() << " details.entity:" << details.entity->getEntityItemID();
qDebug() << " details.oldContainingElementCube:" << details.oldContainingElementCube;
qDebug() << " details.oldCube:" << details.oldCube;
qDebug() << " details.newCube:" << details.newCube;
qDebug() << " details.newCubeClamped:" << details.newCubeClamped;
qDebug() << " _lookingCount:" << _lookingCount;
qDebug() << "--------------------------------------------------------------------------";
}
} else {
if (_wantDebug) {
qDebug() << " oldContainingElement->bestFitBounds(newCubeClamped) IS BEST FIT... NOTHING TO DO";
}
}
if (_wantDebug) {
qDebug() << "--------------------------------------------------------------------------";
} }
} }
@ -58,11 +110,29 @@ bool MovingEntitiesOperator::shouldRecurseSubTree(OctreeElement* element) {
// check the bounds // check the bounds
if (_entitiesToMove.size() > 0) { if (_entitiesToMove.size() > 0) {
AACube elementCube = element->getAACube(); AACube elementCube = element->getAACube();
int detailIndex = 0;
foreach(const EntityToMoveDetails& details, _entitiesToMove) { foreach(const EntityToMoveDetails& details, _entitiesToMove) {
if (elementCube.contains(details.oldCube) || elementCube.contains(details.newCube)) {
if (_wantDebug) {
qDebug() << "MovingEntitiesOperator::shouldRecurseSubTree() details["<< detailIndex <<"]-----------------------------";
qDebug() << " element:" << element->getAACube();
qDebug() << " details.entity:" << details.entity->getEntityItemID();
qDebug() << " details.oldContainingElementCube:" << details.oldContainingElementCube;
qDebug() << " details.oldCube:" << details.oldCube;
qDebug() << " details.newCube:" << details.newCube;
qDebug() << " details.newCubeClamped:" << details.newCubeClamped;
qDebug() << " elementCube.contains(details.oldCube)" << elementCube.contains(details.oldCube);
qDebug() << " elementCube.contains(details.newCube)" << elementCube.contains(details.newCube);
qDebug() << " elementCube.contains(details.oldCubeClamped)" << elementCube.contains(details.oldCubeClamped);
qDebug() << " elementCube.contains(details.newCubeClamped)" << elementCube.contains(details.newCubeClamped);
qDebug() << "--------------------------------------------------------------------------";
}
if (elementCube.contains(details.oldCubeClamped) || elementCube.contains(details.newCubeClamped)) {
containsEntity = true; containsEntity = true;
break; // if it contains at least one, we're good to go break; // if it contains at least one, we're good to go
} }
detailIndex++;
} }
} }
return containsEntity; return containsEntity;
@ -87,13 +157,36 @@ bool MovingEntitiesOperator::preRecursion(OctreeElement* element) {
if (keepSearching && shouldRecurseSubTree(element)) { if (keepSearching && shouldRecurseSubTree(element)) {
// check against each of our search entities // check against each of our search entities
int detailIndex = 0;
foreach(const EntityToMoveDetails& details, _entitiesToMove) { foreach(const EntityToMoveDetails& details, _entitiesToMove) {
if (_wantDebug) {
qDebug() << "MovingEntitiesOperator::preRecursion() details["<< detailIndex <<"]-----------------------------";
qDebug() << " entityTreeElement:" << entityTreeElement->getAACube();
qDebug() << " entityTreeElement->bestFitBounds(details.newCube):" << entityTreeElement->bestFitBounds(details.newCube);
qDebug() << " details.entity:" << details.entity->getEntityItemID();
qDebug() << " details.oldContainingElementCube:" << details.oldContainingElementCube;
qDebug() << " entityTreeElement:" << entityTreeElement;
qDebug() << " details.oldCube:" << details.oldCube;
qDebug() << " details.newCube:" << details.newCube;
qDebug() << " details.newCubeClamped:" << details.newCubeClamped;
qDebug() << " _lookingCount:" << _lookingCount;
qDebug() << " _foundOldCount:" << _foundOldCount;
qDebug() << "--------------------------------------------------------------------------";
}
// If this is one of the old elements we're looking for, then ask it to remove the old entity // If this is one of the old elements we're looking for, then ask it to remove the old entity
if (!details.oldFound && entityTreeElement == details.oldContainingElement) { if (!details.oldFound && entityTreeElement == details.oldContainingElement) {
entityTreeElement->removeEntityItem(details.entity); entityTreeElement->removeEntityItem(details.entity);
_foundOldCount++; _foundOldCount++;
//details.oldFound = true; // TODO: would be nice to add this optimization //details.oldFound = true; // TODO: would be nice to add this optimization
if (_wantDebug) {
qDebug() << "MovingEntitiesOperator::preRecursion() -----------------------------";
qDebug() << " FOUND OLD - REMOVING";
qDebug() << " entityTreeElement == details.oldContainingElement";
qDebug() << "--------------------------------------------------------------------------";
}
} }
// If this element is the best fit for the new bounds of this entity then add the entity to the element // If this element is the best fit for the new bounds of this entity then add the entity to the element
@ -103,7 +196,14 @@ bool MovingEntitiesOperator::preRecursion(OctreeElement* element) {
_tree->setContainingElement(entityItemID, entityTreeElement); _tree->setContainingElement(entityItemID, entityTreeElement);
_foundNewCount++; _foundNewCount++;
//details.newFound = true; // TODO: would be nice to add this optimization //details.newFound = true; // TODO: would be nice to add this optimization
if (_wantDebug) {
qDebug() << "MovingEntitiesOperator::preRecursion() -----------------------------";
qDebug() << " FOUND NEW - ADDING";
qDebug() << " entityTreeElement->bestFitBounds(details.newCube)";
qDebug() << "--------------------------------------------------------------------------";
}
} }
detailIndex++;
} }
// if we haven't found all of our search for entities, then keep looking // if we haven't found all of our search for entities, then keep looking
keepSearching = (_foundOldCount < _lookingCount) || (_foundNewCount < _lookingCount); keepSearching = (_foundOldCount < _lookingCount) || (_foundNewCount < _lookingCount);
@ -163,9 +263,9 @@ OctreeElement* MovingEntitiesOperator::possiblyCreateChildAt(OctreeElement* elem
foreach(const EntityToMoveDetails& details, _entitiesToMove) { foreach(const EntityToMoveDetails& details, _entitiesToMove) {
// if the scale of our desired cube is smaller than our children, then consider making a child // if the scale of our desired cube is smaller than our children, then consider making a child
if (details.newBox.getLargestDimension() <= childElementScale) { if (details.newCubeClamped.getLargestDimension() <= childElementScale) {
int indexOfChildContainingNewEntity = element->getMyChildContaining(details.newBox); int indexOfChildContainingNewEntity = element->getMyChildContaining(details.newCubeClamped);
// If the childIndex we were asked if we wanted to create contains this newCube, // If the childIndex we were asked if we wanted to create contains this newCube,
// then we will create this branch and continue. We can exit this loop immediately // then we will create this branch and continue. We can exit this loop immediately

View file

@ -17,7 +17,8 @@ public:
EntityItem* entity; EntityItem* entity;
AACube oldCube; AACube oldCube;
AACube newCube; AACube newCube;
AABox newBox; AABox oldCubeClamped;
AABox newCubeClamped;
EntityTreeElement* oldContainingElement; EntityTreeElement* oldContainingElement;
AACube oldContainingElementCube; AACube oldContainingElementCube;
bool oldFound; bool oldFound;
@ -50,6 +51,8 @@ private:
int _foundNewCount; int _foundNewCount;
int _lookingCount; int _lookingCount;
bool shouldRecurseSubTree(OctreeElement* element); bool shouldRecurseSubTree(OctreeElement* element);
bool _wantDebug;
}; };
#endif // hifi_MovingEntitiesOperator_h #endif // hifi_MovingEntitiesOperator_h

View file

@ -43,15 +43,7 @@ EntityItemProperties SphereEntityItem::getProperties() const {
bool SphereEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) { bool SphereEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) {
bool somethingChanged = EntityItem::setProperties(properties, forceCopy); // set the properties in our base class bool somethingChanged = EntityItem::setProperties(properties, forceCopy); // set the properties in our base class
if (properties.colorChanged() || forceCopy) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
setColor(properties.getColor());
somethingChanged = true;
}
if (properties.glowLevelChanged() || forceCopy) {
setGlowLevel(properties.getGlowLevel());
somethingChanged = true;
}
if (somethingChanged) { if (somethingChanged) {
bool wantDebug = false; bool wantDebug = false;

View file

@ -31,23 +31,27 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTree* tree,
_dontMove(false), // assume we'll be moving _dontMove(false), // assume we'll be moving
_changeTime(usecTimestampNow()), _changeTime(usecTimestampNow()),
_oldEntityCube(), _oldEntityCube(),
_newEntityCube() _newEntityCube(),
_wantDebug(false)
{ {
// caller must have verified existence of containingElement and oldEntity // caller must have verified existence of containingElement and oldEntity
assert(_containingElement && _existingEntity); assert(_containingElement && _existingEntity);
_oldEntityCube = _existingEntity->getAACube(); // Here we have a choice to make, do we want to "tight fit" the actual minimum for the
// entity into the the element, or do we want to use the entities "relaxed" bounds
// which can handle all potential rotations?
// the getMaximumAACube is the relaxed form.
_oldEntityCube = _existingEntity->getMaximumAACube();
_oldEntityBox = _oldEntityCube.clamp(0.0f, 1.0f); // clamp to domain bounds _oldEntityBox = _oldEntityCube.clamp(0.0f, 1.0f); // clamp to domain bounds
// If the new properties has position OR radius changes, but not both, we need to // If the new properties has position OR dimension changes, but not both, we need to
// get the old property value and set it in our properties in order for our bounds // get the old property value and set it in our properties in order for our bounds
// calculations to work. // calculations to work.
if (_properties.containsPositionChange() && !_properties.containsRadiusChange()) { if (_properties.containsPositionChange() && !_properties.containsDimensionsChange()) {
float oldRadiusInMeters = _existingEntity->getRadius() * (float)TREE_SCALE; glm::vec3 oldDimensionsInMeters = _existingEntity->getDimensions() * (float)TREE_SCALE;
_properties.setRadius(oldRadiusInMeters); _properties.setDimensions(oldDimensionsInMeters);
} }
if (!_properties.containsPositionChange() && _properties.containsDimensionsChange()) {
if (!_properties.containsPositionChange() && _properties.containsRadiusChange()) {
glm::vec3 oldPositionInMeters = _existingEntity->getPosition() * (float)TREE_SCALE; glm::vec3 oldPositionInMeters = _existingEntity->getPosition() * (float)TREE_SCALE;
_properties.setPosition(oldPositionInMeters); _properties.setPosition(oldPositionInMeters);
} }
@ -72,11 +76,24 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTree* tree,
_newEntityCube = _oldEntityCube; _newEntityCube = _oldEntityCube;
_dontMove = true; _dontMove = true;
} else { } else {
_newEntityCube = _properties.getAACubeTreeUnits(); _newEntityCube = _properties.getMaximumAACubeInTreeUnits();
_removeOld = true; // our properties are going to move us, so remember this for later processing _removeOld = true; // our properties are going to move us, so remember this for later processing
} }
_newEntityBox = _newEntityCube.clamp(0.0f, 1.0f); // clamp to domain bounds _newEntityBox = _newEntityCube.clamp(0.0f, 1.0f); // clamp to domain bounds
if (_wantDebug) {
qDebug() << "UpdateEntityOperator::UpdateEntityOperator() -----------------------------";
qDebug() << " _entityItemID:" << _entityItemID;
qDebug() << " _containingElementCube:" << _containingElementCube;
qDebug() << " _oldEntityCube:" << _oldEntityCube;
qDebug() << " _oldEntityBox:" << _oldEntityBox;
qDebug() << " _newEntityCube:" << _newEntityCube;
qDebug() << " _newEntityBox:" << _newEntityBox;
qDebug() << "--------------------------------------------------------------------------";
}
} }

View file

@ -42,6 +42,8 @@ private:
bool subTreeContainsOldEntity(OctreeElement* element); bool subTreeContainsOldEntity(OctreeElement* element);
bool subTreeContainsNewEntity(OctreeElement* element); bool subTreeContainsNewEntity(OctreeElement* element);
bool _wantDebug;
}; };
#endif // hifi_UpdateEntityOperator_h #endif // hifi_UpdateEntityOperator_h

View file

@ -75,8 +75,11 @@ PacketVersion versionForPacketType(PacketType type) {
return 1; return 1;
case PacketTypeParticleErase: case PacketTypeParticleErase:
return 1; return 1;
case PacketTypeEntityAddOrEdit:
case PacketTypeEntityData: case PacketTypeEntityData:
return VERSION_ENTITIES_SUPPORT_SPLIT_MTU; return VERSION_ENTITIES_SUPPORT_DIMENSIONS;
case PacketTypeEntityErase: case PacketTypeEntityErase:
return 2; return 2;
case PacketTypeAudioStreamStats: case PacketTypeAudioStreamStats:

View file

@ -117,6 +117,7 @@ const PacketVersion VERSION_ENTITIES_HAVE_ANIMATION = 1;
const PacketVersion VERSION_ROOT_ELEMENT_HAS_DATA = 2; const PacketVersion VERSION_ROOT_ELEMENT_HAS_DATA = 2;
const PacketVersion VERSION_ENTITIES_SUPPORT_SPLIT_MTU = 3; const PacketVersion VERSION_ENTITIES_SUPPORT_SPLIT_MTU = 3;
const PacketVersion VERSION_ENTITIES_HAS_FILE_BREAKS = VERSION_ENTITIES_SUPPORT_SPLIT_MTU; const PacketVersion VERSION_ENTITIES_HAS_FILE_BREAKS = VERSION_ENTITIES_SUPPORT_SPLIT_MTU;
const PacketVersion VERSION_ENTITIES_SUPPORT_DIMENSIONS = 4;
const PacketVersion VERSION_VOXELS_HAS_FILE_BREAKS = 1; const PacketVersion VERSION_VOXELS_HAS_FILE_BREAKS = 1;
#endif // hifi_PacketHeaders_h #endif // hifi_PacketHeaders_h

View file

@ -392,6 +392,41 @@ ViewFrustum::location ViewFrustum::cubeInFrustum(const AACube& cube) const {
return regularResult; return regularResult;
} }
ViewFrustum::location ViewFrustum::boxInFrustum(const AABox& box) const {
ViewFrustum::location regularResult = INSIDE;
ViewFrustum::location keyholeResult = OUTSIDE;
// If we have a keyholeRadius, check that first, since it's cheaper
if (_keyholeRadius >= 0.0f) {
keyholeResult = boxInKeyhole(box);
}
if (keyholeResult == INSIDE) {
return keyholeResult;
}
// TODO: These calculations are expensive, taking up 80% of our time in this function.
// This appears to be expensive because we have to test the distance to each plane.
// One suggested optimization is to first check against the approximated cone. We might
// also be able to test against the cone to the bounding sphere of the box.
for(int i=0; i < 6; i++) {
const glm::vec3& normal = _planes[i].getNormal();
const glm::vec3& boxVertexP = box.getVertexP(normal);
float planeToBoxVertexPDistance = _planes[i].distance(boxVertexP);
const glm::vec3& boxVertexN = box.getVertexN(normal);
float planeToBoxVertexNDistance = _planes[i].distance(boxVertexN);
if (planeToBoxVertexPDistance < 0) {
// This is outside the regular frustum, so just return the value from checking the keyhole
return keyholeResult;
} else if (planeToBoxVertexNDistance < 0) {
regularResult = INTERSECT;
}
}
return regularResult;
}
bool testMatches(glm::quat lhs, glm::quat rhs, float epsilon = EPSILON) { bool testMatches(glm::quat lhs, glm::quat rhs, float epsilon = EPSILON) {
return (fabs(lhs.x - rhs.x) <= epsilon && fabs(lhs.y - rhs.y) <= epsilon && fabs(lhs.z - rhs.z) <= epsilon return (fabs(lhs.x - rhs.x) <= epsilon && fabs(lhs.y - rhs.y) <= epsilon && fabs(lhs.z - rhs.z) <= epsilon
&& fabs(lhs.w - rhs.w) <= epsilon); && fabs(lhs.w - rhs.w) <= epsilon);

View file

@ -36,6 +36,10 @@ public:
bool isEmpty() const { return minimum == maximum; } bool isEmpty() const { return minimum == maximum; }
bool isValid() const { return !((minimum == glm::vec3(FLT_MAX)) && (maximum == glm::vec3(-FLT_MAX))); } bool isValid() const { return !((minimum == glm::vec3(FLT_MAX)) && (maximum == glm::vec3(-FLT_MAX))); }
/// \param vec3 for delta amount to shift the extents by
/// \return true if point is within current limits
void shiftBy(const glm::vec3& delta) { minimum += delta; maximum += delta; }
/// rotate the extents around orign by rotation /// rotate the extents around orign by rotation
void rotate(const glm::quat& rotation); void rotate(const glm::quat& rotation);

View file

@ -45,7 +45,7 @@ void EntityTests::entityTreeTests(bool verbose) {
entityID.isKnownID = false; // this is a temporary workaround to allow local tree entities to be added with known IDs entityID.isKnownID = false; // this is a temporary workaround to allow local tree entities to be added with known IDs
EntityItemProperties properties; EntityItemProperties properties;
float oneMeter = 1.0f; float oneMeter = 1.0f;
float halfMeter = oneMeter / 2.0f; //float halfMeter = oneMeter / 2.0f;
float halfOfDomain = TREE_SCALE * 0.5f; float halfOfDomain = TREE_SCALE * 0.5f;
glm::vec3 positionNearOriginInMeters(oneMeter, oneMeter, oneMeter); // when using properties, these are in meter not tree units glm::vec3 positionNearOriginInMeters(oneMeter, oneMeter, oneMeter); // when using properties, these are in meter not tree units
glm::vec3 positionAtCenterInMeters(halfOfDomain, halfOfDomain, halfOfDomain); glm::vec3 positionAtCenterInMeters(halfOfDomain, halfOfDomain, halfOfDomain);
@ -60,7 +60,8 @@ void EntityTests::entityTreeTests(bool verbose) {
} }
properties.setPosition(positionAtCenterInMeters); properties.setPosition(positionAtCenterInMeters);
properties.setRadius(halfMeter); // TODO: Fix these unit tests.
//properties.setRadius(halfMeter);
//properties.setModelURL("https://s3-us-west-1.amazonaws.com/highfidelity-public/ozan/theater.fbx"); //properties.setModelURL("https://s3-us-west-1.amazonaws.com/highfidelity-public/ozan/theater.fbx");
tree.addEntity(entityID, properties); tree.addEntity(entityID, properties);
@ -265,8 +266,10 @@ void EntityTests::entityTreeTests(bool verbose) {
glm::vec3 randomPositionInTreeUnits = randomPositionInMeters / (float)TREE_SCALE; glm::vec3 randomPositionInTreeUnits = randomPositionInMeters / (float)TREE_SCALE;
properties.setPosition(randomPositionInMeters); properties.setPosition(randomPositionInMeters);
properties.setRadius(halfMeter);
//properties.setModelURL("https://s3-us-west-1.amazonaws.com/highfidelity-public/ozan/theater.fbx"); // TODO: fix these unit tests
//properties.setRadius(halfMeter);
//properties.setModelURL("https://s3-us-west-1.amazonaws.com/highfidelity-public/ozan/theater.fbx");
if (extraVerbose) { if (extraVerbose) {
qDebug() << "iteration:" << i qDebug() << "iteration:" << i