mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge branch 'master' of https://github.com/highfidelity/hifi into voxelEctomy
Conflicts: examples/editVoxels.js examples/fallingSand.js examples/growTrees.js interface/src/Application.cpp interface/src/Application.h libraries/avatars/CMakeLists.txt libraries/entities/src/EntityItem.h
This commit is contained in:
commit
9630279459
120 changed files with 3471 additions and 557 deletions
18
cmake/macros/IncludeBullet.cmake
Normal file
18
cmake/macros/IncludeBullet.cmake
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#
|
||||||
|
# IncludeBullet.cmake
|
||||||
|
#
|
||||||
|
# Copyright 2014 High Fidelity, Inc.
|
||||||
|
#
|
||||||
|
# Distributed under the Apache License, Version 2.0.
|
||||||
|
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
#
|
||||||
|
|
||||||
|
macro(INCLUDE_BULLET)
|
||||||
|
find_package(Bullet)
|
||||||
|
if (BULLET_FOUND)
|
||||||
|
include_directories("${BULLET_INCLUDE_DIRS}")
|
||||||
|
if (APPLE OR UNIX)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_BULLET_PHYSICS -isystem ${BULLET_INCLUDE_DIRS}")
|
||||||
|
endif ()
|
||||||
|
endif (BULLET_FOUND)
|
||||||
|
endmacro(INCLUDE_BULLET)
|
|
@ -1884,7 +1884,7 @@ function controller(wichSide) {
|
||||||
this.jointsIntersectingFromStart = [];
|
this.jointsIntersectingFromStart = [];
|
||||||
|
|
||||||
this.laser = Overlays.addOverlay("line3d", {
|
this.laser = Overlays.addOverlay("line3d", {
|
||||||
position: { x: 0, y: 0, z: 0 },
|
start: { x: 0, y: 0, z: 0 },
|
||||||
end: { x: 0, y: 0, z: 0 },
|
end: { x: 0, y: 0, z: 0 },
|
||||||
color: LASER_COLOR,
|
color: LASER_COLOR,
|
||||||
alpha: 1,
|
alpha: 1,
|
||||||
|
@ -1904,7 +1904,7 @@ function controller(wichSide) {
|
||||||
anchor: "MyAvatar"
|
anchor: "MyAvatar"
|
||||||
});
|
});
|
||||||
this.leftRight = Overlays.addOverlay("line3d", {
|
this.leftRight = Overlays.addOverlay("line3d", {
|
||||||
position: { x: 0, y: 0, z: 0 },
|
start: { x: 0, y: 0, z: 0 },
|
||||||
end: { x: 0, y: 0, z: 0 },
|
end: { x: 0, y: 0, z: 0 },
|
||||||
color: { red: 0, green: 0, blue: 255 },
|
color: { red: 0, green: 0, blue: 255 },
|
||||||
alpha: 1,
|
alpha: 1,
|
||||||
|
@ -1913,7 +1913,7 @@ function controller(wichSide) {
|
||||||
anchor: "MyAvatar"
|
anchor: "MyAvatar"
|
||||||
});
|
});
|
||||||
this.topDown = Overlays.addOverlay("line3d", {
|
this.topDown = Overlays.addOverlay("line3d", {
|
||||||
position: { x: 0, y: 0, z: 0 },
|
start: { x: 0, y: 0, z: 0 },
|
||||||
end: { x: 0, y: 0, z: 0 },
|
end: { x: 0, y: 0, z: 0 },
|
||||||
color: { red: 0, green: 0, blue: 255 },
|
color: { red: 0, green: 0, blue: 255 },
|
||||||
alpha: 1,
|
alpha: 1,
|
||||||
|
@ -2066,7 +2066,7 @@ function controller(wichSide) {
|
||||||
var endPosition = Vec3.sum(startPosition, direction);
|
var endPosition = Vec3.sum(startPosition, direction);
|
||||||
|
|
||||||
Overlays.editOverlay(this.laser, {
|
Overlays.editOverlay(this.laser, {
|
||||||
position: startPosition,
|
start: startPosition,
|
||||||
end: endPosition
|
end: endPosition
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2075,10 +2075,11 @@ function controller(wichSide) {
|
||||||
position: endPosition
|
position: endPosition
|
||||||
});
|
});
|
||||||
Overlays.editOverlay(this.leftRight, {
|
Overlays.editOverlay(this.leftRight, {
|
||||||
position: Vec3.sum(endPosition, Vec3.multiply(this.right, 2 * this.guideScale)),
|
start: Vec3.sum(endPosition, Vec3.multiply(this.right, 2 * this.guideScale)),
|
||||||
end: Vec3.sum(endPosition, Vec3.multiply(this.right, -2 * this.guideScale))
|
end: Vec3.sum(endPosition, Vec3.multiply(this.right, -2 * this.guideScale))
|
||||||
});
|
});
|
||||||
Overlays.editOverlay(this.topDown, { position: Vec3.sum(endPosition, Vec3.multiply(this.up, 2 * this.guideScale)),
|
Overlays.editOverlay(this.topDown, {
|
||||||
|
start: Vec3.sum(endPosition, Vec3.multiply(this.up, 2 * this.guideScale)),
|
||||||
end: Vec3.sum(endPosition, Vec3.multiply(this.up, -2 * this.guideScale))
|
end: Vec3.sum(endPosition, Vec3.multiply(this.up, -2 * this.guideScale))
|
||||||
});
|
});
|
||||||
this.showLaser(!this.grabbing || mode == 0);
|
this.showLaser(!this.grabbing || mode == 0);
|
||||||
|
|
|
@ -75,7 +75,7 @@ var warpSphere = Overlays.addOverlay("sphere", {
|
||||||
|
|
||||||
var WARP_LINE_HEIGHT = 10;
|
var WARP_LINE_HEIGHT = 10;
|
||||||
var warpLine = Overlays.addOverlay("line3d", {
|
var warpLine = Overlays.addOverlay("line3d", {
|
||||||
position: { x: 0, y: 0, z:0 },
|
start: { x: 0, y: 0, z:0 },
|
||||||
end: { x: 0, y: 0, z: 0 },
|
end: { x: 0, y: 0, z: 0 },
|
||||||
color: { red: 0, green: 255, blue: 255},
|
color: { red: 0, green: 255, blue: 255},
|
||||||
alpha: 1,
|
alpha: 1,
|
||||||
|
@ -131,7 +131,7 @@ function updateWarp() {
|
||||||
visible: true,
|
visible: true,
|
||||||
});
|
});
|
||||||
Overlays.editOverlay(warpLine, {
|
Overlays.editOverlay(warpLine, {
|
||||||
position: warpPosition,
|
start: warpPosition,
|
||||||
end: Vec3.sum(warpPosition, { x: 0, y: WARP_LINE_HEIGHT, z: 0 }),
|
end: Vec3.sum(warpPosition, { x: 0, y: WARP_LINE_HEIGHT, z: 0 }),
|
||||||
visible: true,
|
visible: true,
|
||||||
});
|
});
|
||||||
|
|
|
@ -36,7 +36,7 @@ var warpSphere = Overlays.addOverlay("sphere", {
|
||||||
|
|
||||||
var WARP_LINE_HEIGHT = 5;
|
var WARP_LINE_HEIGHT = 5;
|
||||||
var warpLine = Overlays.addOverlay("line3d", {
|
var warpLine = Overlays.addOverlay("line3d", {
|
||||||
position: { x: 0, y: 0, z:0 },
|
start: { x: 0, y: 0, z:0 },
|
||||||
end: { x: 0, y: 0, z: 0 },
|
end: { x: 0, y: 0, z: 0 },
|
||||||
color: { red: 0, green: 255, blue: 255},
|
color: { red: 0, green: 255, blue: 255},
|
||||||
alpha: 1,
|
alpha: 1,
|
||||||
|
@ -120,7 +120,7 @@ function updateWarp() {
|
||||||
visible: willMove,
|
visible: willMove,
|
||||||
});
|
});
|
||||||
Overlays.editOverlay(warpLine, {
|
Overlays.editOverlay(warpLine, {
|
||||||
position: Vec3.sum(warpPosition, { x: 0, y: -WARP_LINE_HEIGHT / 2.0, z: 0 }),
|
start: Vec3.sum(warpPosition, { x: 0, y: -WARP_LINE_HEIGHT / 2.0, z: 0 }),
|
||||||
end: Vec3.sum(warpPosition, { x: 0, y: WARP_LINE_HEIGHT / 2.0, z: 0 }),
|
end: Vec3.sum(warpPosition, { x: 0, y: WARP_LINE_HEIGHT / 2.0, z: 0 }),
|
||||||
visible: willMove,
|
visible: willMove,
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
Script.include("libraries/overlayUtils.js");
|
||||||
|
|
||||||
var MOUSE_SENSITIVITY = 0.9;
|
var MOUSE_SENSITIVITY = 0.9;
|
||||||
var SCROLL_SENSITIVITY = 0.05;
|
var SCROLL_SENSITIVITY = 0.05;
|
||||||
var PAN_ZOOM_SCALE_RATIO = 0.4;
|
var PAN_ZOOM_SCALE_RATIO = 0.4;
|
||||||
|
@ -41,6 +43,17 @@ var easeOutCubic = function(t) {
|
||||||
|
|
||||||
EASE_TIME = 0.5;
|
EASE_TIME = 0.5;
|
||||||
|
|
||||||
|
function mergeObjects(obj1, obj2) {
|
||||||
|
var newObj = {};
|
||||||
|
for (key in obj1) {
|
||||||
|
newObj[key] = obj1[key];
|
||||||
|
}
|
||||||
|
for (key in obj2) {
|
||||||
|
newObj[key] = obj2[key];
|
||||||
|
}
|
||||||
|
return newObj;
|
||||||
|
}
|
||||||
|
|
||||||
CameraManager = function() {
|
CameraManager = function() {
|
||||||
var that = {};
|
var that = {};
|
||||||
|
|
||||||
|
@ -92,8 +105,6 @@ CameraManager = function() {
|
||||||
Camera.mode = "independent";
|
Camera.mode = "independent";
|
||||||
|
|
||||||
that.updateCamera();
|
that.updateCamera();
|
||||||
|
|
||||||
cameraTool.setVisible(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
that.disable = function(ignoreCamera) {
|
that.disable = function(ignoreCamera) {
|
||||||
|
@ -104,7 +115,6 @@ CameraManager = function() {
|
||||||
if (!ignoreCamera) {
|
if (!ignoreCamera) {
|
||||||
Camera.mode = that.previousCameraMode;
|
Camera.mode = that.previousCameraMode;
|
||||||
}
|
}
|
||||||
cameraTool.setVisible(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
that.focus = function(position, dimensions, easeOrientation) {
|
that.focus = function(position, dimensions, easeOrientation) {
|
||||||
|
@ -140,6 +150,11 @@ CameraManager = function() {
|
||||||
that.updateCamera();
|
that.updateCamera();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
that.setTargetPitchYaw = function(pitch, yaw) {
|
||||||
|
that.targetPitch = pitch;
|
||||||
|
that.targetYaw = yaw;
|
||||||
|
}
|
||||||
|
|
||||||
that.moveFocalPoint = function(dPos) {
|
that.moveFocalPoint = function(dPos) {
|
||||||
that.setFocalPoint(Vec3.sum(that.focalPoint, dPos));
|
that.setFocalPoint(Vec3.sum(that.focalPoint, dPos));
|
||||||
}
|
}
|
||||||
|
@ -228,6 +243,10 @@ CameraManager = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
that.mousePressEvent = function(event) {
|
that.mousePressEvent = function(event) {
|
||||||
|
if (cameraTool.mousePressEvent(event)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!that.enabled) return;
|
if (!that.enabled) return;
|
||||||
|
|
||||||
if (event.isRightButton || (event.isLeftButton && event.isControl && !event.isShifted)) {
|
if (event.isRightButton || (event.isLeftButton && event.isControl && !event.isShifted)) {
|
||||||
|
@ -247,7 +266,7 @@ CameraManager = function() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cameraTool.mousePressEvent(event);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
that.mouseReleaseEvent = function(event) {
|
that.mouseReleaseEvent = function(event) {
|
||||||
|
@ -271,7 +290,10 @@ CameraManager = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
that.updateCamera = function() {
|
that.updateCamera = function() {
|
||||||
if (!that.enabled || Camera.mode != "independent") return;
|
if (!that.enabled || Camera.mode != "independent") {
|
||||||
|
cameraTool.update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var yRot = Quat.angleAxis(that.yaw, { x: 0, y: 1, z: 0 });
|
var yRot = Quat.angleAxis(that.yaw, { x: 0, y: 1, z: 0 });
|
||||||
var xRot = Quat.angleAxis(that.pitch, { x: 1, y: 0, z: 0 });
|
var xRot = Quat.angleAxis(that.pitch, { x: 1, y: 0, z: 0 });
|
||||||
|
@ -290,6 +312,8 @@ CameraManager = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Camera.setOrientation(q);
|
Camera.setOrientation(q);
|
||||||
|
|
||||||
|
cameraTool.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeDegrees(degrees) {
|
function normalizeDegrees(degrees) {
|
||||||
|
@ -301,6 +325,7 @@ CameraManager = function() {
|
||||||
// Ease the position and orbit of the camera
|
// Ease the position and orbit of the camera
|
||||||
that.update = function(dt) {
|
that.update = function(dt) {
|
||||||
if (Camera.mode != "independent") {
|
if (Camera.mode != "independent") {
|
||||||
|
that.updateCamera();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,316 +388,210 @@ CameraManager = function() {
|
||||||
return that;
|
return that;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ZoomTool = function(opts) {
|
|
||||||
var that = {};
|
|
||||||
|
|
||||||
var position = opts.position || { x: 0, y: 0 };
|
|
||||||
var height = opts.height || 200;
|
|
||||||
var color = opts.color || { red: 255, green: 0, blue: 0 };
|
|
||||||
var arrowButtonSize = opts.buttonSize || 20;
|
|
||||||
var arrowButtonBackground = opts.arrowBackground || { red: 255, green: 255, blue: 255 };
|
|
||||||
var zoomBackground = { red: 128, green: 0, blue: 0 };
|
|
||||||
var zoomHeight = height - (arrowButtonSize * 2);
|
|
||||||
var zoomBarY = position.y + arrowButtonSize,
|
|
||||||
|
|
||||||
var onIncreasePressed = opts.onIncreasePressed;
|
|
||||||
var onDecreasePressed = opts.onDecreasePressed;
|
|
||||||
var onPercentageSet = opts.onPercentageSet;
|
|
||||||
|
|
||||||
var increaseButton = Overlays.addOverlay("text", {
|
|
||||||
x: position.x,
|
|
||||||
y: position.y,
|
|
||||||
width: arrowButtonSize,
|
|
||||||
height: arrowButtonSize,
|
|
||||||
color: color,
|
|
||||||
backgroundColor: arrowButtonBackground,
|
|
||||||
topMargin: 4,
|
|
||||||
leftMargin: 4,
|
|
||||||
text: "+",
|
|
||||||
alpha: 1.0,
|
|
||||||
backgroundAlpha: 1.0,
|
|
||||||
visible: true,
|
|
||||||
});
|
|
||||||
var decreaseButton = Overlays.addOverlay("text", {
|
|
||||||
x: position.x,
|
|
||||||
y: position.y + arrowButtonSize + zoomHeight,
|
|
||||||
width: arrowButtonSize,
|
|
||||||
height: arrowButtonSize,
|
|
||||||
color: color,
|
|
||||||
backgroundColor: arrowButtonBackground,
|
|
||||||
topMargin: 4,
|
|
||||||
leftMargin: 4,
|
|
||||||
text: "-",
|
|
||||||
alpha: 1.0,
|
|
||||||
backgroundAlpha: 1.0,
|
|
||||||
visible: true,
|
|
||||||
});
|
|
||||||
var zoomBar = Overlays.addOverlay("text", {
|
|
||||||
x: position.x + 5,
|
|
||||||
y: zoomBarY,
|
|
||||||
width: 10,
|
|
||||||
height: zoomHeight,
|
|
||||||
color: { red: 0, green: 255, blue: 0 },
|
|
||||||
backgroundColor: zoomBackground,
|
|
||||||
topMargin: 4,
|
|
||||||
leftMargin: 4,
|
|
||||||
text: "",
|
|
||||||
alpha: 1.0,
|
|
||||||
backgroundAlpha: 1.0,
|
|
||||||
visible: true,
|
|
||||||
});
|
|
||||||
var zoomHandle = Overlays.addOverlay("text", {
|
|
||||||
x: position.x,
|
|
||||||
y: position.y + arrowButtonSize,
|
|
||||||
width: arrowButtonSize,
|
|
||||||
height: 10,
|
|
||||||
backgroundColor: { red: 0, green: 255, blue: 0 },
|
|
||||||
topMargin: 4,
|
|
||||||
leftMargin: 4,
|
|
||||||
text: "",
|
|
||||||
alpha: 1.0,
|
|
||||||
backgroundAlpha: 1.0,
|
|
||||||
visible: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
var allOverlays = [
|
|
||||||
increaseButton,
|
|
||||||
decreaseButton,
|
|
||||||
zoomBar,
|
|
||||||
zoomHandle,
|
|
||||||
];
|
|
||||||
|
|
||||||
that.destroy = function() {
|
|
||||||
for (var i = 0; i < allOverlays.length; i++) {
|
|
||||||
Overlays.deleteOverlay(allOverlays[i]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
that.setVisible = function(visible) {
|
|
||||||
for (var i = 0; i < allOverlays.length; i++) {
|
|
||||||
Overlays.editOverlay(allOverlays[i], { visible: visible });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
that.setZoomPercentage = function(pct) {
|
|
||||||
var yOffset = (zoomHeight - 10) * pct;
|
|
||||||
Overlays.editOverlay(zoomHandle, {
|
|
||||||
y: position.y + arrowButtonSize + yOffset,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
that.mouseReleaseEvent = function(event) {
|
|
||||||
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
|
||||||
var clicked = false;
|
|
||||||
if (clickedOverlay == increaseButton) {
|
|
||||||
if (onIncreasePressed) onIncreasePressed();
|
|
||||||
clicked = true;
|
|
||||||
} else if (clickedOverlay == decreaseButton) {
|
|
||||||
if (onDecreasePressed) onDecreasePressed();
|
|
||||||
clicked = true;
|
|
||||||
} else if (clickedOverlay == zoomBar) {
|
|
||||||
if (onPercentageSet) onPercentageSet((event.y - zoomBarY) / zoomHeight);
|
|
||||||
clicked = true;
|
|
||||||
}
|
|
||||||
return clicked;
|
|
||||||
}
|
|
||||||
|
|
||||||
return that;
|
|
||||||
};
|
|
||||||
|
|
||||||
var ArrowTool = function(opts) {
|
|
||||||
var that = {};
|
|
||||||
|
|
||||||
var position = opts.position || { x: 0, y: 0 };
|
|
||||||
var arrowButtonSize = opts.buttonSize || 20;
|
|
||||||
var color = opts.color || { red: 255, green: 0, blue: 0 };
|
|
||||||
var arrowButtonBackground = opts.arrowBackground || { red: 255, green: 255, blue: 255 };
|
|
||||||
var centerButtonBackground = opts.centerBackground || { red: 255, green: 255, blue: 255 };
|
|
||||||
var onUpPressed = opts.onUpPressed;
|
|
||||||
var onDownPressed = opts.onDownPressed;
|
|
||||||
var onLeftPressed = opts.onLeftPressed;
|
|
||||||
var onRightPressed = opts.onRightPressed;
|
|
||||||
var onCenterPressed = opts.onCenterPressed;
|
|
||||||
|
|
||||||
var upButton = Overlays.addOverlay("text", {
|
|
||||||
x: position.x + arrowButtonSize,
|
|
||||||
y: position.y,
|
|
||||||
width: arrowButtonSize,
|
|
||||||
height: arrowButtonSize,
|
|
||||||
color: color,
|
|
||||||
backgroundColor: arrowButtonBackground,
|
|
||||||
topMargin: 4,
|
|
||||||
leftMargin: 4,
|
|
||||||
text: "^",
|
|
||||||
alpha: 1.0,
|
|
||||||
backgroundAlpha: 1.0,
|
|
||||||
visible: true,
|
|
||||||
});
|
|
||||||
var leftButton = Overlays.addOverlay("text", {
|
|
||||||
x: position.x,
|
|
||||||
y: position.y + arrowButtonSize,
|
|
||||||
width: arrowButtonSize,
|
|
||||||
height: arrowButtonSize,
|
|
||||||
color: color,
|
|
||||||
backgroundColor: arrowButtonBackground,
|
|
||||||
topMargin: 4,
|
|
||||||
leftMargin: 4,
|
|
||||||
text: "<",
|
|
||||||
alpha: 1.0,
|
|
||||||
backgroundAlpha: 1.0,
|
|
||||||
visible: true,
|
|
||||||
});
|
|
||||||
var rightButton = Overlays.addOverlay("text", {
|
|
||||||
x: position.x + (arrowButtonSize * 2),
|
|
||||||
y: position.y + arrowButtonSize,
|
|
||||||
width: arrowButtonSize,
|
|
||||||
height: arrowButtonSize,
|
|
||||||
color: color,
|
|
||||||
backgroundColor: arrowButtonBackground,
|
|
||||||
topMargin: 4,
|
|
||||||
leftMargin: 4,
|
|
||||||
text: ">",
|
|
||||||
alpha: 1.0,
|
|
||||||
visible: true,
|
|
||||||
});
|
|
||||||
var downButton = Overlays.addOverlay("text", {
|
|
||||||
x: position.x + arrowButtonSize,
|
|
||||||
y: position.y + (arrowButtonSize * 2),
|
|
||||||
width: arrowButtonSize,
|
|
||||||
height: arrowButtonSize,
|
|
||||||
color: color,
|
|
||||||
backgroundColor: arrowButtonBackground,
|
|
||||||
topMargin: 4,
|
|
||||||
leftMargin: 4,
|
|
||||||
text: "v",
|
|
||||||
alpha: 1.0,
|
|
||||||
backgroundAlpha: 1.0,
|
|
||||||
visible: true,
|
|
||||||
});
|
|
||||||
var centerButton = Overlays.addOverlay("text", {
|
|
||||||
x: position.x + arrowButtonSize,
|
|
||||||
y: position.y + arrowButtonSize,
|
|
||||||
width: arrowButtonSize,
|
|
||||||
height: arrowButtonSize,
|
|
||||||
color: color,
|
|
||||||
backgroundColor: centerButtonBackground,
|
|
||||||
topMargin: 4,
|
|
||||||
leftMargin: 4,
|
|
||||||
text: "",
|
|
||||||
alpha: 1.0,
|
|
||||||
backgroundAlpha: 1.0,
|
|
||||||
visible: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
var allOverlays = [
|
|
||||||
upButton,
|
|
||||||
downButton,
|
|
||||||
leftButton,
|
|
||||||
rightButton,
|
|
||||||
centerButton,
|
|
||||||
];
|
|
||||||
|
|
||||||
that.destroy = function() {
|
|
||||||
for (var i = 0; i < allOverlays.length; i++) {
|
|
||||||
Overlays.deleteOverlay(allOverlays[i]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
that.setVisible = function(visible) {
|
|
||||||
for (var i = 0; i < allOverlays.length; i++) {
|
|
||||||
Overlays.editOverlay(allOverlays[i], { visible: visible });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
that.mouseReleaseEvent = function(event) {
|
|
||||||
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
|
||||||
var clicked = false;
|
|
||||||
if (clickedOverlay == leftButton) {
|
|
||||||
if (onLeftPressed) onLeftPressed();
|
|
||||||
clicked = true;
|
|
||||||
} else if (clickedOverlay == rightButton) {
|
|
||||||
if (onRightPressed) onRightPressed();
|
|
||||||
clicked = true;
|
|
||||||
} else if (clickedOverlay == upButton) {
|
|
||||||
if (onUpPressed) onUpPressed();
|
|
||||||
clicked = true;
|
|
||||||
} else if (clickedOverlay == downButton) {
|
|
||||||
if (onDownPressed) onDownPressed();
|
|
||||||
clicked = true;
|
|
||||||
} else if (clickedOverlay == centerButton) {
|
|
||||||
if (onCenterPressed) onCenterPressed();
|
|
||||||
clicked = true;
|
|
||||||
}
|
|
||||||
return clicked;
|
|
||||||
}
|
|
||||||
|
|
||||||
return that;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
CameraTool = function(cameraManager) {
|
CameraTool = function(cameraManager) {
|
||||||
var that = {};
|
var that = {};
|
||||||
|
|
||||||
var toolsPosition = { x: 20, y: 280 };
|
var RED = { red: 191, green: 78, blue: 38 };
|
||||||
var orbitToolPosition = toolsPosition;
|
var GREEN = { red: 26, green: 193, blue: 105 };
|
||||||
var panToolPosition = { x: toolsPosition.x + 80, y: toolsPosition.y };
|
var BLUE = { red: 0, green: 131, blue: 204 };
|
||||||
var zoomToolPosition = { x: toolsPosition.x + 20, y: toolsPosition.y + 80 };
|
|
||||||
|
|
||||||
var orbitIncrement = 15;
|
var ORIENTATION_OVERLAY_SIZE = 20;
|
||||||
orbitTool = ArrowTool({
|
var ORIENTATION_OVERLAY_HALF_SIZE = ORIENTATION_OVERLAY_SIZE / 2;
|
||||||
position: orbitToolPosition,
|
var ORIENTATION_OVERLAY_CUBE_SIZE = 8,
|
||||||
arrowBackground: { red: 192, green: 192, blue: 192 },
|
|
||||||
centerBackground: { red: 128, green: 128, blue: 255 },
|
var ORIENTATION_OVERLAY_OFFSET = {
|
||||||
color: { red: 0, green: 0, blue: 0 },
|
x: 96,
|
||||||
onUpPressed: function() { cameraManager.addPitch(orbitIncrement); },
|
y: 30,
|
||||||
onDownPressed: function() { cameraManager.addPitch(-orbitIncrement); },
|
}
|
||||||
onLeftPressed: function() { cameraManager.addYaw(-orbitIncrement); },
|
|
||||||
onRightPressed: function() { cameraManager.addYaw(orbitIncrement); },
|
var UI_URL = HIFI_PUBLIC_BUCKET + "images/tools/camera-controls.svg";
|
||||||
onCenterPressed: function() { cameraManager.focus(); },
|
|
||||||
|
var UI_WIDTH = 128;
|
||||||
|
var UI_HEIGHT = 61;
|
||||||
|
var UI_PADDING = 10;
|
||||||
|
|
||||||
|
var UI_BUTTON_WIDTH = 64;
|
||||||
|
var UI_BUTTON_HEIGHT = 30;
|
||||||
|
|
||||||
|
var UI_SUBIMAGE_FIRST_PERSON = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: UI_WIDTH,
|
||||||
|
height: UI_HEIGHT
|
||||||
|
},
|
||||||
|
var UI_SUBIMAGE_THIRD_PERSON = {
|
||||||
|
x: 0,
|
||||||
|
y: UI_HEIGHT,
|
||||||
|
width: UI_WIDTH,
|
||||||
|
height: UI_HEIGHT
|
||||||
|
},
|
||||||
|
var UI_SUBIMAGE_OTHER = {
|
||||||
|
x: 0,
|
||||||
|
y: UI_HEIGHT * 2,
|
||||||
|
width: UI_WIDTH,
|
||||||
|
height: UI_HEIGHT
|
||||||
|
},
|
||||||
|
|
||||||
|
var lastKnownWidth = Window.innerWidth;
|
||||||
|
|
||||||
|
var uiPosition = {
|
||||||
|
x: lastKnownWidth - UI_WIDTH - UI_PADDING,
|
||||||
|
y: UI_PADDING,
|
||||||
|
};
|
||||||
|
|
||||||
|
var ui = Overlays.addOverlay("image", {
|
||||||
|
imageURL: UI_URL,
|
||||||
|
x: uiPosition.x,
|
||||||
|
y: uiPosition.y,
|
||||||
|
subImage: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: UI_WIDTH,
|
||||||
|
height: UI_HEIGHT
|
||||||
|
},
|
||||||
|
width: UI_WIDTH,
|
||||||
|
height: UI_HEIGHT,
|
||||||
|
alpha: 1.0,
|
||||||
|
visible: true
|
||||||
});
|
});
|
||||||
panTool = ArrowTool({
|
|
||||||
position: panToolPosition,
|
var defaultCubeProps = {
|
||||||
arrowBackground: { red: 192, green: 192, blue: 192 },
|
size: ORIENTATION_OVERLAY_CUBE_SIZE,
|
||||||
centerBackground: { red: 128, green: 128, blue: 255 },
|
alpha: 1,
|
||||||
color: { red: 0, green: 0, blue: 0 },
|
color: { red: 255, green: 0, blue: 0 },
|
||||||
onUpPressed: function() { cameraManager.pan({ x: 0, y: 15 }); },
|
solid: true,
|
||||||
onDownPressed: function() { cameraManager.pan({ x: 0, y: -15 }); },
|
visible: true,
|
||||||
onLeftPressed: function() { cameraManager.pan({ x: -15, y: 0 }); },
|
drawOnHUD: true,
|
||||||
onRightPressed: function() { cameraManager.pan({ x: 15, y: 0 }); },
|
};
|
||||||
});
|
var defaultLineProps = {
|
||||||
zoomTool = ZoomTool({
|
lineWidth: 1.5,
|
||||||
position: zoomToolPosition,
|
alpha: 1,
|
||||||
arrowBackground: { red: 192, green: 192, blue: 192 },
|
position: { x: 0, y: 0, z: 0 },
|
||||||
color: { red: 0, green: 0, blue: 0 },
|
start: { x: 0, y: 0, z: 0 },
|
||||||
onIncreasePressed: function() { cameraManager.addZoom(-10); },
|
end: { x: 0, y: 0, z: 0 },
|
||||||
onDecreasePressed: function() { cameraManager.addZoom(10); },
|
color: { red: 255, green: 0, blue: 0 },
|
||||||
onPercentageSet: function(pct) { cameraManager.setZoomPercentage(pct); }
|
visible: true,
|
||||||
|
drawOnHUD: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var orientationOverlay = OverlayGroup({
|
||||||
|
position: {
|
||||||
|
x: uiPosition.x + ORIENTATION_OVERLAY_OFFSET.x,
|
||||||
|
y: uiPosition.y + ORIENTATION_OVERLAY_OFFSET.y,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var OOHS = ORIENTATION_OVERLAY_HALF_SIZE;
|
||||||
|
var cubeX = orientationOverlay.createOverlay("cube", mergeObjects(defaultCubeProps, {
|
||||||
|
position: { x: -OOHS, y: OOHS, z: OOHS },
|
||||||
|
color: RED,
|
||||||
|
}));
|
||||||
|
var cubeY = orientationOverlay.createOverlay("cube", mergeObjects(defaultCubeProps, {
|
||||||
|
position: { x: OOHS, y: -OOHS, z: OOHS },
|
||||||
|
color: GREEN,
|
||||||
|
}));
|
||||||
|
var cubeZ = orientationOverlay.createOverlay("cube", mergeObjects(defaultCubeProps, {
|
||||||
|
position: { x: OOHS, y: OOHS, z: -OOHS },
|
||||||
|
color: BLUE,
|
||||||
|
}));
|
||||||
|
orientationOverlay.createOverlay("line3d", mergeObjects(defaultLineProps, {
|
||||||
|
start: { x: -OOHS, y: OOHS, z: OOHS },
|
||||||
|
end: { x: OOHS, y: OOHS, z: OOHS },
|
||||||
|
color: RED,
|
||||||
|
}));
|
||||||
|
orientationOverlay.createOverlay("line3d", mergeObjects(defaultLineProps, {
|
||||||
|
start: { x: OOHS, y: -OOHS, z: OOHS },
|
||||||
|
end: { x: OOHS, y: OOHS, z: OOHS },
|
||||||
|
color: GREEN,
|
||||||
|
}));
|
||||||
|
orientationOverlay.createOverlay("line3d", mergeObjects(defaultLineProps, {
|
||||||
|
start: { x: OOHS, y: OOHS, z: -OOHS },
|
||||||
|
end: { x: OOHS, y: OOHS, z: OOHS },
|
||||||
|
color: BLUE,
|
||||||
|
}));
|
||||||
|
|
||||||
Script.scriptEnding.connect(function() {
|
Script.scriptEnding.connect(function() {
|
||||||
orbitTool.destroy();
|
orientationOverlay.destroy();
|
||||||
panTool.destroy();
|
Overlays.deleteOverlay(ui);
|
||||||
zoomTool.destroy();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var flip = Quat.fromPitchYawRollDegrees(0, 180, 0);
|
||||||
|
that.update = function() {
|
||||||
|
orientationOverlay.setProperties({
|
||||||
|
rotation: Quat.multiply(flip, Quat.inverse(Camera.orientation)),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Window.innerWidth != lastKnownWidth) {
|
||||||
|
lastKnownWidth = Window.innerWidth;
|
||||||
|
uiPosition = {
|
||||||
|
x: lastKnownWidth - UI_WIDTH - UI_PADDING,
|
||||||
|
y: UI_PADDING,
|
||||||
|
};
|
||||||
|
Overlays.editOverlay(ui, {
|
||||||
|
x: uiPosition.x,
|
||||||
|
y: uiPosition.y
|
||||||
|
});
|
||||||
|
orientationOverlay.setProperties({
|
||||||
|
position: {
|
||||||
|
x: uiPosition.x + ORIENTATION_OVERLAY_OFFSET.x,
|
||||||
|
y: uiPosition.y + ORIENTATION_OVERLAY_OFFSET.y,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
that.mousePressEvent = function(event) {
|
that.mousePressEvent = function(event) {
|
||||||
return orbitTool.mouseReleaseEvent(event)
|
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
||||||
|| panTool.mouseReleaseEvent(event)
|
|
||||||
|| zoomTool.mouseReleaseEvent(event);
|
if (clickedOverlay == cubeX) {
|
||||||
|
targetPitch = 0;
|
||||||
|
targetYaw = event.isLeftButton ? 90 : -90;
|
||||||
|
cameraManager.setTargetPitchYaw(targetPitch, targetYaw);
|
||||||
|
return true;
|
||||||
|
} else if (clickedOverlay == cubeY) {
|
||||||
|
targetPitch = event.isLeftButton ? 90 : -90;
|
||||||
|
targetYaw = 0;
|
||||||
|
cameraManager.setTargetPitchYaw(targetPitch, targetYaw);
|
||||||
|
return true;
|
||||||
|
} else if (clickedOverlay == cubeZ) {
|
||||||
|
targetPitch = 0;
|
||||||
|
targetYaw = event.isLeftButton ? 0 : 180;
|
||||||
|
cameraManager.setTargetPitchYaw(targetPitch, targetYaw);
|
||||||
|
return true;
|
||||||
|
} else if (clickedOverlay == ui) {
|
||||||
|
var x = event.x - uiPosition.x;
|
||||||
|
var y = event.y - uiPosition.y;
|
||||||
|
|
||||||
|
// Did we hit a button?
|
||||||
|
if (x < UI_BUTTON_WIDTH) {
|
||||||
|
if (y < UI_BUTTON_HEIGHT) {
|
||||||
|
Camera.mode = "first person";
|
||||||
|
} else {
|
||||||
|
Camera.mode = "third person";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function updateMode() {
|
||||||
|
var mode = Camera.mode;
|
||||||
|
|
||||||
|
var subImage = UI_SUBIMAGE_OTHER;
|
||||||
|
if (mode == "first person") {
|
||||||
|
subImage = UI_SUBIMAGE_FIRST_PERSON;
|
||||||
|
} else if (mode == "third person") {
|
||||||
|
subImage = UI_SUBIMAGE_THIRD_PERSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
Overlays.editOverlay(ui, { subImage: subImage });
|
||||||
|
}
|
||||||
|
|
||||||
|
Camera.modeUpdated.connect(updateMode);
|
||||||
|
updateMode();
|
||||||
|
|
||||||
that.setVisible = function(visible) {
|
that.setVisible = function(visible) {
|
||||||
orbitTool.setVisible(visible);
|
|
||||||
panTool.setVisible(visible);
|
|
||||||
zoomTool.setVisible(visible);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Script.update.connect(function() {
|
|
||||||
cameraManager.getZoomPercentage();
|
|
||||||
zoomTool.setZoomPercentage(cameraManager.getZoomPercentage());
|
|
||||||
});
|
|
||||||
|
|
||||||
that.setVisible(false);
|
|
||||||
|
|
||||||
return that;
|
return that;
|
||||||
};
|
};
|
||||||
|
|
64
examples/libraries/overlayUtils.js
Normal file
64
examples/libraries/overlayUtils.js
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/**
|
||||||
|
* OverlayGroup provides a way to create composite overlays and control their
|
||||||
|
* position relative to a settable rootPosition and rootRotation.
|
||||||
|
*/
|
||||||
|
OverlayGroup = function(opts) {
|
||||||
|
var that = {};
|
||||||
|
|
||||||
|
var overlays = {};
|
||||||
|
|
||||||
|
var rootPosition = opts.position || { x: 0, y: 0, z: 0 };
|
||||||
|
var rootRotation = opts.rotation || Quat.fromPitchYawRollRadians(0, 0, 0);
|
||||||
|
var visible = true;
|
||||||
|
|
||||||
|
function updateOverlays() {
|
||||||
|
for (overlayID in overlays) {
|
||||||
|
var overlay = overlays[overlayID];
|
||||||
|
var newPosition = Vec3.multiplyQbyV(rootRotation, overlay.position);
|
||||||
|
newPosition = Vec3.sum(rootPosition, newPosition);
|
||||||
|
Overlays.editOverlay(overlayID, {
|
||||||
|
visible: visible,
|
||||||
|
position: newPosition,
|
||||||
|
rotation: Quat.multiply(rootRotation, overlay.rotation),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
that.createOverlay = function(type, properties) {
|
||||||
|
properties.position = properties.position || { x: 0, y: 0, z: 0 };
|
||||||
|
properties.rotation = properties.rotation || Quat.fromPitchYawRollRadians(0, 0, 0);
|
||||||
|
|
||||||
|
var overlay = Overlays.addOverlay(type, properties);
|
||||||
|
|
||||||
|
overlays[overlay] = {
|
||||||
|
position: properties.position,
|
||||||
|
rotation: properties.rotation,
|
||||||
|
};
|
||||||
|
|
||||||
|
updateOverlays();
|
||||||
|
|
||||||
|
return overlay;
|
||||||
|
}
|
||||||
|
|
||||||
|
that.setProperties = function(properties) {
|
||||||
|
if (properties.position !== undefined) {
|
||||||
|
rootPosition = properties.position;
|
||||||
|
}
|
||||||
|
if (properties.rotation !== undefined) {
|
||||||
|
rootRotation = properties.rotation;
|
||||||
|
}
|
||||||
|
if (properties.visible !== undefined) {
|
||||||
|
visible = properties.visible;
|
||||||
|
}
|
||||||
|
updateOverlays();
|
||||||
|
};
|
||||||
|
|
||||||
|
that.destroy = function() {
|
||||||
|
for (var overlay in overlays) {
|
||||||
|
Overlays.deleteOverlay(overlay);
|
||||||
|
}
|
||||||
|
overlays = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return that;
|
||||||
|
};
|
|
@ -154,7 +154,7 @@ var sphere = Overlays.addOverlay("sphere", {
|
||||||
});
|
});
|
||||||
|
|
||||||
var line3d = Overlays.addOverlay("line3d", {
|
var line3d = Overlays.addOverlay("line3d", {
|
||||||
position: { x: 0, y: 0, z:0 },
|
start: { x: 0, y: 0, z:0 },
|
||||||
end: { x: 10, y: 10, z:10 },
|
end: { x: 10, y: 10, z:10 },
|
||||||
color: { red: 0, green: 255, blue: 255},
|
color: { red: 0, green: 255, blue: 255},
|
||||||
alpha: 1,
|
alpha: 1,
|
||||||
|
|
|
@ -38,6 +38,7 @@ endif ()
|
||||||
|
|
||||||
# set up the external glm library
|
# set up the external glm library
|
||||||
include_glm()
|
include_glm()
|
||||||
|
include_bullet()
|
||||||
|
|
||||||
# create the InterfaceConfig.h file based on GL_HEADERS above
|
# create the InterfaceConfig.h file based on GL_HEADERS above
|
||||||
configure_file(InterfaceConfig.h.in "${PROJECT_BINARY_DIR}/includes/InterfaceConfig.h")
|
configure_file(InterfaceConfig.h.in "${PROJECT_BINARY_DIR}/includes/InterfaceConfig.h")
|
||||||
|
|
|
@ -70,6 +70,7 @@
|
||||||
#include <PacketHeaders.h>
|
#include <PacketHeaders.h>
|
||||||
#include <PathUtils.h>
|
#include <PathUtils.h>
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
|
#include <PhysicsEngine.h>
|
||||||
#include <ProgramObject.h>
|
#include <ProgramObject.h>
|
||||||
#include <ResourceCache.h>
|
#include <ResourceCache.h>
|
||||||
#include <SoundCache.h>
|
#include <SoundCache.h>
|
||||||
|
@ -150,6 +151,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||||
_frameCount(0),
|
_frameCount(0),
|
||||||
_fps(60.0f),
|
_fps(60.0f),
|
||||||
_justStarted(true),
|
_justStarted(true),
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
_physicsEngine(glm::vec3(0.0f)),
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
_entities(true, this, this),
|
_entities(true, this, this),
|
||||||
_entityCollisionSystem(),
|
_entityCollisionSystem(),
|
||||||
_entityClipboardRenderer(false, this, this),
|
_entityClipboardRenderer(false, this, this),
|
||||||
|
@ -1763,6 +1767,12 @@ void Application::init() {
|
||||||
// save settings when avatar changes
|
// save settings when avatar changes
|
||||||
connect(_myAvatar, &MyAvatar::transformChanged, this, &Application::bumpSettings);
|
connect(_myAvatar, &MyAvatar::transformChanged, this, &Application::bumpSettings);
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
EntityTree* tree = _entities.getTree();
|
||||||
|
_physicsEngine.setEntityTree(tree);
|
||||||
|
tree->setSimulation(&_physicsEngine);
|
||||||
|
_physicsEngine.init(&_entityEditSender);
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
// make sure our texture cache knows about window size changes
|
// make sure our texture cache knows about window size changes
|
||||||
DependencyManager::get<TextureCache>()->associateWithWidget(glCanvas.data());
|
DependencyManager::get<TextureCache>()->associateWithWidget(glCanvas.data());
|
||||||
|
|
||||||
|
@ -2070,6 +2080,13 @@ void Application::update(float deltaTime) {
|
||||||
updateDialogs(deltaTime); // update various stats dialogs if present
|
updateDialogs(deltaTime); // update various stats dialogs if present
|
||||||
updateCursor(deltaTime); // Handle cursor updates
|
updateCursor(deltaTime); // Handle cursor updates
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
{
|
||||||
|
PerformanceTimer perfTimer("physics");
|
||||||
|
_physicsEngine.stepSimulation();
|
||||||
|
}
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
|
|
||||||
if (!_aboutToQuit) {
|
if (!_aboutToQuit) {
|
||||||
PerformanceTimer perfTimer("entities");
|
PerformanceTimer perfTimer("entities");
|
||||||
// NOTE: the _entities.update() call below will wait for lock
|
// NOTE: the _entities.update() call below will wait for lock
|
||||||
|
@ -2615,12 +2632,12 @@ void Application::updateShadowMap() {
|
||||||
// render JS/scriptable overlays
|
// render JS/scriptable overlays
|
||||||
{
|
{
|
||||||
PerformanceTimer perfTimer("3dOverlays");
|
PerformanceTimer perfTimer("3dOverlays");
|
||||||
_overlays.render3D(false, RenderArgs::SHADOW_RENDER_MODE);
|
_overlays.renderWorld(false, RenderArgs::SHADOW_RENDER_MODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
PerformanceTimer perfTimer("3dOverlaysFront");
|
PerformanceTimer perfTimer("3dOverlaysFront");
|
||||||
_overlays.render3D(true, RenderArgs::SHADOW_RENDER_MODE);
|
_overlays.renderWorld(true, RenderArgs::SHADOW_RENDER_MODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||||
|
@ -2840,7 +2857,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly, RenderAr
|
||||||
// render JS/scriptable overlays
|
// render JS/scriptable overlays
|
||||||
{
|
{
|
||||||
PerformanceTimer perfTimer("3dOverlays");
|
PerformanceTimer perfTimer("3dOverlays");
|
||||||
_overlays.render3D(false);
|
_overlays.renderWorld(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// render the ambient occlusion effect if enabled
|
// render the ambient occlusion effect if enabled
|
||||||
|
@ -2929,7 +2946,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly, RenderAr
|
||||||
{
|
{
|
||||||
PerformanceTimer perfTimer("3dOverlaysFront");
|
PerformanceTimer perfTimer("3dOverlaysFront");
|
||||||
glClear(GL_DEPTH_BUFFER_BIT);
|
glClear(GL_DEPTH_BUFFER_BIT);
|
||||||
_overlays.render3D(true);
|
_overlays.renderWorld(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3333,16 +3350,6 @@ void Application::updateWindowTitle(){
|
||||||
QString title = QString() + (!username.isEmpty() ? username + " @ " : QString())
|
QString title = QString() + (!username.isEmpty() ? username + " @ " : QString())
|
||||||
+ AddressManager::getInstance().getCurrentDomain() + connectionStatus + buildVersion;
|
+ AddressManager::getInstance().getCurrentDomain() + connectionStatus + buildVersion;
|
||||||
|
|
||||||
AccountManager& accountManager = AccountManager::getInstance();
|
|
||||||
if (accountManager.getAccountInfo().hasBalance()) {
|
|
||||||
float creditBalance = accountManager.getAccountInfo().getBalance() / SATOSHIS_PER_CREDIT;
|
|
||||||
|
|
||||||
QString creditBalanceString;
|
|
||||||
creditBalanceString.sprintf("%.8f", creditBalance);
|
|
||||||
|
|
||||||
title += " - ₵" + creditBalanceString;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
// crashes with vs2013/win32
|
// crashes with vs2013/win32
|
||||||
qDebug("Application title set to: %s", title.toStdString().c_str());
|
qDebug("Application title set to: %s", title.toStdString().c_str());
|
||||||
|
@ -3854,6 +3861,25 @@ void Application::openUrl(const QUrl& url) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::updateMyAvatarTransform() {
|
||||||
|
bumpSettings();
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
const float SIMULATION_OFFSET_QUANTIZATION = 16.0f; // meters
|
||||||
|
glm::vec3 avatarPosition = _myAvatar->getPosition();
|
||||||
|
glm::vec3 physicsWorldOffset = _physicsEngine.getOriginOffset();
|
||||||
|
if (glm::distance(avatarPosition, physicsWorldOffset) > SIMULATION_OFFSET_QUANTIZATION) {
|
||||||
|
glm::vec3 newOriginOffset = avatarPosition;
|
||||||
|
int halfExtent = (int)HALF_SIMULATION_EXTENT;
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
newOriginOffset[i] = (float)(glm::max(halfExtent,
|
||||||
|
((int)(avatarPosition[i] / SIMULATION_OFFSET_QUANTIZATION)) * (int)SIMULATION_OFFSET_QUANTIZATION));
|
||||||
|
}
|
||||||
|
// TODO: Andrew to replace this with method that actually moves existing object positions in PhysicsEngine
|
||||||
|
_physicsEngine.setOriginOffset(newOriginOffset);
|
||||||
|
}
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
|
}
|
||||||
|
|
||||||
void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject) {
|
void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject) {
|
||||||
|
|
||||||
// from the domain-handler, figure out the satoshi cost per voxel and per meter cubed
|
// from the domain-handler, figure out the satoshi cost per voxel and per meter cubed
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
#include "MetavoxelSystem.h"
|
#include "MetavoxelSystem.h"
|
||||||
#include "PacketHeaders.h"
|
#include "PacketHeaders.h"
|
||||||
|
#include "Physics.h"
|
||||||
#include "Stars.h"
|
#include "Stars.h"
|
||||||
#include "avatar/Avatar.h"
|
#include "avatar/Avatar.h"
|
||||||
#include "avatar/AvatarManager.h"
|
#include "avatar/AvatarManager.h"
|
||||||
|
@ -344,6 +345,7 @@ public slots:
|
||||||
|
|
||||||
void openUrl(const QUrl& url);
|
void openUrl(const QUrl& url);
|
||||||
|
|
||||||
|
void updateMyAvatarTransform();
|
||||||
void bumpSettings() { ++_numChangedSettings; }
|
void bumpSettings() { ++_numChangedSettings; }
|
||||||
|
|
||||||
void domainSettingsReceived(const QJsonObject& domainSettingsObject);
|
void domainSettingsReceived(const QJsonObject& domainSettingsObject);
|
||||||
|
@ -453,6 +455,10 @@ private:
|
||||||
bool _justStarted;
|
bool _justStarted;
|
||||||
Stars _stars;
|
Stars _stars;
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
PhysicsEngine _physicsEngine;
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
|
|
||||||
EntityTreeRenderer _entities;
|
EntityTreeRenderer _entities;
|
||||||
EntityCollisionSystem _entityCollisionSystem;
|
EntityCollisionSystem _entityCollisionSystem;
|
||||||
EntityTreeRenderer _entityClipboardRenderer;
|
EntityTreeRenderer _entityClipboardRenderer;
|
||||||
|
|
|
@ -41,4 +41,4 @@ void applyDamping(float deltaTime, glm::vec3& velocity, float linearStrength, fl
|
||||||
|
|
||||||
void applyDampedSpring(float deltaTime, glm::vec3& velocity, glm::vec3& position, glm::vec3& targetPosition, float k, float damping) {
|
void applyDampedSpring(float deltaTime, glm::vec3& velocity, glm::vec3& position, glm::vec3& targetPosition, float k, float damping) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,13 +46,18 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||||
|
Avatar* owningAvatar = static_cast<Avatar*>(_owningHead->_owningAvatar);
|
||||||
// get the rotation axes in joint space and use them to adjust the rotation
|
// get the rotation axes in joint space and use them to adjust the rotation
|
||||||
glm::mat3 axes = glm::mat3_cast(glm::quat());
|
glm::mat3 axes = glm::mat3_cast(glm::quat());
|
||||||
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(state.getDefaultTranslationInConstrainedFrame()) *
|
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(state.getDefaultTranslationInConstrainedFrame()) *
|
||||||
joint.preTransform * glm::mat4_cast(joint.preRotation)));
|
joint.preTransform * glm::mat4_cast(joint.preRotation)));
|
||||||
state.setRotationInConstrainedFrame(glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalRoll(), glm::normalize(inverse * axes[2]))
|
state.setRotationInConstrainedFrame(
|
||||||
* glm::angleAxis(RADIANS_PER_DEGREE * _owningHead->getFinalYaw(), glm::normalize(inverse * axes[1]))
|
glm::angleAxis(- RADIANS_PER_DEGREE * (_owningHead->getFinalRoll() - owningAvatar->getHead()->getFinalLeanSideways()),
|
||||||
* glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalPitch(), glm::normalize(inverse * axes[0]))
|
glm::normalize(inverse * axes[2]))
|
||||||
|
* glm::angleAxis(RADIANS_PER_DEGREE * (_owningHead->getFinalYaw() - _owningHead->getTorsoTwist()),
|
||||||
|
glm::normalize(inverse * axes[1]))
|
||||||
|
* glm::angleAxis(- RADIANS_PER_DEGREE * (_owningHead->getFinalPitch() - owningAvatar->getHead()->getFinalLeanForward()),
|
||||||
|
glm::normalize(inverse * axes[0]))
|
||||||
* joint.rotation, DEFAULT_PRIORITY);
|
* joint.rotation, DEFAULT_PRIORITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,7 @@ Head::Head(Avatar* owningAvatar) :
|
||||||
_deltaRoll(0.0f),
|
_deltaRoll(0.0f),
|
||||||
_deltaLeanSideways(0.0f),
|
_deltaLeanSideways(0.0f),
|
||||||
_deltaLeanForward(0.0f),
|
_deltaLeanForward(0.0f),
|
||||||
|
_torsoTwist(0.0f),
|
||||||
_isCameraMoving(false),
|
_isCameraMoving(false),
|
||||||
_isLookingAtMe(false),
|
_isLookingAtMe(false),
|
||||||
_faceModel(this)
|
_faceModel(this)
|
||||||
|
@ -88,7 +89,14 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
|
||||||
_blendshapeCoefficients = faceTracker->getBlendshapeCoefficients();
|
_blendshapeCoefficients = faceTracker->getBlendshapeCoefficients();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Twist the upper body to follow the rotation of the head, but only do this with my avatar,
|
||||||
|
// since everyone else will see the full joint rotations for other people.
|
||||||
|
const float BODY_FOLLOW_HEAD_YAW_RATE = 0.1f;
|
||||||
|
const float BODY_FOLLOW_HEAD_FACTOR = 0.66f;
|
||||||
|
float currentTwist = getTorsoTwist();
|
||||||
|
setTorsoTwist(currentTwist + (getFinalYaw() * BODY_FOLLOW_HEAD_FACTOR - currentTwist) * BODY_FOLLOW_HEAD_YAW_RATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update audio trailing average for rendering facial animations
|
// Update audio trailing average for rendering facial animations
|
||||||
const float AUDIO_AVERAGING_SECS = 0.05f;
|
const float AUDIO_AVERAGING_SECS = 0.05f;
|
||||||
const float AUDIO_LONG_TERM_AVERAGING_SECS = 30.0f;
|
const float AUDIO_LONG_TERM_AVERAGING_SECS = 30.0f;
|
||||||
|
|
|
@ -76,6 +76,9 @@ public:
|
||||||
float getFinalLeanSideways() const { return _leanSideways + _deltaLeanSideways; }
|
float getFinalLeanSideways() const { return _leanSideways + _deltaLeanSideways; }
|
||||||
float getFinalLeanForward() const { return _leanForward + _deltaLeanForward; }
|
float getFinalLeanForward() const { return _leanForward + _deltaLeanForward; }
|
||||||
|
|
||||||
|
float getTorsoTwist() const { return _torsoTwist; }
|
||||||
|
void setTorsoTwist(float torsoTwist) { _torsoTwist = torsoTwist; }
|
||||||
|
|
||||||
glm::quat getEyeRotation(const glm::vec3& eyePosition) const;
|
glm::quat getEyeRotation(const glm::vec3& eyePosition) const;
|
||||||
|
|
||||||
const glm::vec3& getRightEyePosition() const { return _rightEyePosition; }
|
const glm::vec3& getRightEyePosition() const { return _rightEyePosition; }
|
||||||
|
@ -148,6 +151,8 @@ private:
|
||||||
// delta lean angles for lean perturbations (driven by collisions)
|
// delta lean angles for lean perturbations (driven by collisions)
|
||||||
float _deltaLeanSideways;
|
float _deltaLeanSideways;
|
||||||
float _deltaLeanForward;
|
float _deltaLeanForward;
|
||||||
|
|
||||||
|
float _torsoTwist;
|
||||||
|
|
||||||
bool _isCameraMoving;
|
bool _isCameraMoving;
|
||||||
bool _isLookingAtMe;
|
bool _isLookingAtMe;
|
||||||
|
|
|
@ -297,11 +297,13 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, Joint
|
||||||
}
|
}
|
||||||
// get the rotation axes in joint space and use them to adjust the rotation
|
// get the rotation axes in joint space and use them to adjust the rotation
|
||||||
glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
|
glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
|
||||||
|
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
|
||||||
glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
|
glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
|
||||||
glm::quat inverse = glm::inverse(parentState.getRotation() * state.getDefaultRotationInParentFrame());
|
glm::quat inverse = glm::inverse(parentState.getRotation() * state.getDefaultRotationInParentFrame());
|
||||||
state.setRotationInConstrainedFrame(
|
state.setRotationInConstrainedFrame(
|
||||||
glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(), inverse * zAxis)
|
glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(), inverse * zAxis)
|
||||||
* glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(), inverse * xAxis)
|
* glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(), inverse * xAxis)
|
||||||
|
* glm::angleAxis(RADIANS_PER_DEGREE * _owningAvatar->getHead()->getTorsoTwist(), inverse * yAxis)
|
||||||
* state.getFBXJoint().rotation, LEAN_PRIORITY);
|
* state.getFBXJoint().rotation, LEAN_PRIORITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -182,8 +182,10 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) {
|
||||||
}
|
}
|
||||||
|
|
||||||
glPushMatrix(); {
|
glPushMatrix(); {
|
||||||
|
const float NEAR_CLIP = -10000;
|
||||||
|
const float FAR_CLIP = 10000;
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
glOrtho(0, glCanvas->width(), glCanvas->height(), 0, -1.0, 1.0);
|
glOrtho(0, glCanvas->width(), glCanvas->height(), 0, NEAR_CLIP, FAR_CLIP);
|
||||||
|
|
||||||
renderAudioMeter();
|
renderAudioMeter();
|
||||||
|
|
||||||
|
@ -196,7 +198,7 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) {
|
||||||
// give external parties a change to hook in
|
// give external parties a change to hook in
|
||||||
emit application->renderingOverlay();
|
emit application->renderingOverlay();
|
||||||
|
|
||||||
overlays.render2D();
|
overlays.renderHUD();
|
||||||
|
|
||||||
renderPointers();
|
renderPointers();
|
||||||
|
|
||||||
|
@ -1103,7 +1105,7 @@ void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() {
|
||||||
delete _framebufferObject;
|
delete _framebufferObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
_framebufferObject = new QOpenGLFramebufferObject(size);
|
_framebufferObject = new QOpenGLFramebufferObject(size, QOpenGLFramebufferObject::Depth);
|
||||||
bindTexture();
|
bindTexture();
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <QGLWidget>
|
#include <QGLWidget>
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
|
|
||||||
|
#include "Application.h"
|
||||||
#include "Base3DOverlay.h"
|
#include "Base3DOverlay.h"
|
||||||
|
|
||||||
const glm::vec3 DEFAULT_POSITION = glm::vec3(0.0f, 0.0f, 0.0f);
|
const glm::vec3 DEFAULT_POSITION = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||||
|
@ -28,7 +29,8 @@ Base3DOverlay::Base3DOverlay() :
|
||||||
_isSolid(DEFAULT_IS_SOLID),
|
_isSolid(DEFAULT_IS_SOLID),
|
||||||
_isDashedLine(DEFAULT_IS_DASHED_LINE),
|
_isDashedLine(DEFAULT_IS_DASHED_LINE),
|
||||||
_ignoreRayIntersection(false),
|
_ignoreRayIntersection(false),
|
||||||
_drawInFront(false)
|
_drawInFront(false),
|
||||||
|
_drawOnHUD(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +48,13 @@ Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
|
||||||
Base3DOverlay::~Base3DOverlay() {
|
Base3DOverlay::~Base3DOverlay() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Base3DOverlay::setDrawOnHUD(bool value) {
|
||||||
|
if (_drawOnHUD != value) {
|
||||||
|
_drawOnHUD = value;
|
||||||
|
Application::getInstance()->getOverlays().overlayDrawOnChanged(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Base3DOverlay::setProperties(const QScriptValue& properties) {
|
void Base3DOverlay::setProperties(const QScriptValue& properties) {
|
||||||
Overlay::setProperties(properties);
|
Overlay::setProperties(properties);
|
||||||
|
|
||||||
|
@ -56,16 +65,20 @@ void Base3DOverlay::setProperties(const QScriptValue& properties) {
|
||||||
setDrawInFront(value);
|
setDrawInFront(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QScriptValue drawOnHUD = properties.property("drawOnHUD");
|
||||||
|
|
||||||
|
if (drawOnHUD.isValid()) {
|
||||||
|
bool value = drawOnHUD.toVariant().toBool();
|
||||||
|
setDrawOnHUD(value);
|
||||||
|
}
|
||||||
|
|
||||||
QScriptValue position = properties.property("position");
|
QScriptValue position = properties.property("position");
|
||||||
|
|
||||||
// if "position" property was not there, check to see if they included aliases: start, point, p1
|
// if "position" property was not there, check to see if they included aliases: point, p1
|
||||||
if (!position.isValid()) {
|
if (!position.isValid()) {
|
||||||
position = properties.property("start");
|
position = properties.property("p1");
|
||||||
if (!position.isValid()) {
|
if (!position.isValid()) {
|
||||||
position = properties.property("p1");
|
position = properties.property("point");
|
||||||
if (!position.isValid()) {
|
|
||||||
position = properties.property("point");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,6 +175,9 @@ QScriptValue Base3DOverlay::getProperty(const QString& property) {
|
||||||
if (property == "drawInFront") {
|
if (property == "drawInFront") {
|
||||||
return _drawInFront;
|
return _drawInFront;
|
||||||
}
|
}
|
||||||
|
if (property == "drawOnHUD") {
|
||||||
|
return _drawOnHUD;
|
||||||
|
}
|
||||||
|
|
||||||
return Overlay::getProperty(property);
|
return Overlay::getProperty(property);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ public:
|
||||||
const glm::quat& getRotation() const { return _rotation; }
|
const glm::quat& getRotation() const { return _rotation; }
|
||||||
bool getIgnoreRayIntersection() const { return _ignoreRayIntersection; }
|
bool getIgnoreRayIntersection() const { return _ignoreRayIntersection; }
|
||||||
bool getDrawInFront() const { return _drawInFront; }
|
bool getDrawInFront() const { return _drawInFront; }
|
||||||
|
bool getDrawOnHUD() const { return _drawOnHUD; }
|
||||||
|
|
||||||
// setters
|
// setters
|
||||||
void setPosition(const glm::vec3& position) { _position = position; }
|
void setPosition(const glm::vec3& position) { _position = position; }
|
||||||
|
@ -46,6 +47,7 @@ public:
|
||||||
void setRotation(const glm::quat& value) { _rotation = value; }
|
void setRotation(const glm::quat& value) { _rotation = value; }
|
||||||
void setIgnoreRayIntersection(bool value) { _ignoreRayIntersection = value; }
|
void setIgnoreRayIntersection(bool value) { _ignoreRayIntersection = value; }
|
||||||
void setDrawInFront(bool value) { _drawInFront = value; }
|
void setDrawInFront(bool value) { _drawInFront = value; }
|
||||||
|
void setDrawOnHUD(bool value);
|
||||||
|
|
||||||
virtual void setProperties(const QScriptValue& properties);
|
virtual void setProperties(const QScriptValue& properties);
|
||||||
virtual QScriptValue getProperty(const QString& property);
|
virtual QScriptValue getProperty(const QString& property);
|
||||||
|
@ -67,6 +69,7 @@ protected:
|
||||||
bool _isDashedLine;
|
bool _isDashedLine;
|
||||||
bool _ignoreRayIntersection;
|
bool _ignoreRayIntersection;
|
||||||
bool _drawInFront;
|
bool _drawInFront;
|
||||||
|
bool _drawOnHUD;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_Base3DOverlay_h
|
#endif // hifi_Base3DOverlay_h
|
||||||
|
|
|
@ -76,7 +76,13 @@ void Cube3DOverlay::render(RenderArgs* args) {
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glColor4f(1.0f, 1.0f, 1.0f, alpha);
|
glColor4f(1.0f, 1.0f, 1.0f, alpha);
|
||||||
glScalef(dimensions.x * _borderSize, dimensions.y * _borderSize, dimensions.z * _borderSize);
|
glScalef(dimensions.x * _borderSize, dimensions.y * _borderSize, dimensions.z * _borderSize);
|
||||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(1.0f);
|
|
||||||
|
if (_drawOnHUD) {
|
||||||
|
DependencyManager::get<GeometryCache>()->renderSolidCube(1.0f);
|
||||||
|
} else {
|
||||||
|
DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
glDepthMask(GL_TRUE);
|
glDepthMask(GL_TRUE);
|
||||||
}
|
}
|
||||||
|
@ -84,7 +90,11 @@ void Cube3DOverlay::render(RenderArgs* args) {
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
|
glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
|
||||||
glScalef(dimensions.x, dimensions.y, dimensions.z);
|
glScalef(dimensions.x, dimensions.y, dimensions.z);
|
||||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(1.0f);
|
if (_drawOnHUD) {
|
||||||
|
DependencyManager::get<GeometryCache>()->renderSolidCube(1.0f);
|
||||||
|
} else {
|
||||||
|
DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(1.0f);
|
||||||
|
}
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
} else {
|
} else {
|
||||||
glLineWidth(_lineWidth);
|
glLineWidth(_lineWidth);
|
||||||
|
|
|
@ -39,6 +39,8 @@ void Line3DOverlay::render(RenderArgs* args) {
|
||||||
glower = new Glower(glowLevel);
|
glower = new Glower(glowLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glPushMatrix();
|
||||||
|
|
||||||
glDisable(GL_LIGHTING);
|
glDisable(GL_LIGHTING);
|
||||||
glLineWidth(_lineWidth);
|
glLineWidth(_lineWidth);
|
||||||
|
|
||||||
|
@ -47,16 +49,25 @@ void Line3DOverlay::render(RenderArgs* args) {
|
||||||
const float MAX_COLOR = 255.0f;
|
const float MAX_COLOR = 255.0f;
|
||||||
glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
|
glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
|
||||||
|
|
||||||
|
glm::vec3 position = getPosition();
|
||||||
|
glm::quat rotation = getRotation();
|
||||||
|
|
||||||
|
glTranslatef(position.x, position.y, position.z);
|
||||||
|
glm::vec3 axis = glm::axis(rotation);
|
||||||
|
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||||
|
|
||||||
if (getIsDashedLine()) {
|
if (getIsDashedLine()) {
|
||||||
drawDashedLine(_position, _end);
|
drawDashedLine(_position, _end);
|
||||||
} else {
|
} else {
|
||||||
glBegin(GL_LINES);
|
glBegin(GL_LINES);
|
||||||
glVertex3f(_position.x, _position.y, _position.z);
|
glVertex3f(_start.x, _start.y, _start.z);
|
||||||
glVertex3f(_end.x, _end.y, _end.z);
|
glVertex3f(_end.x, _end.y, _end.z);
|
||||||
glEnd();
|
glEnd();
|
||||||
}
|
}
|
||||||
glEnable(GL_LIGHTING);
|
glEnable(GL_LIGHTING);
|
||||||
|
|
||||||
|
glPopMatrix();
|
||||||
|
|
||||||
if (glower) {
|
if (glower) {
|
||||||
delete glower;
|
delete glower;
|
||||||
}
|
}
|
||||||
|
@ -65,13 +76,28 @@ void Line3DOverlay::render(RenderArgs* args) {
|
||||||
void Line3DOverlay::setProperties(const QScriptValue& properties) {
|
void Line3DOverlay::setProperties(const QScriptValue& properties) {
|
||||||
Base3DOverlay::setProperties(properties);
|
Base3DOverlay::setProperties(properties);
|
||||||
|
|
||||||
|
QScriptValue start = properties.property("start");
|
||||||
|
// if "start" property was not there, check to see if they included aliases: startPoint
|
||||||
|
if (!start.isValid()) {
|
||||||
|
start = properties.property("startPoint");
|
||||||
|
}
|
||||||
|
if (start.isValid()) {
|
||||||
|
QScriptValue x = start.property("x");
|
||||||
|
QScriptValue y = start.property("y");
|
||||||
|
QScriptValue z = start.property("z");
|
||||||
|
if (x.isValid() && y.isValid() && z.isValid()) {
|
||||||
|
glm::vec3 newStart;
|
||||||
|
newStart.x = x.toVariant().toFloat();
|
||||||
|
newStart.y = y.toVariant().toFloat();
|
||||||
|
newStart.z = z.toVariant().toFloat();
|
||||||
|
setStart(newStart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QScriptValue end = properties.property("end");
|
QScriptValue end = properties.property("end");
|
||||||
// if "end" property was not there, check to see if they included aliases: endPoint, or p2
|
// if "end" property was not there, check to see if they included aliases: endPoint
|
||||||
if (!end.isValid()) {
|
if (!end.isValid()) {
|
||||||
end = properties.property("endPoint");
|
end = properties.property("endPoint");
|
||||||
if (!end.isValid()) {
|
|
||||||
end = properties.property("p2");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (end.isValid()) {
|
if (end.isValid()) {
|
||||||
QScriptValue x = end.property("x");
|
QScriptValue x = end.property("x");
|
||||||
|
|
|
@ -23,9 +23,11 @@ public:
|
||||||
virtual void render(RenderArgs* args);
|
virtual void render(RenderArgs* args);
|
||||||
|
|
||||||
// getters
|
// getters
|
||||||
|
const glm::vec3& getStart() const { return _start; }
|
||||||
const glm::vec3& getEnd() const { return _end; }
|
const glm::vec3& getEnd() const { return _end; }
|
||||||
|
|
||||||
// setters
|
// setters
|
||||||
|
void setStart(const glm::vec3& start) { _start = start; }
|
||||||
void setEnd(const glm::vec3& end) { _end = end; }
|
void setEnd(const glm::vec3& end) { _end = end; }
|
||||||
|
|
||||||
virtual void setProperties(const QScriptValue& properties);
|
virtual void setProperties(const QScriptValue& properties);
|
||||||
|
@ -34,6 +36,7 @@ public:
|
||||||
virtual Line3DOverlay* createClone() const;
|
virtual Line3DOverlay* createClone() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
glm::vec3 _start;
|
||||||
glm::vec3 _end;
|
glm::vec3 _end;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -36,14 +36,14 @@ Overlays::~Overlays() {
|
||||||
|
|
||||||
{
|
{
|
||||||
QWriteLocker lock(&_lock);
|
QWriteLocker lock(&_lock);
|
||||||
foreach(Overlay* thisOverlay, _overlays2D) {
|
foreach(Overlay* thisOverlay, _overlaysHUD) {
|
||||||
delete thisOverlay;
|
delete thisOverlay;
|
||||||
}
|
}
|
||||||
_overlays2D.clear();
|
_overlaysHUD.clear();
|
||||||
foreach(Overlay* thisOverlay, _overlays3D) {
|
foreach(Overlay* thisOverlay, _overlaysWorld) {
|
||||||
delete thisOverlay;
|
delete thisOverlay;
|
||||||
}
|
}
|
||||||
_overlays3D.clear();
|
_overlaysWorld.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_overlaysToDelete.isEmpty()) {
|
if (!_overlaysToDelete.isEmpty()) {
|
||||||
|
@ -64,10 +64,10 @@ void Overlays::update(float deltatime) {
|
||||||
|
|
||||||
{
|
{
|
||||||
QWriteLocker lock(&_lock);
|
QWriteLocker lock(&_lock);
|
||||||
foreach(Overlay* thisOverlay, _overlays2D) {
|
foreach(Overlay* thisOverlay, _overlaysHUD) {
|
||||||
thisOverlay->update(deltatime);
|
thisOverlay->update(deltatime);
|
||||||
}
|
}
|
||||||
foreach(Overlay* thisOverlay, _overlays3D) {
|
foreach(Overlay* thisOverlay, _overlaysWorld) {
|
||||||
thisOverlay->update(deltatime);
|
thisOverlay->update(deltatime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,21 +81,31 @@ void Overlays::update(float deltatime) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Overlays::render2D() {
|
void Overlays::renderHUD() {
|
||||||
QReadLocker lock(&_lock);
|
QReadLocker lock(&_lock);
|
||||||
|
|
||||||
RenderArgs args = { NULL, Application::getInstance()->getViewFrustum(),
|
RenderArgs args = { NULL, Application::getInstance()->getViewFrustum(),
|
||||||
Menu::getInstance()->getOctreeSizeScale(), Menu::getInstance()->getBoundaryLevelAdjust(),
|
Menu::getInstance()->getOctreeSizeScale(), Menu::getInstance()->getBoundaryLevelAdjust(),
|
||||||
RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||||
|
|
||||||
foreach(Overlay* thisOverlay, _overlays2D) {
|
foreach(Overlay* thisOverlay, _overlaysHUD) {
|
||||||
thisOverlay->render(&args);
|
if (thisOverlay->is3D()) {
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glEnable(GL_LIGHTING);
|
||||||
|
|
||||||
|
thisOverlay->render(&args);
|
||||||
|
|
||||||
|
glDisable(GL_LIGHTING);
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
} else{
|
||||||
|
thisOverlay->render(&args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Overlays::render3D(bool drawFront, RenderArgs::RenderMode renderMode, RenderArgs::RenderSide renderSide) {
|
void Overlays::renderWorld(bool drawFront, RenderArgs::RenderMode renderMode, RenderArgs::RenderSide renderSide) {
|
||||||
QReadLocker lock(&_lock);
|
QReadLocker lock(&_lock);
|
||||||
if (_overlays3D.size() == 0) {
|
if (_overlaysWorld.size() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bool myAvatarComputed = false;
|
bool myAvatarComputed = false;
|
||||||
|
@ -111,7 +121,7 @@ void Overlays::render3D(bool drawFront, RenderArgs::RenderMode renderMode, Rende
|
||||||
renderMode, renderSide, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
renderMode, renderSide, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
|
||||||
foreach(Overlay* thisOverlay, _overlays3D) {
|
foreach(Overlay* thisOverlay, _overlaysWorld) {
|
||||||
Base3DOverlay* overlay3D = static_cast<Base3DOverlay*>(thisOverlay);
|
Base3DOverlay* overlay3D = static_cast<Base3DOverlay*>(thisOverlay);
|
||||||
if (overlay3D->getDrawInFront() != drawFront) {
|
if (overlay3D->getDrawInFront() != drawFront) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -190,9 +200,14 @@ unsigned int Overlays::addOverlay(Overlay* overlay) {
|
||||||
unsigned int thisID = _nextOverlayID;
|
unsigned int thisID = _nextOverlayID;
|
||||||
_nextOverlayID++;
|
_nextOverlayID++;
|
||||||
if (overlay->is3D()) {
|
if (overlay->is3D()) {
|
||||||
_overlays3D[thisID] = overlay;
|
Base3DOverlay* overlay3D = static_cast<Base3DOverlay*>(overlay);
|
||||||
|
if (overlay3D->getDrawOnHUD()) {
|
||||||
|
_overlaysHUD[thisID] = overlay;
|
||||||
|
} else {
|
||||||
|
_overlaysWorld[thisID] = overlay;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
_overlays2D[thisID] = overlay;
|
_overlaysHUD[thisID] = overlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
return thisID;
|
return thisID;
|
||||||
|
@ -200,21 +215,23 @@ unsigned int Overlays::addOverlay(Overlay* overlay) {
|
||||||
|
|
||||||
unsigned int Overlays::cloneOverlay(unsigned int id) {
|
unsigned int Overlays::cloneOverlay(unsigned int id) {
|
||||||
Overlay* thisOverlay = NULL;
|
Overlay* thisOverlay = NULL;
|
||||||
if (_overlays2D.contains(id)) {
|
if (_overlaysHUD.contains(id)) {
|
||||||
thisOverlay = _overlays2D[id];
|
thisOverlay = _overlaysHUD[id];
|
||||||
} else if (_overlays3D.contains(id)) {
|
} else if (_overlaysWorld.contains(id)) {
|
||||||
thisOverlay = _overlays3D[id];
|
thisOverlay = _overlaysWorld[id];
|
||||||
}
|
}
|
||||||
return addOverlay(thisOverlay->createClone());
|
return addOverlay(thisOverlay->createClone());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) {
|
bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) {
|
||||||
Overlay* thisOverlay = NULL;
|
Overlay* thisOverlay = NULL;
|
||||||
QWriteLocker lock(&_lock);
|
{
|
||||||
if (_overlays2D.contains(id)) {
|
QReadLocker lock(&_lock);
|
||||||
thisOverlay = _overlays2D[id];
|
if (_overlaysHUD.contains(id)) {
|
||||||
} else if (_overlays3D.contains(id)) {
|
thisOverlay = _overlaysHUD[id];
|
||||||
thisOverlay = _overlays3D[id];
|
} else if (_overlaysWorld.contains(id)) {
|
||||||
|
thisOverlay = _overlaysWorld[id];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (thisOverlay) {
|
if (thisOverlay) {
|
||||||
thisOverlay->setProperties(properties);
|
thisOverlay->setProperties(properties);
|
||||||
|
@ -228,10 +245,10 @@ void Overlays::deleteOverlay(unsigned int id) {
|
||||||
|
|
||||||
{
|
{
|
||||||
QWriteLocker lock(&_lock);
|
QWriteLocker lock(&_lock);
|
||||||
if (_overlays2D.contains(id)) {
|
if (_overlaysHUD.contains(id)) {
|
||||||
overlayToDelete = _overlays2D.take(id);
|
overlayToDelete = _overlaysHUD.take(id);
|
||||||
} else if (_overlays3D.contains(id)) {
|
} else if (_overlaysWorld.contains(id)) {
|
||||||
overlayToDelete = _overlays3D.take(id);
|
overlayToDelete = _overlaysWorld.take(id);
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -248,17 +265,34 @@ unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QReadLocker lock(&_lock);
|
QReadLocker lock(&_lock);
|
||||||
QMapIterator<unsigned int, Overlay*> i(_overlays2D);
|
QMapIterator<unsigned int, Overlay*> i(_overlaysHUD);
|
||||||
i.toBack();
|
i.toBack();
|
||||||
|
|
||||||
|
const float LARGE_NEGATIVE_FLOAT = -9999999;
|
||||||
|
glm::vec3 origin(pointCopy.x, pointCopy.y, LARGE_NEGATIVE_FLOAT);
|
||||||
|
glm::vec3 direction(0, 0, 1);
|
||||||
|
BoxFace thisFace;
|
||||||
|
float distance;
|
||||||
|
|
||||||
while (i.hasPrevious()) {
|
while (i.hasPrevious()) {
|
||||||
i.previous();
|
i.previous();
|
||||||
unsigned int thisID = i.key();
|
unsigned int thisID = i.key();
|
||||||
Overlay2D* thisOverlay = static_cast<Overlay2D*>(i.value());
|
if (i.value()->is3D()) {
|
||||||
if (thisOverlay->getVisible() && thisOverlay->isLoaded() &&
|
Base3DOverlay* thisOverlay = static_cast<Base3DOverlay*>(i.value());
|
||||||
thisOverlay->getBounds().contains(pointCopy.x, pointCopy.y, false)) {
|
if (!thisOverlay->getIgnoreRayIntersection()) {
|
||||||
return thisID;
|
if (thisOverlay->findRayIntersection(origin, direction, distance, thisFace)) {
|
||||||
|
return thisID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Overlay2D* thisOverlay = static_cast<Overlay2D*>(i.value());
|
||||||
|
if (thisOverlay->getVisible() && thisOverlay->isLoaded() &&
|
||||||
|
thisOverlay->getBounds().contains(pointCopy.x, pointCopy.y, false)) {
|
||||||
|
return thisID;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0; // not found
|
return 0; // not found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,10 +300,10 @@ OverlayPropertyResult Overlays::getProperty(unsigned int id, const QString& prop
|
||||||
OverlayPropertyResult result;
|
OverlayPropertyResult result;
|
||||||
Overlay* thisOverlay = NULL;
|
Overlay* thisOverlay = NULL;
|
||||||
QReadLocker lock(&_lock);
|
QReadLocker lock(&_lock);
|
||||||
if (_overlays2D.contains(id)) {
|
if (_overlaysHUD.contains(id)) {
|
||||||
thisOverlay = _overlays2D[id];
|
thisOverlay = _overlaysHUD[id];
|
||||||
} else if (_overlays3D.contains(id)) {
|
} else if (_overlaysWorld.contains(id)) {
|
||||||
thisOverlay = _overlays3D[id];
|
thisOverlay = _overlaysWorld[id];
|
||||||
}
|
}
|
||||||
if (thisOverlay) {
|
if (thisOverlay) {
|
||||||
result.value = thisOverlay->getProperty(property);
|
result.value = thisOverlay->getProperty(property);
|
||||||
|
@ -311,7 +345,7 @@ RayToOverlayIntersectionResult Overlays::findRayIntersection(const PickRay& ray)
|
||||||
float bestDistance = std::numeric_limits<float>::max();
|
float bestDistance = std::numeric_limits<float>::max();
|
||||||
bool bestIsFront = false;
|
bool bestIsFront = false;
|
||||||
RayToOverlayIntersectionResult result;
|
RayToOverlayIntersectionResult result;
|
||||||
QMapIterator<unsigned int, Overlay*> i(_overlays3D);
|
QMapIterator<unsigned int, Overlay*> i(_overlaysWorld);
|
||||||
i.toBack();
|
i.toBack();
|
||||||
while (i.hasPrevious()) {
|
while (i.hasPrevious()) {
|
||||||
i.previous();
|
i.previous();
|
||||||
|
@ -416,13 +450,32 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R
|
||||||
value.extraInfo = object.property("extraInfo").toVariant().toString();
|
value.extraInfo = object.property("extraInfo").toVariant().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Overlays::overlayDrawOnChanged(Base3DOverlay* overlay) {
|
||||||
|
QWriteLocker lock(&_lock);
|
||||||
|
if (overlay->getDrawOnHUD()) {
|
||||||
|
for (unsigned int id : _overlaysWorld.keys()) {
|
||||||
|
if (_overlaysWorld[id] == overlay) {
|
||||||
|
_overlaysWorld.remove(id);
|
||||||
|
_overlaysHUD[id] = overlay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (unsigned int id : _overlaysHUD.keys()) {
|
||||||
|
if (_overlaysHUD[id] == overlay) {
|
||||||
|
_overlaysHUD.remove(id);
|
||||||
|
_overlaysWorld[id] = overlay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Overlays::isLoaded(unsigned int id) {
|
bool Overlays::isLoaded(unsigned int id) {
|
||||||
QReadLocker lock(&_lock);
|
QReadLocker lock(&_lock);
|
||||||
Overlay* thisOverlay = NULL;
|
Overlay* thisOverlay = NULL;
|
||||||
if (_overlays2D.contains(id)) {
|
if (_overlaysHUD.contains(id)) {
|
||||||
thisOverlay = _overlays2D[id];
|
thisOverlay = _overlaysHUD[id];
|
||||||
} else if (_overlays3D.contains(id)) {
|
} else if (_overlaysWorld.contains(id)) {
|
||||||
thisOverlay = _overlays3D[id];
|
thisOverlay = _overlaysWorld[id];
|
||||||
} else {
|
} else {
|
||||||
return false; // not found
|
return false; // not found
|
||||||
}
|
}
|
||||||
|
@ -430,13 +483,13 @@ bool Overlays::isLoaded(unsigned int id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QSizeF Overlays::textSize(unsigned int id, const QString& text) const {
|
QSizeF Overlays::textSize(unsigned int id, const QString& text) const {
|
||||||
Overlay* thisOverlay = _overlays2D[id];
|
Overlay* thisOverlay = _overlaysHUD[id];
|
||||||
if (thisOverlay) {
|
if (thisOverlay) {
|
||||||
if (typeid(*thisOverlay) == typeid(TextOverlay)) {
|
if (typeid(*thisOverlay) == typeid(TextOverlay)) {
|
||||||
return static_cast<TextOverlay*>(thisOverlay)->textSize(text);
|
return static_cast<TextOverlay*>(thisOverlay)->textSize(text);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
thisOverlay = _overlays3D[id];
|
thisOverlay = _overlaysWorld[id];
|
||||||
if (thisOverlay) {
|
if (thisOverlay) {
|
||||||
if (typeid(*thisOverlay) == typeid(Text3DOverlay)) {
|
if (typeid(*thisOverlay) == typeid(Text3DOverlay)) {
|
||||||
return static_cast<Text3DOverlay*>(thisOverlay)->textSize(text);
|
return static_cast<Text3DOverlay*>(thisOverlay)->textSize(text);
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <QScriptValue>
|
#include <QScriptValue>
|
||||||
#include <QSignalMapper>
|
#include <QSignalMapper>
|
||||||
|
|
||||||
|
#include "Base3DOverlay.h"
|
||||||
#include "Overlay.h"
|
#include "Overlay.h"
|
||||||
|
|
||||||
class OverlayPropertyResult {
|
class OverlayPropertyResult {
|
||||||
|
@ -52,9 +53,9 @@ public:
|
||||||
~Overlays();
|
~Overlays();
|
||||||
void init(QGLWidget* parent);
|
void init(QGLWidget* parent);
|
||||||
void update(float deltatime);
|
void update(float deltatime);
|
||||||
void render3D(bool drawFront, RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE,
|
void renderWorld(bool drawFront, RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE,
|
||||||
RenderArgs::RenderSide renderSide = RenderArgs::MONO);
|
RenderArgs::RenderSide renderSide = RenderArgs::MONO);
|
||||||
void render2D();
|
void renderHUD();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
/// adds an overlay with the specific properties
|
/// adds an overlay with the specific properties
|
||||||
|
@ -81,6 +82,9 @@ public slots:
|
||||||
|
|
||||||
/// returns details about the closest 3D Overlay hit by the pick ray
|
/// returns details about the closest 3D Overlay hit by the pick ray
|
||||||
RayToOverlayIntersectionResult findRayIntersection(const PickRay& ray);
|
RayToOverlayIntersectionResult findRayIntersection(const PickRay& ray);
|
||||||
|
|
||||||
|
// called by Base3DOverlay when drawOnHUD changes
|
||||||
|
void overlayDrawOnChanged(Base3DOverlay* overlay);
|
||||||
|
|
||||||
/// returns whether the overlay's assets are loaded or not
|
/// returns whether the overlay's assets are loaded or not
|
||||||
bool isLoaded(unsigned int id);
|
bool isLoaded(unsigned int id);
|
||||||
|
@ -90,8 +94,8 @@ public slots:
|
||||||
QSizeF textSize(unsigned int id, const QString& text) const;
|
QSizeF textSize(unsigned int id, const QString& text) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QMap<unsigned int, Overlay*> _overlays2D;
|
QMap<unsigned int, Overlay*> _overlaysHUD;
|
||||||
QMap<unsigned int, Overlay*> _overlays3D;
|
QMap<unsigned int, Overlay*> _overlaysWorld;
|
||||||
QList<Overlay*> _overlaysToDelete;
|
QList<Overlay*> _overlaysToDelete;
|
||||||
unsigned int _nextOverlayID;
|
unsigned int _nextOverlayID;
|
||||||
QGLWidget* _parent;
|
QGLWidget* _parent;
|
||||||
|
|
|
@ -5,7 +5,7 @@ setup_hifi_library(Network Script)
|
||||||
|
|
||||||
include_glm()
|
include_glm()
|
||||||
|
|
||||||
link_hifi_libraries(audio shared octree networking physics gpu model fbx)
|
link_hifi_libraries(audio shared octree networking gpu model fbx)
|
||||||
|
|
||||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||||
include_dependency_includes()
|
include_dependency_includes()
|
||||||
|
|
|
@ -73,16 +73,3 @@ void HeadData::setBlendshape(QString name, float val) {
|
||||||
_blendshapeCoefficients[it.value()] = val;
|
_blendshapeCoefficients[it.value()] = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeadData::addYaw(float yaw) {
|
|
||||||
setBaseYaw(_baseYaw + yaw);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadData::addPitch(float pitch) {
|
|
||||||
setBasePitch(_basePitch + pitch);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeadData::addRoll(float roll) {
|
|
||||||
setBaseRoll(_baseRoll + roll);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -65,11 +65,6 @@ public:
|
||||||
float getPupilDilation() const { return _pupilDilation; }
|
float getPupilDilation() const { return _pupilDilation; }
|
||||||
void setPupilDilation(float pupilDilation) { _pupilDilation = pupilDilation; }
|
void setPupilDilation(float pupilDilation) { _pupilDilation = pupilDilation; }
|
||||||
|
|
||||||
// degrees
|
|
||||||
void addYaw(float yaw);
|
|
||||||
void addPitch(float pitch);
|
|
||||||
void addRoll(float roll);
|
|
||||||
|
|
||||||
const glm::vec3& getLookAtPosition() const { return _lookAtPosition; }
|
const glm::vec3& getLookAtPosition() const { return _lookAtPosition; }
|
||||||
void setLookAtPosition(const glm::vec3& lookAtPosition) { _lookAtPosition = lookAtPosition; }
|
void setLookAtPosition(const glm::vec3& lookAtPosition) { _lookAtPosition = lookAtPosition; }
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
|
||||||
OctreeRenderer(),
|
OctreeRenderer(),
|
||||||
_wantScripts(wantScripts),
|
_wantScripts(wantScripts),
|
||||||
_entitiesScriptEngine(NULL),
|
_entitiesScriptEngine(NULL),
|
||||||
|
_sandboxScriptEngine(NULL),
|
||||||
_lastMouseEventValid(false),
|
_lastMouseEventValid(false),
|
||||||
_viewState(viewState),
|
_viewState(viewState),
|
||||||
_scriptingServices(scriptingServices),
|
_scriptingServices(scriptingServices),
|
||||||
|
@ -58,7 +59,10 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityTreeRenderer::~EntityTreeRenderer() {
|
EntityTreeRenderer::~EntityTreeRenderer() {
|
||||||
// do we need to delete the _entitiesScriptEngine?? or is it deleted by default
|
// NOTE: we don't need to delete _entitiesScriptEngine because it's owned by the application and gets cleaned up
|
||||||
|
// automatically but we do need to delete our sandbox script engine.
|
||||||
|
delete _sandboxScriptEngine;
|
||||||
|
_sandboxScriptEngine = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::clear() {
|
void EntityTreeRenderer::clear() {
|
||||||
|
@ -79,6 +83,8 @@ void EntityTreeRenderer::init() {
|
||||||
_entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities",
|
_entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities",
|
||||||
_scriptingServices->getControllerScriptingInterface());
|
_scriptingServices->getControllerScriptingInterface());
|
||||||
_scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine);
|
_scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine);
|
||||||
|
|
||||||
|
_sandboxScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities Sandbox", NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure our "last avatar position" is something other than our current position, so that on our
|
// make sure our "last avatar position" is something other than our current position, so that on our
|
||||||
|
@ -190,13 +196,15 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) {
|
||||||
if (isURL) {
|
if (isURL) {
|
||||||
_entitiesScriptEngine->setParentURL(entity->getScript());
|
_entitiesScriptEngine->setParentURL(entity->getScript());
|
||||||
}
|
}
|
||||||
QScriptValue entityScriptConstructor = _entitiesScriptEngine->evaluate(scriptContents);
|
QScriptValue entityScriptConstructor = _sandboxScriptEngine->evaluate(scriptContents);
|
||||||
|
|
||||||
if (!entityScriptConstructor.isFunction()) {
|
if (!entityScriptConstructor.isFunction()) {
|
||||||
qDebug() << "EntityTreeRenderer::loadEntityScript() entity:" << entityID;
|
qDebug() << "EntityTreeRenderer::loadEntityScript() entity:" << entityID;
|
||||||
qDebug() << " NOT CONSTRUCTOR";
|
qDebug() << " NOT CONSTRUCTOR";
|
||||||
qDebug() << " SCRIPT:" << entityScript;
|
qDebug() << " SCRIPT:" << entityScript;
|
||||||
return QScriptValue(); // invalid script
|
return QScriptValue(); // invalid script
|
||||||
|
} else {
|
||||||
|
entityScriptConstructor = _entitiesScriptEngine->evaluate(scriptContents);
|
||||||
}
|
}
|
||||||
|
|
||||||
QScriptValue entityScriptObject = entityScriptConstructor.construct();
|
QScriptValue entityScriptObject = entityScriptConstructor.construct();
|
||||||
|
|
|
@ -134,6 +134,7 @@ private:
|
||||||
|
|
||||||
bool _wantScripts;
|
bool _wantScripts;
|
||||||
ScriptEngine* _entitiesScriptEngine;
|
ScriptEngine* _entitiesScriptEngine;
|
||||||
|
ScriptEngine* _sandboxScriptEngine;
|
||||||
|
|
||||||
QScriptValue loadEntityScript(EntityItem* entity);
|
QScriptValue loadEntityScript(EntityItem* entity);
|
||||||
QScriptValue loadEntityScript(const EntityItemID& entityItemID);
|
QScriptValue loadEntityScript(const EntityItemID& entityItemID);
|
||||||
|
|
|
@ -5,7 +5,7 @@ setup_hifi_library(Network Script)
|
||||||
|
|
||||||
include_glm()
|
include_glm()
|
||||||
|
|
||||||
link_hifi_libraries(avatars shared octree gpu model fbx networking animation physics)
|
link_hifi_libraries(avatars shared octree gpu model fbx networking animation)
|
||||||
|
|
||||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||||
include_dependency_includes()
|
include_dependency_includes()
|
||||||
|
|
|
@ -14,9 +14,9 @@
|
||||||
|
|
||||||
#include <ByteCountCoding.h>
|
#include <ByteCountCoding.h>
|
||||||
|
|
||||||
|
#include "BoxEntityItem.h"
|
||||||
#include "EntityTree.h"
|
#include "EntityTree.h"
|
||||||
#include "EntityTreeElement.h"
|
#include "EntityTreeElement.h"
|
||||||
#include "BoxEntityItem.h"
|
|
||||||
|
|
||||||
|
|
||||||
EntityItem* BoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
EntityItem* BoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||||
|
@ -95,3 +95,9 @@ void BoxEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst
|
||||||
|
|
||||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, getColor());
|
APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, getColor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BoxEntityItem::computeShapeInfo(ShapeInfo& info) const {
|
||||||
|
glm::vec3 halfExtents = 0.5f * getDimensionsInMeters();
|
||||||
|
info.setBox(halfExtents);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,11 +46,13 @@ public:
|
||||||
|
|
||||||
void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); }
|
void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); }
|
||||||
void setColor(const xColor& value) {
|
void setColor(const xColor& value) {
|
||||||
_color[RED_INDEX] = value.red;
|
_color[RED_INDEX] = value.red;
|
||||||
_color[GREEN_INDEX] = value.green;
|
_color[GREEN_INDEX] = value.green;
|
||||||
_color[BLUE_INDEX] = value.blue;
|
_color[BLUE_INDEX] = value.blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void computeShapeInfo(ShapeInfo& info) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
rgbColor _color;
|
rgbColor _color;
|
||||||
};
|
};
|
||||||
|
|
|
@ -90,6 +90,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) {
|
||||||
_lastEditedFromRemote = 0;
|
_lastEditedFromRemote = 0;
|
||||||
_lastEditedFromRemoteInRemoteTime = 0;
|
_lastEditedFromRemoteInRemoteTime = 0;
|
||||||
_created = UNKNOWN_CREATED_TIME;
|
_created = UNKNOWN_CREATED_TIME;
|
||||||
|
_physicsInfo = NULL;
|
||||||
_dirtyFlags = 0;
|
_dirtyFlags = 0;
|
||||||
_changedOnServer = 0;
|
_changedOnServer = 0;
|
||||||
initFromEntityItemID(entityItemID);
|
initFromEntityItemID(entityItemID);
|
||||||
|
@ -104,12 +105,18 @@ EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemPropert
|
||||||
_lastEditedFromRemote = 0;
|
_lastEditedFromRemote = 0;
|
||||||
_lastEditedFromRemoteInRemoteTime = 0;
|
_lastEditedFromRemoteInRemoteTime = 0;
|
||||||
_created = UNKNOWN_CREATED_TIME;
|
_created = UNKNOWN_CREATED_TIME;
|
||||||
|
_physicsInfo = NULL;
|
||||||
_dirtyFlags = 0;
|
_dirtyFlags = 0;
|
||||||
_changedOnServer = 0;
|
_changedOnServer = 0;
|
||||||
initFromEntityItemID(entityItemID);
|
initFromEntityItemID(entityItemID);
|
||||||
setProperties(properties);
|
setProperties(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EntityItem::~EntityItem() {
|
||||||
|
// be sure to clean up _physicsInfo before calling this dtor
|
||||||
|
assert(_physicsInfo == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||||
EntityPropertyFlags requestedProperties;
|
EntityPropertyFlags requestedProperties;
|
||||||
|
|
||||||
|
@ -136,7 +143,6 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
|
||||||
|
|
||||||
OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||||
EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData) const {
|
EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData) const {
|
||||||
|
|
||||||
// ALL this fits...
|
// ALL this fits...
|
||||||
// object ID [16 bytes]
|
// object ID [16 bytes]
|
||||||
// ByteCountCoded(type code) [~1 byte]
|
// ByteCountCoded(type code) [~1 byte]
|
||||||
|
@ -514,7 +520,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
||||||
READ_ENTITY_PROPERTY_SETTER(PROP_GRAVITY, glm::vec3, updateGravity);
|
READ_ENTITY_PROPERTY_SETTER(PROP_GRAVITY, glm::vec3, updateGravity);
|
||||||
READ_ENTITY_PROPERTY(PROP_DAMPING, float, _damping);
|
READ_ENTITY_PROPERTY(PROP_DAMPING, float, _damping);
|
||||||
READ_ENTITY_PROPERTY_SETTER(PROP_LIFETIME, float, updateLifetime);
|
READ_ENTITY_PROPERTY_SETTER(PROP_LIFETIME, float, updateLifetime);
|
||||||
READ_ENTITY_PROPERTY_STRING(PROP_SCRIPT,setScript);
|
READ_ENTITY_PROPERTY_STRING(PROP_SCRIPT, setScript);
|
||||||
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, _registrationPoint);
|
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, _registrationPoint);
|
||||||
READ_ENTITY_PROPERTY_SETTER(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity);
|
READ_ENTITY_PROPERTY_SETTER(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity);
|
||||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, _angularDamping);
|
READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, _angularDamping);
|
||||||
|
@ -581,6 +587,11 @@ bool EntityItem::isRestingOnSurface() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::simulate(const quint64& now) {
|
void EntityItem::simulate(const quint64& now) {
|
||||||
|
if (_physicsInfo) {
|
||||||
|
// we rely on bullet for simulation, so bail
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool wantDebug = false;
|
bool wantDebug = false;
|
||||||
|
|
||||||
if (_lastSimulated == 0) {
|
if (_lastSimulated == 0) {
|
||||||
|
@ -793,7 +804,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(gravity, updateGravityInMeters);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(gravity, updateGravityInMeters);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(damping, setDamping);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(damping, setDamping);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, updateLifetime);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lifetime, updateLifetime);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, updateScript);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(registrationPoint, setRegistrationPoint);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(registrationPoint, setRegistrationPoint);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularVelocity, updateAngularVelocity);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularVelocity, updateAngularVelocity);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularDamping, setAngularDamping);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularDamping, setAngularDamping);
|
||||||
|
@ -975,15 +986,29 @@ float EntityItem::getRadius() const {
|
||||||
return radius;
|
return radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntityItem::computeShapeInfo(ShapeInfo& info) const {
|
||||||
|
info.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void EntityItem::recalculateCollisionShape() {
|
void EntityItem::recalculateCollisionShape() {
|
||||||
AACube entityAACube = getMinimumAACube();
|
AACube entityAACube = getMinimumAACube();
|
||||||
entityAACube.scale(TREE_SCALE); // scale to meters
|
entityAACube.scale(TREE_SCALE); // scale to meters
|
||||||
_collisionShape.setTranslation(entityAACube.calcCenter());
|
_collisionShape.setTranslation(entityAACube.calcCenter());
|
||||||
_collisionShape.setScale(entityAACube.getScale());
|
_collisionShape.setScale(entityAACube.getScale());
|
||||||
|
// TODO: use motionState to update physics object
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const float MIN_POSITION_DELTA = 0.0001f;
|
||||||
|
const float MIN_DIMENSION_DELTA = 0.0001f;
|
||||||
|
const float MIN_ALIGNMENT_DOT = 0.9999f;
|
||||||
|
const float MIN_MASS_DELTA = 0.001f;
|
||||||
|
const float MIN_VELOCITY_DELTA = 0.025f;
|
||||||
|
const float MIN_GRAVITY_DELTA = 0.001f;
|
||||||
|
const float MIN_SPIN_DELTA = 0.0003f;
|
||||||
|
|
||||||
void EntityItem::updatePosition(const glm::vec3& value) {
|
void EntityItem::updatePosition(const glm::vec3& value) {
|
||||||
if (_position != value) {
|
glm::vec3 debugPosition = value * (float) TREE_SCALE;
|
||||||
|
if (glm::distance(_position, value) * (float)TREE_SCALE > MIN_POSITION_DELTA) {
|
||||||
_position = value;
|
_position = value;
|
||||||
recalculateCollisionShape();
|
recalculateCollisionShape();
|
||||||
_dirtyFlags |= EntityItem::DIRTY_POSITION;
|
_dirtyFlags |= EntityItem::DIRTY_POSITION;
|
||||||
|
@ -992,7 +1017,7 @@ void EntityItem::updatePosition(const glm::vec3& value) {
|
||||||
|
|
||||||
void EntityItem::updatePositionInMeters(const glm::vec3& value) {
|
void EntityItem::updatePositionInMeters(const glm::vec3& value) {
|
||||||
glm::vec3 position = glm::clamp(value / (float) TREE_SCALE, 0.0f, 1.0f);
|
glm::vec3 position = glm::clamp(value / (float) TREE_SCALE, 0.0f, 1.0f);
|
||||||
if (_position != position) {
|
if (glm::distance(_position, position) * (float)TREE_SCALE > MIN_POSITION_DELTA) {
|
||||||
_position = position;
|
_position = position;
|
||||||
recalculateCollisionShape();
|
recalculateCollisionShape();
|
||||||
_dirtyFlags |= EntityItem::DIRTY_POSITION;
|
_dirtyFlags |= EntityItem::DIRTY_POSITION;
|
||||||
|
@ -1000,24 +1025,24 @@ void EntityItem::updatePositionInMeters(const glm::vec3& value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::updateDimensions(const glm::vec3& value) {
|
void EntityItem::updateDimensions(const glm::vec3& value) {
|
||||||
if (_dimensions != value) {
|
if (glm::distance(_dimensions, value) * (float)TREE_SCALE > MIN_DIMENSION_DELTA) {
|
||||||
_dimensions = value;
|
_dimensions = value;
|
||||||
recalculateCollisionShape();
|
recalculateCollisionShape();
|
||||||
_dirtyFlags |= EntityItem::DIRTY_SHAPE;
|
_dirtyFlags |= (EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::updateDimensionsInMeters(const glm::vec3& value) {
|
void EntityItem::updateDimensionsInMeters(const glm::vec3& value) {
|
||||||
glm::vec3 dimensions = value / (float) TREE_SCALE;
|
glm::vec3 dimensions = value / (float) TREE_SCALE;
|
||||||
if (_dimensions != dimensions) {
|
if (glm::distance(_dimensions, dimensions) * (float)TREE_SCALE > MIN_DIMENSION_DELTA) {
|
||||||
_dimensions = dimensions;
|
_dimensions = dimensions;
|
||||||
recalculateCollisionShape();
|
recalculateCollisionShape();
|
||||||
_dirtyFlags |= EntityItem::DIRTY_SHAPE;
|
_dirtyFlags |= (EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::updateRotation(const glm::quat& rotation) {
|
void EntityItem::updateRotation(const glm::quat& rotation) {
|
||||||
if (_rotation != rotation) {
|
if (glm::dot(_rotation, rotation) < MIN_ALIGNMENT_DOT) {
|
||||||
_rotation = rotation;
|
_rotation = rotation;
|
||||||
recalculateCollisionShape();
|
recalculateCollisionShape();
|
||||||
_dirtyFlags |= EntityItem::DIRTY_POSITION;
|
_dirtyFlags |= EntityItem::DIRTY_POSITION;
|
||||||
|
@ -1025,29 +1050,37 @@ void EntityItem::updateRotation(const glm::quat& rotation) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::updateMass(float value) {
|
void EntityItem::updateMass(float value) {
|
||||||
if (_mass != value) {
|
if (fabsf(_mass - value) > MIN_MASS_DELTA) {
|
||||||
_mass = value;
|
_mass = value;
|
||||||
_dirtyFlags |= EntityItem::DIRTY_MASS;
|
_dirtyFlags |= EntityItem::DIRTY_MASS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::updateVelocity(const glm::vec3& value) {
|
void EntityItem::updateVelocity(const glm::vec3& value) {
|
||||||
if (_velocity != value) {
|
if (glm::distance(_velocity, value) * (float)TREE_SCALE > MIN_VELOCITY_DELTA) {
|
||||||
_velocity = value;
|
if (glm::length(value) * (float)TREE_SCALE < MIN_VELOCITY_DELTA) {
|
||||||
|
_velocity = glm::vec3(0.0f);
|
||||||
|
} else {
|
||||||
|
_velocity = value;
|
||||||
|
}
|
||||||
_dirtyFlags |= EntityItem::DIRTY_VELOCITY;
|
_dirtyFlags |= EntityItem::DIRTY_VELOCITY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::updateVelocityInMeters(const glm::vec3& value) {
|
void EntityItem::updateVelocityInMeters(const glm::vec3& value) {
|
||||||
glm::vec3 velocity = value / (float) TREE_SCALE;
|
glm::vec3 velocity = value / (float) TREE_SCALE;
|
||||||
if (_velocity != velocity) {
|
if (glm::distance(_velocity, velocity) * (float)TREE_SCALE > MIN_VELOCITY_DELTA) {
|
||||||
_velocity = velocity;
|
if (glm::length(value) < MIN_VELOCITY_DELTA) {
|
||||||
|
_velocity = glm::vec3(0.0f);
|
||||||
|
} else {
|
||||||
|
_velocity = velocity;
|
||||||
|
}
|
||||||
_dirtyFlags |= EntityItem::DIRTY_VELOCITY;
|
_dirtyFlags |= EntityItem::DIRTY_VELOCITY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::updateGravity(const glm::vec3& value) {
|
void EntityItem::updateGravity(const glm::vec3& value) {
|
||||||
if (_gravity != value) {
|
if (glm::distance(_gravity, value) * (float)TREE_SCALE > MIN_GRAVITY_DELTA) {
|
||||||
_gravity = value;
|
_gravity = value;
|
||||||
_dirtyFlags |= EntityItem::DIRTY_VELOCITY;
|
_dirtyFlags |= EntityItem::DIRTY_VELOCITY;
|
||||||
}
|
}
|
||||||
|
@ -1055,14 +1088,14 @@ void EntityItem::updateGravity(const glm::vec3& value) {
|
||||||
|
|
||||||
void EntityItem::updateGravityInMeters(const glm::vec3& value) {
|
void EntityItem::updateGravityInMeters(const glm::vec3& value) {
|
||||||
glm::vec3 gravity = value / (float) TREE_SCALE;
|
glm::vec3 gravity = value / (float) TREE_SCALE;
|
||||||
if (_gravity != gravity) {
|
if ( glm::distance(_gravity, gravity) * (float)TREE_SCALE > MIN_GRAVITY_DELTA) {
|
||||||
_gravity = gravity;
|
_gravity = gravity;
|
||||||
_dirtyFlags |= EntityItem::DIRTY_VELOCITY;
|
_dirtyFlags |= EntityItem::DIRTY_VELOCITY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::updateAngularVelocity(const glm::vec3& value) {
|
void EntityItem::updateAngularVelocity(const glm::vec3& value) {
|
||||||
if (_angularVelocity != value) {
|
if (glm::distance(_angularVelocity, value) > MIN_SPIN_DELTA) {
|
||||||
_angularVelocity = value;
|
_angularVelocity = value;
|
||||||
_dirtyFlags |= EntityItem::DIRTY_VELOCITY;
|
_dirtyFlags |= EntityItem::DIRTY_VELOCITY;
|
||||||
}
|
}
|
||||||
|
@ -1089,10 +1122,3 @@ void EntityItem::updateLifetime(float value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::updateScript(const QString& value) {
|
|
||||||
if (_script != value) {
|
|
||||||
_script = value;
|
|
||||||
_dirtyFlags |= EntityItem::DIRTY_SCRIPT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include <Octree.h> // for EncodeBitstreamParams class
|
#include <Octree.h> // for EncodeBitstreamParams class
|
||||||
#include <OctreeElement.h> // for OctreeElement::AppendState
|
#include <OctreeElement.h> // for OctreeElement::AppendState
|
||||||
#include <OctreePacketData.h>
|
#include <OctreePacketData.h>
|
||||||
|
#include <ShapeInfo.h>
|
||||||
|
|
||||||
#include "EntityItemID.h"
|
#include "EntityItemID.h"
|
||||||
#include "EntityItemProperties.h"
|
#include "EntityItemProperties.h"
|
||||||
|
@ -49,16 +50,13 @@ public:
|
||||||
DIRTY_SHAPE = 0x0020,
|
DIRTY_SHAPE = 0x0020,
|
||||||
DIRTY_LIFETIME = 0x0040,
|
DIRTY_LIFETIME = 0x0040,
|
||||||
DIRTY_UPDATEABLE = 0x0080,
|
DIRTY_UPDATEABLE = 0x0080,
|
||||||
// add new simulation-relevant flags above
|
|
||||||
// all other flags below
|
|
||||||
DIRTY_SCRIPT = 0x8000
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly
|
DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly
|
||||||
|
|
||||||
EntityItem(const EntityItemID& entityItemID);
|
EntityItem(const EntityItemID& entityItemID);
|
||||||
EntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
|
EntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
|
||||||
virtual ~EntityItem() { }
|
virtual ~EntityItem();
|
||||||
|
|
||||||
// ID and EntityItemID related methods
|
// ID and EntityItemID related methods
|
||||||
QUuid getID() const { return _id; }
|
QUuid getID() const { return _id; }
|
||||||
|
@ -275,6 +273,7 @@ public:
|
||||||
void applyHardCollision(const CollisionInfo& collisionInfo);
|
void applyHardCollision(const CollisionInfo& collisionInfo);
|
||||||
virtual const Shape& getCollisionShapeInMeters() const { return _collisionShape; }
|
virtual const Shape& getCollisionShapeInMeters() const { return _collisionShape; }
|
||||||
virtual bool contains(const glm::vec3& point) const { return getAABox().contains(point); }
|
virtual bool contains(const glm::vec3& point) const { return getAABox().contains(point); }
|
||||||
|
virtual void computeShapeInfo(ShapeInfo& info) const;
|
||||||
|
|
||||||
// updateFoo() methods to be used when changes need to be accumulated in the _dirtyFlags
|
// updateFoo() methods to be used when changes need to be accumulated in the _dirtyFlags
|
||||||
void updatePosition(const glm::vec3& value);
|
void updatePosition(const glm::vec3& value);
|
||||||
|
@ -291,13 +290,15 @@ public:
|
||||||
void updateIgnoreForCollisions(bool value);
|
void updateIgnoreForCollisions(bool value);
|
||||||
void updateCollisionsWillMove(bool value);
|
void updateCollisionsWillMove(bool value);
|
||||||
void updateLifetime(float value);
|
void updateLifetime(float value);
|
||||||
void updateScript(const QString& value);
|
|
||||||
|
|
||||||
uint32_t getDirtyFlags() const { return _dirtyFlags; }
|
uint32_t getDirtyFlags() const { return _dirtyFlags; }
|
||||||
void clearDirtyFlags(uint32_t mask = 0xffff) { _dirtyFlags &= ~mask; }
|
void clearDirtyFlags(uint32_t mask = 0xffff) { _dirtyFlags &= ~mask; }
|
||||||
|
|
||||||
bool isMoving() const;
|
bool isMoving() const;
|
||||||
|
|
||||||
|
void* getPhysicsInfo() const { return _physicsInfo; }
|
||||||
|
void setPhysicsInfo(void* data) { _physicsInfo = data; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init
|
virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init
|
||||||
|
@ -309,9 +310,9 @@ protected:
|
||||||
bool _newlyCreated;
|
bool _newlyCreated;
|
||||||
quint64 _lastSimulated; // last time this entity called simulate()
|
quint64 _lastSimulated; // last time this entity called simulate()
|
||||||
quint64 _lastUpdated; // last time this entity called update()
|
quint64 _lastUpdated; // last time this entity called update()
|
||||||
quint64 _lastEdited; // this is the last official local or remote edit time
|
quint64 _lastEdited; // last official local or remote edit time
|
||||||
quint64 _lastEditedFromRemote; // this is the last time we received and edit from the server
|
quint64 _lastEditedFromRemote; // last time we received and edit from the server
|
||||||
quint64 _lastEditedFromRemoteInRemoteTime; // time in server time space the last time we received and edit from the server
|
quint64 _lastEditedFromRemoteInRemoteTime; // last time we received and edit from the server (in server-time-frame)
|
||||||
quint64 _created;
|
quint64 _created;
|
||||||
quint64 _changedOnServer;
|
quint64 _changedOnServer;
|
||||||
|
|
||||||
|
@ -343,6 +344,10 @@ protected:
|
||||||
|
|
||||||
AACubeShape _collisionShape;
|
AACubeShape _collisionShape;
|
||||||
|
|
||||||
|
// _physicsInfo is a hook reserved for use by the EntitySimulation, which is guaranteed to set _physicsInfo
|
||||||
|
// to a non-NULL value when the EntityItem has a representation in the physics engine.
|
||||||
|
void* _physicsInfo; // only set by EntitySimulation
|
||||||
|
|
||||||
// DirtyFlags are set whenever a property changes that the EntitySimulation needs to know about.
|
// DirtyFlags are set whenever a property changes that the EntitySimulation needs to know about.
|
||||||
uint32_t _dirtyFlags; // things that have changed from EXTERNAL changes (via script or packet) but NOT from simulation
|
uint32_t _dirtyFlags; // things that have changed from EXTERNAL changes (via script or packet) but NOT from simulation
|
||||||
};
|
};
|
||||||
|
|
|
@ -134,6 +134,13 @@ public:
|
||||||
void debugDump() const;
|
void debugDump() const;
|
||||||
void setLastEdited(quint64 usecTime);
|
void setLastEdited(quint64 usecTime);
|
||||||
|
|
||||||
|
// Note: DEFINE_PROPERTY(PROP_FOO, Foo, foo, type) creates the following methods and variables:
|
||||||
|
// type getFoo() const;
|
||||||
|
// void setFoo(type);
|
||||||
|
// bool fooChanged() const;
|
||||||
|
// type _foo;
|
||||||
|
// bool _fooChanged;
|
||||||
|
|
||||||
DEFINE_PROPERTY(PROP_VISIBLE, Visible, visible, bool);
|
DEFINE_PROPERTY(PROP_VISIBLE, Visible, visible, bool);
|
||||||
DEFINE_PROPERTY_REF_WITH_SETTER(PROP_POSITION, Position, position, glm::vec3);
|
DEFINE_PROPERTY_REF_WITH_SETTER(PROP_POSITION, Position, position, glm::vec3);
|
||||||
DEFINE_PROPERTY_REF(PROP_DIMENSIONS, Dimensions, dimensions, glm::vec3);
|
DEFINE_PROPERTY_REF(PROP_DIMENSIONS, Dimensions, dimensions, glm::vec3);
|
||||||
|
|
|
@ -101,12 +101,13 @@ void EntitySimulation::sortEntitiesThatMoved() {
|
||||||
}
|
}
|
||||||
++itemItr;
|
++itemItr;
|
||||||
}
|
}
|
||||||
_entitiesToBeSorted.clear();
|
|
||||||
|
|
||||||
if (moveOperator.hasMovingEntities()) {
|
if (moveOperator.hasMovingEntities()) {
|
||||||
PerformanceTimer perfTimer("recurseTreeWithOperator");
|
PerformanceTimer perfTimer("recurseTreeWithOperator");
|
||||||
_entityTree->recurseTreeWithOperator(&moveOperator);
|
_entityTree->recurseTreeWithOperator(&moveOperator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sortEntitiesThatMovedInternal();
|
||||||
|
_entitiesToBeSorted.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntitySimulation::addEntity(EntityItem* entity) {
|
void EntitySimulation::addEntity(EntityItem* entity) {
|
||||||
|
@ -122,6 +123,10 @@ void EntitySimulation::addEntity(EntityItem* entity) {
|
||||||
_updateableEntities.insert(entity);
|
_updateableEntities.insert(entity);
|
||||||
}
|
}
|
||||||
addEntityInternal(entity);
|
addEntityInternal(entity);
|
||||||
|
|
||||||
|
// DirtyFlags are used to signal changes to entities that have already been added,
|
||||||
|
// so we can clear them for this entity which has just been added.
|
||||||
|
entity->clearDirtyFlags();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntitySimulation::removeEntity(EntityItem* entity) {
|
void EntitySimulation::removeEntity(EntityItem* entity) {
|
||||||
|
@ -173,7 +178,6 @@ void EntitySimulation::entityChanged(EntityItem* entity) {
|
||||||
}
|
}
|
||||||
entityChangedInternal(entity);
|
entityChangedInternal(entity);
|
||||||
}
|
}
|
||||||
entity->clearDirtyFlags();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntitySimulation::clearEntities() {
|
void EntitySimulation::clearEntities() {
|
||||||
|
|
|
@ -16,8 +16,21 @@
|
||||||
|
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
|
|
||||||
|
#include "EntityItem.h"
|
||||||
#include "EntityTree.h"
|
#include "EntityTree.h"
|
||||||
|
|
||||||
|
// the EntitySimulation needs to know when these things change on an entity,
|
||||||
|
// so it can sort EntityItem or relay its state to the PhysicsEngine.
|
||||||
|
const int DIRTY_SIMULATION_FLAGS =
|
||||||
|
EntityItem::DIRTY_POSITION |
|
||||||
|
EntityItem::DIRTY_VELOCITY |
|
||||||
|
EntityItem::DIRTY_MASS |
|
||||||
|
EntityItem::DIRTY_COLLISION_GROUP |
|
||||||
|
EntityItem::DIRTY_MOTION_TYPE |
|
||||||
|
EntityItem::DIRTY_SHAPE |
|
||||||
|
EntityItem::DIRTY_LIFETIME |
|
||||||
|
EntityItem::DIRTY_UPDATEABLE;
|
||||||
|
|
||||||
class EntitySimulation {
|
class EntitySimulation {
|
||||||
public:
|
public:
|
||||||
EntitySimulation() : _entityTree(NULL) { }
|
EntitySimulation() : _entityTree(NULL) { }
|
||||||
|
@ -49,10 +62,18 @@ protected:
|
||||||
|
|
||||||
// These pure virtual methods are protected because they are not to be called will-nilly. The base class
|
// These pure virtual methods are protected because they are not to be called will-nilly. The base class
|
||||||
// calls them in the right places.
|
// calls them in the right places.
|
||||||
|
|
||||||
|
// NOTE: updateEntitiesInternal() should clear all dirty flags on each changed entity as side effect
|
||||||
virtual void updateEntitiesInternal(const quint64& now) = 0;
|
virtual void updateEntitiesInternal(const quint64& now) = 0;
|
||||||
|
|
||||||
virtual void addEntityInternal(EntityItem* entity) = 0;
|
virtual void addEntityInternal(EntityItem* entity) = 0;
|
||||||
|
|
||||||
virtual void removeEntityInternal(EntityItem* entity) = 0;
|
virtual void removeEntityInternal(EntityItem* entity) = 0;
|
||||||
|
|
||||||
virtual void entityChangedInternal(EntityItem* entity) = 0;
|
virtual void entityChangedInternal(EntityItem* entity) = 0;
|
||||||
|
|
||||||
|
virtual void sortEntitiesThatMovedInternal() {}
|
||||||
|
|
||||||
virtual void clearEntitiesInternal() = 0;
|
virtual void clearEntitiesInternal() = 0;
|
||||||
|
|
||||||
void expireMortalEntities(const quint64& now);
|
void expireMortalEntities(const quint64& now);
|
||||||
|
|
|
@ -132,19 +132,21 @@ bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemPro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
QString entityScriptBefore = entity->getScript();
|
uint32_t preFlags = entity->getDirtyFlags();
|
||||||
|
|
||||||
UpdateEntityOperator theOperator(this, containingElement, entity, properties);
|
UpdateEntityOperator theOperator(this, containingElement, entity, properties);
|
||||||
recurseTreeWithOperator(&theOperator);
|
recurseTreeWithOperator(&theOperator);
|
||||||
_isDirty = true;
|
_isDirty = true;
|
||||||
|
|
||||||
if (_simulation && entity->getDirtyFlags() != 0) {
|
uint32_t newFlags = entity->getDirtyFlags() & ~preFlags;
|
||||||
_simulation->entityChanged(entity);
|
if (newFlags) {
|
||||||
}
|
if (_simulation) {
|
||||||
|
if (newFlags & DIRTY_SIMULATION_FLAGS) {
|
||||||
QString entityScriptAfter = entity->getScript();
|
_simulation->entityChanged(entity);
|
||||||
if (entityScriptBefore != entityScriptAfter) {
|
}
|
||||||
emit entityScriptChanging(entity->getEntityItemID()); // the entity script has changed
|
} else {
|
||||||
|
// normally the _simulation clears ALL updateFlags, but since there is none we do it explicitly
|
||||||
|
entity->clearDirtyFlags();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#include "EntityItem.h"
|
#include "EntityItem.h"
|
||||||
#include "SimpleEntitySimulation.h"
|
#include "SimpleEntitySimulation.h"
|
||||||
|
|
||||||
|
|
||||||
void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) {
|
void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) {
|
||||||
QSet<EntityItem*>::iterator itemItr = _movingEntities.begin();
|
QSet<EntityItem*>::iterator itemItr = _movingEntities.begin();
|
||||||
while (itemItr != _movingEntities.end()) {
|
while (itemItr != _movingEntities.end()) {
|
||||||
|
@ -57,6 +56,7 @@ void SimpleEntitySimulation::entityChangedInternal(EntityItem* entity) {
|
||||||
_movableButStoppedEntities.remove(entity);
|
_movableButStoppedEntities.remove(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
entity->clearDirtyFlags();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimpleEntitySimulation::clearEntitiesInternal() {
|
void SimpleEntitySimulation::clearEntitiesInternal() {
|
||||||
|
|
|
@ -97,6 +97,12 @@ void SphereEntityItem::recalculateCollisionShape() {
|
||||||
_sphereShape.setRadius(largestDiameter / 2.0f);
|
_sphereShape.setRadius(largestDiameter / 2.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SphereEntityItem::computeShapeInfo(ShapeInfo& info) const {
|
||||||
|
glm::vec3 halfExtents = 0.5f * getDimensionsInMeters();
|
||||||
|
// TODO: support ellipsoid shapes
|
||||||
|
info.setSphere(halfExtents.x);
|
||||||
|
}
|
||||||
|
|
||||||
bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
|
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
|
||||||
void** intersectedObject, bool precisionPicking) const {
|
void** intersectedObject, bool precisionPicking) const {
|
||||||
|
@ -122,6 +128,3 @@ bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, cons
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,8 @@ public:
|
||||||
|
|
||||||
// TODO: implement proper contains for 3D ellipsoid
|
// TODO: implement proper contains for 3D ellipsoid
|
||||||
//virtual bool contains(const glm::vec3& point) const;
|
//virtual bool contains(const glm::vec3& point) const;
|
||||||
|
|
||||||
|
void computeShapeInfo(ShapeInfo& info) const;
|
||||||
|
|
||||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
virtual bool supportsDetailedRayIntersection() const { return true; }
|
||||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
|
|
|
@ -5,7 +5,7 @@ setup_hifi_library()
|
||||||
|
|
||||||
include_glm()
|
include_glm()
|
||||||
|
|
||||||
link_hifi_libraries(shared networking physics)
|
link_hifi_libraries(shared networking)
|
||||||
|
|
||||||
# find ZLIB
|
# find ZLIB
|
||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
|
|
|
@ -4,16 +4,13 @@ set(TARGET_NAME physics)
|
||||||
setup_hifi_library()
|
setup_hifi_library()
|
||||||
|
|
||||||
include_glm()
|
include_glm()
|
||||||
|
include_bullet()
|
||||||
|
if (BULLET_FOUND)
|
||||||
|
target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})
|
||||||
|
endif (BULLET_FOUND)
|
||||||
|
|
||||||
link_hifi_libraries(shared)
|
link_hifi_libraries(shared fbx entities)
|
||||||
|
include_hifi_library_headers(fbx)
|
||||||
## find BULLET
|
|
||||||
#find_package(BULLET REQUIRED)
|
|
||||||
#
|
|
||||||
#include_directories(SYSTEM "${BULLET_INCLUDE_DIRS}")
|
|
||||||
#
|
|
||||||
## append BULLET to our list of libraries to link
|
|
||||||
#list(APPEND ${TARGET_NAME}_LIBRARIES_TO_LINK "${BULLET_LIBRARIES}")
|
|
||||||
|
|
||||||
# call macro to include our dependency includes and bubble them up via a property on our target
|
# call macro to include our dependency includes and bubble them up via a property on our target
|
||||||
include_dependency_includes()
|
include_dependency_includes()
|
||||||
|
|
38
libraries/physics/src/BulletUtil.h
Normal file
38
libraries/physics/src/BulletUtil.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
//
|
||||||
|
// BulletUtil.h
|
||||||
|
// libraries/physcis/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2014.11.02
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_BulletUtil_h
|
||||||
|
#define hifi_BulletUtil_h
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
|
||||||
|
#include <btBulletDynamicsCommon.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
|
inline glm::vec3 bulletToGLM(const btVector3& b) {
|
||||||
|
return glm::vec3(b.getX(), b.getY(), b.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline glm::quat bulletToGLM(const btQuaternion& b) {
|
||||||
|
return glm::quat(b.getW(), b.getX(), b.getY(), b.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline btVector3 glmToBullet(const glm::vec3& g) {
|
||||||
|
return btVector3(g.x, g.y, g.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline btQuaternion glmToBullet(const glm::quat& g) {
|
||||||
|
return btQuaternion(g.x, g.y, g.z, g.w);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
|
#endif // hifi_BulletUtil_h
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Constraint.h
|
// Constraint.h
|
||||||
// libraries/shared/src
|
// libraries/physics/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows 2014.07.24
|
// Created by Andrew Meadows 2014.07.24
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// ContactConstraint.cpp
|
// ContactConstraint.cpp
|
||||||
// interface/src/avatar
|
// libraries/physcis/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows 2014.07.24
|
// Created by Andrew Meadows 2014.07.24
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// ContactConstraint.h
|
// ContactConstraint.h
|
||||||
// interface/src/avatar
|
// libraries/physcis/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows 2014.07.24
|
// Created by Andrew Meadows 2014.07.24
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// ContactPoint.cpp
|
// ContactPoint.cpp
|
||||||
// libraries/shared/src
|
// libraries/physics/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows 2014.07.30
|
// Created by Andrew Meadows 2014.07.30
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// ContactPoint.h
|
// ContactPoint.h
|
||||||
// libraries/shared/src
|
// libraries/physics/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows 2014.07.30
|
// Created by Andrew Meadows 2014.07.30
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
@ -15,7 +15,8 @@
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
#include "CollisionInfo.h"
|
#include <CollisionInfo.h>
|
||||||
|
|
||||||
#include "VerletPoint.h"
|
#include "VerletPoint.h"
|
||||||
|
|
||||||
class Shape;
|
class Shape;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// DistanceConstraint.cpp
|
// DistanceConstraint.cpp
|
||||||
// libraries/shared/src
|
// libraries/physics/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows 2014.07.24
|
// Created by Andrew Meadows 2014.07.24
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// DistanceConstraint.h
|
// DistanceConstraint.h
|
||||||
// libraries/shared/src
|
// libraries/physics/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows 2014.07.24
|
// Created by Andrew Meadows 2014.07.24
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
43
libraries/physics/src/DoubleHashKey.cpp
Normal file
43
libraries/physics/src/DoubleHashKey.cpp
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
//
|
||||||
|
// DoubleHashKey.cpp
|
||||||
|
// libraries/physcis/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2014.11.02
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "DoubleHashKey.h"
|
||||||
|
|
||||||
|
const int NUM_PRIMES = 64;
|
||||||
|
const unsigned int PRIMES[] = {
|
||||||
|
4194301U, 4194287U, 4194277U, 4194271U, 4194247U, 4194217U, 4194199U, 4194191U,
|
||||||
|
4194187U, 4194181U, 4194173U, 4194167U, 4194143U, 4194137U, 4194131U, 4194107U,
|
||||||
|
4194103U, 4194023U, 4194011U, 4194007U, 4193977U, 4193971U, 4193963U, 4193957U,
|
||||||
|
4193939U, 4193929U, 4193909U, 4193869U, 4193807U, 4193803U, 4193801U, 4193789U,
|
||||||
|
4193759U, 4193753U, 4193743U, 4193701U, 4193663U, 4193633U, 4193573U, 4193569U,
|
||||||
|
4193551U, 4193549U, 4193531U, 4193513U, 4193507U, 4193459U, 4193447U, 4193443U,
|
||||||
|
4193417U, 4193411U, 4193393U, 4193389U, 4193381U, 4193377U, 4193369U, 4193359U,
|
||||||
|
4193353U, 4193327U, 4193309U, 4193303U, 4193297U, 4193279U, 4193269U, 4193263U
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned int DoubleHashKey::hashFunction(unsigned int value, int primeIndex) {
|
||||||
|
unsigned int hash = PRIMES[primeIndex % NUM_PRIMES] * (value + 1U);
|
||||||
|
hash += ~(hash << 15);
|
||||||
|
hash ^= (hash >> 10);
|
||||||
|
hash += (hash << 3);
|
||||||
|
hash ^= (hash >> 6);
|
||||||
|
hash += ~(hash << 11);
|
||||||
|
return hash ^ (hash >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int DoubleHashKey::hashFunction2(unsigned int value) {
|
||||||
|
unsigned hash = 0x811c9dc5U;
|
||||||
|
for (int i = 0; i < 4; i++ ) {
|
||||||
|
unsigned int byte = (value << (i * 8)) >> (24 - i * 8);
|
||||||
|
hash = ( hash ^ byte ) * 0x01000193U;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
38
libraries/physics/src/DoubleHashKey.h
Normal file
38
libraries/physics/src/DoubleHashKey.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
//
|
||||||
|
// DoubleHashKey.h
|
||||||
|
// libraries/physcis/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2014.11.02
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_DoubleHashKey_h
|
||||||
|
#define hifi_DoubleHashKey_h
|
||||||
|
|
||||||
|
// DoubleHashKey for use with btHashMap
|
||||||
|
class DoubleHashKey {
|
||||||
|
public:
|
||||||
|
static unsigned int hashFunction(unsigned int value, int primeIndex);
|
||||||
|
static unsigned int hashFunction2(unsigned int value);
|
||||||
|
|
||||||
|
DoubleHashKey() : _hash(0), _hash2(0) { }
|
||||||
|
|
||||||
|
DoubleHashKey(unsigned int value, int primeIndex = 0) :
|
||||||
|
_hash(hashFunction(value, primeIndex)),
|
||||||
|
_hash2(hashFunction2(value)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool equals(const DoubleHashKey& other) const {
|
||||||
|
return _hash == other._hash && _hash2 == other._hash2;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int getHash() const { return (unsigned int)_hash; }
|
||||||
|
|
||||||
|
int _hash;
|
||||||
|
int _hash2;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_DoubleHashKey_h
|
172
libraries/physics/src/EntityMotionState.cpp
Normal file
172
libraries/physics/src/EntityMotionState.cpp
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
//
|
||||||
|
// EntityMotionState.cpp
|
||||||
|
// libraries/entities/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows on 2014.11.06
|
||||||
|
// Copyright 2013 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 <EntityItem.h>
|
||||||
|
#include <EntityEditPacketSender.h>
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
#include "BulletUtil.h"
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
|
#include "EntityMotionState.h"
|
||||||
|
|
||||||
|
QSet<EntityItem*>* _outgoingEntityList;
|
||||||
|
|
||||||
|
// static
|
||||||
|
void EntityMotionState::setOutgoingEntityList(QSet<EntityItem*>* list) {
|
||||||
|
assert(list);
|
||||||
|
_outgoingEntityList = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void EntityMotionState::enqueueOutgoingEntity(EntityItem* entity) {
|
||||||
|
assert(_outgoingEntityList);
|
||||||
|
_outgoingEntityList->insert(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityMotionState::EntityMotionState(EntityItem* entity)
|
||||||
|
: _entity(entity) {
|
||||||
|
assert(entity != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityMotionState::~EntityMotionState() {
|
||||||
|
assert(_entity);
|
||||||
|
_entity->setPhysicsInfo(NULL);
|
||||||
|
_entity = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
MotionType EntityMotionState::computeMotionType() const {
|
||||||
|
// HACK: According to EntityTree the meaning of "static" is "not moving" whereas
|
||||||
|
// to Bullet it means "can't move". For demo purposes we temporarily interpret
|
||||||
|
// Entity::weightless to mean Bullet::static.
|
||||||
|
return _entity->getCollisionsWillMove() ? MOTION_TYPE_DYNAMIC : MOTION_TYPE_STATIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
// This callback is invoked by the physics simulation in two cases:
|
||||||
|
// (1) when the RigidBody is first added to the world
|
||||||
|
// (irregardless of MotionType: STATIC, DYNAMIC, or KINEMATIC)
|
||||||
|
// (2) at the beginning of each simulation frame for KINEMATIC RigidBody's --
|
||||||
|
// it is an opportunity for outside code to update the object's simulation position
|
||||||
|
void EntityMotionState::getWorldTransform(btTransform& worldTrans) const {
|
||||||
|
worldTrans.setOrigin(glmToBullet(_entity->getPositionInMeters() - ObjectMotionState::getWorldOffset()));
|
||||||
|
worldTrans.setRotation(glmToBullet(_entity->getRotation()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This callback is invoked by the physics simulation at the end of each simulation frame...
|
||||||
|
// iff the corresponding RigidBody is DYNAMIC and has moved.
|
||||||
|
void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||||
|
_entity->setPositionInMeters(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset());
|
||||||
|
_entity->setRotation(bulletToGLM(worldTrans.getRotation()));
|
||||||
|
|
||||||
|
glm::vec3 v;
|
||||||
|
getVelocity(v);
|
||||||
|
_entity->setVelocityInMeters(v);
|
||||||
|
|
||||||
|
getAngularVelocity(v);
|
||||||
|
_entity->setAngularVelocity(v);
|
||||||
|
|
||||||
|
_outgoingPacketFlags = DIRTY_PHYSICS_FLAGS;
|
||||||
|
EntityMotionState::enqueueOutgoingEntity(_entity);
|
||||||
|
}
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
|
|
||||||
|
void EntityMotionState::applyVelocities() const {
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
if (_body) {
|
||||||
|
setVelocity(_entity->getVelocityInMeters());
|
||||||
|
setAngularVelocity(_entity->getAngularVelocity());
|
||||||
|
_body->setActivationState(ACTIVE_TAG);
|
||||||
|
}
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityMotionState::applyGravity() const {
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
if (_body) {
|
||||||
|
setGravity(_entity->getGravityInMeters());
|
||||||
|
_body->setActivationState(ACTIVE_TAG);
|
||||||
|
}
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityMotionState::computeShapeInfo(ShapeInfo& info) {
|
||||||
|
_entity->computeShapeInfo(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame) {
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
if (_outgoingPacketFlags) {
|
||||||
|
EntityItemProperties properties = _entity->getProperties();
|
||||||
|
|
||||||
|
if (_outgoingPacketFlags & EntityItem::DIRTY_POSITION) {
|
||||||
|
btTransform worldTrans = _body->getWorldTransform();
|
||||||
|
_sentPosition = bulletToGLM(worldTrans.getOrigin());
|
||||||
|
properties.setPosition(_sentPosition + ObjectMotionState::getWorldOffset());
|
||||||
|
|
||||||
|
_sentRotation = bulletToGLM(worldTrans.getRotation());
|
||||||
|
properties.setRotation(_sentRotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_outgoingPacketFlags & EntityItem::DIRTY_VELOCITY) {
|
||||||
|
if (_body->isActive()) {
|
||||||
|
_sentVelocity = bulletToGLM(_body->getLinearVelocity());
|
||||||
|
_sentAngularVelocity = bulletToGLM(_body->getAngularVelocity());
|
||||||
|
|
||||||
|
// if the speeds are very small we zero them out
|
||||||
|
const float MINIMUM_EXTRAPOLATION_SPEED_SQUARED = 4.0e-6f; // 2mm/sec
|
||||||
|
bool zeroSpeed = (glm::length2(_sentVelocity) < MINIMUM_EXTRAPOLATION_SPEED_SQUARED);
|
||||||
|
if (zeroSpeed) {
|
||||||
|
_sentVelocity = glm::vec3(0.0f);
|
||||||
|
}
|
||||||
|
const float MINIMUM_EXTRAPOLATION_SPIN_SQUARED = 0.004f; // ~0.01 rotation/sec
|
||||||
|
bool zeroSpin = glm::length2(_sentAngularVelocity) < MINIMUM_EXTRAPOLATION_SPIN_SQUARED;
|
||||||
|
if (zeroSpin) {
|
||||||
|
_sentAngularVelocity = glm::vec3(0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
_sentMoving = ! (zeroSpeed && zeroSpin);
|
||||||
|
} else {
|
||||||
|
_sentVelocity = _sentAngularVelocity = glm::vec3(0.0f);
|
||||||
|
_sentMoving = false;
|
||||||
|
}
|
||||||
|
properties.setVelocity(_sentVelocity);
|
||||||
|
_sentAcceleration = bulletToGLM(_body->getGravity());
|
||||||
|
properties.setGravity(_sentAcceleration);
|
||||||
|
properties.setAngularVelocity(_sentAngularVelocity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// RELIABLE_SEND_HACK: count number of updates for entities at rest so we can stop sending them after some limit.
|
||||||
|
if (_sentMoving) {
|
||||||
|
_numNonMovingUpdates = 0;
|
||||||
|
} else {
|
||||||
|
_numNonMovingUpdates++;
|
||||||
|
}
|
||||||
|
if (_numNonMovingUpdates <= 1) {
|
||||||
|
// we only update lastEdited when we're sending new physics data
|
||||||
|
// (i.e. NOT when we just simulate the positions forward, nore when we resend non-moving data)
|
||||||
|
quint64 lastSimulated = _entity->getLastSimulated();
|
||||||
|
_entity->setLastEdited(lastSimulated);
|
||||||
|
properties.setLastEdited(lastSimulated);
|
||||||
|
} else {
|
||||||
|
properties.setLastEdited(_entity->getLastEdited());
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityItemID id(_entity->getID());
|
||||||
|
EntityEditPacketSender* entityPacketSender = static_cast<EntityEditPacketSender*>(packetSender);
|
||||||
|
entityPacketSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, id, properties);
|
||||||
|
|
||||||
|
// The outgoing flags only itemized WHAT to send, not WHETHER to send, hence we always set them
|
||||||
|
// to the full set. These flags may be momentarily cleared by incoming external changes.
|
||||||
|
_outgoingPacketFlags = DIRTY_PHYSICS_FLAGS;
|
||||||
|
_sentFrame = frame;
|
||||||
|
}
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
|
}
|
71
libraries/physics/src/EntityMotionState.h
Normal file
71
libraries/physics/src/EntityMotionState.h
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
//
|
||||||
|
// EntityMotionState.h
|
||||||
|
// libraries/entities/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows on 2014.11.06
|
||||||
|
// Copyright 2013 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_EntityMotionState_h
|
||||||
|
#define hifi_EntityMotionState_h
|
||||||
|
|
||||||
|
#include <AACube.h>
|
||||||
|
|
||||||
|
#include "ObjectMotionState.h"
|
||||||
|
#ifndef USE_BULLET_PHYSICS
|
||||||
|
// ObjectMotionState stubbery
|
||||||
|
class ObjectMotionState {
|
||||||
|
public:
|
||||||
|
// so that this stub implementation is not completely empty we give the class a data member
|
||||||
|
bool _stubData;
|
||||||
|
};
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
|
|
||||||
|
class EntityItem;
|
||||||
|
|
||||||
|
// From the MotionState's perspective:
|
||||||
|
// Inside = physics simulation
|
||||||
|
// Outside = external agents (scripts, user interaction, other simulations)
|
||||||
|
|
||||||
|
class EntityMotionState : public ObjectMotionState {
|
||||||
|
public:
|
||||||
|
|
||||||
|
// The OutgoingEntityQueue is a pointer to a QSet (owned by an EntitySimulation) of EntityItem*'s
|
||||||
|
// that have been changed by the physics simulation.
|
||||||
|
// All ObjectMotionState's with outgoing changes put themselves on the list.
|
||||||
|
static void setOutgoingEntityList(QSet<EntityItem*>* list);
|
||||||
|
static void enqueueOutgoingEntity(EntityItem* entity);
|
||||||
|
|
||||||
|
EntityMotionState(EntityItem* item);
|
||||||
|
virtual ~EntityMotionState();
|
||||||
|
|
||||||
|
/// \return MOTION_TYPE_DYNAMIC or MOTION_TYPE_STATIC based on params set in EntityItem
|
||||||
|
MotionType computeMotionType() const;
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
// this relays incoming position/rotation to the RigidBody
|
||||||
|
void getWorldTransform(btTransform& worldTrans) const;
|
||||||
|
|
||||||
|
// this relays outgoing position/rotation to the EntityItem
|
||||||
|
void setWorldTransform(const btTransform& worldTrans);
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
|
|
||||||
|
// these relay incoming values to the RigidBody
|
||||||
|
void applyVelocities() const;
|
||||||
|
void applyGravity() const;
|
||||||
|
|
||||||
|
void computeShapeInfo(ShapeInfo& info);
|
||||||
|
|
||||||
|
void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame);
|
||||||
|
|
||||||
|
uint32_t getIncomingDirtyFlags() const { return _entity->getDirtyFlags(); }
|
||||||
|
void clearIncomingDirtyFlags(uint32_t flags) { _entity->clearDirtyFlags(flags); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
EntityItem* _entity;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_EntityMotionState_h
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// FixedConstraint.cpp
|
// FixedConstraint.cpp
|
||||||
// libraries/shared/src
|
// libraries/physics/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows 2014.07.24
|
// Created by Andrew Meadows 2014.07.24
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// FixedConstraint.h
|
// FixedConstraint.h
|
||||||
// libraries/shared/src
|
// libraries/physics/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows 2014.07.24
|
// Created by Andrew Meadows 2014.07.24
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
173
libraries/physics/src/ObjectMotionState.cpp
Normal file
173
libraries/physics/src/ObjectMotionState.cpp
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
//
|
||||||
|
// ObjectMotionState.cpp
|
||||||
|
// libraries/physcis/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2014.11.05
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "BulletUtil.h"
|
||||||
|
#include "ObjectMotionState.h"
|
||||||
|
|
||||||
|
const float MIN_DENSITY = 200.0f;
|
||||||
|
const float DEFAULT_DENSITY = 1000.0f;
|
||||||
|
const float MAX_DENSITY = 20000.0f;
|
||||||
|
|
||||||
|
const float MIN_VOLUME = 0.001f;
|
||||||
|
const float DEFAULT_VOLUME = 1.0f;
|
||||||
|
const float MAX_VOLUME = 1000000.0f;
|
||||||
|
|
||||||
|
const float DEFAULT_FRICTION = 0.5f;
|
||||||
|
const float MAX_FRICTION = 10.0f;
|
||||||
|
|
||||||
|
const float DEFAULT_RESTITUTION = 0.0f;
|
||||||
|
|
||||||
|
// origin of physics simulation in world frame
|
||||||
|
glm::vec3 _worldOffset(0.0f);
|
||||||
|
|
||||||
|
// static
|
||||||
|
void ObjectMotionState::setWorldOffset(const glm::vec3& offset) {
|
||||||
|
_worldOffset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
const glm::vec3& ObjectMotionState::getWorldOffset() {
|
||||||
|
return _worldOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ObjectMotionState::ObjectMotionState() :
|
||||||
|
_density(DEFAULT_DENSITY),
|
||||||
|
_volume(DEFAULT_VOLUME),
|
||||||
|
_friction(DEFAULT_FRICTION),
|
||||||
|
_restitution(DEFAULT_RESTITUTION),
|
||||||
|
_wasInWorld(false),
|
||||||
|
_motionType(MOTION_TYPE_STATIC),
|
||||||
|
_body(NULL),
|
||||||
|
_sentMoving(false),
|
||||||
|
_numNonMovingUpdates(0),
|
||||||
|
_outgoingPacketFlags(DIRTY_PHYSICS_FLAGS),
|
||||||
|
_sentFrame(0),
|
||||||
|
_sentPosition(0.0f),
|
||||||
|
_sentRotation(),
|
||||||
|
_sentVelocity(0.0f),
|
||||||
|
_sentAngularVelocity(0.0f),
|
||||||
|
_sentAcceleration(0.0f) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectMotionState::~ObjectMotionState() {
|
||||||
|
// NOTE: you MUST remove this MotionState from the world before you call the dtor.
|
||||||
|
assert(_body == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMotionState::setDensity(float density) {
|
||||||
|
_density = btMax(btMin(fabsf(density), MAX_DENSITY), MIN_DENSITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMotionState::setFriction(float friction) {
|
||||||
|
_friction = btMax(btMin(fabsf(friction), MAX_FRICTION), 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMotionState::setRestitution(float restitution) {
|
||||||
|
_restitution = btMax(btMin(fabsf(restitution), 1.0f), 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMotionState::setVolume(float volume) {
|
||||||
|
_volume = btMax(btMin(fabsf(volume), MAX_VOLUME), MIN_VOLUME);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMotionState::setVelocity(const glm::vec3& velocity) const {
|
||||||
|
_body->setLinearVelocity(glmToBullet(velocity));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMotionState::setAngularVelocity(const glm::vec3& velocity) const {
|
||||||
|
_body->setAngularVelocity(glmToBullet(velocity));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMotionState::setGravity(const glm::vec3& gravity) const {
|
||||||
|
_body->setGravity(glmToBullet(gravity));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMotionState::getVelocity(glm::vec3& velocityOut) const {
|
||||||
|
velocityOut = bulletToGLM(_body->getLinearVelocity());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectMotionState::getAngularVelocity(glm::vec3& angularVelocityOut) const {
|
||||||
|
angularVelocityOut = bulletToGLM(_body->getAngularVelocity());
|
||||||
|
}
|
||||||
|
|
||||||
|
// RELIABLE_SEND_HACK: until we have truly reliable resends of non-moving updates
|
||||||
|
// we alwasy resend packets for objects that have stopped moving up to some max limit.
|
||||||
|
const int MAX_NUM_NON_MOVING_UPDATES = 5;
|
||||||
|
|
||||||
|
bool ObjectMotionState::doesNotNeedToSendUpdate() const {
|
||||||
|
return !_body->isActive() && _numNonMovingUpdates > MAX_NUM_NON_MOVING_UPDATES;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float FIXED_SUBSTEP = 1.0f / 60.0f;
|
||||||
|
|
||||||
|
bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame, float subStepRemainder) const {
|
||||||
|
assert(_body);
|
||||||
|
float dt = (float)(simulationFrame - _sentFrame) * FIXED_SUBSTEP + subStepRemainder;
|
||||||
|
bool isActive = _body->isActive();
|
||||||
|
|
||||||
|
if (isActive) {
|
||||||
|
const float MAX_UPDATE_PERIOD_FOR_ACTIVE_THINGS = 10.0f;
|
||||||
|
if (dt > MAX_UPDATE_PERIOD_FOR_ACTIVE_THINGS) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (_sentMoving) {
|
||||||
|
if (!isActive) {
|
||||||
|
// this object just went inactive so send an update immediately
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const float NON_MOVING_UPDATE_PERIOD = 1.0f;
|
||||||
|
if (dt > NON_MOVING_UPDATE_PERIOD && _numNonMovingUpdates < MAX_NUM_NON_MOVING_UPDATES) {
|
||||||
|
// RELIABLE_SEND_HACK: since we're not yet using a reliable method for non-moving update packets we repeat these
|
||||||
|
// at a faster rate than the MAX period above, and only send a limited number of them.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else we measure the error between current and extrapolated transform (according to expected behavior
|
||||||
|
// of remote EntitySimulation) and return true if the error is significant.
|
||||||
|
|
||||||
|
// NOTE: math in done the simulation-frame, which is NOT necessarily the same as the world-frame
|
||||||
|
// due to _worldOffset.
|
||||||
|
|
||||||
|
// TODO: Andrew to reconcile Bullet and legacy damping coefficients.
|
||||||
|
|
||||||
|
// compute position error
|
||||||
|
glm::vec3 extrapolatedPosition = _sentPosition + dt * (_sentVelocity + (0.5f * dt) * _sentAcceleration);
|
||||||
|
|
||||||
|
btTransform worldTrans = _body->getWorldTransform();
|
||||||
|
glm::vec3 position = bulletToGLM(worldTrans.getOrigin());
|
||||||
|
|
||||||
|
float dx2 = glm::distance2(position, extrapolatedPosition);
|
||||||
|
const float MAX_POSITION_ERROR_SQUARED = 0.001f; // 0.001 m^2 ~~> 0.03 m
|
||||||
|
if (dx2 > MAX_POSITION_ERROR_SQUARED) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute rotation error
|
||||||
|
float spin = glm::length(_sentAngularVelocity);
|
||||||
|
glm::quat extrapolatedRotation = _sentRotation;
|
||||||
|
const float MIN_SPIN = 1.0e-4f;
|
||||||
|
if (spin > MIN_SPIN) {
|
||||||
|
glm::vec3 axis = _sentAngularVelocity / spin;
|
||||||
|
extrapolatedRotation = glm::angleAxis(dt * spin, axis) * _sentRotation;
|
||||||
|
}
|
||||||
|
const float MIN_ROTATION_DOT = 0.98f;
|
||||||
|
glm::quat actualRotation = bulletToGLM(worldTrans.getRotation());
|
||||||
|
return (glm::dot(actualRotation, extrapolatedRotation) < MIN_ROTATION_DOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
115
libraries/physics/src/ObjectMotionState.h
Normal file
115
libraries/physics/src/ObjectMotionState.h
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
//
|
||||||
|
// ObjectMotionState.h
|
||||||
|
// libraries/physcis/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2014.11.05
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_ObjectMotionState_h
|
||||||
|
#define hifi_ObjectMotionState_h
|
||||||
|
|
||||||
|
#include <EntityItem.h>
|
||||||
|
|
||||||
|
enum MotionType {
|
||||||
|
MOTION_TYPE_STATIC, // no motion
|
||||||
|
MOTION_TYPE_DYNAMIC, // motion according to physical laws
|
||||||
|
MOTION_TYPE_KINEMATIC // keyframed motion
|
||||||
|
};
|
||||||
|
|
||||||
|
// The update flags trigger two varieties of updates: "hard" which require the body to be pulled
|
||||||
|
// and re-added to the physics engine and "easy" which just updates the body properties.
|
||||||
|
typedef unsigned int uint32_t;
|
||||||
|
const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_MOTION_TYPE | EntityItem::DIRTY_SHAPE);
|
||||||
|
const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY |
|
||||||
|
EntityItem::DIRTY_MASS | EntityItem::DIRTY_COLLISION_GROUP);
|
||||||
|
|
||||||
|
// These are the set of incoming flags that the PhysicsEngine needs to hear about:
|
||||||
|
const uint32_t DIRTY_PHYSICS_FLAGS = HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS;
|
||||||
|
|
||||||
|
// These are the outgoing flags that the PhysicsEngine can affect:
|
||||||
|
const uint32_t OUTGOING_DIRTY_PHYSICS_FLAGS = EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY;
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
|
||||||
|
#include <btBulletDynamicsCommon.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <EntityItem.h> // for EntityItem::DIRTY_FOO bitmasks
|
||||||
|
|
||||||
|
#include "ShapeInfo.h"
|
||||||
|
|
||||||
|
class OctreeEditPacketSender;
|
||||||
|
|
||||||
|
class ObjectMotionState : public btMotionState {
|
||||||
|
public:
|
||||||
|
// The WorldOffset is used to keep the positions of objects in the simulation near the origin, to
|
||||||
|
// reduce numerical error when computing vector differences. In other words: The EntityMotionState
|
||||||
|
// class translates between the simulation-frame and the world-frame as known by the render pipeline,
|
||||||
|
// various object trees, etc. The EntityMotionState class uses a static "worldOffset" to help in
|
||||||
|
// the translations.
|
||||||
|
static void setWorldOffset(const glm::vec3& offset);
|
||||||
|
static const glm::vec3& getWorldOffset();
|
||||||
|
|
||||||
|
ObjectMotionState();
|
||||||
|
~ObjectMotionState();
|
||||||
|
|
||||||
|
virtual void applyVelocities() const = 0;
|
||||||
|
virtual void applyGravity() const = 0;
|
||||||
|
|
||||||
|
virtual void computeShapeInfo(ShapeInfo& info) = 0;
|
||||||
|
|
||||||
|
virtual MotionType getMotionType() const { return _motionType; }
|
||||||
|
|
||||||
|
void setDensity(float density);
|
||||||
|
void setFriction(float friction);
|
||||||
|
void setRestitution(float restitution);
|
||||||
|
void setVolume(float volume);
|
||||||
|
|
||||||
|
float getMass() const { return _volume * _density; }
|
||||||
|
|
||||||
|
void setVelocity(const glm::vec3& velocity) const;
|
||||||
|
void setAngularVelocity(const glm::vec3& velocity) const;
|
||||||
|
void setGravity(const glm::vec3& gravity) const;
|
||||||
|
|
||||||
|
void getVelocity(glm::vec3& velocityOut) const;
|
||||||
|
void getAngularVelocity(glm::vec3& angularVelocityOut) const;
|
||||||
|
|
||||||
|
virtual uint32_t getIncomingDirtyFlags() const = 0;
|
||||||
|
virtual void clearIncomingDirtyFlags(uint32_t flags) = 0;
|
||||||
|
void clearOutgoingPacketFlags(uint32_t flags) { _outgoingPacketFlags &= ~flags; }
|
||||||
|
|
||||||
|
bool doesNotNeedToSendUpdate() const;
|
||||||
|
virtual bool shouldSendUpdate(uint32_t simulationFrame, float subStepRemainder) const;
|
||||||
|
virtual void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame) = 0;
|
||||||
|
|
||||||
|
virtual MotionType computeMotionType() const = 0;
|
||||||
|
|
||||||
|
friend class PhysicsEngine;
|
||||||
|
protected:
|
||||||
|
float _density;
|
||||||
|
float _volume;
|
||||||
|
float _friction;
|
||||||
|
float _restitution;
|
||||||
|
bool _wasInWorld;
|
||||||
|
MotionType _motionType;
|
||||||
|
|
||||||
|
// _body has NO setters -- it is only changed by PhysicsEngine
|
||||||
|
btRigidBody* _body;
|
||||||
|
|
||||||
|
bool _sentMoving; // true if last update was moving
|
||||||
|
int _numNonMovingUpdates; // RELIABLE_SEND_HACK for "not so reliable" resends of packets for non-moving objects
|
||||||
|
|
||||||
|
uint32_t _outgoingPacketFlags;
|
||||||
|
uint32_t _sentFrame;
|
||||||
|
glm::vec3 _sentPosition; // in simulation-frame (not world-frame)
|
||||||
|
glm::quat _sentRotation;;
|
||||||
|
glm::vec3 _sentVelocity;
|
||||||
|
glm::vec3 _sentAngularVelocity;
|
||||||
|
glm::vec3 _sentAcceleration;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
|
#endif // hifi_ObjectMotionState_h
|
454
libraries/physics/src/PhysicsEngine.cpp
Normal file
454
libraries/physics/src/PhysicsEngine.cpp
Normal file
|
@ -0,0 +1,454 @@
|
||||||
|
//
|
||||||
|
// PhysicsEngine.cpp
|
||||||
|
// libraries/physcis/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2014.10.29
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "PhysicsEngine.h"
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
|
||||||
|
#include "ShapeInfoUtil.h"
|
||||||
|
#include "ThreadSafeDynamicsWorld.h"
|
||||||
|
|
||||||
|
class EntityTree;
|
||||||
|
|
||||||
|
PhysicsEngine::PhysicsEngine(const glm::vec3& offset)
|
||||||
|
: _collisionConfig(NULL),
|
||||||
|
_collisionDispatcher(NULL),
|
||||||
|
_broadphaseFilter(NULL),
|
||||||
|
_constraintSolver(NULL),
|
||||||
|
_dynamicsWorld(NULL),
|
||||||
|
_originOffset(offset),
|
||||||
|
_voxels(),
|
||||||
|
_entityPacketSender(NULL),
|
||||||
|
_frameCount(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
PhysicsEngine::~PhysicsEngine() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// begin EntitySimulation overrides
|
||||||
|
void PhysicsEngine::updateEntitiesInternal(const quint64& now) {
|
||||||
|
// NOTE: the grand order of operations is:
|
||||||
|
// (1) relay incoming changes
|
||||||
|
// (2) step simulation
|
||||||
|
// (3) synchronize outgoing motion states
|
||||||
|
// (4) send outgoing packets
|
||||||
|
|
||||||
|
// this is step (4)
|
||||||
|
QSet<ObjectMotionState*>::iterator stateItr = _outgoingPackets.begin();
|
||||||
|
uint32_t frame = getFrameCount();
|
||||||
|
float subStepRemainder = getSubStepRemainder();
|
||||||
|
while (stateItr != _outgoingPackets.end()) {
|
||||||
|
ObjectMotionState* state = *stateItr;
|
||||||
|
if (state->doesNotNeedToSendUpdate()) {
|
||||||
|
stateItr = _outgoingPackets.erase(stateItr);
|
||||||
|
} else if (state->shouldSendUpdate(frame, subStepRemainder)) {
|
||||||
|
state->sendUpdate(_entityPacketSender, frame);
|
||||||
|
++stateItr;
|
||||||
|
} else {
|
||||||
|
++stateItr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhysicsEngine::addEntityInternal(EntityItem* entity) {
|
||||||
|
assert(entity);
|
||||||
|
void* physicsInfo = entity->getPhysicsInfo();
|
||||||
|
if (!physicsInfo) {
|
||||||
|
EntityMotionState* motionState = new EntityMotionState(entity);
|
||||||
|
if (addObject(motionState)) {
|
||||||
|
entity->setPhysicsInfo(static_cast<void*>(motionState));
|
||||||
|
_entityMotionStates.insert(motionState);
|
||||||
|
} else {
|
||||||
|
// We failed to add the entity to the simulation. Probably because we couldn't create a shape for it.
|
||||||
|
qDebug() << "failed to add entity " << entity->getEntityItemID() << " to physics engine";
|
||||||
|
delete motionState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhysicsEngine::removeEntityInternal(EntityItem* entity) {
|
||||||
|
assert(entity);
|
||||||
|
void* physicsInfo = entity->getPhysicsInfo();
|
||||||
|
if (physicsInfo) {
|
||||||
|
EntityMotionState* motionState = static_cast<EntityMotionState*>(physicsInfo);
|
||||||
|
removeObject(motionState);
|
||||||
|
_entityMotionStates.remove(motionState);
|
||||||
|
_incomingChanges.remove(motionState);
|
||||||
|
_outgoingPackets.remove(motionState);
|
||||||
|
delete motionState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhysicsEngine::entityChangedInternal(EntityItem* entity) {
|
||||||
|
// queue incoming changes: from external sources (script, EntityServer, etc) to physics engine
|
||||||
|
assert(entity);
|
||||||
|
void* physicsInfo = entity->getPhysicsInfo();
|
||||||
|
if (physicsInfo) {
|
||||||
|
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
|
||||||
|
_incomingChanges.insert(motionState);
|
||||||
|
} else {
|
||||||
|
// try to add this entity again (maybe something changed such that it will work this time)
|
||||||
|
addEntity(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhysicsEngine::sortEntitiesThatMovedInternal() {
|
||||||
|
// entities that have been simulated forward (hence in the _entitiesToBeSorted list)
|
||||||
|
// also need to be put in the outgoingPackets list
|
||||||
|
QSet<EntityItem*>::iterator entityItr = _entitiesToBeSorted.begin();
|
||||||
|
while (entityItr != _entitiesToBeSorted.end()) {
|
||||||
|
EntityItem* entity = *entityItr;
|
||||||
|
void* physicsInfo = entity->getPhysicsInfo();
|
||||||
|
assert(physicsInfo);
|
||||||
|
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(physicsInfo);
|
||||||
|
_outgoingPackets.insert(motionState);
|
||||||
|
++entityItr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhysicsEngine::clearEntitiesInternal() {
|
||||||
|
// For now we assume this would only be called on shutdown in which case we can just let the memory get lost.
|
||||||
|
QSet<EntityMotionState*>::const_iterator stateItr = _entityMotionStates.begin();
|
||||||
|
for (stateItr = _entityMotionStates.begin(); stateItr != _entityMotionStates.end(); ++stateItr) {
|
||||||
|
removeObject(*stateItr);
|
||||||
|
delete (*stateItr);
|
||||||
|
}
|
||||||
|
_entityMotionStates.clear();
|
||||||
|
_incomingChanges.clear();
|
||||||
|
_outgoingPackets.clear();
|
||||||
|
}
|
||||||
|
// end EntitySimulation overrides
|
||||||
|
|
||||||
|
void PhysicsEngine::relayIncomingChangesToSimulation() {
|
||||||
|
// process incoming changes
|
||||||
|
QSet<ObjectMotionState*>::iterator stateItr = _incomingChanges.begin();
|
||||||
|
while (stateItr != _incomingChanges.end()) {
|
||||||
|
ObjectMotionState* motionState = *stateItr;
|
||||||
|
uint32_t flags = motionState->getIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS;
|
||||||
|
|
||||||
|
btRigidBody* body = motionState->_body;
|
||||||
|
if (body) {
|
||||||
|
if (flags & HARD_DIRTY_PHYSICS_FLAGS) {
|
||||||
|
// a HARD update requires the body be pulled out of physics engine, changed, then reinserted
|
||||||
|
// but it also handles all EASY changes
|
||||||
|
updateObjectHard(body, motionState, flags);
|
||||||
|
} else if (flags) {
|
||||||
|
// an EASY update does NOT require that the body be pulled out of physics engine
|
||||||
|
updateObjectEasy(body, motionState, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: the grand order of operations is:
|
||||||
|
// (1) relay incoming changes
|
||||||
|
// (2) step simulation
|
||||||
|
// (3) synchronize outgoing motion states
|
||||||
|
// (4) send outgoing packets
|
||||||
|
//
|
||||||
|
// We're in the middle of step (1) hence incoming changes should trump corresponding
|
||||||
|
// outgoing changes at this point.
|
||||||
|
motionState->clearOutgoingPacketFlags(flags); // clear outgoing flags that were trumped
|
||||||
|
motionState->clearIncomingDirtyFlags(flags); // clear incoming flags that were processed
|
||||||
|
++stateItr;
|
||||||
|
}
|
||||||
|
_incomingChanges.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
|
void PhysicsEngine::init(EntityEditPacketSender* packetSender) {
|
||||||
|
// _entityTree should be set prior to the init() call
|
||||||
|
assert(_entityTree);
|
||||||
|
|
||||||
|
if (!_dynamicsWorld) {
|
||||||
|
_collisionConfig = new btDefaultCollisionConfiguration();
|
||||||
|
_collisionDispatcher = new btCollisionDispatcher(_collisionConfig);
|
||||||
|
_broadphaseFilter = new btDbvtBroadphase();
|
||||||
|
_constraintSolver = new btSequentialImpulseConstraintSolver;
|
||||||
|
_dynamicsWorld = new ThreadSafeDynamicsWorld(_collisionDispatcher, _broadphaseFilter, _constraintSolver, _collisionConfig, _entityTree);
|
||||||
|
|
||||||
|
// default gravity of the world is zero, so each object must specify its own gravity
|
||||||
|
// TODO: set up gravity zones
|
||||||
|
_dynamicsWorld->setGravity(btVector3(0.0f, 0.0f, 0.0f));
|
||||||
|
|
||||||
|
// GROUND HACK: add a big planar floor (and walls for testing) to catch falling objects
|
||||||
|
btTransform groundTransform;
|
||||||
|
groundTransform.setIdentity();
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
btVector3 normal(0.0f, 0.0f, 0.0f);
|
||||||
|
normal[i] = 1.0f;
|
||||||
|
btCollisionShape* plane = new btStaticPlaneShape(normal, 0.0f);
|
||||||
|
|
||||||
|
btCollisionObject* groundObject = new btCollisionObject();
|
||||||
|
groundObject->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT);
|
||||||
|
groundObject->setCollisionShape(plane);
|
||||||
|
|
||||||
|
groundObject->setWorldTransform(groundTransform);
|
||||||
|
_dynamicsWorld->addCollisionObject(groundObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(packetSender);
|
||||||
|
_entityPacketSender = packetSender;
|
||||||
|
EntityMotionState::setOutgoingEntityList(&_entitiesToBeSorted);
|
||||||
|
}
|
||||||
|
|
||||||
|
const float FIXED_SUBSTEP = 1.0f / 60.0f;
|
||||||
|
|
||||||
|
void PhysicsEngine::stepSimulation() {
|
||||||
|
// NOTE: the grand order of operations is:
|
||||||
|
// (1) relay incoming changes
|
||||||
|
// (2) step simulation
|
||||||
|
// (3) synchronize outgoing motion states
|
||||||
|
// (4) send outgoing packets
|
||||||
|
|
||||||
|
// this is step (1)
|
||||||
|
relayIncomingChangesToSimulation();
|
||||||
|
|
||||||
|
const int MAX_NUM_SUBSTEPS = 4;
|
||||||
|
const float MAX_TIMESTEP = (float)MAX_NUM_SUBSTEPS * FIXED_SUBSTEP;
|
||||||
|
float dt = 1.0e-6f * (float)(_clock.getTimeMicroseconds());
|
||||||
|
_clock.reset();
|
||||||
|
float timeStep = btMin(dt, MAX_TIMESTEP);
|
||||||
|
|
||||||
|
// steps (2) and (3) are performed by ThreadSafeDynamicsWorld::stepSimulation())
|
||||||
|
int numSubSteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, FIXED_SUBSTEP);
|
||||||
|
_frameCount += (uint32_t)numSubSteps;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PhysicsEngine::addVoxel(const glm::vec3& position, float scale) {
|
||||||
|
glm::vec3 halfExtents = glm::vec3(0.5f * scale);
|
||||||
|
glm::vec3 trueCenter = position + halfExtents;
|
||||||
|
PositionHashKey key(trueCenter);
|
||||||
|
VoxelObject* proxy = _voxels.find(key);
|
||||||
|
if (!proxy) {
|
||||||
|
// create a shape
|
||||||
|
ShapeInfo info;
|
||||||
|
info.setBox(halfExtents);
|
||||||
|
btCollisionShape* shape = _shapeManager.getShape(info);
|
||||||
|
|
||||||
|
// NOTE: the shape creation will fail when the size of the voxel is out of range
|
||||||
|
if (shape) {
|
||||||
|
// create a collisionObject
|
||||||
|
btCollisionObject* object = new btCollisionObject();
|
||||||
|
object->setCollisionShape(shape);
|
||||||
|
btTransform transform;
|
||||||
|
transform.setIdentity();
|
||||||
|
// we shift the center into the simulation's frame
|
||||||
|
glm::vec3 shiftedCenter = (position - _originOffset) + halfExtents;
|
||||||
|
transform.setOrigin(btVector3(shiftedCenter.x, shiftedCenter.y, shiftedCenter.z));
|
||||||
|
object->setWorldTransform(transform);
|
||||||
|
|
||||||
|
// add to map and world
|
||||||
|
_voxels.insert(key, VoxelObject(trueCenter, object));
|
||||||
|
_dynamicsWorld->addCollisionObject(object);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PhysicsEngine::removeVoxel(const glm::vec3& position, float scale) {
|
||||||
|
glm::vec3 halfExtents = glm::vec3(0.5f * scale);
|
||||||
|
glm::vec3 trueCenter = position + halfExtents;
|
||||||
|
PositionHashKey key(trueCenter);
|
||||||
|
VoxelObject* proxy = _voxels.find(key);
|
||||||
|
if (proxy) {
|
||||||
|
// remove from world
|
||||||
|
assert(proxy->_object);
|
||||||
|
_dynamicsWorld->removeCollisionObject(proxy->_object);
|
||||||
|
|
||||||
|
// release shape
|
||||||
|
ShapeInfo info;
|
||||||
|
info.setBox(halfExtents);
|
||||||
|
bool released = _shapeManager.releaseShape(info);
|
||||||
|
assert(released);
|
||||||
|
|
||||||
|
// delete object and remove from voxel map
|
||||||
|
delete proxy->_object;
|
||||||
|
_voxels.remove(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bullet collision flags are as follows:
|
||||||
|
// CF_STATIC_OBJECT= 1,
|
||||||
|
// CF_KINEMATIC_OBJECT= 2,
|
||||||
|
// CF_NO_CONTACT_RESPONSE = 4,
|
||||||
|
// CF_CUSTOM_MATERIAL_CALLBACK = 8,//this allows per-triangle material (friction/restitution)
|
||||||
|
// CF_CHARACTER_OBJECT = 16,
|
||||||
|
// CF_DISABLE_VISUALIZE_OBJECT = 32, //disable debug drawing
|
||||||
|
// CF_DISABLE_SPU_COLLISION_PROCESSING = 64//disable parallel/SPU processing
|
||||||
|
|
||||||
|
bool PhysicsEngine::addObject(ObjectMotionState* motionState) {
|
||||||
|
assert(motionState);
|
||||||
|
ShapeInfo info;
|
||||||
|
motionState->computeShapeInfo(info);
|
||||||
|
btCollisionShape* shape = _shapeManager.getShape(info);
|
||||||
|
if (shape) {
|
||||||
|
btVector3 inertia(0.0f, 0.0f, 0.0f);
|
||||||
|
float mass = 0.0f;
|
||||||
|
btRigidBody* body = NULL;
|
||||||
|
switch(motionState->computeMotionType()) {
|
||||||
|
case MOTION_TYPE_KINEMATIC: {
|
||||||
|
body = new btRigidBody(mass, motionState, shape, inertia);
|
||||||
|
body->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
|
||||||
|
body->setActivationState(DISABLE_DEACTIVATION);
|
||||||
|
body->updateInertiaTensor();
|
||||||
|
motionState->_body = body;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MOTION_TYPE_DYNAMIC: {
|
||||||
|
mass = motionState->getMass();
|
||||||
|
shape->calculateLocalInertia(mass, inertia);
|
||||||
|
body = new btRigidBody(mass, motionState, shape, inertia);
|
||||||
|
body->updateInertiaTensor();
|
||||||
|
motionState->_body = body;
|
||||||
|
motionState->applyVelocities();
|
||||||
|
motionState->applyGravity();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MOTION_TYPE_STATIC:
|
||||||
|
default: {
|
||||||
|
body = new btRigidBody(mass, motionState, shape, inertia);
|
||||||
|
body->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT);
|
||||||
|
body->updateInertiaTensor();
|
||||||
|
motionState->_body = body;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// wtf?
|
||||||
|
body->setFlags(BT_DISABLE_WORLD_GRAVITY);
|
||||||
|
body->setRestitution(motionState->_restitution);
|
||||||
|
body->setFriction(motionState->_friction);
|
||||||
|
_dynamicsWorld->addRigidBody(body);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PhysicsEngine::removeObject(ObjectMotionState* motionState) {
|
||||||
|
assert(motionState);
|
||||||
|
btRigidBody* body = motionState->_body;
|
||||||
|
if (body) {
|
||||||
|
const btCollisionShape* shape = body->getCollisionShape();
|
||||||
|
ShapeInfo info;
|
||||||
|
ShapeInfoUtil::collectInfoFromShape(shape, info);
|
||||||
|
_dynamicsWorld->removeRigidBody(body);
|
||||||
|
_shapeManager.releaseShape(info);
|
||||||
|
delete body;
|
||||||
|
motionState->_body = NULL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags) {
|
||||||
|
MotionType newType = motionState->computeMotionType();
|
||||||
|
|
||||||
|
// pull body out of physics engine
|
||||||
|
_dynamicsWorld->removeRigidBody(body);
|
||||||
|
|
||||||
|
if (flags & EntityItem::DIRTY_SHAPE) {
|
||||||
|
btCollisionShape* oldShape = body->getCollisionShape();
|
||||||
|
ShapeInfo info;
|
||||||
|
motionState->computeShapeInfo(info);
|
||||||
|
btCollisionShape* newShape = _shapeManager.getShape(info);
|
||||||
|
if (newShape != oldShape) {
|
||||||
|
body->setCollisionShape(newShape);
|
||||||
|
_shapeManager.releaseShape(oldShape);
|
||||||
|
} else {
|
||||||
|
// whoops, shape hasn't changed after all so we must release the reference
|
||||||
|
// that was created when looking it up
|
||||||
|
_shapeManager.releaseShape(newShape);
|
||||||
|
}
|
||||||
|
// MASS bit should be set whenever SHAPE is set
|
||||||
|
assert(flags & EntityItem::DIRTY_MASS);
|
||||||
|
}
|
||||||
|
bool easyUpdate = flags & EASY_DIRTY_PHYSICS_FLAGS;
|
||||||
|
if (easyUpdate) {
|
||||||
|
updateObjectEasy(body, motionState, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the motion parameters
|
||||||
|
switch (newType) {
|
||||||
|
case MOTION_TYPE_KINEMATIC: {
|
||||||
|
int collisionFlags = body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT;
|
||||||
|
collisionFlags &= ~(btCollisionObject::CF_STATIC_OBJECT);
|
||||||
|
body->setCollisionFlags(collisionFlags);
|
||||||
|
body->forceActivationState(DISABLE_DEACTIVATION);
|
||||||
|
|
||||||
|
body->setMassProps(0.0f, btVector3(0.0f, 0.0f, 0.0f));
|
||||||
|
body->updateInertiaTensor();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MOTION_TYPE_DYNAMIC: {
|
||||||
|
int collisionFlags = body->getCollisionFlags() & ~(btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT);
|
||||||
|
body->setCollisionFlags(collisionFlags);
|
||||||
|
if (! (flags & EntityItem::DIRTY_MASS)) {
|
||||||
|
// always update mass properties when going dynamic (unless it's already been done)
|
||||||
|
btVector3 inertia(0.0f, 0.0f, 0.0f);
|
||||||
|
float mass = motionState->getMass();
|
||||||
|
body->getCollisionShape()->calculateLocalInertia(mass, inertia);
|
||||||
|
body->setMassProps(mass, inertia);
|
||||||
|
body->updateInertiaTensor();
|
||||||
|
}
|
||||||
|
body->forceActivationState(ACTIVE_TAG);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// MOTION_TYPE_STATIC
|
||||||
|
int collisionFlags = body->getCollisionFlags() | btCollisionObject::CF_STATIC_OBJECT;
|
||||||
|
collisionFlags &= ~(btCollisionObject::CF_KINEMATIC_OBJECT);
|
||||||
|
body->setCollisionFlags(collisionFlags);
|
||||||
|
body->forceActivationState(DISABLE_SIMULATION);
|
||||||
|
|
||||||
|
body->setMassProps(0.0f, btVector3(0.0f, 0.0f, 0.0f));
|
||||||
|
body->updateInertiaTensor();
|
||||||
|
|
||||||
|
body->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f));
|
||||||
|
body->setAngularVelocity(btVector3(0.0f, 0.0f, 0.0f));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reinsert body into physics engine
|
||||||
|
_dynamicsWorld->addRigidBody(body);
|
||||||
|
|
||||||
|
body->activate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
void PhysicsEngine::updateObjectEasy(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags) {
|
||||||
|
if (flags & EntityItem::DIRTY_POSITION) {
|
||||||
|
btTransform transform;
|
||||||
|
motionState->getWorldTransform(transform);
|
||||||
|
body->setWorldTransform(transform);
|
||||||
|
}
|
||||||
|
if (flags & EntityItem::DIRTY_VELOCITY) {
|
||||||
|
motionState->applyVelocities();
|
||||||
|
motionState->applyGravity();
|
||||||
|
}
|
||||||
|
body->setRestitution(motionState->_restitution);
|
||||||
|
body->setFriction(motionState->_friction);
|
||||||
|
|
||||||
|
if (flags & EntityItem::DIRTY_MASS) {
|
||||||
|
float mass = motionState->getMass();
|
||||||
|
btVector3 inertia(0.0f, 0.0f, 0.0f);
|
||||||
|
body->getCollisionShape()->calculateLocalInertia(mass, inertia);
|
||||||
|
body->setMassProps(mass, inertia);
|
||||||
|
body->updateInertiaTensor();
|
||||||
|
}
|
||||||
|
body->activate();
|
||||||
|
|
||||||
|
// TODO: support collision groups
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
123
libraries/physics/src/PhysicsEngine.h
Normal file
123
libraries/physics/src/PhysicsEngine.h
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
//
|
||||||
|
// PhysicsEngine.h
|
||||||
|
// libraries/physcis/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2014.10.29
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_PhysicsEngine_h
|
||||||
|
#define hifi_PhysicsEngine_h
|
||||||
|
|
||||||
|
typedef unsigned int uint32_t;
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
|
||||||
|
#include <QSet>
|
||||||
|
#include <btBulletDynamicsCommon.h>
|
||||||
|
|
||||||
|
#include <EntityItem.h>
|
||||||
|
#include <EntitySimulation.h>
|
||||||
|
|
||||||
|
#include "BulletUtil.h"
|
||||||
|
#include "EntityMotionState.h"
|
||||||
|
#include "PositionHashKey.h"
|
||||||
|
#include "ShapeManager.h"
|
||||||
|
#include "ThreadSafeDynamicsWorld.h"
|
||||||
|
#include "VoxelObject.h"
|
||||||
|
|
||||||
|
const float HALF_SIMULATION_EXTENT = 512.0f; // meters
|
||||||
|
|
||||||
|
class PhysicsEngine : public EntitySimulation {
|
||||||
|
public:
|
||||||
|
|
||||||
|
PhysicsEngine(const glm::vec3& offset);
|
||||||
|
|
||||||
|
~PhysicsEngine();
|
||||||
|
|
||||||
|
// overrides for EntitySimulation
|
||||||
|
void updateEntitiesInternal(const quint64& now);
|
||||||
|
void addEntityInternal(EntityItem* entity);
|
||||||
|
void removeEntityInternal(EntityItem* entity);
|
||||||
|
void entityChangedInternal(EntityItem* entity);
|
||||||
|
void sortEntitiesThatMovedInternal();
|
||||||
|
void clearEntitiesInternal();
|
||||||
|
|
||||||
|
virtual void init(EntityEditPacketSender* packetSender);
|
||||||
|
|
||||||
|
void stepSimulation();
|
||||||
|
|
||||||
|
/// \param offset position of simulation origin in domain-frame
|
||||||
|
void setOriginOffset(const glm::vec3& offset) { _originOffset = offset; }
|
||||||
|
|
||||||
|
/// \return position of simulation origin in domain-frame
|
||||||
|
const glm::vec3& getOriginOffset() const { return _originOffset; }
|
||||||
|
|
||||||
|
/// \param position the minimum corner of the voxel
|
||||||
|
/// \param scale the length of the voxel side
|
||||||
|
/// \return true if Voxel added
|
||||||
|
bool addVoxel(const glm::vec3& position, float scale);
|
||||||
|
|
||||||
|
/// \param position the minimum corner of the voxel
|
||||||
|
/// \param scale the length of the voxel side
|
||||||
|
/// \return true if Voxel removed
|
||||||
|
bool removeVoxel(const glm::vec3& position, float scale);
|
||||||
|
|
||||||
|
/// \param motionState pointer to Object's MotionState
|
||||||
|
/// \return true if Object added
|
||||||
|
bool addObject(ObjectMotionState* motionState);
|
||||||
|
|
||||||
|
/// \param motionState pointer to Object's MotionState
|
||||||
|
/// \return true if Object removed
|
||||||
|
bool removeObject(ObjectMotionState* motionState);
|
||||||
|
|
||||||
|
/// process queue of changed from external sources
|
||||||
|
void relayIncomingChangesToSimulation();
|
||||||
|
|
||||||
|
/// \return duration of fixed simulation substep
|
||||||
|
float getFixedSubStep() const;
|
||||||
|
|
||||||
|
/// \return number of simulation frames the physics engine has taken
|
||||||
|
uint32_t getFrameCount() const { return _frameCount; }
|
||||||
|
|
||||||
|
/// \return substep remainder used for Bullet MotionState extrapolation
|
||||||
|
// Bullet will extrapolate the positions provided to MotionState::setWorldTransform() in an effort to provide
|
||||||
|
// smoother visible motion when the render frame rate does not match that of the simulation loop. We provide
|
||||||
|
// access to this fraction for improved filtering of update packets to interested parties.
|
||||||
|
float getSubStepRemainder() { return _dynamicsWorld->getLocalTimeAccumulation(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags);
|
||||||
|
void updateObjectEasy(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags);
|
||||||
|
|
||||||
|
btClock _clock;
|
||||||
|
btDefaultCollisionConfiguration* _collisionConfig;
|
||||||
|
btCollisionDispatcher* _collisionDispatcher;
|
||||||
|
btBroadphaseInterface* _broadphaseFilter;
|
||||||
|
btSequentialImpulseConstraintSolver* _constraintSolver;
|
||||||
|
ThreadSafeDynamicsWorld* _dynamicsWorld;
|
||||||
|
ShapeManager _shapeManager;
|
||||||
|
|
||||||
|
private:
|
||||||
|
glm::vec3 _originOffset;
|
||||||
|
btHashMap<PositionHashKey, VoxelObject> _voxels;
|
||||||
|
|
||||||
|
// EntitySimulation stuff
|
||||||
|
QSet<EntityMotionState*> _entityMotionStates; // all entities that we track
|
||||||
|
QSet<ObjectMotionState*> _incomingChanges; // entities with pending physics changes by script or packet
|
||||||
|
QSet<ObjectMotionState*> _outgoingPackets; // MotionStates with pending changes that need to be sent over wire
|
||||||
|
|
||||||
|
EntityEditPacketSender* _entityPacketSender;
|
||||||
|
|
||||||
|
uint32_t _frameCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
#else // USE_BULLET_PHYSICS
|
||||||
|
// PhysicsEngine stubbery until Bullet is required
|
||||||
|
class PhysicsEngine {
|
||||||
|
};
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
|
#endif // hifi_PhysicsEngine_h
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// PhysicsEntity.cpp
|
// PhysicsEntity.cpp
|
||||||
// libraries/shared/src
|
// libraries/physics/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows 2014.06.11
|
// Created by Andrew Meadows 2014.06.11
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// PhysicsEntity.h
|
// PhysicsEntity.h
|
||||||
// libraries/shared/src
|
// libraries/physics/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows 2014.05.30
|
// Created by Andrew Meadows 2014.05.30
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
@ -18,8 +18,12 @@
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
#include "CollisionInfo.h"
|
#include <CollisionInfo.h>
|
||||||
#include "RayIntersectionInfo.h"
|
#include <RayIntersectionInfo.h>
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
#include "PhysicsEngine.h"
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
|
|
||||||
class Shape;
|
class Shape;
|
||||||
class PhysicsSimulation;
|
class PhysicsSimulation;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// PhysicsSimulation.cpp
|
// PhysicsSimulation.cpp
|
||||||
// interface/src/avatar
|
// libraries/physcis/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows 2014.06.06
|
// Created by Andrew Meadows 2014.06.06
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// PhysicsSimulation.h
|
// PhysicsSimulation.h
|
||||||
// interface/src/avatar
|
// libraries/physcis/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows 2014.06.06
|
// Created by Andrew Meadows 2014.06.06
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
37
libraries/physics/src/PositionHashKey.cpp
Normal file
37
libraries/physics/src/PositionHashKey.cpp
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
//
|
||||||
|
// PositionHashKey.cpp
|
||||||
|
// libraries/physcis/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2014.11.05
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "PositionHashKey.h"
|
||||||
|
|
||||||
|
// static
|
||||||
|
int computeHash(const glm::vec3& center) {
|
||||||
|
// NOTE: 0.49f is used to bump the float up almost half a millimeter
|
||||||
|
// so the cast to int produces a round() effect rather than a floor()
|
||||||
|
int hash = DoubleHashKey::hashFunction((int)(center.x * MILLIMETERS_PER_METER + copysignf(1.0f, center.x) * 0.49f), 0);
|
||||||
|
hash ^= DoubleHashKey::hashFunction((int)(center.y * MILLIMETERS_PER_METER + copysignf(1.0f, center.y) * 0.49f), 1);
|
||||||
|
return hash ^ DoubleHashKey::hashFunction((int)(center.z * MILLIMETERS_PER_METER + copysignf(1.0f, center.z) * 0.49f), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
int computeHash2(const glm::vec3& center) {
|
||||||
|
// NOTE: 0.49f is used to bump the float up almost half a millimeter
|
||||||
|
// so the cast to int produces a round() effect rather than a floor()
|
||||||
|
int hash = DoubleHashKey::hashFunction2((int)(center.x * MILLIMETERS_PER_METER + copysignf(1.0f, center.x) * 0.49f));
|
||||||
|
hash ^= DoubleHashKey::hashFunction2((int)(center.y * MILLIMETERS_PER_METER + copysignf(1.0f, center.y) * 0.49f));
|
||||||
|
return hash ^ DoubleHashKey::hashFunction2((int)(center.z * MILLIMETERS_PER_METER + copysignf(1.0f, center.z) * 0.49f));
|
||||||
|
}
|
||||||
|
|
||||||
|
PositionHashKey::PositionHashKey(glm::vec3 center) : DoubleHashKey() {
|
||||||
|
_hash = computeHash(center);
|
||||||
|
_hash2 = computeHash2(center);
|
||||||
|
}
|
26
libraries/physics/src/PositionHashKey.h
Normal file
26
libraries/physics/src/PositionHashKey.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
//
|
||||||
|
// PositionHashKey.h
|
||||||
|
// libraries/physcis/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2014.11.05
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_PositionHashKey_h
|
||||||
|
#define hifi_PositionHashKey_h
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
#include <SharedUtil.h>
|
||||||
|
|
||||||
|
#include "DoubleHashKey.h"
|
||||||
|
|
||||||
|
class PositionHashKey : public DoubleHashKey {
|
||||||
|
public:
|
||||||
|
PositionHashKey(glm::vec3 center);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_PositionHashKey_h
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Ragdoll.cpp
|
// Ragdoll.cpp
|
||||||
// libraries/shared/src
|
// libraries/physics/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows 2014.05.30
|
// Created by Andrew Meadows 2014.05.30
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Ragdoll.h
|
// Ragdoll.h
|
||||||
// libraries/shared/src
|
// libraries/physics/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows 2014.05.30
|
// Created by Andrew Meadows 2014.05.30
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
172
libraries/physics/src/ShapeInfoUtil.cpp
Normal file
172
libraries/physics/src/ShapeInfoUtil.cpp
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
//
|
||||||
|
// ShapeInfoUtil.cpp
|
||||||
|
// libraries/physcis/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2014.12.01
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <Shape.h> // for FOO_SHAPE types
|
||||||
|
#include <SharedUtil.h> // for MILLIMETERS_PER_METER
|
||||||
|
|
||||||
|
#include "ShapeInfoUtil.h"
|
||||||
|
#include "BulletUtil.h"
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
|
||||||
|
|
||||||
|
int ShapeInfoUtil::toBulletShapeType(int shapeInfoType) {
|
||||||
|
int bulletShapeType = INVALID_SHAPE_PROXYTYPE;
|
||||||
|
switch(shapeInfoType) {
|
||||||
|
case BOX_SHAPE:
|
||||||
|
bulletShapeType = BOX_SHAPE_PROXYTYPE;
|
||||||
|
break;
|
||||||
|
case SPHERE_SHAPE:
|
||||||
|
bulletShapeType = SPHERE_SHAPE_PROXYTYPE;
|
||||||
|
break;
|
||||||
|
case CAPSULE_SHAPE:
|
||||||
|
bulletShapeType = CAPSULE_SHAPE_PROXYTYPE;
|
||||||
|
break;
|
||||||
|
case CYLINDER_SHAPE:
|
||||||
|
bulletShapeType = CYLINDER_SHAPE_PROXYTYPE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return bulletShapeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ShapeInfoUtil::fromBulletShapeType(int bulletShapeType) {
|
||||||
|
int shapeInfoType = INVALID_SHAPE;
|
||||||
|
switch(bulletShapeType) {
|
||||||
|
case BOX_SHAPE_PROXYTYPE:
|
||||||
|
shapeInfoType = BOX_SHAPE;
|
||||||
|
break;
|
||||||
|
case SPHERE_SHAPE_PROXYTYPE:
|
||||||
|
shapeInfoType = SPHERE_SHAPE;
|
||||||
|
break;
|
||||||
|
case CAPSULE_SHAPE_PROXYTYPE:
|
||||||
|
shapeInfoType = CAPSULE_SHAPE;
|
||||||
|
break;
|
||||||
|
case CYLINDER_SHAPE_PROXYTYPE:
|
||||||
|
shapeInfoType = CYLINDER_SHAPE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return shapeInfoType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShapeInfoUtil::collectInfoFromShape(const btCollisionShape* shape, ShapeInfo& info) {
|
||||||
|
if (shape) {
|
||||||
|
int type = ShapeInfoUtil::fromBulletShapeType(shape->getShapeType());
|
||||||
|
switch(type) {
|
||||||
|
case BOX_SHAPE: {
|
||||||
|
const btBoxShape* boxShape = static_cast<const btBoxShape*>(shape);
|
||||||
|
info.setBox(bulletToGLM(boxShape->getHalfExtentsWithMargin()));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SPHERE_SHAPE: {
|
||||||
|
const btSphereShape* sphereShape = static_cast<const btSphereShape*>(shape);
|
||||||
|
info.setSphere(sphereShape->getRadius());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CYLINDER_SHAPE: {
|
||||||
|
// NOTE: we only support cylinders along yAxis
|
||||||
|
const btCylinderShape* cylinderShape = static_cast<const btCylinderShape*>(shape);
|
||||||
|
btVector3 halfExtents = cylinderShape->getHalfExtentsWithMargin();
|
||||||
|
info.setCylinder(halfExtents.getX(), halfExtents.getY());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CAPSULE_SHAPE: {
|
||||||
|
// NOTE: we only support capsules along yAxis
|
||||||
|
const btCapsuleShape* capsuleShape = static_cast<const btCapsuleShape*>(shape);
|
||||||
|
info.setCapsule(capsuleShape->getRadius(), capsuleShape->getHalfHeight());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
info.clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
btCollisionShape* ShapeInfoUtil::createShapeFromInfo(const ShapeInfo& info) {
|
||||||
|
btCollisionShape* shape = NULL;
|
||||||
|
const QVector<glm::vec3>& data = info.getData();
|
||||||
|
switch(info.getType()) {
|
||||||
|
case BOX_SHAPE: {
|
||||||
|
// data[0] is halfExtents
|
||||||
|
shape = new btBoxShape(glmToBullet(data[0]));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SPHERE_SHAPE: {
|
||||||
|
float radius = data[0].z;
|
||||||
|
shape = new btSphereShape(radius);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CYLINDER_SHAPE: {
|
||||||
|
// NOTE: default cylinder has (UpAxis = 1) axis along yAxis and radius stored in X
|
||||||
|
// data[0] = btVector3(radius, halfHeight, unused)
|
||||||
|
shape = new btCylinderShape(glmToBullet(data[0]));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CAPSULE_SHAPE: {
|
||||||
|
float radius = data[0].x;
|
||||||
|
float height = 2.0f * data[0].y;
|
||||||
|
shape = new btCapsuleShape(radius, height);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
DoubleHashKey ShapeInfoUtil::computeHash(const ShapeInfo& info) {
|
||||||
|
DoubleHashKey key;
|
||||||
|
// compute hash
|
||||||
|
// scramble the bits of the type
|
||||||
|
// TODO?: provide lookup table for hash of info._type rather than recompute?
|
||||||
|
int primeIndex = 0;
|
||||||
|
unsigned int hash = DoubleHashKey::hashFunction((unsigned int)info.getType(), primeIndex++);
|
||||||
|
const QVector<glm::vec3>& data = info.getData();
|
||||||
|
|
||||||
|
glm::vec3 tmpData;
|
||||||
|
int numData = data.size();
|
||||||
|
for (int i = 0; i < numData; ++i) {
|
||||||
|
tmpData = data[i];
|
||||||
|
for (int j = 0; j < 3; ++j) {
|
||||||
|
// NOTE: 0.49f is used to bump the float up almost half a millimeter
|
||||||
|
// so the cast to int produces a round() effect rather than a floor()
|
||||||
|
unsigned int floatHash =
|
||||||
|
DoubleHashKey::hashFunction((int)(tmpData[j] * MILLIMETERS_PER_METER + copysignf(1.0f, tmpData[j]) * 0.49f), primeIndex++);
|
||||||
|
hash ^= floatHash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
key._hash = (int)hash;
|
||||||
|
|
||||||
|
// compute hash2
|
||||||
|
// scramble the bits of the type
|
||||||
|
// TODO?: provide lookup table for hash2 of info._type rather than recompute?
|
||||||
|
hash = DoubleHashKey::hashFunction2((unsigned int)info.getType());
|
||||||
|
|
||||||
|
for (int i = 0; i < numData; ++i) {
|
||||||
|
tmpData = data[i];
|
||||||
|
for (int j = 0; j < 3; ++j) {
|
||||||
|
// NOTE: 0.49f is used to bump the float up almost half a millimeter
|
||||||
|
// so the cast to int produces a round() effect rather than a floor()
|
||||||
|
unsigned int floatHash =
|
||||||
|
DoubleHashKey::hashFunction2((int)(tmpData[j] * MILLIMETERS_PER_METER + copysignf(1.0f, tmpData[j]) * 0.49f));
|
||||||
|
hash += ~(floatHash << 17);
|
||||||
|
hash ^= (floatHash >> 11);
|
||||||
|
hash += (floatHash << 4);
|
||||||
|
hash ^= (floatHash >> 7);
|
||||||
|
hash += ~(floatHash << 10);
|
||||||
|
hash = (hash << 16) | (hash >> 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
key._hash2 = (int)hash;
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
39
libraries/physics/src/ShapeInfoUtil.h
Normal file
39
libraries/physics/src/ShapeInfoUtil.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
//
|
||||||
|
// ShapeInfoUtil.h
|
||||||
|
// libraries/physcis/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2014.12.01
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_ShapeInfoUtil_h
|
||||||
|
#define hifi_ShapeInfoUtil_h
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
|
||||||
|
#include <btBulletDynamicsCommon.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
#include <ShapeInfo.h>
|
||||||
|
|
||||||
|
#include "DoubleHashKey.h"
|
||||||
|
|
||||||
|
// translates between ShapeInfo and btShape
|
||||||
|
|
||||||
|
namespace ShapeInfoUtil {
|
||||||
|
void collectInfoFromShape(const btCollisionShape* shape, ShapeInfo& info);
|
||||||
|
|
||||||
|
btCollisionShape* createShapeFromInfo(const ShapeInfo& info);
|
||||||
|
|
||||||
|
DoubleHashKey computeHash(const ShapeInfo& info);
|
||||||
|
|
||||||
|
// TODO? just use bullet shape types everywhere?
|
||||||
|
int toBulletShapeType(int shapeInfoType);
|
||||||
|
int fromBulletShapeType(int bulletShapeType);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
|
#endif // hifi_ShapeInfoUtil_h
|
109
libraries/physics/src/ShapeManager.cpp
Normal file
109
libraries/physics/src/ShapeManager.cpp
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
//
|
||||||
|
// ShapeManager.cpp
|
||||||
|
// libraries/physcis/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2014.10.29
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
|
||||||
|
#include <glm/gtx/norm.hpp>
|
||||||
|
|
||||||
|
#include "ShapeInfoUtil.h"
|
||||||
|
#include "ShapeManager.h"
|
||||||
|
|
||||||
|
ShapeManager::ShapeManager() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ShapeManager::~ShapeManager() {
|
||||||
|
int numShapes = _shapeMap.size();
|
||||||
|
for (int i = 0; i < numShapes; ++i) {
|
||||||
|
ShapeReference* shapeRef = _shapeMap.getAtIndex(i);
|
||||||
|
delete shapeRef->_shape;
|
||||||
|
}
|
||||||
|
_shapeMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
||||||
|
// Very small or large objects are not supported.
|
||||||
|
float diagonal = glm::length2(info.getBoundingBoxDiagonal());
|
||||||
|
const float MIN_SHAPE_DIAGONAL_SQUARED = 3.0e-4f; // 1 cm cube
|
||||||
|
const float MAX_SHAPE_DIAGONAL_SQUARED = 3.0e4f; // 100 m cube
|
||||||
|
if (diagonal < MIN_SHAPE_DIAGONAL_SQUARED || diagonal > MAX_SHAPE_DIAGONAL_SQUARED) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
DoubleHashKey key = ShapeInfoUtil::computeHash(info);
|
||||||
|
ShapeReference* shapeRef = _shapeMap.find(key);
|
||||||
|
if (shapeRef) {
|
||||||
|
shapeRef->_refCount++;
|
||||||
|
return shapeRef->_shape;
|
||||||
|
}
|
||||||
|
btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info);
|
||||||
|
if (shape) {
|
||||||
|
ShapeReference newRef;
|
||||||
|
newRef._refCount = 1;
|
||||||
|
newRef._shape = shape;
|
||||||
|
_shapeMap.insert(key, newRef);
|
||||||
|
}
|
||||||
|
return shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShapeManager::releaseShape(const ShapeInfo& info) {
|
||||||
|
DoubleHashKey key = ShapeInfoUtil::computeHash(info);
|
||||||
|
ShapeReference* shapeRef = _shapeMap.find(key);
|
||||||
|
if (shapeRef) {
|
||||||
|
if (shapeRef->_refCount > 0) {
|
||||||
|
shapeRef->_refCount--;
|
||||||
|
if (shapeRef->_refCount == 0) {
|
||||||
|
_pendingGarbage.push_back(key);
|
||||||
|
const int MAX_GARBAGE_CAPACITY = 127;
|
||||||
|
if (_pendingGarbage.size() > MAX_GARBAGE_CAPACITY) {
|
||||||
|
collectGarbage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// attempt to remove shape that has no refs
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// attempt to remove unmanaged shape
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShapeManager::releaseShape(const btCollisionShape* shape) {
|
||||||
|
ShapeInfo info;
|
||||||
|
ShapeInfoUtil::collectInfoFromShape(shape, info);
|
||||||
|
return releaseShape(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShapeManager::collectGarbage() {
|
||||||
|
int numShapes = _pendingGarbage.size();
|
||||||
|
for (int i = 0; i < numShapes; ++i) {
|
||||||
|
DoubleHashKey& key = _pendingGarbage[i];
|
||||||
|
ShapeReference* shapeRef = _shapeMap.find(key);
|
||||||
|
if (shapeRef && shapeRef->_refCount == 0) {
|
||||||
|
delete shapeRef->_shape;
|
||||||
|
_shapeMap.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_pendingGarbage.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ShapeManager::getNumReferences(const ShapeInfo& info) const {
|
||||||
|
DoubleHashKey key = ShapeInfoUtil::computeHash(info);
|
||||||
|
const ShapeReference* shapeRef = _shapeMap.find(key);
|
||||||
|
if (shapeRef) {
|
||||||
|
return shapeRef->_refCount;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
56
libraries/physics/src/ShapeManager.h
Normal file
56
libraries/physics/src/ShapeManager.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
//
|
||||||
|
// ShapeManager.h
|
||||||
|
// libraries/physcis/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2014.10.29
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_ShapeManager_h
|
||||||
|
#define hifi_ShapeManager_h
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
|
||||||
|
#include <btBulletDynamicsCommon.h>
|
||||||
|
#include <LinearMath/btHashMap.h>
|
||||||
|
|
||||||
|
#include <ShapeInfo.h>
|
||||||
|
|
||||||
|
#include "DoubleHashKey.h"
|
||||||
|
|
||||||
|
class ShapeManager {
|
||||||
|
public:
|
||||||
|
|
||||||
|
ShapeManager();
|
||||||
|
~ShapeManager();
|
||||||
|
|
||||||
|
/// \return pointer to shape
|
||||||
|
btCollisionShape* getShape(const ShapeInfo& info);
|
||||||
|
|
||||||
|
/// \return true if shape was found and released
|
||||||
|
bool releaseShape(const ShapeInfo& info);
|
||||||
|
bool releaseShape(const btCollisionShape* shape);
|
||||||
|
|
||||||
|
/// delete shapes that have zero references
|
||||||
|
void collectGarbage();
|
||||||
|
|
||||||
|
// validation methods
|
||||||
|
int getNumShapes() const { return _shapeMap.size(); }
|
||||||
|
int getNumReferences(const ShapeInfo& info) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct ShapeReference {
|
||||||
|
int _refCount;
|
||||||
|
btCollisionShape* _shape;
|
||||||
|
ShapeReference() : _refCount(0), _shape(NULL) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
btHashMap<DoubleHashKey, ShapeReference> _shapeMap;
|
||||||
|
btAlignedObjectArray<DoubleHashKey> _pendingGarbage;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
|
#endif // hifi_ShapeManager_h
|
91
libraries/physics/src/ThreadSafeDynamicsWorld.cpp
Normal file
91
libraries/physics/src/ThreadSafeDynamicsWorld.cpp
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* Bullet Continuous Collision Detection and Physics Library
|
||||||
|
* Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.org
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied warranty.
|
||||||
|
* In no event will the authors be held liable for any damages arising from the use of this software.
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it freely,
|
||||||
|
* subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||||
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
*
|
||||||
|
* Copied and modified from btDiscreteDynamicsWorld.cpp by AndrewMeadows on 2014.11.12.
|
||||||
|
* */
|
||||||
|
|
||||||
|
#include <EntityTree.h>
|
||||||
|
|
||||||
|
#include "ThreadSafeDynamicsWorld.h"
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld(
|
||||||
|
btDispatcher* dispatcher,
|
||||||
|
btBroadphaseInterface* pairCache,
|
||||||
|
btConstraintSolver* constraintSolver,
|
||||||
|
btCollisionConfiguration* collisionConfiguration,
|
||||||
|
EntityTree* entities)
|
||||||
|
: btDiscreteDynamicsWorld(dispatcher, pairCache, constraintSolver, collisionConfiguration) {
|
||||||
|
assert(entities);
|
||||||
|
_entities = entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadSafeDynamicsWorld::synchronizeMotionStates() {
|
||||||
|
_entities->lockForWrite();
|
||||||
|
btDiscreteDynamicsWorld::synchronizeMotionStates();
|
||||||
|
_entities->unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep) {
|
||||||
|
int subSteps = 0;
|
||||||
|
if (maxSubSteps) {
|
||||||
|
//fixed timestep with interpolation
|
||||||
|
m_fixedTimeStep = fixedTimeStep;
|
||||||
|
m_localTime += timeStep;
|
||||||
|
if (m_localTime >= fixedTimeStep)
|
||||||
|
{
|
||||||
|
subSteps = int( m_localTime / fixedTimeStep);
|
||||||
|
m_localTime -= subSteps * fixedTimeStep;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//variable timestep
|
||||||
|
fixedTimeStep = timeStep;
|
||||||
|
m_localTime = m_latencyMotionStateInterpolation ? 0 : timeStep;
|
||||||
|
m_fixedTimeStep = 0;
|
||||||
|
if (btFuzzyZero(timeStep))
|
||||||
|
{
|
||||||
|
subSteps = 0;
|
||||||
|
maxSubSteps = 0;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
subSteps = 1;
|
||||||
|
maxSubSteps = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//process some debugging flags
|
||||||
|
if (getDebugDrawer()) {
|
||||||
|
btIDebugDraw* debugDrawer = getDebugDrawer ();
|
||||||
|
gDisableDeactivation = (debugDrawer->getDebugMode() & btIDebugDraw::DBG_NoDeactivation) != 0;
|
||||||
|
}*/
|
||||||
|
if (subSteps) {
|
||||||
|
//clamp the number of substeps, to prevent simulation grinding spiralling down to a halt
|
||||||
|
int clampedSimulationSteps = (subSteps > maxSubSteps)? maxSubSteps : subSteps;
|
||||||
|
|
||||||
|
saveKinematicState(fixedTimeStep*clampedSimulationSteps);
|
||||||
|
|
||||||
|
applyGravity();
|
||||||
|
|
||||||
|
for (int i=0;i<clampedSimulationSteps;i++) {
|
||||||
|
internalSingleStepSimulation(fixedTimeStep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only sync motion states once at the end of all substeps.
|
||||||
|
// This is to avoid placing multiple, repeated thread locks on _entities.
|
||||||
|
synchronizeMotionStates();
|
||||||
|
clearForces();
|
||||||
|
return subSteps;
|
||||||
|
}
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
59
libraries/physics/src/ThreadSafeDynamicsWorld.h
Normal file
59
libraries/physics/src/ThreadSafeDynamicsWorld.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* Bullet Continuous Collision Detection and Physics Library
|
||||||
|
* Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.org
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied warranty.
|
||||||
|
* In no event will the authors be held liable for any damages arising from the use of this software.
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it freely,
|
||||||
|
* subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||||
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
*
|
||||||
|
* Copied and modified from btDiscreteDynamicsWorld.h by AndrewMeadows on 2014.11.12.
|
||||||
|
* */
|
||||||
|
|
||||||
|
#ifndef hifi_ThreadSafeDynamicsWorld_h
|
||||||
|
#define hifi_ThreadSafeDynamicsWorld_h
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
#include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
|
||||||
|
|
||||||
|
class EntityTree;
|
||||||
|
|
||||||
|
ATTRIBUTE_ALIGNED16(class) ThreadSafeDynamicsWorld : public btDiscreteDynamicsWorld {
|
||||||
|
public:
|
||||||
|
BT_DECLARE_ALIGNED_ALLOCATOR();
|
||||||
|
|
||||||
|
ThreadSafeDynamicsWorld(
|
||||||
|
btDispatcher* dispatcher,
|
||||||
|
btBroadphaseInterface* pairCache,
|
||||||
|
btConstraintSolver* constraintSolver,
|
||||||
|
btCollisionConfiguration* collisionConfiguration,
|
||||||
|
EntityTree* entities);
|
||||||
|
|
||||||
|
// virtual overrides from btDiscreteDynamicsWorld
|
||||||
|
int stepSimulation( btScalar timeStep, int maxSubSteps=1, btScalar fixedTimeStep=btScalar(1.)/btScalar(60.));
|
||||||
|
void synchronizeMotionStates();
|
||||||
|
|
||||||
|
// btDiscreteDynamicsWorld::m_localTime is the portion of real-time that has not yet been simulated
|
||||||
|
// but is used for MotionState::setWorldTransform() extrapolation (a feature that Bullet uses to provide
|
||||||
|
// smoother rendering of objects when the physics simulation loop is ansynchronous to the render loop).
|
||||||
|
float getLocalTimeAccumulation() const { return m_localTime; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
EntityTree* _entities;
|
||||||
|
};
|
||||||
|
|
||||||
|
#else // USE_BULLET_PHYSICS
|
||||||
|
// stubbery for ThreadSafeDynamicsWorld when Bullet not available
|
||||||
|
class ThreadSafeDynamicsWorld {
|
||||||
|
public:
|
||||||
|
ThreadSafeDynamicsWorld() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
|
|
||||||
|
#endif // hifi_ThreadSafeDynamicsWorld_h
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// VerletCapsuleShape.cpp
|
// VerletCapsuleShape.cpp
|
||||||
// libraries/shared/src
|
// libraries/physics/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows on 2014.06.16
|
// Created by Andrew Meadows on 2014.06.16
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// VerletCapsuleShape.h
|
// VerletCapsuleShape.h
|
||||||
// libraries/shared/src
|
// libraries/physics/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows on 2014.06.16
|
// Created by Andrew Meadows on 2014.06.16
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// VerletPoint.cpp
|
// VerletPoint.cpp
|
||||||
// libraries/shared/src
|
// libraries/physics/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows 2014.07.24
|
// Created by Andrew Meadows 2014.07.24
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// VerletPoint.h
|
// VerletPoint.h
|
||||||
// libraries/shared/src
|
// libraries/physics/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows 2014.07.24
|
// Created by Andrew Meadows 2014.07.24
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// VerletSphereShape.cpp
|
// VerletSphereShape.cpp
|
||||||
// libraries/shared/src
|
// libraries/physics/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows on 2014.06.16
|
// Created by Andrew Meadows on 2014.06.16
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// VerletSphereShape.h
|
// VerletSphereShape.h
|
||||||
// libraries/shared/src
|
// libraries/physics/src
|
||||||
//
|
//
|
||||||
// Created by Andrew Meadows on 2014.06.16
|
// Created by Andrew Meadows on 2014.06.16
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
|
31
libraries/physics/src/VoxelObject.h
Normal file
31
libraries/physics/src/VoxelObject.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
//
|
||||||
|
// VoxelObject.h
|
||||||
|
// libraries/physcis/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2014.11.05
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_VoxelObject_h
|
||||||
|
#define hifi_VoxelObject_h
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
|
||||||
|
#include <btBulletDynamicsCommon.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
// VoxelObject is a simple wrapper for tracking a Voxel in a PhysicsEngine
|
||||||
|
class VoxelObject {
|
||||||
|
public:
|
||||||
|
VoxelObject(const glm::vec3& center, btCollisionObject* object) : _object(object), _center(center) {
|
||||||
|
assert(object != NULL);
|
||||||
|
}
|
||||||
|
btCollisionObject* _object;
|
||||||
|
glm::vec3 _center;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
|
#endif // hifi_VoxelObject_h
|
|
@ -25,24 +25,37 @@
|
||||||
#include "RenderUtil.h"
|
#include "RenderUtil.h"
|
||||||
#include "TextureCache.h"
|
#include "TextureCache.h"
|
||||||
|
|
||||||
|
#include "simple_vert.h"
|
||||||
|
#include "simple_frag.h"
|
||||||
|
|
||||||
|
#include "deferred_light_vert.h"
|
||||||
|
#include "deferred_light_limited_vert.h"
|
||||||
|
|
||||||
|
#include "directional_light_frag.h"
|
||||||
|
#include "directional_light_shadow_map_frag.h"
|
||||||
|
#include "directional_light_cascaded_shadow_map_frag.h"
|
||||||
|
|
||||||
|
#include "point_light_frag.h"
|
||||||
|
#include "spot_light_frag.h"
|
||||||
|
|
||||||
|
|
||||||
void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) {
|
void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) {
|
||||||
_viewState = viewState;
|
_viewState = viewState;
|
||||||
_simpleProgram.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + "shaders/simple.vert");
|
_simpleProgram.addShaderFromSourceCode(QGLShader::Vertex, simple_vert);
|
||||||
_simpleProgram.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + "shaders/simple.frag");
|
_simpleProgram.addShaderFromSourceCode(QGLShader::Fragment, simple_frag);
|
||||||
_simpleProgram.link();
|
_simpleProgram.link();
|
||||||
|
|
||||||
_simpleProgram.bind();
|
_simpleProgram.bind();
|
||||||
_glowIntensityLocation = _simpleProgram.uniformLocation("glowIntensity");
|
_glowIntensityLocation = _simpleProgram.uniformLocation("glowIntensity");
|
||||||
_simpleProgram.release();
|
_simpleProgram.release();
|
||||||
|
|
||||||
loadLightProgram("shaders/directional_light.frag", false, _directionalLight, _directionalLightLocations);
|
loadLightProgram(directional_light_frag, false, _directionalLight, _directionalLightLocations);
|
||||||
loadLightProgram("shaders/directional_light_shadow_map.frag", false, _directionalLightShadowMap,
|
loadLightProgram(directional_light_shadow_map_frag, false, _directionalLightShadowMap,
|
||||||
_directionalLightShadowMapLocations);
|
_directionalLightShadowMapLocations);
|
||||||
loadLightProgram("shaders/directional_light_cascaded_shadow_map.frag", false, _directionalLightCascadedShadowMap,
|
loadLightProgram(directional_light_cascaded_shadow_map_frag, false, _directionalLightCascadedShadowMap,
|
||||||
_directionalLightCascadedShadowMapLocations);
|
_directionalLightCascadedShadowMapLocations);
|
||||||
loadLightProgram("shaders/point_light.frag", true, _pointLight, _pointLightLocations);
|
loadLightProgram(point_light_frag, true, _pointLight, _pointLightLocations);
|
||||||
loadLightProgram("shaders/spot_light.frag", true, _spotLight, _spotLightLocations);
|
loadLightProgram(spot_light_frag, true, _spotLight, _spotLightLocations);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeferredLightingEffect::bindSimpleProgram() {
|
void DeferredLightingEffect::bindSimpleProgram() {
|
||||||
|
@ -400,10 +413,9 @@ void DeferredLightingEffect::render() {
|
||||||
_postLightingRenderables.clear();
|
_postLightingRenderables.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeferredLightingEffect::loadLightProgram(const char* name, bool limited, ProgramObject& program, LightLocations& locations) {
|
void DeferredLightingEffect::loadLightProgram(const char* fragSource, bool limited, ProgramObject& program, LightLocations& locations) {
|
||||||
program.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() +
|
program.addShaderFromSourceCode(QGLShader::Vertex, (limited ? deferred_light_limited_vert : deferred_light_vert));
|
||||||
(limited ? "shaders/deferred_light_limited.vert" : "shaders/deferred_light.vert"));
|
program.addShaderFromSourceCode(QGLShader::Fragment, fragSource);
|
||||||
program.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + name);
|
|
||||||
program.link();
|
program.link();
|
||||||
|
|
||||||
program.bind();
|
program.bind();
|
||||||
|
|
|
@ -86,7 +86,7 @@ private:
|
||||||
int radius;
|
int radius;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void loadLightProgram(const char* name, bool limited, ProgramObject& program, LightLocations& locations);
|
static void loadLightProgram(const char* fragSource, bool limited, ProgramObject& program, LightLocations& locations);
|
||||||
|
|
||||||
ProgramObject _simpleProgram;
|
ProgramObject _simpleProgram;
|
||||||
int _glowIntensityLocation;
|
int _glowIntensityLocation;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#version 120
|
<@include Config.slh@>
|
||||||
|
<$VERSION_HEADER$>
|
||||||
|
// Generated on <$_SCRIBE_DATE$>
|
||||||
//
|
//
|
||||||
// deferred_light.vert
|
// deferred_light.vert
|
||||||
// vertex shader
|
// vertex shader
|
|
@ -1,5 +1,6 @@
|
||||||
#version 120
|
<@include Config.slh@>
|
||||||
|
<$VERSION_HEADER$>
|
||||||
|
// Generated on <$_SCRIBE_DATE$>
|
||||||
//
|
//
|
||||||
// deferred_light_limited.vert
|
// deferred_light_limited.vert
|
||||||
// vertex shader
|
// vertex shader
|
|
@ -1,5 +1,6 @@
|
||||||
#version 120
|
<@include Config.slh@>
|
||||||
|
<$VERSION_HEADER$>
|
||||||
|
// Generated on <$_SCRIBE_DATE$>
|
||||||
//
|
//
|
||||||
// directional_light.frag
|
// directional_light.frag
|
||||||
// fragment shader
|
// fragment shader
|
|
@ -1,5 +1,6 @@
|
||||||
#version 120
|
<@include Config.slh@>
|
||||||
|
<$VERSION_HEADER$>
|
||||||
|
// Generated on <$_SCRIBE_DATE$>
|
||||||
//
|
//
|
||||||
// directional_light.frag
|
// directional_light.frag
|
||||||
// fragment shader
|
// fragment shader
|
|
@ -1,5 +1,6 @@
|
||||||
#version 120
|
<@include Config.slh@>
|
||||||
|
<$VERSION_HEADER$>
|
||||||
|
// Generated on <$_SCRIBE_DATE$>
|
||||||
//
|
//
|
||||||
// directional_light.frag
|
// directional_light.frag
|
||||||
// fragment shader
|
// fragment shader
|
|
@ -1,5 +1,6 @@
|
||||||
#version 120
|
<@include Config.slh@>
|
||||||
|
<$VERSION_HEADER$>
|
||||||
|
// Generated on <$_SCRIBE_DATE$>
|
||||||
//
|
//
|
||||||
// spot_light.frag
|
// spot_light.frag
|
||||||
// fragment shader
|
// fragment shader
|
|
@ -1,5 +1,6 @@
|
||||||
#version 120
|
<@include Config.slh@>
|
||||||
|
<$VERSION_HEADER$>
|
||||||
|
// Generated on <$_SCRIBE_DATE$>
|
||||||
//
|
//
|
||||||
// simple.frag
|
// simple.frag
|
||||||
// fragment shader
|
// fragment shader
|
|
@ -1,5 +1,6 @@
|
||||||
#version 120
|
<@include Config.slh@>
|
||||||
|
<$VERSION_HEADER$>
|
||||||
|
// Generated on <$_SCRIBE_DATE$>
|
||||||
//
|
//
|
||||||
// simple.vert
|
// simple.vert
|
||||||
// vertex shader
|
// vertex shader
|
|
@ -1,5 +1,6 @@
|
||||||
#version 120
|
<@include Config.slh@>
|
||||||
|
<$VERSION_HEADER$>
|
||||||
|
// Generated on <$_SCRIBE_DATE$>
|
||||||
//
|
//
|
||||||
// spot_light.frag
|
// spot_light.frag
|
||||||
// fragment shader
|
// fragment shader
|
|
@ -78,6 +78,10 @@ inline bool operator==(const AACube& a, const AACube& b) {
|
||||||
return a.getCorner() == b.getCorner() && a.getScale() == b.getScale();
|
return a.getCorner() == b.getCorner() && a.getScale() == b.getScale();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool operator!=(const AACube& a, const AACube& b) {
|
||||||
|
return a.getCorner() != b.getCorner() || a.getScale() != b.getScale();
|
||||||
|
}
|
||||||
|
|
||||||
inline QDebug operator<<(QDebug debug, const AACube& cube) {
|
inline QDebug operator<<(QDebug debug, const AACube& cube) {
|
||||||
debug << "AACube[ ("
|
debug << "AACube[ ("
|
||||||
<< cube.getCorner().x << "," << cube.getCorner().y << "," << cube.getCorner().z << " ) to ("
|
<< cube.getCorner().x << "," << cube.getCorner().y << "," << cube.getCorner().z << " ) to ("
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include <glm/gtx/norm.hpp>
|
#include <glm/gtx/norm.hpp>
|
||||||
|
|
||||||
#include "AACubeShape.h"
|
#include "AACubeShape.h"
|
||||||
#include <SharedUtil.h> // for SQUARE_ROOT_OF_3
|
#include "SharedUtil.h" // for SQUARE_ROOT_OF_3
|
||||||
|
|
||||||
glm::vec3 faceNormals[3] = { glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f) };
|
glm::vec3 faceNormals[3] = { glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f) };
|
||||||
|
|
|
@ -12,11 +12,9 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <glm/gtx/vector_angle.hpp>
|
#include <glm/gtx/vector_angle.hpp>
|
||||||
|
|
||||||
#include <GeometryUtil.h>
|
|
||||||
#include <SharedUtil.h>
|
|
||||||
|
|
||||||
#include "CapsuleShape.h"
|
#include "CapsuleShape.h"
|
||||||
|
#include "GeometryUtil.h"
|
||||||
|
#include "SharedUtil.h"
|
||||||
|
|
||||||
CapsuleShape::CapsuleShape() : Shape(CAPSULE_SHAPE), _radius(0.0f), _halfHeight(0.0f) {}
|
CapsuleShape::CapsuleShape() : Shape(CAPSULE_SHAPE), _radius(0.0f), _halfHeight(0.0f) {}
|
||||||
|
|
|
@ -12,9 +12,8 @@
|
||||||
#ifndef hifi_CapsuleShape_h
|
#ifndef hifi_CapsuleShape_h
|
||||||
#define hifi_CapsuleShape_h
|
#define hifi_CapsuleShape_h
|
||||||
|
|
||||||
#include <SharedUtil.h>
|
|
||||||
|
|
||||||
#include "Shape.h"
|
#include "Shape.h"
|
||||||
|
#include "SharedUtil.h"
|
||||||
|
|
||||||
// default axis of CapsuleShape is Y-axis
|
// default axis of CapsuleShape is Y-axis
|
||||||
const glm::vec3 DEFAULT_CAPSULE_AXIS(0.0f, 1.0f, 0.0f);
|
const glm::vec3 DEFAULT_CAPSULE_AXIS(0.0f, 1.0f, 0.0f);
|
|
@ -10,10 +10,10 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
#include <SharedUtil.h>
|
|
||||||
|
|
||||||
#include "CollisionInfo.h"
|
#include "CollisionInfo.h"
|
||||||
#include "Shape.h"
|
#include "Shape.h"
|
||||||
|
#include "SharedUtil.h"
|
||||||
|
|
||||||
CollisionInfo::CollisionInfo() :
|
CollisionInfo::CollisionInfo() :
|
||||||
_data(NULL),
|
_data(NULL),
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue