Merge pull request #3402 from ZappoMan/stretchableEntities

New Entities Features
This commit is contained in:
Philip Rosedale 2014-09-12 13:57:45 -07:00
commit 3fbe183196
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
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);
// 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());
datagramProcessor->moveToThread(_datagramProcessingThread);

View file

@ -37,6 +37,7 @@ function vInterpolate(a, b, fraction) {
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 range = 1.0; // Over what distance in meters do you want the flock to fly around
var frame = 0;
@ -78,7 +79,13 @@ function addButterfly() {
var color = { red: 100, green: 100, blue: 100 };
var size = 0;
var minSize = 0.06;
var randomSize = 0.2;
var maxSize = minSize + randomSize;
size = 0.06 + Math.random() * 0.2;
var dimensions = Vec3.multiply(NATURAL_SIZE_OF_BUTTERFLY, (size / maxSize));
flockPosition = Vec3.sum(MyAvatar.position,Vec3.sum(
Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_ABOVE_ME),
@ -91,7 +98,7 @@ function addButterfly() {
velocity: { x: 0, y: 0.0, z: 0 },
gravity: { x: 0, y: 1.0, z: 0 },
damping: 0.1,
radius : size,
dimensions: dimensions,
color: color,
rotation: rotation,
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 (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;
}
@ -214,7 +222,7 @@ function updateButterflies(deltaTime) {
}
// 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);
// Change position relative to previous offset.
butterflies[i].previousFlapOffset = offset;

View file

@ -40,7 +40,7 @@ var LEFT = 0;
var RIGHT = 1;
var SPAWN_DISTANCE = 1;
var DEFAULT_RADIUS = 0.10;
var DEFAULT_DIMENSION = 0.20;
var modelURLs = [
"http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX",
@ -1220,7 +1220,7 @@ var toolBar = (function () {
Entities.addEntity({
type: "Model",
position: position,
radius: DEFAULT_RADIUS,
dimensions: { x: DEFAULT_DIMENSION, y: DEFAULT_DIMENSION, z: DEFAULT_DIMENSION },
modelURL: url
});
print("Model added: " + url);
@ -1311,8 +1311,9 @@ var toolBar = (function () {
Entities.addEntity({
type: "Box",
position: position,
radius: DEFAULT_RADIUS,
dimensions: { x: DEFAULT_DIMENSION, y: DEFAULT_DIMENSION, z: DEFAULT_DIMENSION },
color: { red: 255, green: 0, blue: 0 }
});
} else {
print("Can't create box: Box would be out of bounds.");
@ -1327,7 +1328,7 @@ var toolBar = (function () {
Entities.addEntity({
type: "Sphere",
position: position,
radius: DEFAULT_RADIUS,
dimensions: { x: DEFAULT_DIMENSION, y: DEFAULT_DIMENSION, z: DEFAULT_DIMENSION },
color: { red: 255, green: 0, blue: 0 }
});
} else {
@ -1804,7 +1805,7 @@ function controller(wichSide) {
this.modelURL = "";
this.oldModelRotation;
this.oldModelPosition;
this.oldModelRadius;
this.oldModelHalfDiagonal;
this.positionAtGrab;
this.rotationAtGrab;
@ -1864,7 +1865,7 @@ function controller(wichSide) {
this.oldModelPosition = properties.position;
this.oldModelRotation = properties.rotation;
this.oldModelRadius = properties.radius;
this.oldModelHalfDiagonal = Vec3.length(properties.dimensions) / 2.0;
this.positionAtGrab = this.palmPosition;
this.rotationAtGrab = this.rotation;
@ -1873,7 +1874,7 @@ function controller(wichSide) {
this.jointsIntersectingFromStart = [];
for (var i = 0; i < jointList.length; i++) {
var distance = Vec3.distance(MyAvatar.getJointPosition(jointList[i]), this.oldModelPosition);
if (distance < this.oldModelRadius) {
if (distance < this.oldModelHalfDiagonal) {
this.jointsIntersectingFromStart.push(i);
}
}
@ -1897,10 +1898,10 @@ function controller(wichSide) {
if (closestJointIndex != -1) {
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 ||
(leftController.grabbing && rightController.grabbing &&
@ -1916,7 +1917,7 @@ function controller(wichSide) {
var attachmentRotation = Quat.multiply(Quat.inverse(jointRotation), this.oldModelRotation);
MyAvatar.attach(this.modelURL, jointList[closestJointIndex],
attachmentOffset, attachmentRotation, 2.0 * this.oldModelRadius,
attachmentOffset, attachmentRotation, 2.0 * this.oldModelHalfDiagonal,
true, false);
Entities.deleteEntity(this.entityID);
}
@ -1970,11 +1971,12 @@ function controller(wichSide) {
var z = Vec3.dot(Vec3.subtract(P, A), this.right);
var X = Vec3.sum(A, Vec3.multiply(B, 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 (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 };
}
@ -2021,7 +2023,10 @@ function controller(wichSide) {
origin: this.palmPosition,
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) {
this.glowedIntersectingModel = intersection.entityID;
Entities.editEntity(this.glowedIntersectingModel, { glowLevel: 0.25 });
@ -2099,7 +2104,7 @@ function controller(wichSide) {
var indicesToRemove = [];
for (var i = 0; i < this.jointsIntersectingFromStart.length; ++i) {
var distance = Vec3.distance(MyAvatar.getJointPosition(this.jointsIntersectingFromStart[i]), this.oldModelPosition);
if (distance >= this.oldModelRadius) {
if (distance >= this.oldModelHalfDiagonal) {
indicesToRemove.push(this.jointsIntersectingFromStart[i]);
}
@ -2192,7 +2197,12 @@ function controller(wichSide) {
Vec3.multiplyQbyV(MyAvatar.getJointCombinedRotation(attachments[attachmentIndex].jointName), attachments[attachmentIndex].translation)),
rotation: Quat.multiply(MyAvatar.getJointCombinedRotation(attachments[attachmentIndex].jointName),
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
};
@ -2310,15 +2320,21 @@ function moveEntities() {
Entities.editEntity(leftController.entityID, {
position: newPosition,
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.oldModelRotation = rotation;
leftController.oldModelRadius *= ratio;
leftController.oldModelHalfDiagonal *= ratio;
rightController.oldModelPosition = newPosition;
rightController.oldModelRotation = rotation;
rightController.oldModelRadius *= ratio;
rightController.oldModelHalfDiagonal *= ratio;
return;
}
leftController.moveEntity();
@ -2379,7 +2395,7 @@ function Tooltip() {
this.x = 285;
this.y = 115;
this.width = 500;
this.height = 145;
this.height = 180; // 145;
this.margin = 5;
this.decimals = 3;
@ -2407,7 +2423,14 @@ function Tooltip() {
text += "Pitch: " + angles.x.toFixed(this.decimals) + "\n"
text += "Yaw: " + angles.y.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"
if (properties.type == "Model") {
text += "Model URL: " + properties.modelURL + "\n"
@ -2426,6 +2449,7 @@ function Tooltip() {
text += "Lifetime: " + properties.lifetime + "\n"
}
text += "Age: " + properties.ageAsText + "\n"
text += "Script: " + properties.script + "\n"
Overlays.editOverlay(this.textOverlay, { text: text });
@ -2477,7 +2501,9 @@ function mousePressEvent(event) {
if (isLocked(properties)) {
print("Model locked " + properties.id);
} 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
// /| A - Palm
// / | 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.sum(A, Vec3.multiply(B, 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 (angularSize < MAX_ANGULAR_SIZE) {
entitySelected = true;
@ -2506,13 +2533,13 @@ function mousePressEvent(event) {
orientation = MyAvatar.orientation;
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
} 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) {
selectedEntityProperties.oldRadius = selectedEntityProperties.radius;
selectedEntityProperties.oldDimensions = selectedEntityProperties.dimensions;
selectedEntityProperties.oldPosition = {
x: selectedEntityProperties.position.x,
y: selectedEntityProperties.position.y,
@ -2550,8 +2577,12 @@ function mouseMoveEvent(event) {
glowedEntityID.id = -1;
glowedEntityID.isKnownID = false;
}
var halfDiagonal = Vec3.length(entityIntersection.properties.dimensions) / 2.0;
var angularSize = 2 * Math.atan(entityIntersection.properties.radius / Vec3.distance(Camera.getPosition(), entityIntersection.properties.position)) * 180 / 3.14;
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) {
Entities.editEntity(entityIntersection.entityID, { glowLevel: 0.25 });
glowedEntityID = entityIntersection.entityID;
@ -2573,7 +2604,7 @@ function mouseMoveEvent(event) {
}
pickRay = Camera.computePickRay(event.x, event.y);
if (wasShifted != event.isShifted || modifier != oldModifier) {
selectedEntityProperties.oldRadius = selectedEntityProperties.radius;
selectedEntityProperties.oldDimensions = selectedEntityProperties.dimensions;
selectedEntityProperties.oldPosition = {
x: selectedEntityProperties.position.x,
@ -2603,9 +2634,12 @@ function mouseMoveEvent(event) {
return;
case 1:
// Let's Scale
selectedEntityProperties.radius = (selectedEntityProperties.oldRadius *
selectedEntityProperties.dimensions = Vec3.multiply(selectedEntityProperties.dimensions,
(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.");
return;
}
@ -2753,6 +2787,13 @@ Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
setupModelMenus();
var propertiesForEditedEntity;
var editEntityFormArray;
var editModelID = -1;
var dimensionX;
var dimensionY;
var dimensionZ;
function handeMenuEvent(menuItem) {
print("menuItemEvent() in JS... menuItem=" + menuItem);
if (menuItem == "Delete") {
@ -2781,7 +2822,7 @@ function handeMenuEvent(menuItem) {
print(" Delete Entity.... not holding...");
}
} else if (menuItem == "Edit Properties...") {
var editModelID = -1;
editModelID = -1;
if (leftController.grabbing) {
print(" Edit Properties.... leftController.entityID="+ leftController.entityID);
editModelID = leftController.entityID;
@ -2797,79 +2838,111 @@ function handeMenuEvent(menuItem) {
if (editModelID != -1) {
print(" Edit Properties.... about to edit properties...");
var properties = Entities.getEntityProperties(editModelID);
propertiesForEditedEntity = Entities.getEntityProperties(editModelID);
var properties = propertiesForEditedEntity;
var array = new Array();
var index = 0;
var decimals = 3;
if (properties.type == "Model") {
array.push({ label: "Model URL:", value: properties.modelURL });
index++;
array.push({ label: "Animation URL:", value: properties.animationURL });
index++;
array.push({ label: "Animation is playing:", value: properties.animationIsPlaying });
index++;
array.push({ label: "Animation FPS:", value: properties.animationFPS });
index++;
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) });
index++;
array.push({ label: "Y:", value: properties.position.y.toFixed(decimals) });
index++;
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);
array.push({ label: "Pitch:", value: angles.x.toFixed(decimals) });
index++;
array.push({ label: "Yaw:", value: angles.y.toFixed(decimals) });
index++;
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: "Velocity Y:", value: properties.velocity.y.toFixed(decimals) });
array.push({ label: "Velocity Z:", value: properties.velocity.z.toFixed(decimals) });
array.push({ label: "Damping:", value: properties.damping.toFixed(decimals) });
array.push({ label: "Dimensions:", type: "header" });
index++;
array.push({ label: "Width:", value: properties.dimensions.x.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) });
index++;
array.push({ label: "Gravity Y:", value: properties.gravity.y.toFixed(decimals) });
index++;
array.push({ label: "Gravity Z:", value: properties.gravity.z.toFixed(decimals) });
index++;
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") {
array.push({ label: "Color:", type: "header" });
index++;
array.push({ label: "Red:", value: properties.color.red });
index++;
array.push({ label: "Green:", value: properties.color.green });
index++;
array.push({ label: "Blue:", value: properties.color.blue });
index++;
}
array.push({ button: "Cancel" });
if (Window.form("Edit Properties", array)) {
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;
}
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;
index++;
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;
editEntityFormArray = array;
Window.nonBlockingForm("Edit Properties", array);
}
} else if (menuItem == "Paste Models") {
modelImporter.paste();
@ -2930,3 +3003,76 @@ Controller.keyReleaseEvent.connect(function (event) {
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);
if (item != NULL) {
_refScale = item->getRadius();
_refScale = item->getLargestDimension();
_refRotation = item->getRotation();
_refPosition = item->getPosition() * (float)TREE_SCALE;
update();
@ -52,7 +52,7 @@ ModelReferential::ModelReferential(const QUuid& entityID, EntityTree* tree, Avat
return;
}
_refScale = item->getRadius();
_refScale = item->getLargestDimension();
_refRotation = item->getRotation();
_refPosition = item->getPosition() * (float)TREE_SCALE;
@ -69,8 +69,8 @@ void ModelReferential::update() {
}
bool somethingChanged = false;
if (item->getRadius() != _refScale) {
_refScale = item->getRadius();
if (item->getLargestDimension() != _refScale) {
_refScale = item->getLargestDimension();
_avatar->setTargetScale(_refScale * _scale, true);
somethingChanged = true;
}
@ -109,7 +109,7 @@ JointReferential::JointReferential(Referential* referential, EntityTree* tree, A
const EntityItem* item = _tree->findEntityByID(_entityID);
const Model* model = getModel(item);
if (!isValid() || model == NULL || _jointIndex >= (uint32_t)(model->getJointStateCount())) {
_refScale = item->getRadius();
_refScale = item->getLargestDimension();
model->getJointRotationInWorldFrame(_jointIndex, _refRotation);
model->getJointPositionInWorldFrame(_jointIndex, _refPosition);
}
@ -129,7 +129,7 @@ JointReferential::JointReferential(uint32_t jointIndex, const QUuid& entityID, E
return;
}
_refScale = item->getRadius();
_refScale = item->getLargestDimension();
model->getJointRotationInWorldFrame(_jointIndex, _refRotation);
model->getJointPositionInWorldFrame(_jointIndex, _refPosition);
@ -147,8 +147,8 @@ void JointReferential::update() {
}
bool somethingChanged = false;
if (item->getRadius() != _refScale) {
_refScale = item->getRadius();
if (item->getLargestDimension() != _refScale) {
_refScale = item->getLargestDimension();
_avatar->setTargetScale(_refScale * _scale, true);
somethingChanged = true;
}

View file

@ -192,7 +192,71 @@ bool EntityTreeRenderer::shouldRenderEntity(float largestDimension, float distan
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) {
bool wantDebug = false;
args->_elementsTouched++;
// actually render it here...
// 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++) {
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
// when they are outside of the view frustum...
float distance = distanceToCamera(entityCube.calcCenter(), *args->_viewFrustum);
if (shouldRenderEntity(entityCube.getLargestDimension(), distance) &&
args->_viewFrustum->cubeInFrustum(entityCube) != ViewFrustum::OUTSIDE) {
if (entityItem->isVisible()) {
// render entityItem
AABox entityBox = entityItem->getAABox();
Glower* glower = NULL;
if (entityItem->getGlowLevel() > 0.0f) {
glower = new Glower(entityItem->getGlowLevel());
entityBox.scale(TREE_SCALE);
// 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) {
delete glower;
if (shouldRenderEntity(entityBox.getLargestDimension(), distance) &&
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;
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) {
PerformanceTimer perfTimer("RenderableBoxEntityItem::render");
assert(getType() == EntityTypes::Box);
glm::vec3 position = getPosition() * (float)TREE_SCALE;
float size = getSize() * (float)TREE_SCALE;
glm::vec3 position = getPositionInMeters();
glm::vec3 center = getCenter() * (float)TREE_SCALE;
glm::vec3 dimensions = getDimensions() * (float)TREE_SCALE;
glm::vec3 halfDimensions = dimensions / 2.0f;
glm::quat rotation = getRotation();
const bool useGlutCube = true;
if (useGlutCube) {
@ -45,10 +46,14 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
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);
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();
} else {
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,v5,v6,v1 (top)
@ -79,20 +84,19 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
glNormalPointer(GL_FLOAT, 0, normals);
glVertexPointer(3, GL_FLOAT, 0, vertices);
//glEnable(GL_BLEND);
glColor3ub(getColor()[RED_INDEX], getColor()[GREEN_INDEX], getColor()[BLUE_INDEX]);
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);
// we need to do half the size because the geometry in the VBOs are from -1,-1,-1 to 1,1,1
float halfSize = size/2.0f;
glScalef(halfSize, halfSize, halfSize);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, indices);
glPushMatrix();
glm::vec3 positionToCenter = center - position;
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(halfDimensions.x, halfDimensions.y, halfDimensions.z);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, indices);
glPopMatrix();
glPopMatrix();
glDisableClientState(GL_VERTEX_ARRAY); // disable vertex arrays

View file

@ -65,8 +65,8 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
bool drawAsModel = hasModel();
glm::vec3 position = getPosition() * (float)TREE_SCALE;
float radius = getRadius() * (float)TREE_SCALE;
float size = getSize() * (float)TREE_SCALE;
glm::vec3 dimensions = getDimensions() * (float)TREE_SCALE;
if (drawAsModel) {
glPushMatrix();
@ -98,8 +98,8 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
glm::quat rotation = getRotation();
if (needsSimulation() && _model->isActive()) {
_model->setScaleToFit(true, radius * 2.0f);
_model->setSnapModelToCenter(true);
_model->setScaleToFit(true, dimensions);
_model->setSnapModelToRegistrationPoint(true, getRegistrationPoint());
_model->setRotation(rotation);
_model->setTranslation(position);
@ -128,52 +128,6 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
glutWireCube(size);
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 {
// if we couldn't get a model, then just draw a cube
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) {
PerformanceTimer perfTimer("RenderableSphereEntityItem::render");
assert(getType() == EntityTypes::Sphere);
glm::vec3 position = getPosition() * (float)TREE_SCALE;
float radius = getRadius() * (float)TREE_SCALE;
glm::vec3 position = getPositionInMeters();
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]);
glPushMatrix();
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();
};

View file

@ -54,10 +54,10 @@ Model::Model(QObject* parent) :
QObject(parent),
_scale(1.0f, 1.0f, 1.0f),
_scaleToFit(false),
_scaleToFitLargestDimension(0.0f),
_scaleToFitDimensions(0.0f),
_scaledToFit(false),
_snapModelToCenter(false),
_snappedToCenter(false),
_snapModelToRegistrationPoint(false),
_snappedToRegistrationPoint(false),
_showTrueJointTransforms(true),
_lodDistance(0.0f),
_pupilDilation(0.0f),
@ -157,8 +157,8 @@ void Model::setOffset(const glm::vec3& offset) {
_offset = offset;
// if someone manually sets our offset, then we are no longer snapped to center
_snapModelToCenter = false;
_snappedToCenter = false;
_snapModelToRegistrationPoint = false;
_snappedToRegistrationPoint = false;
}
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));
}
void Model::setScaleToFit(bool scaleToFit, float largestDimension) {
if (_scaleToFit != scaleToFit || _scaleToFitLargestDimension != largestDimension) {
void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions) {
if (_scaleToFit != scaleToFit || _scaleToFitDimensions != dimensions) {
_scaleToFit = scaleToFit;
_scaleToFitLargestDimension = largestDimension;
_scaleToFitDimensions = dimensions;
_scaledToFit = false; // force rescaling
}
}
void Model::setScaleToFit(bool scaleToFit, float largestDimension) {
setScaleToFit(scaleToFit, glm::vec3(largestDimension, largestDimension, largestDimension));
}
void Model::scaleToFit() {
Extents modelMeshExtents = getUnscaledMeshExtents();
// 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...
float maxDimension = glm::distance(modelMeshExtents.maximum, modelMeshExtents.minimum);
float maxScale = _scaleToFitLargestDimension / maxDimension;
glm::vec3 scale(maxScale, maxScale, maxScale);
setScaleInternal(scale);
glm::vec3 meshDimensions = modelMeshExtents.maximum - modelMeshExtents.minimum;
glm::vec3 rescaleDimensions = _scaleToFitDimensions / meshDimensions;
setScaleInternal(rescaleDimensions);
_scaledToFit = true;
}
void Model::setSnapModelToCenter(bool snapModelToCenter) {
if (_snapModelToCenter != snapModelToCenter) {
_snapModelToCenter = snapModelToCenter;
_snappedToCenter = false; // force re-centering
void Model::setSnapModelToRegistrationPoint(bool snapModelToRegistrationPoint, const glm::vec3& registrationPoint) {
glm::vec3 clampedRegistrationPoint = glm::clamp(registrationPoint, 0.0f, 1.0f);
if (_snapModelToRegistrationPoint != snapModelToRegistrationPoint || _registrationPoint != clampedRegistrationPoint) {
_snapModelToRegistrationPoint = snapModelToRegistrationPoint;
_registrationPoint = clampedRegistrationPoint;
_snappedToRegistrationPoint = false; // force re-centering
}
}
void Model::snapToCenter() {
void Model::snapToRegistrationPoint() {
Extents modelMeshExtents = getUnscaledMeshExtents();
glm::vec3 halfDimensions = (modelMeshExtents.maximum - modelMeshExtents.minimum) * 0.5f;
glm::vec3 offset = -modelMeshExtents.minimum - halfDimensions;
glm::vec3 dimensions = (modelMeshExtents.maximum - modelMeshExtents.minimum);
glm::vec3 offset = -modelMeshExtents.minimum - (dimensions * _registrationPoint);
_offset = offset;
_snappedToCenter = true;
_snappedToRegistrationPoint = true;
}
void Model::simulate(float deltaTime, bool fullUpdate) {
fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit) || (_snapModelToCenter && !_snappedToCenter);
fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit)
|| (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint);
if (isActive() && fullUpdate) {
// check for scale to fit
if (_scaleToFit && !_scaledToFit) {
scaleToFit();
}
if (_snapModelToCenter && !_snappedToCenter) {
snapToCenter();
if (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint) {
snapToRegistrationPoint();
}
simulateInternal(deltaTime);
}

View file

@ -52,10 +52,18 @@ public:
void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f);
bool getScaleToFit() const { return _scaleToFit; } /// is scale to fit enabled
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);
bool getSnapModelToCenter() { return _snapModelToCenter; }
void setSnapModelToCenter(bool 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);
const glm::vec3& getScale() const { return _scale; }
@ -181,11 +189,13 @@ protected:
glm::vec3 _offset;
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 _snapModelToCenter; /// is the model's offset automatically adjusted to center around 0,0,0 in model space
bool _snappedToCenter; /// are we currently snapped to center
bool _snapModelToRegistrationPoint; /// is the model's offset automatically adjusted to a registration point in model space
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;
QVector<LocalLight> _localLights;
@ -206,7 +216,7 @@ protected:
void setScaleInternal(const glm::vec3& scale);
void scaleToFit();
void snapToCenter();
void snapToRegistrationPoint();
void simulateInternal(float deltaTime);

View file

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

View file

@ -20,13 +20,15 @@
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) :
EntityItem(entityItemID, properties)
{
EntityItem(entityItemID)
{
_type = EntityTypes::Box;
_created = properties.getCreated();
setProperties(properties, true);
}
@ -44,18 +46,9 @@ EntityItemProperties BoxEntityItem::getProperties() const {
bool BoxEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) {
bool somethingChanged = false;
somethingChanged = EntityItem::setProperties(properties, forceCopy); // set the properties in our base class
if (properties._colorChanged || forceCopy) {
setColor(properties._color);
somethingChanged = true;
}
if (properties._glowLevelChanged || forceCopy) {
setGlowLevel(properties._glowLevel);
somethingChanged = true;
}
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
if (somethingChanged) {
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_MASS = 1.0f;
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 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::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::DEFAULT_GRAVITY = EntityItem::NO_GRAVITY;
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) {
_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"
_position = glm::vec3(0,0,0);
_radius = 0;
_rotation = ENTITY_DEFAULT_ROTATION;
_rotation = DEFAULT_ROTATION;
_dimensions = DEFAULT_DIMENSIONS;
_glowLevel = DEFAULT_GLOW_LEVEL;
_mass = DEFAULT_MASS;
@ -59,6 +66,20 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) {
_gravity = DEFAULT_GRAVITY;
_damping = DEFAULT_DAMPING;
_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) {
@ -76,7 +97,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
EntityPropertyFlags requestedProperties;
requestedProperties += PROP_POSITION;
requestedProperties += PROP_RADIUS;
requestedProperties += PROP_DIMENSIONS; // NOTE: PROP_RADIUS obsolete
requestedProperties += PROP_ROTATION;
requestedProperties += PROP_MASS;
requestedProperties += PROP_VELOCITY;
@ -84,6 +105,10 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
requestedProperties += PROP_DAMPING;
requestedProperties += PROP_LIFETIME;
requestedProperties += PROP_SCRIPT;
requestedProperties += PROP_REGISTRATION_POINT;
requestedProperties += PROP_ANGULAR_VELOCITY;
requestedProperties += PROP_ANGULAR_DAMPING;
requestedProperties += PROP_VISIBLE;
return requestedProperties;
}
@ -178,10 +203,14 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
// These items would go here once supported....
// PROP_PAGED_PROPERTY,
// PROP_CUSTOM_PROPERTIES_INCLUDED,
// PROP_VISIBLE,
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_MASS, appendValue, getMass());
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_LIFETIME, appendValue, getLifetime());
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,
requestedProperties,
@ -408,9 +441,36 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
EntityPropertyFlags propertyFlags = encodedPropertyFlags;
dataAt += propertyFlags.getEncodedLength();
bytesRead += propertyFlags.getEncodedLength();
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(PROP_MASS, float, _mass);
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_LIFETIME, float, _lifetime);
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);
@ -429,7 +498,7 @@ void EntityItem::debugDump() const {
qDebug() << "EntityItem id:" << getEntityItemID();
qDebug(" edited ago:%f", getEditedAgo());
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
@ -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 {
return _position.y <= _radius
&& _velocity.y >= -EPSILON && _velocity.y <= EPSILON
glm::vec3 downwardVelocity = glm::vec3(0.0f, _velocity.y, 0.0f);
return _position.y <= getDistanceToBottomOfEntity()
&& (glm::length(downwardVelocity) <= EPSILON_VELOCITY_LENGTH)
&& _gravity.y < 0.0f;
}
@ -466,9 +540,39 @@ void EntityItem::update(const quint64& updateTime) {
if (wantDebug) {
qDebug() << "********** EntityItem::update()";
qDebug() << " entity ID=" << getEntityItemID();
qDebug() << " updateTime=" << updateTime;
qDebug() << " _lastUpdated=" << _lastUpdated;
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;
@ -477,22 +581,54 @@ void EntityItem::update(const quint64& updateTime) {
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()) {
glm::vec3 position = getPosition();
glm::vec3 velocity = getVelocity();
glm::vec3 newPosition = position + (velocity * timeElapsed);
if (wantDebug) {
qDebug() << "EntityItem::update()....";
qDebug() << " timeElapsed:" << timeElapsed;
qDebug() << " old AACube:" << getAACube();
qDebug() << " old AACube:" << getMaximumAACube();
qDebug() << " old position:" << position;
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...
if (position.y <= _radius) {
// handle bounces off the ground... We bounce at the distance to the bottom of our entity
if (position.y <= getDistanceToBottomOfEntity()) {
velocity = velocity * glm::vec3(1,-1,1);
// if we've slowed considerably, then just stop moving
@ -500,7 +636,7 @@ void EntityItem::update(const quint64& updateTime) {
velocity = NO_VELOCITY;
}
position.y = _radius;
position.y = getDistanceToBottomOfEntity();
}
// handle gravity....
@ -512,10 +648,10 @@ void EntityItem::update(const quint64& updateTime) {
// "ground" plane of the domain, but for now it
if (hasGravity() && isRestingOnSurface()) {
velocity.y = 0.0f;
position.y = _radius;
position.y = getDistanceToBottomOfEntity();
}
// handle damping
// handle damping for velocity
glm::vec3 dampingResistance = velocity * getDamping();
if (wantDebug) {
qDebug() << " getDamping():" << getDamping();
@ -526,21 +662,23 @@ void EntityItem::update(const quint64& updateTime) {
if (wantDebug) {
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...
if (glm::length(velocity) <= EPSILON_VELOCITY_LENGTH) {
velocity = NO_VELOCITY;
}
setPosition(position);
setVelocity(velocity);
if (wantDebug) {
qDebug() << " new position:" << position;
qDebug() << " new velocity:" << velocity;
}
setPosition(position);
setVelocity(velocity);
if (wantDebug) {
qDebug() << " new AACube:" << getAACube();
qDebug() << " new AACube:" << getMaximumAACube();
qDebug() << " old getAABox:" << getAABox();
}
}
}
@ -549,6 +687,9 @@ EntityItem::SimulationState EntityItem::getSimulationState() const {
if (hasVelocity() || (hasGravity() && !isRestingOnSurface())) {
return EntityItem::Moving;
}
if (hasAngularVelocity()) {
return EntityItem::Changing;
}
if (isMortal()) {
return EntityItem::Mortal;
}
@ -566,33 +707,26 @@ void EntityItem::copyChangedProperties(const EntityItem& other) {
EntityItemProperties EntityItem::getProperties() const {
EntityItemProperties properties;
properties._id = getID();
properties._idSet = true;
properties._created = _created;
properties._type = getType();
properties._position = getPosition() * (float) TREE_SCALE;
properties._radius = getRadius() * (float) TREE_SCALE;
properties._rotation = getRotation();
properties._mass = getMass();
properties._velocity = getVelocity() * (float) TREE_SCALE;
properties._gravity = getGravity() * (float) TREE_SCALE;
properties._damping = getDamping();
properties._lifetime = getLifetime();
properties._script = getScript();
properties._positionChanged = false;
properties._radiusChanged = false;
properties._rotationChanged = false;
properties._massChanged = false;
properties._velocityChanged = false;
properties._gravityChanged = false;
properties._dampingChanged = false;
properties._lifetimeChanged = false;
properties._scriptChanged = false;
COPY_ENTITY_PROPERTY_TO_PROPERTIES(position, getPositionInMeters);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(dimensions, getDimensionsInMeters); // NOTE: radius is obsolete
COPY_ENTITY_PROPERTY_TO_PROPERTIES(rotation, getRotation);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(mass, getMass);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(velocity, getVelocityInMeters);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(gravity, getGravityInMeters);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(damping, getDamping);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(lifetime, getLifetime);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(script, getScript);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(registrationPoint, getRegistrationPoint);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(angularVelocity, getAngularVelocity);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(angularDamping, getAngularDamping);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(glowLevel, getGlowLevel);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(visible, getVisible);
properties._defaultSettings = false;
@ -610,55 +744,21 @@ bool EntityItem::setProperties(const EntityItemProperties& properties, bool forc
_created = properties.getCreated();
}
}
if (properties._positionChanged || forceCopy) {
// clamp positions to the domain to prevent someone from moving an entity out of the domain
setPosition(glm::clamp(properties._position / (float) TREE_SCALE, 0.0f, 1.0f));
somethingChanged = true;
}
if (properties._radiusChanged || forceCopy) {
setRadius(properties._radius / (float) TREE_SCALE);
somethingChanged = true;
}
if (properties._rotationChanged || forceCopy) {
setRotation(properties._rotation);
somethingChanged = true;
}
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;
}
SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, setPositionInMeters);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(dimensions, setDimensionsInMeters); // NOTE: radius is obsolete
SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, setRotation);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(mass, setMass);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(velocity, setVelocityInMeters);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(gravity, setGravityInMeters);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(damping, setDamping);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, setLifetime);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(registrationPoint, setRegistrationPoint);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularVelocity, setAngularVelocity);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularDamping, setAngularDamping);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(glowLevel, setGlowLevel);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(visible, setVisible);
if (somethingChanged) {
somethingChangedNotification(); // notify derived classes that something has changed
@ -675,3 +775,127 @@ bool EntityItem::setProperties(const EntityItemProperties& properties, bool forc
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 "EntityTypes.h"
class EntityTreeElement;
class EntityTreeElementExtraEncodeData;
#define DONT_ALLOW_INSTANTIATION virtual void pureVirtualFunctionPlaceHolder() = 0;
@ -39,6 +40,7 @@ class EntityItem {
public:
DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly
EntityItem(const EntityItemID& entityItemID);
EntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
virtual ~EntityItem() { }
@ -119,11 +121,27 @@ public:
// attributes applicable to all entity types
EntityTypes::EntityType getType() const { return _type; }
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 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)
void setRadius(float value) { _radius = value; } /// set 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)
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; }
void setRotation(const glm::quat& rotation) { _rotation = rotation; }
@ -138,15 +156,19 @@ public:
static const glm::vec3 DEFAULT_VELOCITY;
static const glm::vec3 NO_VELOCITY;
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 setVelocityInMeters(const glm::vec3& value) { _velocity = value / (float) TREE_SCALE; } /// velocity in meters
bool hasVelocity() const { return _velocity != NO_VELOCITY; }
static const glm::vec3 DEFAULT_GRAVITY;
static const glm::vec3 REGULAR_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
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 setGravityInMeters(const glm::vec3& value) { _gravity = value / (float) TREE_SCALE; } /// gravity in meters
bool hasGravity() const { return _gravity != NO_GRAVITY; }
// TODO: this should eventually be updated to support resting on collisions with other surfaces
@ -173,14 +195,38 @@ public:
bool lifetimeHasExpired() const;
// position, size, and bounds related helpers
float getSize() const { return _radius * 2.0f; } /// get maximum dimension in domain scale units (0.0 - 1.0)
glm::vec3 getMinimumPoint() const { return _position - glm::vec3(_radius, _radius, _radius); }
glm::vec3 getMaximumPoint() const { return _position + glm::vec3(_radius, _radius, _radius); }
AACube getAACube() const { return AACube(getMinimumPoint(), getSize()); } /// AACube in domain scale units (0.0 - 1.0)
float getSize() const; /// get maximum dimension in domain scale units (0.0 - 1.0)
AACube getMaximumAACube() const;
AACube getMinimumAACube() const;
AABox getAABox() const; /// axis aligned bounding box in domain scale units (0.0 - 1.0)
static const QString DEFAULT_SCRIPT;
const QString& getScript() const { return _script; }
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:
virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init
@ -196,7 +242,7 @@ protected:
quint64 _created;
glm::vec3 _position;
float _radius;
glm::vec3 _dimensions;
glm::quat _rotation;
float _glowLevel;
float _mass;
@ -205,6 +251,17 @@ protected:
float _damping;
float _lifetime;
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 "EntityItemProperties.h"
#include "ModelEntityItem.h"
EntityItemProperties::EntityItemProperties() :
@ -28,17 +29,21 @@ EntityItemProperties::EntityItemProperties() :
_type(EntityTypes::Unknown),
_position(0),
_radius(ENTITY_DEFAULT_RADIUS),
_rotation(ENTITY_DEFAULT_ROTATION),
_dimensions(EntityItem::DEFAULT_DIMENSIONS),
_rotation(EntityItem::DEFAULT_ROTATION),
_mass(EntityItem::DEFAULT_MASS),
_velocity(EntityItem::DEFAULT_VELOCITY),
_gravity(EntityItem::DEFAULT_GRAVITY),
_damping(EntityItem::DEFAULT_DAMPING),
_lifetime(EntityItem::DEFAULT_LIFETIME),
_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),
_radiusChanged(false),
_dimensionsChanged(false),
_rotationChanged(false),
_massChanged(false),
_velocityChanged(false),
@ -46,15 +51,20 @@ EntityItemProperties::EntityItemProperties() :
_dampingChanged(false),
_lifetimeChanged(false),
_scriptChanged(false),
_registrationPointChanged(false),
_angularVelocityChanged(false),
_angularDampingChanged(false),
_visibleChanged(false),
_color(),
_modelURL(""),
_animationURL(""),
_animationIsPlaying(false),
_animationFrameIndex(0.0),
_animationFPS(ENTITY_DEFAULT_ANIMATION_FPS),
_animationIsPlaying(ModelEntityItem::DEFAULT_ANIMATION_IS_PLAYING),
_animationFrameIndex(ModelEntityItem::DEFAULT_ANIMATION_FRAME_INDEX),
_animationFPS(ModelEntityItem::DEFAULT_ANIMATION_FPS),
_glowLevel(0.0f),
_naturalDimensions(1.0f, 1.0f, 1.0f),
_colorChanged(false),
_modelURLChanged(false),
_animationURLChanged(false),
@ -73,7 +83,7 @@ void EntityItemProperties::debugDump() const {
qDebug() << " _id=" << _id;
qDebug() << " _idSet=" << _idSet;
qDebug() << " _position=" << _position.x << "," << _position.y << "," << _position.z;
qDebug() << " _radius=" << _radius;
qDebug() << " _dimensions=" << getDimensions();
qDebug() << " _modelURL=" << _modelURL;
qDebug() << " changed properties...";
EntityPropertyFlags props = getChangedProperties();
@ -82,65 +92,26 @@ void EntityItemProperties::debugDump() const {
EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
EntityPropertyFlags changedProperties;
if (_radiusChanged) {
changedProperties += PROP_RADIUS;
}
if (_positionChanged) {
changedProperties += PROP_POSITION;
}
if (_rotationChanged) {
changedProperties += PROP_ROTATION;
}
if (_massChanged) {
changedProperties += PROP_MASS;
}
if (_velocityChanged) {
changedProperties += PROP_VELOCITY;
}
if (_gravityChanged) {
changedProperties += PROP_GRAVITY;
}
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;
}
CHECK_PROPERTY_CHANGE(PROP_DIMENSIONS, dimensions);
CHECK_PROPERTY_CHANGE(PROP_POSITION, position);
CHECK_PROPERTY_CHANGE(PROP_ROTATION, rotation);
CHECK_PROPERTY_CHANGE(PROP_MASS, mass);
CHECK_PROPERTY_CHANGE(PROP_VELOCITY, velocity);
CHECK_PROPERTY_CHANGE(PROP_GRAVITY, gravity);
CHECK_PROPERTY_CHANGE(PROP_DAMPING, damping);
CHECK_PROPERTY_CHANGE(PROP_LIFETIME, lifetime);
CHECK_PROPERTY_CHANGE(PROP_SCRIPT, script);
CHECK_PROPERTY_CHANGE(PROP_COLOR, color);
CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_URL, animationURL);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_PLAYING, animationIsPlaying);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FRAME_INDEX, animationFrameIndex);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FPS, animationFPS);
CHECK_PROPERTY_CHANGE(PROP_VISIBLE, visible);
CHECK_PROPERTY_CHANGE(PROP_REGISTRATION_POINT, registrationPoint);
CHECK_PROPERTY_CHANGE(PROP_ANGULAR_VELOCITY, angularVelocity);
CHECK_PROPERTY_CHANGE(PROP_ANGULAR_DAMPING, angularDamping);
return changedProperties;
}
@ -149,38 +120,35 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
QScriptValue properties = engine->newObject();
if (_idSet) {
properties.setProperty("id", _id.toString());
bool isKnownID = (_id != UNKNOWN_ENTITY_ID);
properties.setProperty("isKnownID", isKnownID);
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(id, _id.toString());
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(isKnownID, (_id != UNKNOWN_ENTITY_ID));
} else {
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(isKnownID, false);
}
properties.setProperty("type", EntityTypes::getEntityTypeName(_type));
QScriptValue position = vec3toScriptValue(engine, _position);
properties.setProperty("position", position);
properties.setProperty("radius", _radius);
QScriptValue rotation = quatToScriptValue(engine, _rotation);
properties.setProperty("rotation", rotation);
properties.setProperty("mass", _mass);
QScriptValue velocity = vec3toScriptValue(engine, _velocity);
properties.setProperty("velocity", velocity);
QScriptValue gravity = vec3toScriptValue(engine, _gravity);
properties.setProperty("gravity", gravity);
properties.setProperty("damping", _damping);
properties.setProperty("lifetime", _lifetime);
properties.setProperty("age", getAge()); // gettable, but not settable
properties.setProperty("ageAsText", formatSecondsElapsed(getAge())); // gettable, but not settable
properties.setProperty("script", _script);
QScriptValue color = xColorToScriptValue(engine, _color);
properties.setProperty("color", color);
properties.setProperty("modelURL", _modelURL);
properties.setProperty("animationURL", _animationURL);
properties.setProperty("animationIsPlaying", _animationIsPlaying);
properties.setProperty("animationFrameIndex", _animationFrameIndex);
properties.setProperty("animationFPS", _animationFPS);
properties.setProperty("glowLevel", _glowLevel);
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(type, EntityTypes::getEntityTypeName(_type));
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(position);
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(dimensions);
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(naturalDimensions); // gettable, but not settable
COPY_PROPERTY_TO_QSCRIPTVALUE_QUAT(rotation);
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(velocity);
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(gravity);
COPY_PROPERTY_TO_QSCRIPTVALUE(damping);
COPY_PROPERTY_TO_QSCRIPTVALUE(lifetime);
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(age, getAge()); // gettable, but not settable
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(ageAsText, formatSecondsElapsed(getAge())); // gettable, but not settable
COPY_PROPERTY_TO_QSCRIPTVALUE(script);
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(registrationPoint);
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(angularVelocity);
COPY_PROPERTY_TO_QSCRIPTVALUE(angularDamping);
COPY_PROPERTY_TO_QSCRIPTVALUE(visible);
COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR(color);
COPY_PROPERTY_TO_QSCRIPTVALUE(modelURL);
COPY_PROPERTY_TO_QSCRIPTVALUE(animationURL);
COPY_PROPERTY_TO_QSCRIPTVALUE(animationIsPlaying);
COPY_PROPERTY_TO_QSCRIPTVALUE(animationFrameIndex);
COPY_PROPERTY_TO_QSCRIPTVALUE(animationFPS);
COPY_PROPERTY_TO_QSCRIPTVALUE(glowLevel);
// Sitting properties support
QScriptValue sittingPoints = engine->newObject();
@ -192,217 +160,39 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
sittingPoints.setProperty(i, sittingPoint);
}
sittingPoints.setProperty("length", _sittingPoints.size());
properties.setProperty("sittingPoints", sittingPoints);
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(sittingPoints, sittingPoints); // gettable, but not settable
return properties;
}
void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
QScriptValue typeScriptValue = object.property("type");
if (typeScriptValue.isValid()) {
QString typeName;
typeName = typeScriptValue.toVariant().toString();
_type = EntityTypes::getEntityTypeFromName(typeName);
setType(typeScriptValue.toVariant().toString());
}
QScriptValue position = object.property("position");
if (position.isValid()) {
QScriptValue x = position.property("x");
QScriptValue y = position.property("y");
QScriptValue z = position.property("z");
if (x.isValid() && y.isValid() && z.isValid()) {
glm::vec3 newPosition;
newPosition.x = x.toVariant().toFloat();
newPosition.y = y.toVariant().toFloat();
newPosition.z = z.toVariant().toFloat();
if (_defaultSettings || newPosition != _position) {
setPosition(newPosition); // gives us automatic clamping
}
}
}
QScriptValue radius = object.property("radius");
if (radius.isValid()) {
float newRadius;
newRadius = radius.toVariant().toFloat();
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;
}
}
COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(position, setPosition);
COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(dimensions, setDimensions);
COPY_PROPERTY_FROM_QSCRIPTVALUE_QUAT(rotation, setRotation);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(mass, setMass);
COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(velocity, setVelocity);
COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(gravity, setGravity);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(damping, setDamping);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(lifetime, setLifetime);
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(script, setScript);
COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(registrationPoint, setRegistrationPoint);
COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(angularVelocity, setAngularVelocity);
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);
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(animationIsPlaying, setAnimationIsPlaying);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(animationFPS, setAnimationFPS);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(animationFrameIndex, setAnimationFrameIndex);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(glowLevel, setGlowLevel);
_lastEdited = usecTimestampNow();
}
@ -432,7 +222,6 @@ void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemP
//
bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItemID id, const EntityItemProperties& properties,
unsigned char* bufferOut, int sizeIn, int& sizeOut) {
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
@ -532,23 +321,26 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
// These items would go here once supported....
// PROP_PAGED_PROPERTY,
// PROP_CUSTOM_PROPERTIES_INCLUDED,
// PROP_VISIBLE,
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_MASS, appendValue, properties.getMass());
APPEND_ENTITY_PROPERTY(PROP_VELOCITY, appendValue, properties.getVelocity());
APPEND_ENTITY_PROPERTY(PROP_GRAVITY, appendValue, properties.getGravity());
APPEND_ENTITY_PROPERTY(PROP_DAMPING, appendValue, properties.getDamping());
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_MODEL_URL, appendValue, properties.getModelURL());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, appendValue, properties.getAnimationURL());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, appendValue, properties.getAnimationFPS());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, appendValue, properties.getAnimationFrameIndex());
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) {
int endOfEntityItemData = packetData->getUncompressedByteOffset();
@ -727,20 +519,24 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
processedBytes += propertyFlags.getEncodedLength();
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_TO_PROPERTIES(PROP_MASS, float, setMass);
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_DAMPING, float, setDamping);
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_STRING_TO_PROPERTIES(PROP_MODEL_URL, setModelURL);
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_FRAME_INDEX, float, setAnimationFrameIndex);
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;
}
@ -774,10 +570,8 @@ bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityIt
return true;
}
void EntityItemProperties::markAllChanged() {
_positionChanged = true;
_radiusChanged = true;
_rotationChanged = true;
_massChanged = true;
_velocityChanged = true;
@ -785,7 +579,10 @@ void EntityItemProperties::markAllChanged() {
_dampingChanged = true;
_lifetimeChanged = true;
_scriptChanged = true;
_registrationPointChanged = true;
_angularVelocityChanged = true;
_angularDampingChanged = true;
_visibleChanged = true;
_colorChanged = true;
_modelURLChanged = true;
_animationURLChanged = true;
@ -793,5 +590,36 @@ void EntityItemProperties::markAllChanged() {
_animationFrameIndexChanged = true;
_animationFPSChanged = 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 <glm/glm.hpp>
#include <glm/gtx/extented_min_max.hpp>
#include <QtScript/QScriptEngine>
#include <QtCore/QObject>
@ -28,20 +29,9 @@
#include "EntityItemID.h"
#include "EntityItemPropertiesMacros.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
enum EntityPropertyList {
PROP_PAGED_PROPERTY,
@ -50,7 +40,8 @@ enum EntityPropertyList {
// these properties are supported by the EntityItem base class
PROP_VISIBLE,
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_MASS,
PROP_VELOCITY,
@ -67,16 +58,24 @@ enum EntityPropertyList {
PROP_ANIMATION_FRAME_INDEX,
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;
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
/// 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
/// all units for position, radius, etc are in meter units
/// all units for position, dimensions, etc are in meter units
class EntityItemProperties {
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
@ -96,31 +95,27 @@ public:
/// used by EntityScriptingInterface to return EntityItemProperties for unknown models
void setIsUnknownID() { _id = UNKNOWN_ENTITY_ID; _idSet = true; }
glm::vec3 getMinimumPointMeters() const { return _position - glm::vec3(_radius, _radius, _radius); }
glm::vec3 getMaximumPointMeters() const { return _position + glm::vec3(_radius, _radius, _radius); }
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);
}
AACube getMaximumAACubeInTreeUnits() const;
AACube getMaximumAACubeInMeters() const;
void debugDump() const;
// properties of all entities
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; }
const glm::vec3& getPosition() const { return _position; }
/// 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 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; }
float getMass() const { return _mass; }
@ -148,9 +143,9 @@ public:
// NOTE: how do we handle _defaultSettings???
bool containsBoundsProperties() const { return (_positionChanged || _radiusChanged); }
bool containsBoundsProperties() const { return (_positionChanged || _dimensionsChanged); }
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
// named getter/setters, but we want to move them to generic types...
@ -162,6 +157,7 @@ public:
bool getAnimationIsPlaying() const { return _animationIsPlaying; }
float getAnimationFPS() const { return _animationFPS; }
float getGlowLevel() const { return _glowLevel; }
const QString& getScript() const { return _script; }
// model related properties
void setColor(const xColor& value) { _color = value; _colorChanged = true; }
@ -171,6 +167,7 @@ public:
void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; _animationIsPlayingChanged = true; }
void setAnimationFPS(float value) { _animationFPS = value; _animationFPSChanged = 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,
@ -184,7 +181,6 @@ public:
EntityItemID& entityID, EntityItemProperties& properties);
bool positionChanged() const { return _positionChanged; }
bool radiusChanged() const { return _radiusChanged; }
bool rotationChanged() const { return _rotationChanged; }
bool massChanged() const { return _massChanged; }
bool velocityChanged() const { return _velocityChanged; }
@ -192,6 +188,8 @@ public:
bool dampingChanged() const { return _dampingChanged; }
bool lifetimeChanged() const { return _lifetimeChanged; }
bool scriptChanged() const { return _scriptChanged; }
bool dimensionsChanged() const { return _dimensionsChanged; }
bool registrationPointChanged() const { return _registrationPointChanged; }
bool colorChanged() const { return _colorChanged; }
bool modelURLChanged() const { return _modelURLChanged; }
bool animationURLChanged() const { return _animationURLChanged; }
@ -203,6 +201,24 @@ public:
void clearID() { _id = UNKNOWN_ENTITY_ID; _idSet = false; }
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:
void setLastEdited(quint64 usecTime) { _lastEdited = usecTime; }
@ -212,8 +228,11 @@ private:
quint64 _created;
EntityTypes::EntityType _type;
void setType(const QString& typeName) { _type = EntityTypes::getEntityTypeFromName(typeName); }
glm::vec3 _position;
float _radius;
glm::vec3 _dimensions;
glm::quat _rotation;
float _mass;
glm::vec3 _velocity;
@ -221,9 +240,13 @@ private:
float _damping;
float _lifetime;
QString _script;
glm::vec3 _registrationPoint;
glm::vec3 _angularVelocity;
float _angularDamping;
bool _visible;
bool _positionChanged;
bool _radiusChanged;
bool _dimensionsChanged;
bool _rotationChanged;
bool _massChanged;
bool _velocityChanged;
@ -231,6 +254,10 @@ private:
bool _dampingChanged;
bool _lifetimeChanged;
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
// named getter/setters, but we want to move them to generic types...
@ -242,6 +269,7 @@ private:
float _animationFPS;
float _glowLevel;
QVector<SittingPoint> _sittingPoints;
glm::vec3 _naturalDimensions;
bool _colorChanged;
bool _modelURLChanged;
@ -257,107 +285,4 @@ Q_DECLARE_METATYPE(EntityItemProperties);
QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const 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

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 );
// queue the packet
queueEntityMessage(PacketTypeEntityAddOrEdit, id, properties);
// If we have a local entity tree set, then also update it.
if (_entityTree) {
_entityTree->lockForWrite();
@ -41,6 +38,9 @@ EntityItemID EntityScriptingInterface::addEntity(const EntityItemProperties& pro
_entityTree->unlock();
}
// queue the packet
queueEntityMessage(PacketTypeEntityAddOrEdit, id, properties);
return id;
}
@ -68,18 +68,20 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(EntityItemID
EntityItem* entity = const_cast<EntityItem*>(_entityTree->findEntityByEntityItemID(identity));
if (entity) {
// TODO: improve sitting points in the future, for now we've included the old model behavior for entity
// types that are models
results = entity->getProperties();
// TODO: improve sitting points and naturalDimensions in the future,
// 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) {
ModelEntityItem* model = dynamic_cast<ModelEntityItem*>(entity);
const FBXGeometry* geometry = _entityTree->getGeometryForEntity(entity);
if (geometry) {
model->setSittingPoints(geometry->sittingPoints);
results.setSittingPoints(geometry->sittingPoints);
Extents meshExtents = geometry->getUnscaledMeshExtents();
results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum);
}
}
results = entity->getProperties();
} else {
results.setIsUnknownID();
}
@ -95,13 +97,10 @@ EntityItemID EntityScriptingInterface::editEntity(EntityItemID entityID, const E
// if the entity is unknown, attempt to look it up
if (!entityID.isKnownID) {
actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID);
}
// if at this point, we know the id, send the update to the entity server
if (actualID.id != UNKNOWN_ENTITY_ID) {
entityID.id = actualID.id;
entityID.isKnownID = true;
queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, properties);
if (actualID.id != UNKNOWN_ENTITY_ID) {
entityID.id = actualID.id;
entityID.isKnownID = true;
}
}
// 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->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;
}
@ -121,13 +126,10 @@ void EntityScriptingInterface::deleteEntity(EntityItemID entityID) {
// if the entity is unknown, attempt to look it up
if (!entityID.isKnownID) {
actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID);
}
// if at this point, we know the id, send the update to the entity server
if (actualID.id != UNKNOWN_ENTITY_ID) {
entityID.id = actualID.id;
entityID.isKnownID = true;
getEntityPacketSender()->queueEraseEntityMessage(entityID);
if (actualID.id != UNKNOWN_ENTITY_ID) {
entityID.id = actualID.id;
entityID.isKnownID = true;
}
}
// 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->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 {

View file

@ -652,13 +652,14 @@ void EntityTree::updateMovingEntities(quint64 now, QSet<EntityItemID>& entitiesT
entitiesToDelete << thisEntity->getEntityItemID();
entitiesBecomingStatic << thisEntity;
} else {
AACube oldCube = thisEntity->getAACube();
AACube oldCube = thisEntity->getMaximumAACube();
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.
AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), 1.0f);
if (!domainBounds.touches(newCube)) {
qDebug() << "Entity " << thisEntity->getEntityItemID() << " moved out of domain bounds.";
entitiesToDelete << thisEntity->getEntityItemID();
entitiesBecomingStatic << thisEntity;
} else {

View file

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

View file

@ -296,7 +296,12 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
}
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);
if (params.viewFrustum->cubeInFrustum(entityCube) == ViewFrustum::OUTSIDE) {
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 {
return containsBounds(entity->getMinimumPoint(), entity->getMaximumPoint());
return containsBounds(entity->getMaximumAACube());
}
bool EntityTreeElement::bestFitEntityBounds(const EntityItem* entity) const {
return bestFitBounds(entity->getMinimumPoint(), entity->getMaximumPoint());
return bestFitBounds(entity->getMaximumAACube());
}
bool EntityTreeElement::containsBounds(const EntityItemProperties& properties) const {
return containsBounds(properties.getMinimumPointTreeUnits(), properties.getMaximumPointTreeUnits());
return containsBounds(properties.getMaximumAACubeInTreeUnits());
}
bool EntityTreeElement::bestFitBounds(const EntityItemProperties& properties) const {
return bestFitBounds(properties.getMinimumPointTreeUnits(), properties.getMaximumPointTreeUnits());
return bestFitBounds(properties.getMaximumAACubeInTreeUnits());
}
bool EntityTreeElement::containsBounds(const AACube& bounds) const {
@ -477,76 +482,37 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con
while(entityItr != entityEnd) {
EntityItem* entity = (*entityItr);
AACube entityCube = entity->getAACube();
AABox entityBox = entity->getAABox();
float localDistance;
BoxFace localFace;
// if the ray doesn't intersect with our cube, we can stop searching!
if (entityCube.findRayIntersection(origin, direction, localDistance, localFace)) {
const FBXGeometry* fbxGeometry = _myTree->getGeometryForEntity(entity);
if (fbxGeometry && fbxGeometry->meshExtents.isValid()) {
Extents extents = fbxGeometry->meshExtents;
if (entityBox.findRayIntersection(origin, direction, localDistance, localFace)) {
// NOTE: If the entity has a bad mesh, then extents will be 0,0,0 & 0,0,0
if (extents.minimum == extents.maximum && extents.minimum == glm::vec3(0,0,0)) {
extents.maximum = glm::vec3(1.0f,1.0f,1.0f); // in this case we will simulate the unit cube
// 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);
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;
}
// TODO: change this to use better bounding shape for entity than sphere
bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float radius,
glm::vec3& penetration, void** penetratedObject) const {
QList<EntityItem*>::iterator entityItr = _entityItems->begin();
@ -605,6 +572,7 @@ const EntityItem* EntityTreeElement::getClosestEntity(glm::vec3 position) const
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 {
uint16_t numberOfEntities = _entityItems->size();
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) {
QList<EntityItem*>::iterator entityItr = _entityItems->begin();
QList<EntityItem*>::iterator entityEnd = _entityItems->end();

View file

@ -17,6 +17,13 @@
#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) {
return new ModelEntityItem(entityID, properties);
}
@ -40,7 +47,6 @@ EntityItemProperties ModelEntityItem::getProperties() const {
properties._animationIsPlaying = getAnimationIsPlaying();
properties._animationFrameIndex = getAnimationFrameIndex();
properties._animationFPS = getAnimationFPS();
properties._sittingPoints = getSittingPoints(); // sitting support
properties._colorChanged = false;
properties._modelURLChanged = false;
properties._animationURLChanged = false;
@ -55,40 +61,13 @@ EntityItemProperties ModelEntityItem::getProperties() const {
bool ModelEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) {
bool somethingChanged = false;
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) {
setModelURL(properties._modelURL);
somethingChanged = true;
}
if (properties._animationURLChanged || forceCopy) {
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;
}
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(modelURL, setModelURL);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationURL, setAnimationURL);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationIsPlaying, setAnimationIsPlaying);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationFrameIndex, setAnimationFrameIndex);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationFPS, setAnimationFPS);
if (somethingChanged) {
bool wantDebug = false;
@ -169,9 +148,11 @@ int ModelEntityItem::oldVersionReadEntityDataFromBuffer(const unsigned char* dat
<< "old ID=" << oldID << "new ID=" << _id;
// radius
memcpy(&_radius, dataAt, sizeof(_radius));
dataAt += sizeof(_radius);
bytesRead += sizeof(_radius);
float radius;
memcpy(&radius, dataAt, sizeof(radius));
dataAt += sizeof(radius);
bytesRead += sizeof(radius);
setRadius(radius);
// position
memcpy(&_position, dataAt, sizeof(_position));
@ -393,7 +374,7 @@ void ModelEntityItem::debugDump() const {
qDebug() << "ModelEntityItem id:" << getEntityItemID();
qDebug() << " edited ago:" << getEditedAgo();
qDebug() << " position:" << getPosition() * (float)TREE_SCALE;
qDebug() << " radius:" << getRadius() * (float)TREE_SCALE;
qDebug() << " dimensions:" << getDimensions() * (float)TREE_SCALE;
qDebug() << " model URL:" << getModelURL();
}

View file

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

View file

@ -20,21 +20,53 @@ MovingEntitiesOperator::MovingEntitiesOperator(EntityTree* tree) :
_changeTime(usecTimestampNow()),
_foundOldCount(0),
_foundNewCount(0),
_lookingCount(0)
_lookingCount(0),
_wantDebug(false)
{
}
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) {
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
// 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
EntityToMoveDetails details;
details.oldContainingElement = oldContainingElement;
@ -44,9 +76,29 @@ void MovingEntitiesOperator::addEntityToMoveList(EntityItem* entity, const AACub
details.newFound = false;
details.oldCube = oldCube;
details.newCube = newCube;
details.newBox = newBox;
details.oldCubeClamped = oldCubeClamped;
details.newCubeClamped = newCubeClamped;
_entitiesToMove << details;
_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
if (_entitiesToMove.size() > 0) {
AACube elementCube = element->getAACube();
int detailIndex = 0;
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;
break; // if it contains at least one, we're good to go
}
detailIndex++;
}
}
return containsEntity;
@ -87,13 +157,36 @@ bool MovingEntitiesOperator::preRecursion(OctreeElement* element) {
if (keepSearching && shouldRecurseSubTree(element)) {
// check against each of our search entities
int detailIndex = 0;
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 (!details.oldFound && entityTreeElement == details.oldContainingElement) {
entityTreeElement->removeEntityItem(details.entity);
_foundOldCount++;
//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
@ -103,7 +196,14 @@ bool MovingEntitiesOperator::preRecursion(OctreeElement* element) {
_tree->setContainingElement(entityItemID, entityTreeElement);
_foundNewCount++;
//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
keepSearching = (_foundOldCount < _lookingCount) || (_foundNewCount < _lookingCount);
@ -163,9 +263,9 @@ OctreeElement* MovingEntitiesOperator::possiblyCreateChildAt(OctreeElement* elem
foreach(const EntityToMoveDetails& details, _entitiesToMove) {
// 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,
// then we will create this branch and continue. We can exit this loop immediately

View file

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

View file

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

View file

@ -31,23 +31,27 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTree* tree,
_dontMove(false), // assume we'll be moving
_changeTime(usecTimestampNow()),
_oldEntityCube(),
_newEntityCube()
_newEntityCube(),
_wantDebug(false)
{
// caller must have verified existence of containingElement and oldEntity
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
// 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
// calculations to work.
if (_properties.containsPositionChange() && !_properties.containsRadiusChange()) {
float oldRadiusInMeters = _existingEntity->getRadius() * (float)TREE_SCALE;
_properties.setRadius(oldRadiusInMeters);
if (_properties.containsPositionChange() && !_properties.containsDimensionsChange()) {
glm::vec3 oldDimensionsInMeters = _existingEntity->getDimensions() * (float)TREE_SCALE;
_properties.setDimensions(oldDimensionsInMeters);
}
if (!_properties.containsPositionChange() && _properties.containsRadiusChange()) {
if (!_properties.containsPositionChange() && _properties.containsDimensionsChange()) {
glm::vec3 oldPositionInMeters = _existingEntity->getPosition() * (float)TREE_SCALE;
_properties.setPosition(oldPositionInMeters);
}
@ -72,11 +76,24 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTree* tree,
_newEntityCube = _oldEntityCube;
_dontMove = true;
} else {
_newEntityCube = _properties.getAACubeTreeUnits();
_newEntityCube = _properties.getMaximumAACubeInTreeUnits();
_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
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 subTreeContainsNewEntity(OctreeElement* element);
bool _wantDebug;
};
#endif // hifi_UpdateEntityOperator_h

View file

@ -75,8 +75,11 @@ PacketVersion versionForPacketType(PacketType type) {
return 1;
case PacketTypeParticleErase:
return 1;
case PacketTypeEntityAddOrEdit:
case PacketTypeEntityData:
return VERSION_ENTITIES_SUPPORT_SPLIT_MTU;
return VERSION_ENTITIES_SUPPORT_DIMENSIONS;
case PacketTypeEntityErase:
return 2;
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_ENTITIES_SUPPORT_SPLIT_MTU = 3;
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;
#endif // hifi_PacketHeaders_h

View file

@ -392,6 +392,41 @@ ViewFrustum::location ViewFrustum::cubeInFrustum(const AACube& cube) const {
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) {
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);

View file

@ -35,6 +35,10 @@ public:
/// \return whether or not the extents are empty
bool isEmpty() const { return minimum == maximum; }
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
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
EntityItemProperties properties;
float oneMeter = 1.0f;
float halfMeter = oneMeter / 2.0f;
//float halfMeter = oneMeter / 2.0f;
float halfOfDomain = TREE_SCALE * 0.5f;
glm::vec3 positionNearOriginInMeters(oneMeter, oneMeter, oneMeter); // when using properties, these are in meter not tree units
glm::vec3 positionAtCenterInMeters(halfOfDomain, halfOfDomain, halfOfDomain);
@ -60,7 +60,8 @@ void EntityTests::entityTreeTests(bool verbose) {
}
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");
tree.addEntity(entityID, properties);
@ -265,8 +266,10 @@ void EntityTests::entityTreeTests(bool verbose) {
glm::vec3 randomPositionInTreeUnits = randomPositionInMeters / (float)TREE_SCALE;
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) {
qDebug() << "iteration:" << i