diff --git a/examples/edit.js b/examples/edit.js index 2b32769337..59dcbaad19 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -28,6 +28,7 @@ Script.include([ "libraries/gridTool.js", "libraries/entityList.js", "libraries/lightOverlayManager.js", + "libraries/zoneOverlayManager.js", ]); var selectionDisplay = SelectionDisplay; @@ -35,6 +36,7 @@ var selectionManager = SelectionManager; var entityPropertyDialogBox = EntityPropertyDialogBox; var lightOverlayManager = new LightOverlayManager(); +var zoneOverlayManager = new ZoneOverlayManager(); var cameraManager = new CameraManager(); @@ -47,6 +49,7 @@ var entityListTool = EntityListTool(); selectionManager.addEventListener(function() { selectionDisplay.updateHandles(); lightOverlayManager.updatePositions(); + zoneOverlayManager.updatePositions(); }); var windowDimensions = Controller.getViewportDimensions(); @@ -77,11 +80,13 @@ var DEFAULT_LIGHT_DIMENSIONS = Vec3.multiply(20, DEFAULT_DIMENSIONS); var MENU_AUTO_FOCUS_ON_SELECT = "Auto Focus on Select"; var MENU_EASE_ON_FOCUS = "Ease Orientation on Focus"; var MENU_SHOW_LIGHTS_IN_EDIT_MODE = "Show Lights in Edit Mode"; +var MENU_SHOW_ZONES_IN_EDIT_MODE = "Show Zones in Edit Mode"; var SETTING_INSPECT_TOOL_ENABLED = "inspectToolEnabled"; var SETTING_AUTO_FOCUS_ON_SELECT = "autoFocusOnSelect"; var SETTING_EASE_ON_FOCUS = "cameraEaseOnFocus"; var SETTING_SHOW_LIGHTS_IN_EDIT_MODE = "showLightsInEditMode"; +var SETTING_SHOW_ZONES_IN_EDIT_MODE = "showZonesInEditMode"; var INSUFFICIENT_PERMISSIONS_ERROR_MSG = "You do not have the necessary permissions to edit on this domain." var INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG = "You do not have the necessary permissions to place items on this domain." @@ -135,6 +140,7 @@ var toolBar = (function () { newSphereButton, newLightButton, newTextButton, + newZoneButton, browseMarketplaceButton; function initialize() { @@ -201,6 +207,14 @@ var toolBar = (function () { alpha: 0.9, visible: false }); + newZoneButton = toolBar.addTool({ + imageURL: toolIconUrl + "zonecube3.svg", + subImage: { x: 0, y: Tool.IMAGE_WIDTH + 208, width: 256, height: 256 }, + width: toolWidth, + height: toolHeight, + alpha: 0.9, + visible: false + }); that.setActive(false); } @@ -232,6 +246,7 @@ var toolBar = (function () { } toolBar.selectTool(activeButton, isActive); lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE)); + zoneOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); }; // Sets visibility of tool buttons, excluding the power button @@ -241,6 +256,7 @@ var toolBar = (function () { toolBar.showTool(newSphereButton, doShow); toolBar.showTool(newLightButton, doShow); toolBar.showTool(newTextButton, doShow); + toolBar.showTool(newZoneButton, doShow); }; var RESIZE_INTERVAL = 50; @@ -412,6 +428,21 @@ var toolBar = (function () { return true; } + if (newZoneButton === toolBar.clicked(clickedOverlay)) { + var position = getPositionToCreateEntity(); + + if (position.x > 0 && position.y > 0 && position.z > 0) { + placingEntityID = Entities.addEntity({ + type: "Zone", + position: grid.snapToSurface(grid.snapToGrid(position, false, DEFAULT_DIMENSIONS), DEFAULT_DIMENSIONS), + dimensions: { x: 10, y: 10, z: 10 }, + }); + } else { + print("Can't create box: Text would be out of bounds."); + } + return true; + } + return false; }; @@ -486,12 +517,22 @@ function rayPlaneIntersection(pickRay, point, normal) { } function findClickedEntity(event) { + var pickZones = event.isControl; + + if (pickZones) { + Entities.setZonesArePickable(true); + } + var pickRay = Camera.computePickRay(event.x, event.y); var entityResult = Entities.findRayIntersection(pickRay, true); // want precision picking var lightResult = lightOverlayManager.findRayIntersection(pickRay); lightResult.accurate = true; + if (pickZones) { + Entities.setZonesArePickable(false); + } + var result; if (!entityResult.intersects && !lightResult.intersects) { @@ -776,6 +817,8 @@ function setupModelMenus() { isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" }); Menu.addMenuItem({ menuName: "View", menuItemName: MENU_SHOW_LIGHTS_IN_EDIT_MODE, afterItem: MENU_EASE_ON_FOCUS, isCheckable: true, isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE) == "true" }); + Menu.addMenuItem({ menuName: "View", menuItemName: MENU_SHOW_ZONES_IN_EDIT_MODE, afterItem: MENU_SHOW_LIGHTS_IN_EDIT_MODE, + isCheckable: true, isChecked: Settings.getValue(SETTING_SHOW_ZONES_IN_EDIT_MODE) == "true" }); Entities.setLightsArePickable(false); } @@ -804,12 +847,14 @@ function cleanupModelMenus() { Menu.removeMenuItem("View", MENU_AUTO_FOCUS_ON_SELECT); Menu.removeMenuItem("View", MENU_EASE_ON_FOCUS); Menu.removeMenuItem("View", MENU_SHOW_LIGHTS_IN_EDIT_MODE); + Menu.removeMenuItem("View", MENU_SHOW_ZONES_IN_EDIT_MODE); } Script.scriptEnding.connect(function() { Settings.setValue(SETTING_AUTO_FOCUS_ON_SELECT, Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT)); Settings.setValue(SETTING_EASE_ON_FOCUS, Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); Settings.setValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE)); + Settings.setValue(SETTING_SHOW_ZONES_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); progressDialog.cleanup(); toolBar.cleanup(); @@ -942,6 +987,8 @@ function handeMenuEvent(menuItem) { selectAllEtitiesInCurrentSelectionBox(true); } else if (menuItem == MENU_SHOW_LIGHTS_IN_EDIT_MODE) { lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE)); + } else if (menuItem == MENU_SHOW_ZONES_IN_EDIT_MODE) { + zoneOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); } tooltip.show(false); } diff --git a/examples/example/entities/fullDomainZoneEntityExample.js b/examples/example/entities/fullDomainZoneEntityExample.js new file mode 100644 index 0000000000..e422cfd398 --- /dev/null +++ b/examples/example/entities/fullDomainZoneEntityExample.js @@ -0,0 +1,23 @@ +// +// fullDomainZoneEntityExample.js +// examples +// +// Created by Brad Hefta-Gaub on 4/16/15. +// Copyright 2015 High Fidelity, Inc. +// +// This is an example script that demonstrates creating a Zone which is as large as the entire domain +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Entities.addEntity({ + type: "Zone", + position: { x: -10000, y: -10000, z: -10000 }, + dimensions: { x: 30000, y: 30000, z: 30000 }, + keyLightColor: { red: 255, green: 255, blue: 0 }, + keyLightDirection: { x: 0, y: -1.0, z: 0 }, + keyLightIntensity: 1.0, + keyLightAmbientIntensity: 0.5 +}); + diff --git a/examples/example/entities/zoneEntityExample.js b/examples/example/entities/zoneEntityExample.js index 052de06a1d..b5831a2bb5 100644 --- a/examples/example/entities/zoneEntityExample.js +++ b/examples/example/entities/zoneEntityExample.js @@ -22,7 +22,8 @@ var zoneEntityA = Entities.addEntity({ dimensions: { x: 10, y: 10, z: 10 }, keyLightColor: { red: 255, green: 0, blue: 0 }, stageSunModelEnabled: false, - keyLightDirection: { x: 0, y: -1.0, z: 0 } + keyLightDirection: { x: 0, y: -1.0, z: 0 }, + shapeType: "sphere" }); print("zoneEntityA:" + zoneEntityA); @@ -51,7 +52,9 @@ var zoneEntityC = Entities.addEntity({ keyLightColor: { red: 0, green: 0, blue: 255 }, keyLightIntensity: 0.75, keyLightDirection: { x: 0, y: 0, z: -1 }, - stageSunModelEnabled: false + stageSunModelEnabled: false, + shapeType: "compound", + compoundShapeURL: "http://headache.hungry.com/~seth/hifi/cube.fbx" }); print("zoneEntityC:" + zoneEntityC); diff --git a/examples/example/games/billiards.js b/examples/example/games/billiards.js index d4bc71ba37..8e890515c0 100644 --- a/examples/example/games/billiards.js +++ b/examples/example/games/billiards.js @@ -39,126 +39,130 @@ hitSounds.push(SoundCache.getSound(HIFI_PUBLIC_BUCKET + "Collisions-ballhitsandc HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; var screenSize = Controller.getViewportDimensions(); var reticle = Overlays.addOverlay("image", { - x: screenSize.x / 2 - 16, - y: screenSize.y / 2 - 16, - width: 32, - height: 32, - imageURL: HIFI_PUBLIC_BUCKET + "images/billiardsReticle.png", - color: { red: 255, green: 255, blue: 255}, - alpha: 1 - }); + x: screenSize.x / 2 - 16, + y: screenSize.y / 2 - 16, + width: 32, + height: 32, + imageURL: HIFI_PUBLIC_BUCKET + "images/billiardsReticle.png", + color: { red: 255, green: 255, blue: 255}, + alpha: 1 +}); function makeTable(pos) { - // Top - tableParts.push(Entities.addEntity( + // Top + tableParts.push(Entities.addEntity( { type: "Box", - position: pos, - dimensions: { x: LENGTH * SCALE, y: HEIGHT, z: WIDTH * SCALE }, - color: { red: 0, green: 255, blue: 0 } })); - // Long Bumpers - tableParts.push(Entities.addEntity( + position: pos, + dimensions: { x: LENGTH * SCALE, y: HEIGHT, z: WIDTH * SCALE }, + color: { red: 0, green: 255, blue: 0 } })); + // Long Bumpers + tableParts.push(Entities.addEntity( { type: "Box", - position: { x: pos.x - LENGTH / 2.0, - y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0), - z: pos.z - (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE }, - dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE }, - color: { red: 237, green: 201, blue: 175 } })); - tableParts.push(Entities.addEntity( + position: { x: pos.x - LENGTH / 2.0, + y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0), + z: pos.z - (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE }, + dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE }, + color: { red: 237, green: 201, blue: 175 } })); + tableParts.push(Entities.addEntity( { type: "Box", - position: { x: pos.x + LENGTH / 2.0, - y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0), - z: pos.z - (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE }, - dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE }, - color: { red: 237, green: 201, blue: 175 } })); + position: { x: pos.x + LENGTH / 2.0, + y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0), + z: pos.z - (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE }, + dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE }, + color: { red: 237, green: 201, blue: 175 } })); - tableParts.push(Entities.addEntity( + tableParts.push(Entities.addEntity( { type: "Box", - position: { x: pos.x - LENGTH / 2.0, - y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0), - z: pos.z + (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE }, - dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE }, - color: { red: 237, green: 201, blue: 175 } })); - tableParts.push(Entities.addEntity( + position: { x: pos.x - LENGTH / 2.0, + y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0), + z: pos.z + (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE }, + dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE }, + color: { red: 237, green: 201, blue: 175 } })); + tableParts.push(Entities.addEntity( { type: "Box", - position: { x: pos.x + LENGTH / 2.0, - y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0), - z: pos.z + (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE }, - dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE }, - color: { red: 237, green: 201, blue: 175 } })); - // End bumpers - tableParts.push(Entities.addEntity( + position: { x: pos.x + LENGTH / 2.0, + y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0), + z: pos.z + (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE }, + dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE }, + color: { red: 237, green: 201, blue: 175 } })); + // End bumpers + tableParts.push(Entities.addEntity( { type: "Box", - position: { x: pos.x + (LENGTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE, - y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0), - z: pos.z }, - dimensions: { z: (WIDTH - 2.0 * HOLE_SIZE) * SCALE, y: BUMPER_HEIGHT, x: BUMPER_WIDTH * SCALE }, - color: { red: 237, green: 201, blue: 175 } })); + position: { x: pos.x + (LENGTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE, + y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0), + z: pos.z }, + dimensions: { z: (WIDTH - 2.0 * HOLE_SIZE) * SCALE, y: BUMPER_HEIGHT, x: BUMPER_WIDTH * SCALE }, + color: { red: 237, green: 201, blue: 175 } })); - tableParts.push(Entities.addEntity( + tableParts.push(Entities.addEntity( { type: "Box", - position: { x: pos.x - (LENGTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE, - y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0), - z: pos.z }, - dimensions: { z: (WIDTH - 2.0 * HOLE_SIZE) * SCALE, y: BUMPER_HEIGHT, x: BUMPER_WIDTH * SCALE }, - color: { red: 237, green: 201, blue: 175 } })); + position: { x: pos.x - (LENGTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE, + y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0), + z: pos.z }, + dimensions: { z: (WIDTH - 2.0 * HOLE_SIZE) * SCALE, y: BUMPER_HEIGHT, x: BUMPER_WIDTH * SCALE }, + color: { red: 237, green: 201, blue: 175 } })); } function makeBalls(pos) { - // Object balls + // Object balls var whichBall = [ 1, 14, 15, 4, 8, 7, 12, 9, 3, 13, 10, 5, 6, 11, 2 ]; var ballNumber = 0; - var ballPosition = { x: pos.x + (LENGTH / 4.0) * SCALE, y: pos.y + HEIGHT / 2.0 + DROP_HEIGHT, z: pos.z }; - for (var row = 1; row <= 5; row++) { - ballPosition.z = pos.z - ((row - 1.0) / 2.0 * (BALL_SIZE + BALL_GAP) * SCALE); - for (var spot = 0; spot < row; spot++) { - balls.push(Entities.addEntity( + var ballPosition = { x: pos.x + (LENGTH / 4.0) * SCALE, y: pos.y + HEIGHT / 2.0 + DROP_HEIGHT, z: pos.z }; + for (var row = 1; row <= 5; row++) { + ballPosition.z = pos.z - ((row - 1.0) / 2.0 * (BALL_SIZE + BALL_GAP) * SCALE); + for (var spot = 0; spot < row; spot++) { + balls.push(Entities.addEntity( { type: "Model", - modelURL: "https://s3.amazonaws.com/hifi-public/models/props/Pool/ball_" + whichBall[ballNumber].toString() + ".fbx", - position: ballPosition, - dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE }, - rotation: Quat.fromPitchYawRollDegrees((Math.random() - 0.5) * 20, (Math.random() - 0.5) * 20, (Math.random() - 0.5) * 20), - color: { red: 255, green: 255, blue: 255 }, - gravity: { x: 0, y: GRAVITY, z: 0 }, - ignoreCollisions: false, - damping: 0.50, - shapeType: "sphere", - collisionsWillMove: true })); - ballPosition.z += (BALL_SIZE + BALL_GAP) * SCALE; + modelURL: "https://s3.amazonaws.com/hifi-public/models/props/Pool/ball_" + + whichBall[ballNumber].toString() + ".fbx", + position: ballPosition, + dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE }, + rotation: Quat.fromPitchYawRollDegrees((Math.random() - 0.5) * 20, + (Math.random() - 0.5) * 20, + (Math.random() - 0.5) * 20), + color: { red: 255, green: 255, blue: 255 }, + gravity: { x: 0, y: GRAVITY, z: 0 }, + velocity: {x: 0, y: -0.2, z: 0 }, + ignoreCollisions: false, + damping: 0.50, + shapeType: "sphere", + collisionsWillMove: true })); + ballPosition.z += (BALL_SIZE + BALL_GAP) * SCALE; ballNumber++; - } - ballPosition.x += (BALL_GAP + Math.sqrt(3.0) / 2.0 * BALL_SIZE) * SCALE; } + ballPosition.x += (BALL_GAP + Math.sqrt(3.0) / 2.0 * BALL_SIZE) * SCALE; + } // Cue Ball cuePosition = { x: pos.x - (LENGTH / 4.0) * SCALE, y: pos.y + HEIGHT / 2.0 + DROP_HEIGHT, z: pos.z }; cueBall = Entities.addEntity( - { type: "Model", - modelURL: "https://s3.amazonaws.com/hifi-public/models/props/Pool/cue_ball.fbx", - position: cuePosition, - dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE }, - color: { red: 255, green: 255, blue: 255 }, - gravity: { x: 0, y: GRAVITY, z: 0 }, - angularVelocity: { x: 0, y: 0, z: 0 }, - velocity: {x: 0, y: 0, z: 0 }, - ignoreCollisions: false, - damping: 0.50, - shapeType: "sphere", - collisionsWillMove: true }); - + { type: "Model", + modelURL: "https://s3.amazonaws.com/hifi-public/models/props/Pool/cue_ball.fbx", + position: cuePosition, + dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE }, + color: { red: 255, green: 255, blue: 255 }, + gravity: { x: 0, y: GRAVITY, z: 0 }, + angularVelocity: { x: 0, y: 0, z: 0 }, + velocity: {x: 0, y: -0.2, z: 0 }, + ignoreCollisions: false, + damping: 0.50, + shapeType: "sphere", + collisionsWillMove: true }); + } function isObjectBall(id) { - for (var i; i < balls.length; i++) { - if (balls[i].id == id) { - return true; - } - } - return false; + for (var i; i < balls.length; i++) { + if (balls[i].id == id) { + return true; + } + } + return false; } function shootCue(velocity) { - var DISTANCE_FROM_CAMERA = BALL_SIZE * 5.0 * SCALE; + var DISTANCE_FROM_CAMERA = BALL_SIZE * 5.0 * SCALE; var camera = Camera.getPosition(); var forwardVector = Quat.getFront(Camera.getOrientation()); var cuePosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA)); @@ -180,14 +184,14 @@ function shootCue(velocity) { density: 8000, ignoreCollisions: false, collisionsWillMove: true - }); + }); print("Shot, velocity = " + velocity); } function keyReleaseEvent(event) { - if ((startStroke > 0) && event.text == "SPACE") { - var endTime = new Date().getTime(); - var delta = endTime - startStroke; + if ((startStroke > 0) && event.text == "SPACE") { + var endTime = new Date().getTime(); + var delta = endTime - startStroke; shootCue(delta / 100.0); startStroke = 0; } @@ -201,49 +205,49 @@ function keyPressEvent(event) { } function cleanup() { - for (var i = 0; i < tableParts.length; i++) { - if (!tableParts[i].isKnownID) { - tableParts[i] = Entities.identifyEntity(tableParts[i]); - } - Entities.deleteEntity(tableParts[i]); + for (var i = 0; i < tableParts.length; i++) { + if (!tableParts[i].isKnownID) { + tableParts[i] = Entities.identifyEntity(tableParts[i]); } - for (var i = 0; i < balls.length; i++) { - if (!balls[i].isKnownID) { - balls[i] = Entities.identifyEntity(balls[i]); - } - Entities.deleteEntity(balls[i]); + Entities.deleteEntity(tableParts[i]); + } + for (var i = 0; i < balls.length; i++) { + if (!balls[i].isKnownID) { + balls[i] = Entities.identifyEntity(balls[i]); } - Overlays.deleteOverlay(reticle); - Entities.deleteEntity(cueBall); + Entities.deleteEntity(balls[i]); + } + Overlays.deleteOverlay(reticle); + Entities.deleteEntity(cueBall); } function update(deltaTime) { - if (!cueBall.isKnownID) { - cueBall = Entities.identifyEntity(cueBall); - } else { - // Check if cue ball has fallen off table, re-drop if so - var cueProperties = Entities.getEntityProperties(cueBall); - if (cueProperties.position.y < tableCenter.y) { - // Replace the cueball - Entities.editEntity(cueBall, { position: cuePosition } ); + if (!cueBall.isKnownID) { + cueBall = Entities.identifyEntity(cueBall); + } else { + // Check if cue ball has fallen off table, re-drop if so + var cueProperties = Entities.getEntityProperties(cueBall); + if (cueProperties.position.y < tableCenter.y) { + // Replace the cueball + Entities.editEntity(cueBall, { position: cuePosition } ); - } } + } } function entityCollisionWithEntity(entity1, entity2, collision) { - /* - NOT WORKING YET - if ((entity1.id == cueBall.id) || (entity2.id == cueBall.id)) { - print("Cue ball collision!"); - //audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); - //Audio.playSound(hitSounds[0], { position: Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())) }); - } + /* + NOT WORKING YET + if ((entity1.id == cueBall.id) || (entity2.id == cueBall.id)) { + print("Cue ball collision!"); + //audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); + //Audio.playSound(hitSounds[0], { position: Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())) }); + } - else if (isObjectBall(entity1.id) || isObjectBall(entity2.id)) { - print("Object ball collision"); - } */ + else if (isObjectBall(entity1.id) || isObjectBall(entity2.id)) { + print("Object ball collision"); + } */ } tableCenter = Vec3.sum(MyAvatar.position, Vec3.multiply(4.0, Quat.getFront(Camera.getOrientation()))); diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index dd5bced393..a8290dc3fa 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -162,7 +162,7 @@ var elModelSections = document.querySelectorAll(".model-section"); var elModelURL = document.getElementById("property-model-url"); - var elCollisionModelURL = document.getElementById("property-collision-model-url"); + var elCompoundShapeURL = document.getElementById("property-compound-shape-url"); var elModelAnimationURL = document.getElementById("property-model-animation-url"); var elModelAnimationPlaying = document.getElementById("property-model-animation-playing"); var elModelAnimationFPS = document.getElementById("property-model-animation-fps"); @@ -182,6 +182,23 @@ var elTextBackgroundColorGreen = document.getElementById("property-text-background-color-green"); var elTextBackgroundColorBlue = document.getElementById("property-text-background-color-blue"); + var elZoneSections = document.querySelectorAll(".zone-section"); + var elZoneStageSunModelEnabled = document.getElementById("property-zone-stage-sun-model-enabled"); + var elZoneKeyLightColorRed = document.getElementById("property-zone-key-light-color-red"); + var elZoneKeyLightColorGreen = document.getElementById("property-zone-key-light-color-green"); + var elZoneKeyLightColorBlue = document.getElementById("property-zone-key-light-color-blue"); + var elZoneKeyLightIntensity = document.getElementById("property-zone-key-intensity"); + var elZoneKeyLightAmbientIntensity = document.getElementById("property-zone-key-ambient-intensity"); + var elZoneKeyLightDirectionX = document.getElementById("property-zone-key-light-direction-x"); + var elZoneKeyLightDirectionY = document.getElementById("property-zone-key-light-direction-y"); + var elZoneKeyLightDirectionZ = document.getElementById("property-zone-key-light-direction-z"); + + var elZoneStageLatitude = document.getElementById("property-zone-stage-latitude"); + var elZoneStageLongitude = document.getElementById("property-zone-stage-longitude"); + var elZoneStageAltitude = document.getElementById("property-zone-stage-altitude"); + var elZoneStageDay = document.getElementById("property-zone-stage-day"); + var elZoneStageHour = document.getElementById("property-zone-stage-hour"); + if (window.EventBridge !== undefined) { EventBridge.scriptEventReceived.connect(function(data) { data = JSON.parse(data); @@ -306,7 +323,7 @@ } elModelURL.value = properties.modelURL; - elCollisionModelURL.value = properties.collisionModelURL; + elCompoundShapeURL.value = properties.compoundShapeURL; elModelAnimationURL.value = properties.animationURL; elModelAnimationPlaying.checked = properties.animationIsPlaying; elModelAnimationFPS.value = properties.animationFPS; @@ -356,6 +373,32 @@ elLightCutoff.value = properties.cutoff; } + if (properties.type != "Zone") { + for (var i = 0; i < elZoneSections.length; i++) { + elZoneSections[i].style.display = 'none'; + } + } else { + for (var i = 0; i < elZoneSections.length; i++) { + elZoneSections[i].style.display = 'block'; + } + + elZoneStageSunModelEnabled.checked = properties.stageSunModelEnabled; + elZoneKeyLightColorRed.value = properties.keyLightColor.red; + elZoneKeyLightColorGreen.value = properties.keyLightColor.green; + elZoneKeyLightColorBlue.value = properties.keyLightColor.blue; + elZoneKeyLightIntensity.value = properties.keyLightIntensity.toFixed(2); + elZoneKeyLightAmbientIntensity.value = properties.keyLightAmbientIntensity.toFixed(2); + elZoneKeyLightDirectionX.value = properties.keyLightDirection.x.toFixed(2); + elZoneKeyLightDirectionY.value = properties.keyLightDirection.y.toFixed(2); + elZoneKeyLightDirectionZ.value = properties.keyLightDirection.z.toFixed(2); + + elZoneStageLatitude.value = properties.stageLatitude.toFixed(2); + elZoneStageLongitude.value = properties.stageLongitude.toFixed(2); + elZoneStageAltitude.value = properties.stageAltitude.toFixed(2); + elZoneStageDay.value = properties.stageDay; + elZoneStageHour.value = properties.stageHour; + } + if (selected) { activeElement.focus(); activeElement.select(); @@ -444,7 +487,7 @@ elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff')); elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL')); - elCollisionModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('collisionModelURL')); + elCompoundShapeURL.addEventListener('change', createEmitTextPropertyUpdateFunction('compoundShapeURL')); elModelAnimationURL.addEventListener('change', createEmitTextPropertyUpdateFunction('animationURL')); elModelAnimationPlaying.addEventListener('change', createEmitCheckedPropertyUpdateFunction('animationIsPlaying')); elModelAnimationFPS.addEventListener('change', createEmitNumberPropertyUpdateFunction('animationFPS')); @@ -468,6 +511,26 @@ elTextBackgroundColorGreen.addEventListener('change', textBackgroundColorChangeFunction); elTextBackgroundColorBlue.addEventListener('change', textBackgroundColorChangeFunction); + elZoneStageSunModelEnabled.addEventListener('change', createEmitCheckedPropertyUpdateFunction('stageSunModelEnabled')); + var zoneKeyLightColorChangeFunction = createEmitColorPropertyUpdateFunction( + 'keyLightColor', elZoneKeyLightColorRed, elZoneKeyLightColorGreen, elZoneKeyLightColorBlue); + elZoneKeyLightColorRed.addEventListener('change', zoneKeyLightColorChangeFunction); + elZoneKeyLightColorGreen.addEventListener('change', zoneKeyLightColorChangeFunction); + elZoneKeyLightColorBlue.addEventListener('change', zoneKeyLightColorChangeFunction); + elZoneKeyLightIntensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('keyLightIntensity')); + elZoneKeyLightAmbientIntensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('keyLightAmbientIntensity')); + var zoneKeyLightDirectionChangeFunction = createEmitVec3PropertyUpdateFunction( + 'keyLightDirection', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY, elZoneKeyLightDirectionZ); + elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction); + elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction); + elZoneKeyLightDirectionZ.addEventListener('change', zoneKeyLightDirectionChangeFunction); + + elZoneStageLatitude.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageLatitude')); + elZoneStageLongitude.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageLongitude')); + elZoneStageAltitude.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageAltitude')); + elZoneStageDay.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageDay')); + elZoneStageHour.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageHour')); + elMoveSelectionToGrid.addEventListener("click", function() { EventBridge.emitWebEvent(JSON.stringify({ type: "action", @@ -712,9 +775,9 @@
-
Collision Model URL
+
Compound Shape URL
- +
@@ -833,6 +896,73 @@
+ +
+ Stage Sun Model Enabled + + + +
+ +
+
Key Light Color
+
+
R
+
G
+
B
+
+
+
+
Key Light Intensity
+
+ +
+
+
+
Key Light Ambient Intensity
+
+ +
+
+
+
Key Light Direction
+
+
Pitch
+
Yaw
+
Roll
+
+
+ +
+
Stage Latitude
+
+ +
+
+
+
Stage Longitude
+
+ +
+
+
+
Stage Altitude
+
+ +
+
+
+
Stage Day
+
+ +
+
+
+
Stage Hour
+
+ +
+
diff --git a/examples/libraries/ToolTip.js b/examples/libraries/ToolTip.js index 680f617436..2b0e125d4b 100644 --- a/examples/libraries/ToolTip.js +++ b/examples/libraries/ToolTip.js @@ -53,7 +53,7 @@ function Tooltip() { text += "ID: " + properties.id + "\n" if (properties.type == "Model") { text += "Model URL: " + properties.modelURL + "\n" - text += "Collision Model URL: " + properties.collisionModelURL + "\n" + text += "Compound Shape URL: " + properties.compoundShapeURL + "\n" text += "Animation URL: " + properties.animationURL + "\n" text += "Animation is playing: " + properties.animationIsPlaying + "\n" if (properties.sittingPoints && properties.sittingPoints.length > 0) { diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js index d8a91da8f9..255cec4265 100644 --- a/examples/libraries/entityPropertyDialogBox.js +++ b/examples/libraries/entityPropertyDialogBox.js @@ -52,7 +52,7 @@ EntityPropertyDialogBox = (function () { if (properties.type == "Model") { array.push({ label: "Model URL:", value: properties.modelURL }); index++; - array.push({ label: "Collision Model URL:", value: properties.collisionModelURL }); + array.push({ label: "Compound Shape URL:", value: properties.compoundShapeURL }); index++; array.push({ label: "Animation URL:", value: properties.animationURL }); index++; @@ -284,7 +284,7 @@ EntityPropertyDialogBox = (function () { properties.locked = array[index++].value; if (properties.type == "Model") { properties.modelURL = array[index++].value; - properties.collisionModelURL = array[index++].value; + properties.compoundShapeURL = array[index++].value; properties.animationURL = array[index++].value; var newAnimationIsPlaying = array[index++].value; diff --git a/examples/libraries/lightOverlayManager.js b/examples/libraries/lightOverlayManager.js index 8032d77c49..a140461a27 100644 --- a/examples/libraries/lightOverlayManager.js +++ b/examples/libraries/lightOverlayManager.js @@ -122,7 +122,7 @@ LightOverlayManager = function() { Entities.clearingEntities.connect(clearEntities); // Add existing entities - var ids = Entities.findEntities(MyAvatar.position, 100); + var ids = Entities.findEntities(MyAvatar.position, 64000); for (var i = 0; i < ids.length; i++) { addEntity(ids[i]); } diff --git a/examples/libraries/zoneOverlayManager.js b/examples/libraries/zoneOverlayManager.js new file mode 100644 index 0000000000..9898b888fa --- /dev/null +++ b/examples/libraries/zoneOverlayManager.js @@ -0,0 +1,145 @@ +ZoneOverlayManager = function(isEntityFunc, entityAddedFunc, entityRemovedFunc, entityMovedFunc) { + var self = this; + + var visible = false; + + // List of all created overlays + var allOverlays = []; + + // List of overlays not currently being used + var unusedOverlays = []; + + // Map from EntityItemID.id to overlay id + var entityOverlays = {}; + + // Map from EntityItemID.id to EntityItemID object + var entityIDs = {}; + + this.updatePositions = function(ids) { + for (var id in entityIDs) { + var entityID = entityIDs[id]; + var properties = Entities.getEntityProperties(entityID); + Overlays.editOverlay(entityOverlays[entityID.id].solid, { + position: properties.position, + rotation: properties.rotation, + dimensions: properties.dimensions, + }); + Overlays.editOverlay(entityOverlays[entityID.id].outline, { + position: properties.position, + rotation: properties.rotation, + dimensions: properties.dimensions, + }); + } + }; + + this.setVisible = function(isVisible) { + if (visible != isVisible) { + visible = isVisible; + for (var id in entityOverlays) { + Overlays.editOverlay(entityOverlays[id].solid, { visible: visible }); + Overlays.editOverlay(entityOverlays[id].outline, { visible: visible }); + } + } + }; + + // Allocate or get an unused overlay + function getOverlay() { + if (unusedOverlays.length == 0) { + var overlay = Overlays.addOverlay("cube", { + }); + allOverlays.push(overlay); + } else { + var overlay = unusedOverlays.pop(); + }; + return overlay; + } + + function releaseOverlay(overlay) { + unusedOverlays.push(overlay); + Overlays.editOverlay(overlay, { visible: false }); + } + + function addEntity(entityID) { + var properties = Entities.getEntityProperties(entityID); + if (properties.type == "Zone" && !(entityID.id in entityOverlays)) { + var overlaySolid = getOverlay(); + var overlayOutline = getOverlay(); + + entityOverlays[entityID.id] = { + solid: overlaySolid, + outline: overlayOutline, + } + entityIDs[entityID.id] = entityID; + + var color = { + red: Math.round(Math.random() * 255), + green: Math.round(Math.random() * 255), + blue: Math.round(Math.random() * 255) + }; + Overlays.editOverlay(overlaySolid, { + position: properties.position, + rotation: properties.rotation, + dimensions: properties.dimensions, + visible: visible, + solid: true, + alpha: 0.1, + color: color, + ignoreRayIntersection: true, + }); + Overlays.editOverlay(overlayOutline, { + position: properties.position, + rotation: properties.rotation, + dimensions: properties.dimensions, + visible: visible, + solid: false, + dashed: false, + lineWidth: 2.0, + alpha: 1.0, + color: color, + ignoreRayIntersection: true, + }); + } + } + + function deleteEntity(entityID) { + if (entityID.id in entityOverlays) { + releaseOverlay(entityOverlays[entityID.id].outline); + releaseOverlay(entityOverlays[entityID.id].solid); + delete entityOverlays[entityID.id]; + } + } + + function changeEntityID(oldEntityID, newEntityID) { + entityOverlays[newEntityID.id] = entityOverlays[oldEntityID.id]; + entityIDs[newEntityID.id] = newEntityID; + + delete entityOverlays[oldEntityID.id]; + delete entityIDs[oldEntityID.id]; + } + + function clearEntities() { + for (var id in entityOverlays) { + releaseOverlay(entityOverlays[id].outline); + releaseOverlay(entityOverlays[id].solid); + } + entityOverlays = {}; + entityIDs = {}; + } + + Entities.addingEntity.connect(addEntity); + Entities.changingEntityID.connect(changeEntityID); + Entities.deletingEntity.connect(deleteEntity); + Entities.clearingEntities.connect(clearEntities); + + // Add existing entities + var ids = Entities.findEntities(MyAvatar.position, 64000); + for (var i = 0; i < ids.length; i++) { + addEntity(ids[i]); + } + + Script.scriptEnding.connect(function() { + for (var i = 0; i < allOverlays.length; i++) { + Overlays.deleteOverlay(allOverlays[i]); + } + }); +}; diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index a9bfbae8dd..60f985b083 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -77,7 +77,7 @@ void Cube3DOverlay::render(RenderArgs* args) { if (_drawOnHUD) { DependencyManager::get()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha)); } else { - DependencyManager::get()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha)); + DependencyManager::get()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha)); } glPopMatrix(); @@ -89,7 +89,7 @@ void Cube3DOverlay::render(RenderArgs* args) { if (_drawOnHUD) { DependencyManager::get()->renderSolidCube(1.0f, cubeColor); } else { - DependencyManager::get()->renderSolidCube(1.0f, cubeColor); + DependencyManager::get()->renderSolidCube(1.0f, cubeColor); } glPopMatrix(); } else { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index f6557b5f9a..dde13552f3 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -34,8 +34,8 @@ #include "RenderableParticleEffectEntityItem.h" #include "RenderableSphereEntityItem.h" #include "RenderableTextEntityItem.h" +#include "RenderableZoneEntityItem.h" #include "EntitiesRendererLogging.h" -#include "ZoneEntityItem.h" EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState, AbstractScriptingServicesInterface* scriptingServices) : @@ -57,6 +57,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Text, RenderableTextEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(ParticleEffect, RenderableParticleEffectEntityItem::factory) + REGISTER_ENTITY_TYPE_WITH_FACTORY(Zone, RenderableZoneEntityItem::factory) _currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID _currentClickingOnEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID @@ -496,7 +497,7 @@ const FBXGeometry* EntityTreeRenderer::getCollisionGeometryForEntity(const Entit if (entityItem->getType() == EntityTypes::Model) { const RenderableModelEntityItem* constModelEntityItem = dynamic_cast(entityItem); - if (constModelEntityItem->hasCollisionModel()) { + if (constModelEntityItem->hasCompoundShapeURL()) { RenderableModelEntityItem* modelEntityItem = const_cast(constModelEntityItem); Model* model = modelEntityItem->getModel(this); if (model) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 86b0be4dd8..8ca6562505 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -224,10 +224,10 @@ Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { // if we have a previously allocated model, but its URL doesn't match // then we need to let our renderer update our model for us. if (_model && QUrl(getModelURL()) != _model->getURL()) { - result = _model = _myRenderer->updateModel(_model, getModelURL(), getCollisionModelURL()); + result = _model = _myRenderer->updateModel(_model, getModelURL(), getCompoundShapeURL()); _needsInitialSimulation = true; } else if (!_model) { // if we don't yet have a model, then we want our renderer to allocate one - result = _model = _myRenderer->allocateModel(getModelURL(), getCollisionModelURL()); + result = _model = _myRenderer->allocateModel(getModelURL(), getCompoundShapeURL()); _needsInitialSimulation = true; } else { // we already have the model we want... result = _model; @@ -267,8 +267,8 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, extraInfo, precisionPicking); } -void RenderableModelEntityItem::setCollisionModelURL(const QString& url) { - ModelEntityItem::setCollisionModelURL(url); +void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) { + ModelEntityItem::setCompoundShapeURL(url); if (_model) { _model->setCollisionModelURL(QUrl(url)); } @@ -410,8 +410,17 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { } glm::vec3 collisionModelDimensions = box.getDimensions(); - info.setParams(type, collisionModelDimensions, _collisionModelURL); + info.setParams(type, collisionModelDimensions, _compoundShapeURL); info.setConvexHulls(_points); } } +bool RenderableModelEntityItem::contains(const glm::vec3& point) const { + if (EntityItem::contains(point) && _model && _model->getCollisionGeometry()) { + const QSharedPointer collisionNetworkGeometry = _model->getCollisionGeometry(); + const FBXGeometry& collisionGeometry = collisionNetworkGeometry->getFBXGeometry(); + return collisionGeometry.convexHullContains(worldToEntity(point)); + } + + return false; +} diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index b632357942..43f18af0db 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -52,10 +52,12 @@ public: bool needsToCallUpdate() const; - virtual void setCollisionModelURL(const QString& url); + virtual void setCompoundShapeURL(const QString& url); bool isReadyToComputeShape(); void computeShapeInfo(ShapeInfo& info); + + virtual bool contains(const glm::vec3& point) const; private: void remapTextures(); diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp new file mode 100644 index 0000000000..e4c56dc419 --- /dev/null +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -0,0 +1,57 @@ +// +// RenderableZoneEntityItem.cpp +// +// +// Created by Clement on 4/22/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "RenderableZoneEntityItem.h" + +#include +#include + +EntityItem* RenderableZoneEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + return new RenderableZoneEntityItem(entityID, properties); +} + +bool RenderableZoneEntityItem::setProperties(const EntityItemProperties& properties) { + QString oldShapeURL = getCompoundShapeURL(); + bool somethingChanged = ZoneEntityItem::setProperties(properties); + if (somethingChanged && oldShapeURL != getCompoundShapeURL()) { + _compoundShapeModel = DependencyManager::get()->getGeometry(getCompoundShapeURL(), QUrl(), true); + } + return somethingChanged; +} + +int RenderableZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData) { + QString oldShapeURL = getCompoundShapeURL(); + int bytesRead = ZoneEntityItem::readEntitySubclassDataFromBuffer(data, bytesLeftToRead, + args, propertyFlags, overwriteLocalData); + if (oldShapeURL != getCompoundShapeURL()) { + _compoundShapeModel = DependencyManager::get()->getGeometry(getCompoundShapeURL(), QUrl(), true); + } + return bytesRead; +} + +bool RenderableZoneEntityItem::contains(const glm::vec3& point) const { + if (getShapeType() != SHAPE_TYPE_COMPOUND) { + return EntityItem::contains(point); + } + if (!_compoundShapeModel && hasCompoundShapeURL()) { + const_cast(this)->_compoundShapeModel = DependencyManager::get()->getGeometry(getCompoundShapeURL(), QUrl(), true); + } + + if (EntityItem::contains(point) && _compoundShapeModel && _compoundShapeModel->isLoaded()) { + const FBXGeometry& geometry = _compoundShapeModel->getFBXGeometry(); + glm::vec3 meshDimensions = geometry.getUnscaledMeshExtents().maximum - geometry.getUnscaledMeshExtents().minimum; + return geometry.convexHullContains(worldToEntity(point) * meshDimensions); + } + + return false; +} \ No newline at end of file diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h new file mode 100644 index 0000000000..8d8d8b4b3f --- /dev/null +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -0,0 +1,39 @@ +// +// RenderableZoneEntityItem.h +// +// +// Created by Clement on 4/22/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_RenderableZoneEntityItem_h +#define hifi_RenderableZoneEntityItem_h + +#include + +class NetworkGeometry; + +class RenderableZoneEntityItem : public ZoneEntityItem { +public: + static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + RenderableZoneEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : + ZoneEntityItem(entityItemID, properties) + { } + + virtual bool setProperties(const EntityItemProperties& properties); + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData); + + virtual bool contains(const glm::vec3& point) const; + +private: + + QSharedPointer _compoundShapeModel; +}; + +#endif // hifi_RenderableZoneEntityItem_h \ No newline at end of file diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 12fb1ba99d..f968244ab3 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -11,6 +11,8 @@ #include +#include + #include #include #include @@ -25,75 +27,53 @@ bool EntityItem::_sendPhysicsUpdates = true; -void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) { - _id = entityItemID.id; - _creatorTokenID = entityItemID.creatorTokenID; - - // init values with defaults before calling setProperties +EntityItem::EntityItem(const EntityItemID& entityItemID) : + _type(EntityTypes::Unknown), + _id(entityItemID.id), + _creatorTokenID(entityItemID.creatorTokenID), + _newlyCreated(false), + _lastSimulated(0), + _lastUpdated(0), + _lastEdited(0), + _lastEditedFromRemote(0), + _lastEditedFromRemoteInRemoteTime(0), + _created(UNKNOWN_CREATED_TIME), + _changedOnServer(0), + _position(ENTITY_ITEM_ZERO_VEC3), + _dimensions(ENTITY_ITEM_DEFAULT_DIMENSIONS), + _rotation(ENTITY_ITEM_DEFAULT_ROTATION), + _glowLevel(ENTITY_ITEM_DEFAULT_GLOW_LEVEL), + _localRenderAlpha(ENTITY_ITEM_DEFAULT_LOCAL_RENDER_ALPHA), + _density(ENTITY_ITEM_DEFAULT_DENSITY), + _volumeMultiplier(1.0f), + _velocity(ENTITY_ITEM_DEFAULT_VELOCITY), + _gravity(ENTITY_ITEM_DEFAULT_GRAVITY), + _acceleration(ENTITY_ITEM_DEFAULT_ACCELERATION), + _damping(ENTITY_ITEM_DEFAULT_DAMPING), + _lifetime(ENTITY_ITEM_DEFAULT_LIFETIME), + _script(ENTITY_ITEM_DEFAULT_SCRIPT), + _registrationPoint(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT), + _angularVelocity(ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY), + _angularDamping(ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING), + _visible(ENTITY_ITEM_DEFAULT_VISIBLE), + _ignoreForCollisions(ENTITY_ITEM_DEFAULT_IGNORE_FOR_COLLISIONS), + _collisionsWillMove(ENTITY_ITEM_DEFAULT_COLLISIONS_WILL_MOVE), + _locked(ENTITY_ITEM_DEFAULT_LOCKED), + _userData(ENTITY_ITEM_DEFAULT_USER_DATA), + _simulatorID(ENTITY_ITEM_DEFAULT_SIMULATOR_ID), + _simulatorIDChangedTime(0), + _marketplaceID(ENTITY_ITEM_DEFAULT_MARKETPLACE_ID), + _physicsInfo(NULL), + _dirtyFlags(0), + _element(NULL) +{ quint64 now = usecTimestampNow(); _lastSimulated = now; _lastUpdated = now; - _lastEdited = 0; - _lastEditedFromRemote = 0; - _lastEditedFromRemoteInRemoteTime = 0; - _created = UNKNOWN_CREATED_TIME; - _changedOnServer = 0; - - _position = ENTITY_ITEM_ZERO_VEC3; - _dimensions = ENTITY_ITEM_DEFAULT_DIMENSIONS; - _density = ENTITY_ITEM_DEFAULT_DENSITY; - _rotation = ENTITY_ITEM_DEFAULT_ROTATION; - _glowLevel = ENTITY_ITEM_DEFAULT_GLOW_LEVEL; - _localRenderAlpha = ENTITY_ITEM_DEFAULT_LOCAL_RENDER_ALPHA; - _velocity = ENTITY_ITEM_DEFAULT_VELOCITY; - _gravity = ENTITY_ITEM_DEFAULT_GRAVITY; - _acceleration = ENTITY_ITEM_DEFAULT_ACCELERATION; - _damping = ENTITY_ITEM_DEFAULT_DAMPING; - _lifetime = ENTITY_ITEM_DEFAULT_LIFETIME; - _script = ENTITY_ITEM_DEFAULT_SCRIPT; - _registrationPoint = ENTITY_ITEM_DEFAULT_REGISTRATION_POINT; - _angularVelocity = ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY; - _angularDamping = ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING; - _visible = ENTITY_ITEM_DEFAULT_VISIBLE; - _ignoreForCollisions = ENTITY_ITEM_DEFAULT_IGNORE_FOR_COLLISIONS; - _collisionsWillMove = ENTITY_ITEM_DEFAULT_COLLISIONS_WILL_MOVE; - _locked = ENTITY_ITEM_DEFAULT_LOCKED; - _userData = ENTITY_ITEM_DEFAULT_USER_DATA; - _simulatorID = ENTITY_ITEM_DEFAULT_SIMULATOR_ID; - _marketplaceID = ENTITY_ITEM_DEFAULT_MARKETPLACE_ID; } -EntityItem::EntityItem(const EntityItemID& entityItemID) { - _type = EntityTypes::Unknown; - quint64 now = usecTimestampNow(); - _lastSimulated = now; - _lastUpdated = now; - _lastEdited = 0; - _lastEditedFromRemote = 0; - _lastEditedFromRemoteInRemoteTime = 0; - _created = UNKNOWN_CREATED_TIME; - _dirtyFlags = 0; - _changedOnServer = 0; - _element = NULL; - _simulatorIDChangedTime = 0; - _shouldClaimSimulationOwnership = false; - initFromEntityItemID(entityItemID); -} - -EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) { - _type = EntityTypes::Unknown; - quint64 now = usecTimestampNow(); - _lastSimulated = now; - _lastUpdated = now; - _lastEdited = 0; - _lastEditedFromRemote = 0; - _lastEditedFromRemoteInRemoteTime = 0; - _created = UNKNOWN_CREATED_TIME; - _dirtyFlags = 0; - _changedOnServer = 0; - _element = NULL; - _simulatorIDChangedTime = 0; - initFromEntityItemID(entityItemID); +EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : EntityItem(entityItemID) +{ setProperties(properties); } @@ -621,7 +601,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef auto nodeList = DependencyManager::get(); const QUuid& myNodeID = nodeList->getSessionUUID(); - if (_simulatorID == myNodeID) { + if (_simulatorID == myNodeID && !_simulatorID.isNull()) { // the packet that produced this bitstream originally came from physics simulations performed by // this node, so our version has to be newer than what the packet contained. _position = savePosition; @@ -843,7 +823,27 @@ bool EntityItem::isMoving() const { return hasVelocity() || hasAngularVelocity(); } -bool EntityItem::lifetimeHasExpired() const { +glm::mat4 EntityItem::getEntityToWorldMatrix() const { + glm::mat4 translation = glm::translate(getPosition()); + glm::mat4 rotation = glm::mat4_cast(getRotation()); + glm::mat4 scale = glm::scale(getDimensions()); + glm::mat4 registration = glm::translate(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()); + return translation * rotation * scale * registration; +} + +glm::mat4 EntityItem::getWorldToEntityMatrix() const { + return glm::inverse(getEntityToWorldMatrix()); +} + +glm::vec3 EntityItem::entityToWorld(const glm::vec3 point) const { + return glm::vec3(getEntityToWorldMatrix() * glm::vec4(point, 1.0f)); +} + +glm::vec3 EntityItem::worldToEntity(const glm::vec3 point) const { + return glm::vec3(getWorldToEntityMatrix() * glm::vec4(point, 1.0f)); +} + +bool EntityItem::lifetimeHasExpired() const { return isMortal() && (getAge() > getLifetime()); } @@ -1033,12 +1033,6 @@ AABox EntityItem::getAABox() const { return AABox(rotatedExtentsRelativeToRegistrationPoint); } -AABox EntityItem::getAABoxInDomainUnits() const { - AABox box = getAABox(); - box.scale(1.0f / (float)TREE_SCALE); - return box; -} - // 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) @@ -1066,6 +1060,16 @@ float EntityItem::getRadius() const { return 0.5f * glm::length(_dimensions); } +bool EntityItem::contains(const glm::vec3& point) const { + if (getShapeType() == SHAPE_TYPE_COMPOUND) { + return getAABox().contains(point); + } else { + ShapeInfo info; + info.setParams(getShapeType(), glm::vec3(0.5f)); + return info.contains(worldToEntity(point)); + } +} + void EntityItem::computeShapeInfo(ShapeInfo& info) { info.setParams(getShapeType(), 0.5f * getDimensions()); } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 28f1c7790b..fda8167564 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -145,25 +145,16 @@ public: // attributes applicable to all entity types EntityTypes::EntityType getType() const { return _type; } - glm::vec3 getPositionInDomainUnits() const { return _position / (float)TREE_SCALE; } /// get position in domain scale units (0.0 - 1.0) const glm::vec3& getPosition() const { return _position; } /// get position in meters - /// set position in domain scale units (0.0 - 1.0) - void setPositionInDomainUnits(const glm::vec3& value) - { setPosition(glm::clamp(value, 0.0f, 1.0f) * (float)TREE_SCALE); } void setPosition(const glm::vec3& value) { _position = value; } - glm::vec3 getCenterInDomainUnits() const { return getCenter() / (float) TREE_SCALE; } glm::vec3 getCenter() const; - glm::vec3 getDimensionsInDomainUnits() const { return _dimensions / (float)TREE_SCALE; } /// get dimensions in domain scale units (0.0 - 1.0) const glm::vec3& getDimensions() const { return _dimensions; } /// get dimensions in meters - /// set dimensions in domain scale units (0.0 - 1.0) - virtual void setDimensionsInDomainUnits(const glm::vec3& value) { _dimensions = glm::abs(value) * (float)TREE_SCALE; } - /// set dimensions in meter units (0.0 - TREE_SCALE) virtual void setDimensions(const glm::vec3& value) { _dimensions = glm::abs(value); } @@ -182,15 +173,11 @@ public: float getDensity() const { return _density; } - glm::vec3 getVelocityInDomainUnits() const { return _velocity / (float)TREE_SCALE; } /// velocity in domain scale units (0.0-1.0) per second const glm::vec3 getVelocity() const { return _velocity; } /// get velocity in meters - void setVelocityInDomainUnits(const glm::vec3& value) { _velocity = value * (float)TREE_SCALE; } /// velocity in domain scale units (0.0-1.0) per second void setVelocity(const glm::vec3& value) { _velocity = value; } /// velocity in meters bool hasVelocity() const { return _velocity != ENTITY_ITEM_ZERO_VEC3; } - glm::vec3 getGravityInDomainUnits() const { return _gravity / (float)TREE_SCALE; } /// gravity in domain scale units (0.0-1.0) per second squared const glm::vec3& getGravity() const { return _gravity; } /// get gravity in meters - void setGravityInDomainUnits(const glm::vec3& value) { _gravity = value * (float)TREE_SCALE; } /// gravity in domain scale units (0.0-1.0) per second squared void setGravity(const glm::vec3& value) { _gravity = value; } /// gravity in meters bool hasGravity() const { return _gravity != ENTITY_ITEM_ZERO_VEC3; } @@ -220,7 +207,6 @@ public: AACube getMaximumAACube() const; AACube getMinimumAACube() const; AABox getAABox() const; /// axis aligned bounding box in world-frame (meters) - AABox getAABoxInDomainUnits() const; /// axis aligned bounding box in domain scale units (0.0 - 1.0) const QString& getScript() const { return _script; } void setScript(const QString& value) { _script = value; } @@ -258,8 +244,6 @@ public: QUuid getSimulatorID() const { return _simulatorID; } void setSimulatorID(const QUuid& value); quint64 getSimulatorIDChangedTime() const { return _simulatorIDChangedTime; } - void setShouldClaimSimulationOwnership(bool value) { _shouldClaimSimulationOwnership = value; } - bool getShouldClaimSimulationOwnership() { return _shouldClaimSimulationOwnership; } const QString& getMarketplaceID() const { return _marketplaceID; } void setMarketplaceID(const QString& value) { _marketplaceID = value; } @@ -267,8 +251,7 @@ public: // TODO: get rid of users of getRadius()... float getRadius() const; - virtual bool contains(const glm::vec3& point) const { return getAABox().contains(point); } - virtual bool containsInDomainUnits(const glm::vec3& point) const { return getAABoxInDomainUnits().contains(point); } + virtual bool contains(const glm::vec3& point) const; virtual bool isReadyToComputeShape() { return true; } virtual void computeShapeInfo(ShapeInfo& info); @@ -312,12 +295,14 @@ public: static void setSendPhysicsUpdates(bool value) { _sendPhysicsUpdates = value; } static bool getSendPhysicsUpdates() { return _sendPhysicsUpdates; } + glm::mat4 getEntityToWorldMatrix() const; + glm::mat4 getWorldToEntityMatrix() const; + glm::vec3 worldToEntity(const glm::vec3 point) const; + glm::vec3 entityToWorld(const glm::vec3 point) const; + protected: static bool _sendPhysicsUpdates; - - virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init - EntityTypes::EntityType _type; QUuid _id; uint32_t _creatorTokenID; @@ -357,7 +342,6 @@ protected: QString _userData; QUuid _simulatorID; // id of Node which is currently responsible for simulating this Entity quint64 _simulatorIDChangedTime; // when was _simulatorID last updated? - bool _shouldClaimSimulationOwnership; QString _marketplaceID; // NOTE: Damping is applied like this: v *= pow(1 - damping, dt) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 5c834f71bf..7d5a40f6fd 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -44,7 +44,7 @@ EntityItemProperties::EntityItemProperties() : CONSTRUCT_PROPERTY(script, ENTITY_ITEM_DEFAULT_SCRIPT), CONSTRUCT_PROPERTY(color, ), CONSTRUCT_PROPERTY(modelURL, ""), - CONSTRUCT_PROPERTY(collisionModelURL, ""), + CONSTRUCT_PROPERTY(compoundShapeURL, ""), CONSTRUCT_PROPERTY(animationURL, ""), CONSTRUCT_PROPERTY(animationFPS, ModelEntityItem::DEFAULT_ANIMATION_FPS), CONSTRUCT_PROPERTY(animationFrameIndex, ModelEntityItem::DEFAULT_ANIMATION_FRAME_INDEX), @@ -175,7 +175,7 @@ void EntityItemProperties::debugDump() const { qCDebug(entities) << " _position=" << _position.x << "," << _position.y << "," << _position.z; qCDebug(entities) << " _dimensions=" << getDimensions(); qCDebug(entities) << " _modelURL=" << _modelURL; - qCDebug(entities) << " _collisionModelURL=" << _collisionModelURL; + qCDebug(entities) << " _compoundShapeURL=" << _compoundShapeURL; qCDebug(entities) << " changed properties..."; EntityPropertyFlags props = getChangedProperties(); props.debugDumpBits(); @@ -245,7 +245,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_SCRIPT, script); CHECK_PROPERTY_CHANGE(PROP_COLOR, color); CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL); - CHECK_PROPERTY_CHANGE(PROP_COLLISION_MODEL_URL, collisionModelURL); + CHECK_PROPERTY_CHANGE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_URL, animationURL); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_PLAYING, animationIsPlaying); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FRAME_INDEX, animationFrameIndex); @@ -322,7 +322,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons COPY_PROPERTY_TO_QSCRIPTVALUE(visible); COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR(color); COPY_PROPERTY_TO_QSCRIPTVALUE(modelURL); - COPY_PROPERTY_TO_QSCRIPTVALUE(collisionModelURL); + COPY_PROPERTY_TO_QSCRIPTVALUE(compoundShapeURL); COPY_PROPERTY_TO_QSCRIPTVALUE(animationURL); COPY_PROPERTY_TO_QSCRIPTVALUE(animationIsPlaying); COPY_PROPERTY_TO_QSCRIPTVALUE(animationFPS); @@ -417,7 +417,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) { 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(collisionModelURL, setCollisionModelURL); + COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(compoundShapeURL, setCompoundShapeURL); COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(animationURL, setAnimationURL); COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(animationIsPlaying, setAnimationIsPlaying); COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(animationFPS, setAnimationFPS); @@ -618,7 +618,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem if (properties.getType() == EntityTypes::Model) { APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, appendValue, properties.getModelURL()); - APPEND_ENTITY_PROPERTY(PROP_COLLISION_MODEL_URL, appendValue, properties.getCollisionModelURL()); + APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, appendValue, properties.getCompoundShapeURL()); 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()); @@ -657,6 +657,9 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, appendValue, properties.getStageAltitude()); APPEND_ENTITY_PROPERTY(PROP_STAGE_DAY, appendValue, properties.getStageDay()); APPEND_ENTITY_PROPERTY(PROP_STAGE_HOUR, appendValue, properties.getStageHour()); + + APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, appendValue, (uint32_t)properties.getShapeType()); + APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, appendValue, properties.getCompoundShapeURL()); } APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, appendValue, properties.getMarketplaceID()); @@ -864,7 +867,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int if (properties.getType() == EntityTypes::Model) { READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_MODEL_URL, setModelURL); - READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_COLLISION_MODEL_URL, setCollisionModelURL); + READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, setCompoundShapeURL); 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); @@ -903,6 +906,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STAGE_ALTITUDE, float, setStageAltitude); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STAGE_DAY, quint16, setStageDay); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STAGE_HOUR, float, setStageHour); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType); + READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, setCompoundShapeURL); } READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_MARKETPLACE_ID, setMarketplaceID); @@ -958,7 +963,7 @@ void EntityItemProperties::markAllChanged() { _visibleChanged = true; _colorChanged = true; _modelURLChanged = true; - _collisionModelURLChanged = true; + _compoundShapeURLChanged = true; _animationURLChanged = true; _animationIsPlayingChanged = true; _animationFrameIndexChanged = true; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 5172b3abba..faec9e1206 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -93,7 +93,7 @@ enum EntityPropertyList { PROP_LOCAL_GRAVITY, PROP_PARTICLE_RADIUS, - PROP_COLLISION_MODEL_URL, + PROP_COMPOUND_SHAPE_URL, PROP_MARKETPLACE_ID, PROP_ACCELERATION, PROP_SIMULATOR_ID, @@ -197,7 +197,7 @@ public: DEFINE_PROPERTY_REF(PROP_SCRIPT, Script, script, QString); DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, xColor); DEFINE_PROPERTY_REF(PROP_MODEL_URL, ModelURL, modelURL, QString); - DEFINE_PROPERTY_REF(PROP_COLLISION_MODEL_URL, CollisionModelURL, collisionModelURL, QString); + DEFINE_PROPERTY_REF(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString); DEFINE_PROPERTY_REF(PROP_ANIMATION_URL, AnimationURL, animationURL, QString); DEFINE_PROPERTY(PROP_ANIMATION_FPS, AnimationFPS, animationFPS, float); DEFINE_PROPERTY(PROP_ANIMATION_FRAME_INDEX, AnimationFrameIndex, animationFrameIndex, float); @@ -340,7 +340,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, Script, script, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Color, color, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, ModelURL, modelURL, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, CollisionModelURL, collisionModelURL, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, CompoundShapeURL, compoundShapeURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationURL, animationURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationFPS, animationFPS, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationFrameIndex, animationFrameIndex, ""); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 53335beda0..04da26a1ff 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -15,6 +15,7 @@ #include "EntityTree.h" #include "LightEntityItem.h" #include "ModelEntityItem.h" +#include "ZoneEntityItem.h" EntityScriptingInterface::EntityScriptingInterface() : @@ -64,17 +65,8 @@ void EntityScriptingInterface::setEntityTree(EntityTree* modelTree) { void setSimId(EntityItemProperties& propertiesWithSimID, EntityItem* entity) { auto nodeList = DependencyManager::get(); const QUuid myNodeID = nodeList->getSessionUUID(); - - // if this entity has non-zero physics/simulation related values, claim simulation ownership - if (propertiesWithSimID.velocityChanged() || - propertiesWithSimID.rotationChanged() || - propertiesWithSimID.containsPositionChange()) { - propertiesWithSimID.setSimulatorID(myNodeID); - entity->setSimulatorID(myNodeID); - } else if (entity->getSimulatorID() == myNodeID) { - propertiesWithSimID.setSimulatorID(QUuid()); // give up simulation ownership - entity->setSimulatorID(QUuid()); - } + propertiesWithSimID.setSimulatorID(myNodeID); + entity->setSimulatorID(myNodeID); } @@ -315,6 +307,14 @@ bool EntityScriptingInterface::getLightsArePickable() const { return LightEntityItem::getLightsArePickable(); } +void EntityScriptingInterface::setZonesArePickable(bool value) { + ZoneEntityItem::setZonesArePickable(value); +} + +bool EntityScriptingInterface::getZonesArePickable() const { + return ZoneEntityItem::getZonesArePickable(); +} + void EntityScriptingInterface::setSendPhysicsUpdates(bool value) { EntityItem::setSendPhysicsUpdates(value); } diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 075f5a712b..151036e926 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -111,6 +111,9 @@ public slots: Q_INVOKABLE void setLightsArePickable(bool value); Q_INVOKABLE bool getLightsArePickable() const; + Q_INVOKABLE void setZonesArePickable(bool value); + Q_INVOKABLE bool getZonesArePickable() const; + Q_INVOKABLE void setSendPhysicsUpdates(bool value); Q_INVOKABLE bool getSendPhysicsUpdates() const; diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index f95eeea8e4..1abb48abcd 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -21,7 +21,7 @@ #include "ModelEntityItem.h" const QString ModelEntityItem::DEFAULT_MODEL_URL = QString(""); -const QString ModelEntityItem::DEFAULT_COLLISION_MODEL_URL = QString(""); +const QString ModelEntityItem::DEFAULT_COMPOUND_SHAPE_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; @@ -47,7 +47,7 @@ EntityItemProperties ModelEntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(modelURL, getModelURL); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionModelURL, getCollisionModelURL); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationURL, getAnimationURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationIsPlaying, getAnimationIsPlaying); COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationFrameIndex, getAnimationFrameIndex); @@ -65,7 +65,7 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); SET_ENTITY_PROPERTY_FROM_PROPERTIES(modelURL, setModelURL); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionModelURL, setCollisionModelURL); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationURL, setAnimationURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationIsPlaying, setAnimationIsPlaying); SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationFrameIndex, setAnimationFrameIndex); @@ -98,11 +98,11 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY_COLOR(PROP_COLOR, _color); READ_ENTITY_PROPERTY_STRING(PROP_MODEL_URL, setModelURL); if (args.bitstreamVersion < VERSION_ENTITIES_HAS_COLLISION_MODEL) { - setCollisionModelURL(""); + setCompoundShapeURL(""); } else if (args.bitstreamVersion == VERSION_ENTITIES_HAS_COLLISION_MODEL) { - READ_ENTITY_PROPERTY_STRING(PROP_COLLISION_MODEL_URL_OLD_VERSION, setCollisionModelURL); + READ_ENTITY_PROPERTY_STRING(PROP_COMPOUND_SHAPE_URL, setCompoundShapeURL); } else { - READ_ENTITY_PROPERTY_STRING(PROP_COLLISION_MODEL_URL, setCollisionModelURL); + READ_ENTITY_PROPERTY_STRING(PROP_COMPOUND_SHAPE_URL, setCompoundShapeURL); } READ_ENTITY_PROPERTY_STRING(PROP_ANIMATION_URL, setAnimationURL); @@ -140,7 +140,7 @@ EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); requestedProperties += PROP_MODEL_URL; - requestedProperties += PROP_COLLISION_MODEL_URL; + requestedProperties += PROP_COMPOUND_SHAPE_URL; requestedProperties += PROP_ANIMATION_URL; requestedProperties += PROP_ANIMATION_FPS; requestedProperties += PROP_ANIMATION_FRAME_INDEX; @@ -164,7 +164,7 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, getColor()); APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, appendValue, getModelURL()); - APPEND_ENTITY_PROPERTY(PROP_COLLISION_MODEL_URL, appendValue, getCollisionModelURL()); + APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, appendValue, getCompoundShapeURL()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, appendValue, getAnimationURL()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, appendValue, getAnimationFPS()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, appendValue, getAnimationFrameIndex()); @@ -272,7 +272,7 @@ void ModelEntityItem::debugDump() const { qCDebug(entities) << " position:" << getPosition(); qCDebug(entities) << " dimensions:" << getDimensions(); qCDebug(entities) << " model URL:" << getModelURL(); - qCDebug(entities) << " collision model URL:" << getCollisionModelURL(); + qCDebug(entities) << " compound shape URL:" << getCompoundShapeURL(); } void ModelEntityItem::updateShapeType(ShapeType type) { @@ -280,8 +280,8 @@ void ModelEntityItem::updateShapeType(ShapeType type) { // we have allowed inconsistent ShapeType's to be stored in SVO files in the past (this was a bug) // but we are now enforcing the entity properties to be consistent. To make the possible we're // introducing a temporary workaround: we will ignore ShapeType updates that conflict with the - // _collisionModelURL. - if (hasCollisionModel()) { + // _compoundShapeURL. + if (hasCompoundShapeURL()) { type = SHAPE_TYPE_COMPOUND; } // END_TEMPORARY_WORKAROUND @@ -295,17 +295,17 @@ void ModelEntityItem::updateShapeType(ShapeType type) { // virtual ShapeType ModelEntityItem::getShapeType() const { if (_shapeType == SHAPE_TYPE_COMPOUND) { - return hasCollisionModel() ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE; + return hasCompoundShapeURL() ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE; } else { return _shapeType; } } -void ModelEntityItem::setCollisionModelURL(const QString& url) { - if (_collisionModelURL != url) { - _collisionModelURL = url; +void ModelEntityItem::setCompoundShapeURL(const QString& url) { + if (_compoundShapeURL != url) { + _compoundShapeURL = url; _dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS; - _shapeType = _collisionModelURL.isEmpty() ? SHAPE_TYPE_NONE : SHAPE_TYPE_COMPOUND; + _shapeType = _compoundShapeURL.isEmpty() ? SHAPE_TYPE_NONE : SHAPE_TYPE_COMPOUND; } } diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 057c5babaf..6fe2adc928 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -57,13 +57,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(); } - virtual bool hasCollisionModel() const { return !_collisionModelURL.isEmpty(); } + virtual bool hasCompoundShapeURL() const { return !_compoundShapeURL.isEmpty(); } static const QString DEFAULT_MODEL_URL; const QString& getModelURL() const { return _modelURL; } - static const QString DEFAULT_COLLISION_MODEL_URL; - const QString& getCollisionModelURL() const { return _collisionModelURL; } + static const QString DEFAULT_COMPOUND_SHAPE_URL; + const QString& getCompoundShapeURL() const { return _compoundShapeURL; } bool hasAnimation() const { return !_animationURL.isEmpty(); } static const QString DEFAULT_ANIMATION_URL; @@ -78,7 +78,7 @@ public: // model related properties void setModelURL(const QString& url) { _modelURL = url; } - virtual void setCollisionModelURL(const QString& url); + virtual void setCompoundShapeURL(const QString& url); void setAnimationURL(const QString& url); static const float DEFAULT_ANIMATION_FRAME_INDEX; void setAnimationFrameIndex(float value); @@ -126,7 +126,7 @@ protected: rgbColor _color; QString _modelURL; - QString _collisionModelURL; + QString _compoundShapeURL; quint64 _lastAnimated; QString _animationURL; diff --git a/libraries/entities/src/SphereEntityItem.cpp b/libraries/entities/src/SphereEntityItem.cpp index 132ad43336..d17e49cb59 100644 --- a/libraries/entities/src/SphereEntityItem.cpp +++ b/libraries/entities/src/SphereEntityItem.cpp @@ -96,11 +96,7 @@ bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, cons bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject, bool precisionPicking) const { // determine the ray in the frame of the entity transformed from a unit sphere - glm::mat4 translation = glm::translate(getPosition()); - glm::mat4 rotation = glm::mat4_cast(getRotation()); - glm::mat4 scale = glm::scale(getDimensions()); - glm::mat4 registration = glm::translate(glm::vec3(0.5f, 0.5f, 0.5f) - getRegistrationPoint()); - glm::mat4 entityToWorldMatrix = translation * rotation * scale * registration; + glm::mat4 entityToWorldMatrix = getEntityToWorldMatrix(); glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); glm::vec3 entityFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f)); glm::vec3 entityFrameDirection = glm::normalize(glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f))); @@ -112,7 +108,7 @@ bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, cons glm::vec3 entityFrameHitAt = entityFrameOrigin + (entityFrameDirection * localDistance); // then translate back to work coordinates glm::vec3 hitAt = glm::vec3(entityToWorldMatrix * glm::vec4(entityFrameHitAt, 1.0f)); - distance = glm::distance(origin,hitAt); + distance = glm::distance(origin, hitAt); return true; } return false; diff --git a/libraries/entities/src/SphereEntityItem.h b/libraries/entities/src/SphereEntityItem.h index f79a2db7ff..b6516b714f 100644 --- a/libraries/entities/src/SphereEntityItem.h +++ b/libraries/entities/src/SphereEntityItem.h @@ -50,9 +50,6 @@ public: _color[BLUE_INDEX] = value.blue; } - // TODO: implement proper contains for 3D ellipsoid - //virtual bool contains(const glm::vec3& point) const; - virtual ShapeType getShapeType() const { return SHAPE_TYPE_SPHERE; } virtual bool supportsDetailedRayIntersection() const { return true; } diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp index 39a4d48d96..34d0ce051c 100644 --- a/libraries/entities/src/TextEntityItem.cpp +++ b/libraries/entities/src/TextEntityItem.cpp @@ -48,11 +48,6 @@ void TextEntityItem::setDimensions(const glm::vec3& value) { _dimensions = glm::vec3(value.x, value.y, TEXT_ENTITY_ITEM_FIXED_DEPTH); } -void TextEntityItem::setDimensionsInDomainUnits(const glm::vec3& value) { - // NOTE: Text Entities always have a "depth" of 1cm. - _dimensions = glm::vec3(value.x * (float)TREE_SCALE, value.y * (float)TREE_SCALE, TEXT_ENTITY_ITEM_FIXED_DEPTH); -} - EntityItemProperties TextEntityItem::getProperties() const { EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class diff --git a/libraries/entities/src/TextEntityItem.h b/libraries/entities/src/TextEntityItem.h index 044975bdc8..d57b5442d6 100644 --- a/libraries/entities/src/TextEntityItem.h +++ b/libraries/entities/src/TextEntityItem.h @@ -24,7 +24,6 @@ public: /// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately virtual void setDimensions(const glm::vec3& value); - virtual void setDimensionsInDomainUnits(const glm::vec3& value); virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; } // methods for getting/setting all properties of an entity diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index e0fe238c57..9ccb2697a0 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -31,6 +31,8 @@ const float ZoneEntityItem::DEFAULT_STAGE_LONGITUDE = 122.407f; const float ZoneEntityItem::DEFAULT_STAGE_ALTITUDE = 0.03f; const quint16 ZoneEntityItem::DEFAULT_STAGE_DAY = 60; const float ZoneEntityItem::DEFAULT_STAGE_HOUR = 12.0f; +const ShapeType ZoneEntityItem::DEFAULT_SHAPE_TYPE = SHAPE_TYPE_BOX; +const QString ZoneEntityItem::DEFAULT_COMPOUND_SHAPE_URL = ""; EntityItem* ZoneEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItem* result = new ZoneEntityItem(entityID, properties); @@ -56,6 +58,8 @@ ZoneEntityItem::ZoneEntityItem(const EntityItemID& entityItemID, const EntityIte _stageAltitude = DEFAULT_STAGE_ALTITUDE; _stageDay = DEFAULT_STAGE_DAY; _stageHour = DEFAULT_STAGE_HOUR; + _shapeType = DEFAULT_SHAPE_TYPE; + _compoundShapeURL = DEFAULT_COMPOUND_SHAPE_URL; setProperties(properties); } @@ -73,6 +77,8 @@ EntityItemProperties ZoneEntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(stageAltitude, getStageAltitude); COPY_ENTITY_PROPERTY_TO_PROPERTIES(stageDay, getStageDay); COPY_ENTITY_PROPERTY_TO_PROPERTIES(stageHour, getStageHour); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL); return properties; } @@ -91,6 +97,8 @@ bool ZoneEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(stageAltitude, setStageAltitude); SET_ENTITY_PROPERTY_FROM_PROPERTIES(stageDay, setStageDay); SET_ENTITY_PROPERTY_FROM_PROPERTIES(stageHour, setStageHour); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, updateShapeType); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL); if (somethingChanged) { bool wantDebug = false; @@ -122,6 +130,8 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, float, _stageAltitude); READ_ENTITY_PROPERTY(PROP_STAGE_DAY, quint16, _stageDay); READ_ENTITY_PROPERTY(PROP_STAGE_HOUR, float, _stageHour); + READ_ENTITY_PROPERTY_SETTER(PROP_SHAPE_TYPE, ShapeType, updateShapeType); + READ_ENTITY_PROPERTY_STRING(PROP_COMPOUND_SHAPE_URL, setCompoundShapeURL); return bytesRead; } @@ -141,6 +151,8 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p requestedProperties += PROP_STAGE_ALTITUDE; requestedProperties += PROP_STAGE_DAY; requestedProperties += PROP_STAGE_HOUR; + requestedProperties += PROP_SHAPE_TYPE; + requestedProperties += PROP_COMPOUND_SHAPE_URL; return requestedProperties; } @@ -165,6 +177,8 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits APPEND_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, appendValue, getStageAltitude()); APPEND_ENTITY_PROPERTY(PROP_STAGE_DAY, appendValue, getStageDay()); APPEND_ENTITY_PROPERTY(PROP_STAGE_HOUR, appendValue, getStageHour()); + APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, appendValue, (uint32_t)getShapeType()); + APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, appendValue, getCompoundShapeURL()); } void ZoneEntityItem::debugDump() const { @@ -185,3 +199,27 @@ void ZoneEntityItem::debugDump() const { qCDebug(entities) << " _stageHour:" << _stageHour; } +ShapeType ZoneEntityItem::getShapeType() const { + // Zones are not allowed to have a SHAPE_TYPE_NONE... they are always at least a SHAPE_TYPE_BOX + if (_shapeType == SHAPE_TYPE_COMPOUND) { + return hasCompoundShapeURL() ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_BOX; + } else { + return _shapeType == SHAPE_TYPE_NONE ? SHAPE_TYPE_BOX : _shapeType; + } +} + +void ZoneEntityItem::setCompoundShapeURL(const QString& url) { + _compoundShapeURL = url; + if (!_compoundShapeURL.isEmpty()) { + updateShapeType(SHAPE_TYPE_COMPOUND); + } else if (_shapeType == SHAPE_TYPE_COMPOUND) { + _shapeType = DEFAULT_SHAPE_TYPE; + } +} + +bool ZoneEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, + void** intersectedObject, bool precisionPicking) const { + + return _zonesArePickable; +} diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index 8f1b15d174..3577e69162 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -91,7 +91,19 @@ public: static bool getZonesArePickable() { return _zonesArePickable; } static void setZonesArePickable(bool value) { _zonesArePickable = value; } - + + virtual bool isReadyToComputeShape() { return false; } + void updateShapeType(ShapeType type) { _shapeType = type; } + virtual ShapeType getShapeType() const; + + virtual bool hasCompoundShapeURL() const { return !_compoundShapeURL.isEmpty(); } + const QString getCompoundShapeURL() const { return _compoundShapeURL; } + virtual void setCompoundShapeURL(const QString& url); + + virtual bool supportsDetailedRayIntersection() const { return true; } + virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, + void** intersectedObject, bool precisionPicking) const; virtual void debugDump() const; @@ -105,6 +117,8 @@ public: static const float DEFAULT_STAGE_ALTITUDE; static const quint16 DEFAULT_STAGE_DAY; static const float DEFAULT_STAGE_HOUR; + static const ShapeType DEFAULT_SHAPE_TYPE; + static const QString DEFAULT_COMPOUND_SHAPE_URL; protected: // properties of the "sun" in the zone @@ -118,6 +132,9 @@ protected: float _stageAltitude; uint16_t _stageDay; float _stageHour; + + ShapeType _shapeType = SHAPE_TYPE_NONE; + QString _compoundShapeURL; static bool _zonesArePickable; }; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 25ea9ef8e1..1c89ee4067 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -125,6 +125,51 @@ Extents FBXGeometry::getUnscaledMeshExtents() const { return scaledExtents; } +// TODO: Move to model::Mesh when Sam's ready +bool FBXGeometry::convexHullContains(const glm::vec3& point) const { + if (!getUnscaledMeshExtents().containsPoint(point)) { + return false; + } + + auto checkEachPrimitive = [=](FBXMesh& mesh, QVector indices, int primitiveSize) -> bool { + // Check whether the point is "behind" all the primitives. + for (unsigned int j = 0; j < indices.size(); j += primitiveSize) { + if (!isPointBehindTrianglesPlane(point, + mesh.vertices[indices[j]], + mesh.vertices[indices[j + 1]], + mesh.vertices[indices[j + 2]])) { + // it's not behind at least one so we bail + return false; + } + } + return true; + }; + + // Check that the point is contained in at least one convex mesh. + for (auto mesh : meshes) { + bool insideMesh = true; + + // To be considered inside a convex mesh, + // the point needs to be "behind" all the primitives respective planes. + for (auto part : mesh.parts) { + // run through all the triangles and quads + if (!checkEachPrimitive(mesh, part.triangleIndices, 3) || + !checkEachPrimitive(mesh, part.quadIndices, 4)) { + // If not, the point is outside, bail for this mesh + insideMesh = false; + continue; + } + } + if (insideMesh) { + // It's inside this mesh, return true. + return true; + } + } + + // It wasn't in any mesh, return false. + return false; +} + QString FBXGeometry::getModelNameOfMesh(int meshIndex) const { if (meshIndicesToModelNames.contains(meshIndex)) { return meshIndicesToModelNames.value(meshIndex); @@ -132,8 +177,6 @@ QString FBXGeometry::getModelNameOfMesh(int meshIndex) const { return QString(); } - - static int fbxGeometryMetaTypeId = qRegisterMetaType(); static int fbxAnimationFrameMetaTypeId = qRegisterMetaType(); static int fbxAnimationFrameVectorMetaTypeId = qRegisterMetaType >(); diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index d1576bc02a..871f3d0581 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -252,6 +252,7 @@ public: /// Returns the unscaled extents of the model's mesh Extents getUnscaledMeshExtents() const; + bool convexHullContains(const glm::vec3& point) const; QHash meshIndicesToModelNames; diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 8f0cee10cf..8fedc6b979 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -74,7 +74,7 @@ PacketVersion versionForPacketType(PacketType type) { return 1; case PacketTypeEntityAddOrEdit: case PacketTypeEntityData: - return VERSION_ENTITIES_ZONE_ENTITIES_EXIST; + return VERSION_ENTITIES_ZONE_ENTITIES_HAVE_DYNAMIC_SHAPE; case PacketTypeEntityErase: return 2; case PacketTypeAudioStreamStats: diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index a05df968bd..01302f5568 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -138,5 +138,6 @@ const PacketVersion VERSION_ENTITIES_HAS_MARKETPLACE_ID = 14; const PacketVersion VERSION_ENTITIES_HAVE_ACCELERATION = 15; const PacketVersion VERSION_ENTITIES_HAVE_UUIDS = 16; const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_EXIST = 17; +const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_HAVE_DYNAMIC_SHAPE = 18; #endif // hifi_PacketHeaders_h diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index e5a12a2b66..d1571fbcc5 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -18,7 +18,8 @@ #include "PhysicsHelpers.h" #include "PhysicsLogging.h" -static const float MEASURED_ACCELERATION_CLOSE_TO_ZERO = 0.05f; +static const float ACCELERATION_EQUIVALENT_EPSILON_RATIO = 0.1f; +static const quint8 STEPS_TO_DECIDE_BALLISTIC = 4; QSet* _outgoingEntityList; @@ -34,8 +35,11 @@ void EntityMotionState::enqueueOutgoingEntity(EntityItem* entity) { _outgoingEntityList->insert(entity); } -EntityMotionState::EntityMotionState(EntityItem* entity) - : _entity(entity) { +EntityMotionState::EntityMotionState(EntityItem* entity) : + _entity(entity), + _accelerationNearlyGravityCount(0), + _shouldClaimSimulationOwnership(false) +{ _type = MOTION_STATE_TYPE_ENTITY; assert(entity != NULL); } @@ -191,7 +195,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationFrame) { return false; } - if (_entity->getShouldClaimSimulationOwnership()) { + if (getShouldClaimSimulationOwnership()) { return true; } @@ -199,7 +203,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationFrame) { const QUuid& myNodeID = nodeList->getSessionUUID(); const QUuid& simulatorID = _entity->getSimulatorID(); - if (!simulatorID.isNull() && simulatorID != myNodeID) { + if (simulatorID != myNodeID) { // some other Node owns the simulating of this, so don't broadcast the results of local simulation. return false; } @@ -214,10 +218,26 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ if (_outgoingPacketFlags) { EntityItemProperties properties = _entity->getProperties(); - if (glm::length(_measuredAcceleration) < MEASURED_ACCELERATION_CLOSE_TO_ZERO) { - _entity->setAcceleration(glm::vec3(0.0f)); + float gravityLength = glm::length(_entity->getGravity()); + float accVsGravity = glm::abs(glm::length(_measuredAcceleration) - gravityLength); + if (accVsGravity < ACCELERATION_EQUIVALENT_EPSILON_RATIO * gravityLength) { + // acceleration measured during the most recent simulation step was close to gravity. + if (getAccelerationNearlyGravityCount() < STEPS_TO_DECIDE_BALLISTIC) { + // only increment this if we haven't reached the threshold yet. this is to avoid + // overflowing the counter. + incrementAccelerationNearlyGravityCount(); + } } else { + // acceleration wasn't similar to this entities gravity, so reset the went-ballistic counter + resetAccelerationNearlyGravityCount(); + } + + // if this entity has been accelerated at close to gravity for a certain number of simulation-steps, let + // the entity server's estimates include gravity. + if (getAccelerationNearlyGravityCount() >= STEPS_TO_DECIDE_BALLISTIC) { _entity->setAcceleration(_entity->getGravity()); + } else { + _entity->setAcceleration(glm::vec3(0.0f)); } if (_outgoingPacketFlags & EntityItem::DIRTY_POSITION) { @@ -266,16 +286,13 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ QUuid myNodeID = nodeList->getSessionUUID(); QUuid simulatorID = _entity->getSimulatorID(); - if (_entity->getShouldClaimSimulationOwnership()) { + if (getShouldClaimSimulationOwnership()) { _entity->setSimulatorID(myNodeID); properties.setSimulatorID(myNodeID); - _entity->setShouldClaimSimulationOwnership(false); + setShouldClaimSimulationOwnership(false); } - else if (simulatorID.isNull() && !(zeroSpeed && zeroSpin)) { - // The object is moving and nobody thinks they own the motion. set this Node as the simulator - _entity->setSimulatorID(myNodeID); - properties.setSimulatorID(myNodeID); - } else if (simulatorID == myNodeID && zeroSpeed && zeroSpin) { + + if (simulatorID == myNodeID && zeroSpeed && zeroSpin) { // we are the simulator and the object has stopped. give up "simulator" status _entity->setSimulatorID(QUuid()); properties.setSimulatorID(QUuid()); diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 2b965a39d3..07f82aaa42 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -62,10 +62,18 @@ public: virtual uint32_t getIncomingDirtyFlags() const; virtual void clearIncomingDirtyFlags(uint32_t flags) { _entity->clearDirtyFlags(flags); } - EntityItem* getEntity() const { return _entity; } + void incrementAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount++; } + void resetAccelerationNearlyGravityCount() { _accelerationNearlyGravityCount = 0; } + quint8 getAccelerationNearlyGravityCount() { return _accelerationNearlyGravityCount; } + + virtual EntityItem* getEntity() const { return _entity; } + virtual void setShouldClaimSimulationOwnership(bool value) { _shouldClaimSimulationOwnership = value; } + virtual bool getShouldClaimSimulationOwnership() { return _shouldClaimSimulationOwnership; } protected: EntityItem* _entity; + quint8 _accelerationNearlyGravityCount; + bool _shouldClaimSimulationOwnership; }; #endif // hifi_EntityMotionState_h diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index ad9ef861b9..9e0917a3ab 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -111,6 +111,12 @@ public: virtual bool isMoving() const = 0; friend class PhysicsEngine; + + // these are here so we can call into EntityMotionObject with a base-class pointer + virtual EntityItem* getEntity() const { return NULL; } + virtual void setShouldClaimSimulationOwnership(bool value) { } + virtual bool getShouldClaimSimulationOwnership() { return false; } + protected: void setRigidBody(btRigidBody* body); diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 44bba990ac..45f3c97e30 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -348,10 +348,10 @@ void PhysicsEngine::stepSimulation() { _characterController->postSimulation(); } + computeCollisionEvents(); + unlock(); _entityTree->unlock(); - - computeCollisionEvents(); } } @@ -369,6 +369,9 @@ void PhysicsEngine::stepNonPhysicalKinematics(const quint64& now) { void PhysicsEngine::computeCollisionEvents() { BT_PROFILE("computeCollisionEvents"); + auto nodeList = DependencyManager::get(); + QUuid myNodeID = nodeList->getSessionUUID(); + const btCollisionObject* characterCollisionObject = _characterController ? _characterController->getCollisionObject() : NULL; @@ -388,21 +391,31 @@ void PhysicsEngine::computeCollisionEvents() { continue; } - void* a = objectA->getUserPointer(); - void* b = objectB->getUserPointer(); + ObjectMotionState* a = static_cast(objectA->getUserPointer()); + ObjectMotionState* b = static_cast(objectB->getUserPointer()); + EntityItem* entityA = a ? a->getEntity() : NULL; + EntityItem* entityB = b ? b->getEntity() : NULL; + bool aIsDynamic = entityA && !objectA->isStaticOrKinematicObject(); + bool bIsDynamic = entityB && !objectB->isStaticOrKinematicObject(); + if (a || b) { // the manifold has up to 4 distinct points, but only extract info from the first _contactMap[ContactKey(a, b)].update(_numContactFrames, contactManifold->getContactPoint(0), _originOffset); - - // if our character capsule is colliding with something dynamic, claim simulation ownership. - // see EntityMotionState::sendUpdate - if (objectA == characterCollisionObject && !objectB->isStaticOrKinematicObject() && b) { - EntityItem* entityB = static_cast(b)->getEntity(); - entityB->setShouldClaimSimulationOwnership(true); + } + // collisions cause infectious spread of simulation-ownership. we also attempt to take + // ownership of anything that collides with our avatar. + if ((aIsDynamic && entityA->getSimulatorID() == myNodeID) || + (a && a->getShouldClaimSimulationOwnership()) || + (objectA == characterCollisionObject)) { + if (bIsDynamic) { + b->setShouldClaimSimulationOwnership(true); } - if (objectB == characterCollisionObject && !objectA->isStaticOrKinematicObject() && a) { - EntityItem* entityA = static_cast(a)->getEntity(); - entityA->setShouldClaimSimulationOwnership(true); + } + if ((bIsDynamic && entityB->getSimulatorID() == myNodeID) || + (b && b->getShouldClaimSimulationOwnership()) || + (objectB == characterCollisionObject)) { + if (aIsDynamic) { + a->setShouldClaimSimulationOwnership(true); } } } @@ -413,7 +426,6 @@ void PhysicsEngine::computeCollisionEvents() { // scan known contacts and trigger events ContactMap::iterator contactItr = _contactMap.begin(); - while (contactItr != _contactMap.end()) { ObjectMotionState* A = static_cast(contactItr->first._a); ObjectMotionState* B = static_cast(contactItr->first._b); @@ -521,9 +533,55 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap motionState->resetMeasuredAcceleration(); } +void PhysicsEngine::bump(EntityItem* bumpEntity) { + // If this node is doing something like deleting an entity, scan for contacts involving the + // entity. For each found, flag the other entity involved as being simulated by this node. + lock(); + int numManifolds = _collisionDispatcher->getNumManifolds(); + for (int i = 0; i < numManifolds; ++i) { + btPersistentManifold* contactManifold = _collisionDispatcher->getManifoldByIndexInternal(i); + if (contactManifold->getNumContacts() > 0) { + const btCollisionObject* objectA = static_cast(contactManifold->getBody0()); + const btCollisionObject* objectB = static_cast(contactManifold->getBody1()); + if (objectA && objectB) { + void* a = objectA->getUserPointer(); + void* b = objectB->getUserPointer(); + if (a && b) { + EntityMotionState* entityMotionStateA = static_cast(a); + EntityMotionState* entityMotionStateB = static_cast(b); + EntityItem* entityA = entityMotionStateA ? entityMotionStateA->getEntity() : NULL; + EntityItem* entityB = entityMotionStateB ? entityMotionStateB->getEntity() : NULL; + if (entityA && entityB) { + if (entityA == bumpEntity) { + entityMotionStateB->setShouldClaimSimulationOwnership(true); + if (!objectB->isActive()) { + objectB->setActivationState(ACTIVE_TAG); + } + } + if (entityB == bumpEntity) { + entityMotionStateA->setShouldClaimSimulationOwnership(true); + if (!objectA->isActive()) { + objectA->setActivationState(ACTIVE_TAG); + } + } + } + } + } + } + } + unlock(); +} + void PhysicsEngine::removeObjectFromBullet(ObjectMotionState* motionState) { assert(motionState); btRigidBody* body = motionState->getRigidBody(); + + // set the about-to-be-deleted entity active in order to wake up the island it's part of. this is done + // so that anything resting on top of it will fall. + // body->setActivationState(ACTIVE_TAG); + EntityItem* entity = static_cast(motionState)->getEntity(); + bump(entity); + if (body) { const btCollisionShape* shape = body->getCollisionShape(); _dynamicsWorld->removeRigidBody(body); diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 6e1f430237..148261c6d2 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -98,6 +98,7 @@ private: // return 'true' of update was successful bool updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); void updateObjectEasy(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); + void bump(EntityItem* bumpEntity); btClock _clock; btDefaultCollisionConfiguration* _collisionConfig = NULL; diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index 4e8fb7d3cd..5d1b70275c 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -203,6 +203,13 @@ glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) { return glm::angleAxis(angle, axis); } +bool isPointBehindTrianglesPlane(glm::vec3 point, glm::vec3 p0, glm::vec3 p1, glm::vec3 p2) { + glm::vec3 v1 = p0 - p1, v2 = p2 - p1; // Non-collinear vectors contained in the plane + glm::vec3 n = glm::cross(v1, v2); // Plane's normal vector, pointing out of the triangle + float d = -glm::dot(n, p0); // Compute plane's equation constant + return (glm::dot(n, point) + d) >= 0; +} + glm::vec3 extractTranslation(const glm::mat4& matrix) { return glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2]); } diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index e5d22d67dc..dda57a9cd9 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -82,6 +82,8 @@ float angleBetween(const glm::vec3& v1, const glm::vec3& v2); glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2); +bool isPointBehindTrianglesPlane(glm::vec3 point, glm::vec3 p0, glm::vec3 p1, glm::vec3 p2); + glm::vec3 extractTranslation(const glm::mat4& matrix); void setTranslation(glm::mat4& matrix, const glm::vec3& translation); diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 544df35b86..1c86f109c5 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -114,7 +114,7 @@ float ShapeInfo::computeVolume() const { } case SHAPE_TYPE_CAPSULE_Y: { float radius = _halfExtents.x; - volume = PI * radius * radius * (2.0f * _halfExtents.y + 4.0f * radius / 3.0f); + volume = PI * radius * radius * (2.0f * (_halfExtents.y - _halfExtents.x) + 4.0f * radius / 3.0f); break; } default: @@ -124,6 +124,54 @@ float ShapeInfo::computeVolume() const { return volume; } +bool ShapeInfo::contains(const glm::vec3& point) const { + switch(_type) { + case SHAPE_TYPE_SPHERE: + return glm::length(point) <= _halfExtents.x; + case SHAPE_TYPE_ELLIPSOID: { + glm::vec3 scaledPoint = glm::abs(point) / _halfExtents; + return glm::length(scaledPoint) <= 1.0f; + } + case SHAPE_TYPE_CYLINDER_X: + return glm::length(glm::vec2(point.y, point.z)) <= _halfExtents.z; + case SHAPE_TYPE_CYLINDER_Y: + return glm::length(glm::vec2(point.x, point.z)) <= _halfExtents.x; + case SHAPE_TYPE_CYLINDER_Z: + return glm::length(glm::vec2(point.x, point.y)) <= _halfExtents.y; + case SHAPE_TYPE_CAPSULE_X: { + if (glm::abs(point.x) <= _halfExtents.x) { + return glm::length(glm::vec2(point.y, point.z)) <= _halfExtents.z; + } else { + glm::vec3 absPoint = glm::abs(point) - _halfExtents.x; + return glm::length(absPoint) <= _halfExtents.z; + } + } + case SHAPE_TYPE_CAPSULE_Y: { + if (glm::abs(point.y) <= _halfExtents.y) { + return glm::length(glm::vec2(point.x, point.z)) <= _halfExtents.x; + } else { + glm::vec3 absPoint = glm::abs(point) - _halfExtents.y; + return glm::length(absPoint) <= _halfExtents.x; + } + } + case SHAPE_TYPE_CAPSULE_Z: { + if (glm::abs(point.z) <= _halfExtents.z) { + return glm::length(glm::vec2(point.x, point.y)) <= _halfExtents.y; + } else { + glm::vec3 absPoint = glm::abs(point) - _halfExtents.z; + return glm::length(absPoint) <= _halfExtents.y; + } + } + case SHAPE_TYPE_BOX: + default: { + glm::vec3 absPoint = glm::abs(point); + return absPoint.x <= _halfExtents.x + && absPoint.y <= _halfExtents.y + && absPoint.z <= _halfExtents.z; + } + } +} + const DoubleHashKey& ShapeInfo::getHash() const { // NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance. if (_doubleHashKey.isNull() && _type != SHAPE_TYPE_NONE) { diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h index 8770ef62c7..114d209788 100644 --- a/libraries/shared/src/ShapeInfo.h +++ b/libraries/shared/src/ShapeInfo.h @@ -57,6 +57,10 @@ public: void appendToPoints (const QVector& newPoints) { _points << newPoints; } float computeVolume() const; + + /// Returns whether point is inside the shape + /// For compound shapes it will only return whether it is inside the bounding box + bool contains(const glm::vec3& point) const; const DoubleHashKey& getHash() const; diff --git a/tests/octree/src/OctreeTests.cpp b/tests/octree/src/OctreeTests.cpp index 705a50aa10..4cfadbccfc 100644 --- a/tests/octree/src/OctreeTests.cpp +++ b/tests/octree/src/OctreeTests.cpp @@ -74,7 +74,7 @@ void OctreeTests::propertyFlagsTests(bool verbose) { props.setHasProperty(PROP_POSITION); props.setHasProperty(PROP_RADIUS); props.setHasProperty(PROP_MODEL_URL); - props.setHasProperty(PROP_COLLISION_MODEL_URL); + props.setHasProperty(PROP_COMPOUND_SHAPE_URL); props.setHasProperty(PROP_ROTATION); QByteArray encoded = props.encode();