Merge branch 'offscreen' into m_and_m

This commit is contained in:
Brad Davis 2015-04-23 23:45:11 -07:00
commit c0d562ed55
43 changed files with 1027 additions and 333 deletions

View file

@ -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);
}

View file

@ -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
});

View file

@ -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);

View file

@ -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())));

View file

@ -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 @@
</div>
</div>
<div class="model-section property">
<div class="label">Collision Model URL</div>
<div class="label">Compound Shape URL</div>
<div class="value">
<input type="text" id="property-collision-model-url" class="url"></input>
<input type="text" id="property-compound-shape-url" class="url"></input>
</div>
</div>
<div class="model-section property">
@ -833,6 +896,73 @@
<input class="coord" type='number' id="property-light-cutoff"></input>
</div>
</div>
<div class="zone-section property">
<span class="label">Stage Sun Model Enabled</span>
<span class="value">
<input type='checkbox' id="property-zone-stage-sun-model-enabled">
</span>
</div>
<div class="zone-section property">
<div class="label">Key Light Color</div>
<div class="value">
<div class="input-area">R <input class="coord" type='number' id="property-zone-key-light-color-red" min="0" max="255" step="1"></input></div>
<div class="input-area">G <input class="coord" type='number' id="property-zone-key-light-color-green" min="0" max="255" step="1"></input></div>
<div class="input-area">B <input class="coord" type='number' id="property-zone-key-light-color-blue" min="0" max="255" step="1"></input></div>
</div>
</div>
<div class="zone-section property">
<div class="label">Key Light Intensity</div>
<div class="value">
<input class="coord" type='number' id="property-zone-key-intensity" min="0" max="10" step="0.1"></input>
</div>
</div>
<div class="zone-section property">
<div class="label">Key Light Ambient Intensity</div>
<div class="value">
<input class="coord" type='number' id="property-zone-key-ambient-intensity" min="0" max="10" step="0.1"></input>
</div>
</div>
<div class="zone-section property">
<div class="label">Key Light Direction</div>
<div class="value">
<div class="input-area">Pitch <input class="coord" type='number' id="property-zone-key-light-direction-x"></input></div>
<div class="input-area">Yaw <input class="coord" type='number' id="property-zone-key-light-direction-y"></input></div>
<div class="input-area">Roll <input class="coord" type='number' id="property-zone-key-light-direction-z"></input></div>
</div>
</div>
<div class="zone-section property">
<div class="label">Stage Latitude</div>
<div class="value">
<input class="coord" type='number' id="property-zone-stage-latitude" min="-90" max="90" step="1"></input>
</div>
</div>
<div class="zone-section property">
<div class="label">Stage Longitude</div>
<div class="value">
<input class="coord" type='number' id="property-zone-stage-longitude" min="-180" max="180" step="1"></input>
</div>
</div>
<div class="zone-section property">
<div class="label">Stage Altitude</div>
<div class="value">
<input class="coord" type='number' id="property-zone-stage-altitude" step="1"></input>
</div>
</div>
<div class="zone-section property">
<div class="label">Stage Day</div>
<div class="value">
<input class="coord" type='number' id="property-zone-stage-day" min="0" max="365" step="1"></input>
</div>
</div>
<div class="zone-section property">
<div class="label">Stage Hour</div>
<div class="value">
<input class="coord" type='number' id="property-zone-stage-hour" min="0" max="24" step="0.5"></input>
</div>
</div>
</div>
</body>

View file

@ -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) {

View file

@ -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;

View file

@ -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]);
}

View file

@ -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]);
}
});
};

View file

@ -77,7 +77,7 @@ void Cube3DOverlay::render(RenderArgs* args) {
if (_drawOnHUD) {
DependencyManager::get<GeometryCache>()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha));
} else {
DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha));
DependencyManager::get<GeometryCache>()->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<GeometryCache>()->renderSolidCube(1.0f, cubeColor);
} else {
DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(1.0f, cubeColor);
DependencyManager::get<GeometryCache>()->renderSolidCube(1.0f, cubeColor);
}
glPopMatrix();
} else {

View file

@ -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<const RenderableModelEntityItem*>(entityItem);
if (constModelEntityItem->hasCollisionModel()) {
if (constModelEntityItem->hasCompoundShapeURL()) {
RenderableModelEntityItem* modelEntityItem = const_cast<RenderableModelEntityItem*>(constModelEntityItem);
Model* model = modelEntityItem->getModel(this);
if (model) {

View file

@ -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<NetworkGeometry> collisionNetworkGeometry = _model->getCollisionGeometry();
const FBXGeometry& collisionGeometry = collisionNetworkGeometry->getFBXGeometry();
return collisionGeometry.convexHullContains(worldToEntity(point));
}
return false;
}

View file

@ -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();

View file

@ -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 <DependencyManager.h>
#include <GeometryCache.h>
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<GeometryCache>()->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<GeometryCache>()->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<RenderableZoneEntityItem*>(this)->_compoundShapeModel = DependencyManager::get<GeometryCache>()->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;
}

View file

@ -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 <ZoneEntityItem.h>
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<NetworkGeometry> _compoundShapeModel;
};
#endif // hifi_RenderableZoneEntityItem_h

View file

@ -11,6 +11,8 @@
#include <QtCore/QObject>
#include <glm/gtx/transform.hpp>
#include <ByteCountCoding.h>
#include <GLMHelpers.h>
#include <Octree.h>
@ -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<NodeList>();
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());
}

View file

@ -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)

View file

@ -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;

View file

@ -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, "");

View file

@ -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<NodeList>();
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);
}

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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; }

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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;
};

View file

@ -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<int> 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<FBXGeometry>();
static int fbxAnimationFrameMetaTypeId = qRegisterMetaType<FBXAnimationFrame>();
static int fbxAnimationFrameVectorMetaTypeId = qRegisterMetaType<QVector<FBXAnimationFrame> >();

View file

@ -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<int, QString> meshIndicesToModelNames;

View file

@ -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:

View file

@ -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

View file

@ -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<EntityItem*>* _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());

View file

@ -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

View file

@ -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);

View file

@ -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<NodeList>();
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<ObjectMotionState*>(objectA->getUserPointer());
ObjectMotionState* b = static_cast<ObjectMotionState*>(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<EntityMotionState*>(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<EntityMotionState*>(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<ObjectMotionState*>(contactItr->first._a);
ObjectMotionState* B = static_cast<ObjectMotionState*>(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<const btCollisionObject*>(contactManifold->getBody0());
const btCollisionObject* objectB = static_cast<const btCollisionObject*>(contactManifold->getBody1());
if (objectA && objectB) {
void* a = objectA->getUserPointer();
void* b = objectB->getUserPointer();
if (a && b) {
EntityMotionState* entityMotionStateA = static_cast<EntityMotionState*>(a);
EntityMotionState* entityMotionStateB = static_cast<EntityMotionState*>(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<EntityMotionState*>(motionState)->getEntity();
bump(entity);
if (body) {
const btCollisionShape* shape = body->getCollisionShape();
_dynamicsWorld->removeRigidBody(body);

View file

@ -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;

View file

@ -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]);
}

View file

@ -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);

View file

@ -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) {

View file

@ -57,6 +57,10 @@ public:
void appendToPoints (const QVector<glm::vec3>& 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;

View file

@ -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();