diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 52a027852c..23e8c0d965 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -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); diff --git a/examples/butterflies.js b/examples/butterflies.js index 1f612ed4bf..6b15680fa0 100644 --- a/examples/butterflies.js +++ b/examples/butterflies.js @@ -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; diff --git a/examples/editModels.js b/examples/editModels.js index cec1bff783..740c992888 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -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; +}); + + diff --git a/interface/src/avatar/ModelReferential.cpp b/interface/src/avatar/ModelReferential.cpp index eb2a61a819..df6e272da7 100644 --- a/interface/src/avatar/ModelReferential.cpp +++ b/interface/src/avatar/ModelReferential.cpp @@ -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; } diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index 8dd88a79cc..095277d960 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -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(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++; } } } diff --git a/interface/src/entities/EntityTreeRenderer.h b/interface/src/entities/EntityTreeRenderer.h index f6a5c41a39..9e0368b61b 100644 --- a/interface/src/entities/EntityTreeRenderer.h +++ b/interface/src/entities/EntityTreeRenderer.h @@ -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); }; diff --git a/interface/src/entities/RenderableBoxEntityItem.cpp b/interface/src/entities/RenderableBoxEntityItem.cpp index c5843bb31a..17cfbdcdaf 100644 --- a/interface/src/entities/RenderableBoxEntityItem.cpp +++ b/interface/src/entities/RenderableBoxEntityItem.cpp @@ -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 diff --git a/interface/src/entities/RenderableModelEntityItem.cpp b/interface/src/entities/RenderableModelEntityItem.cpp index 03812904b5..4347087287 100644 --- a/interface/src/entities/RenderableModelEntityItem.cpp +++ b/interface/src/entities/RenderableModelEntityItem.cpp @@ -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]); diff --git a/interface/src/entities/RenderableSphereEntityItem.cpp b/interface/src/entities/RenderableSphereEntityItem.cpp index 01f41e83ae..d5286b0ab3 100644 --- a/interface/src/entities/RenderableSphereEntityItem.cpp +++ b/interface/src/entities/RenderableSphereEntityItem.cpp @@ -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(); }; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index d49eefa4bd..280f5bd21a 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -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&, vertices), Q_ARG(const QVector&, 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); } diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 66baaac90d..6c27970d8e 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -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 _localLights; @@ -206,7 +216,7 @@ protected: void setScaleInternal(const glm::vec3& scale); void scaleToFit(); - void snapToCenter(); + void snapToRegistrationPoint(); void simulateInternal(float deltaTime); diff --git a/libraries/entities/src/AddEntityOperator.cpp b/libraries/entities/src/AddEntityOperator.cpp index 5a7be69dbf..34ccc75030 100644 --- a/libraries/entities/src/AddEntityOperator.cpp +++ b/libraries/entities/src/AddEntityOperator.cpp @@ -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) { diff --git a/libraries/entities/src/BoxEntityItem.cpp b/libraries/entities/src/BoxEntityItem.cpp index c683a71562..83d6d7eff9 100644 --- a/libraries/entities/src/BoxEntityItem.cpp +++ b/libraries/entities/src/BoxEntityItem.cpp @@ -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; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index a472e7ec39..1406239f23 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -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; +} + diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 2d6af48f15..a41d4523f9 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -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); + }; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 65babb6e16..2ef307517f 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -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); } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index fc506ce10c..a52a75e071 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -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 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 getSittingPoints() const { return _sittingPoints; } + void setSittingPoints(QVector 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 _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 diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h new file mode 100644 index 0000000000..46c78c09ea --- /dev/null +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -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 diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 971d7066eb..8113168655 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -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(_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(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 { diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 4380171df2..8cec19eddd 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -652,13 +652,14 @@ void EntityTree::updateMovingEntities(quint64 now, QSet& 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 { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 47ae04846d..d97a87faca 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -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); diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 4dea9b271c..a58dd9065f 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -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::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& 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& foundEntities) { QList::iterator entityItr = _entityItems->begin(); QList::iterator entityEnd = _entityItems->end(); diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 1c1cd19831..83958c329b 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -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(); } diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index c8d2abf29a..4ed47d4894 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -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 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 sittingPoints) { _sittingPoints = sittingPoints; } void mapJoints(const QStringList& modelJointNames); QVector getAnimationFrame(); @@ -92,7 +99,6 @@ protected: rgbColor _color; QString _modelURL; - QVector _sittingPoints; quint64 _lastAnimated; QString _animationURL; diff --git a/libraries/entities/src/MovingEntitiesOperator.cpp b/libraries/entities/src/MovingEntitiesOperator.cpp index d6ccb1aad4..e6b48da232 100644 --- a/libraries/entities/src/MovingEntitiesOperator.cpp +++ b/libraries/entities/src/MovingEntitiesOperator.cpp @@ -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 diff --git a/libraries/entities/src/MovingEntitiesOperator.h b/libraries/entities/src/MovingEntitiesOperator.h index 047cc0c52b..fbec898cec 100644 --- a/libraries/entities/src/MovingEntitiesOperator.h +++ b/libraries/entities/src/MovingEntitiesOperator.h @@ -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 diff --git a/libraries/entities/src/SphereEntityItem.cpp b/libraries/entities/src/SphereEntityItem.cpp index 836cc24524..09364ddbfe 100644 --- a/libraries/entities/src/SphereEntityItem.cpp +++ b/libraries/entities/src/SphereEntityItem.cpp @@ -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; diff --git a/libraries/entities/src/UpdateEntityOperator.cpp b/libraries/entities/src/UpdateEntityOperator.cpp index 65a86a80ca..284d8e9e24 100644 --- a/libraries/entities/src/UpdateEntityOperator.cpp +++ b/libraries/entities/src/UpdateEntityOperator.cpp @@ -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() << "--------------------------------------------------------------------------"; + } + } diff --git a/libraries/entities/src/UpdateEntityOperator.h b/libraries/entities/src/UpdateEntityOperator.h index 0aef5927b2..8d40ddfd57 100644 --- a/libraries/entities/src/UpdateEntityOperator.h +++ b/libraries/entities/src/UpdateEntityOperator.h @@ -42,6 +42,8 @@ private: bool subTreeContainsOldEntity(OctreeElement* element); bool subTreeContainsNewEntity(OctreeElement* element); + + bool _wantDebug; }; #endif // hifi_UpdateEntityOperator_h diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index f3937dd2e3..5513c09a61 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -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: diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index bb64388dd6..207cf680d3 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -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 diff --git a/libraries/octree/src/ViewFrustum.cpp b/libraries/octree/src/ViewFrustum.cpp index 9552129e29..1e8dc41cdd 100644 --- a/libraries/octree/src/ViewFrustum.cpp +++ b/libraries/octree/src/ViewFrustum.cpp @@ -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); diff --git a/libraries/shared/src/Extents.h b/libraries/shared/src/Extents.h index c0e68bd2b1..2da3042467 100644 --- a/libraries/shared/src/Extents.h +++ b/libraries/shared/src/Extents.h @@ -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); diff --git a/tests/octree/src/ModelTests.cpp b/tests/octree/src/ModelTests.cpp index b91963726f..405b4a95d3 100644 --- a/tests/octree/src/ModelTests.cpp +++ b/tests/octree/src/ModelTests.cpp @@ -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