Merge branch 'master' of https://github.com/highfidelity/hifi into temp0

This commit is contained in:
Sam Gateau 2015-02-13 01:33:21 -08:00
commit bc2faca9bc
59 changed files with 501 additions and 621 deletions

View file

@ -125,6 +125,6 @@ if (NOT ANDROID)
add_subdirectory(interface)
add_subdirectory(tests)
add_subdirectory(tools)
else ()
elseif (ANDROID OR DESKTOP_GVR)
add_subdirectory(gvr-interface)
endif ()

View file

@ -271,9 +271,10 @@ void MetavoxelSession::handleMessage(const QVariant& message) {
_lodPacketNumber = _sequencer.getIncomingPacketNumber();
} else if (userType == MetavoxelEditMessage::Type) {
QMetaObject::invokeMethod(_sender->getServer(), "applyEdit", Q_ARG(const MetavoxelEditMessage&,
message.value<MetavoxelEditMessage>()));
if (_node->getCanAdjustLocks()) {
QMetaObject::invokeMethod(_sender->getServer(), "applyEdit",
Q_ARG(const MetavoxelEditMessage&, message.value<MetavoxelEditMessage>()));
}
} else if (userType == QMetaType::QVariantList) {
foreach (const QVariant& element, message.toList()) {
handleMessage(element);

View file

@ -6,7 +6,7 @@
{
"name": "access_token",
"label": "Access Token",
"help": "This is an access token generated on the <a href='https://data.highfidelity.io/user/security' target='_blank'>My Security</a> page of your High Fidelity account.<br/>Generate a token with the 'domains' scope and paste it here.<br/>This is required to associate this domain-server with a domain in your account."
"help": "This is an access token generated on the <a href='https://metaverse.highfidelity.io/user/security' target='_blank'>My Security</a> page of your High Fidelity account.<br/>Generate a token with the 'domains' scope and paste it here.<br/>This is required to associate this domain-server with a domain in your account."
},
{
"name": "id",
@ -30,7 +30,7 @@
},
{
"value": "disabled",
"label": "None: use the network information I have entered for this domain at data.highfidelity.io"
"label": "None: use the network information I have entered for this domain at metaverse.highfidelity.io"
}
]
},

View file

@ -652,7 +652,7 @@ function chooseFromHighFidelityDomains(clickedButton) {
clickedButton.attr('disabled', 'disabled')
// get a list of user domains from data-web
data_web_domains_url = "https://data.highfidelity.io/api/v1/domains?access_token="
data_web_domains_url = "https://metaverse.highfidelity.io/api/v1/domains?access_token="
$.getJSON(data_web_domains_url + Settings.initialValues.metaverse.access_token, function(data){
modal_buttons = {
@ -682,7 +682,7 @@ function chooseFromHighFidelityDomains(clickedButton) {
modal_buttons["success"] = {
label: 'Create new domain',
callback: function() {
window.open("https://data.highfidelity.io/user/domains", '_blank');
window.open("https://metaverse.highfidelity.io/user/domains", '_blank');
}
}
modal_body = "<p>You do not have any domains in your High Fidelity account." +

View file

@ -105,15 +105,7 @@ var toolBar = (function () {
newSphereButton,
newLightButton,
newTextButton,
browseModelsButton,
loadURLMenuItem,
loadFileMenuItem,
menuItemWidth,
menuItemOffset,
menuItemHeight,
menuItemMargin = 5,
menuTextColor = { red: 255, green: 255, blue: 255 },
menuBackgroundColor = { red: 18, green: 66, blue: 66 };
browseModelsButton;
function initialize() {
toolBar = new ToolBar(0, 0, ToolBar.VERTICAL);
@ -145,34 +137,6 @@ var toolBar = (function () {
visible: true
});
menuItemOffset = toolBar.height / 3 + 2;
menuItemHeight = Tool.IMAGE_HEIGHT / 2 - 2;
loadURLMenuItem = Overlays.addOverlay("text", {
height: menuItemHeight,
backgroundColor: menuBackgroundColor,
topMargin: menuItemMargin,
text: "Model URL",
alpha: 0.9,
backgroundAlpha: 0.9,
visible: false
});
loadFileMenuItem = Overlays.addOverlay("text", {
height: menuItemHeight,
backgroundColor: menuBackgroundColor,
topMargin: menuItemMargin,
text: "Model File",
alpha: 0.9,
backgroundAlpha: 0.9,
visible: false
});
menuItemWidth = Math.max(Overlays.textSize(loadURLMenuItem, "Model URL").width,
Overlays.textSize(loadFileMenuItem, "Model File").width) + 20;
Overlays.editOverlay(loadURLMenuItem, { width: menuItemWidth });
Overlays.editOverlay(loadFileMenuItem, { width: menuItemWidth });
newCubeButton = toolBar.addTool({
imageURL: toolIconUrl + "add-cube.svg",
subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
@ -211,17 +175,6 @@ var toolBar = (function () {
}
function toggleNewModelButton(active) {
if (active === undefined) {
active = !toolBar.toolSelected(newModelButton);
}
toolBar.selectTool(newModelButton, active);
Overlays.editOverlay(loadURLMenuItem, { visible: active });
Overlays.editOverlay(loadFileMenuItem, { visible: active });
}
that.setActive = function(active) {
if (active != isActive) {
isActive = active;
@ -237,6 +190,7 @@ var toolBar = (function () {
cameraManager.enable();
entityListTool.setVisible(true);
gridTool.setVisible(true);
grid.setEnabled(true);
propertiesTool.setVisible(true);
Window.setFocus();
}
@ -245,7 +199,7 @@ var toolBar = (function () {
};
var RESIZE_INTERVAL = 50;
var RESIZE_TIMEOUT = 20000;
var RESIZE_TIMEOUT = 120000; // 2 minutes
var RESIZE_MAX_CHECKS = RESIZE_TIMEOUT / RESIZE_INTERVAL;
function addModel(url) {
var position;
@ -306,9 +260,6 @@ var toolBar = (function () {
toolsY = (windowDimensions.y - toolBar.height) / 2;
toolBar.move(toolsX, toolsY);
Overlays.editOverlay(loadURLMenuItem, { x: toolsX - menuItemWidth, y: toolsY + menuItemOffset });
Overlays.editOverlay(loadFileMenuItem, { x: toolsX - menuItemWidth, y: toolsY + menuItemOffset + menuItemHeight });
};
that.mousePressEvent = function (event) {
@ -324,12 +275,6 @@ var toolBar = (function () {
}
if (newModelButton === toolBar.clicked(clickedOverlay)) {
toggleNewModelButton();
return true;
}
if (clickedOverlay === loadURLMenuItem) {
toggleNewModelButton(false);
url = Window.prompt("Model URL", modelURLs[Math.floor(Math.random() * modelURLs.length)]);
if (url !== null && url !== "") {
addModel(url);
@ -337,22 +282,7 @@ var toolBar = (function () {
return true;
}
if (clickedOverlay === loadFileMenuItem) {
toggleNewModelButton(false);
file = Window.browse("Select your model file ...",
Settings.getValue("LastModelUploadLocation").path(),
"Model files (*.fst *.fbx)");
//"Model files (*.fst *.fbx *.svo)");
if (file !== null) {
Settings.setValue("LastModelUploadLocation", file);
modelUploader.upload(file, addModel);
}
return true;
}
if (browseModelsButton === toolBar.clicked(clickedOverlay)) {
toggleNewModelButton(false);
url = Window.s3Browse(".*(fbx|FBX)");
if (url !== null && url !== "") {
addModel(url);
@ -443,8 +373,6 @@ var toolBar = (function () {
that.cleanup = function () {
toolBar.cleanup();
Overlays.deleteOverlay(loadURLMenuItem);
Overlays.deleteOverlay(loadFileMenuItem);
};
return that;

View file

@ -155,6 +155,7 @@
var elModelAnimationSettings = document.getElementById("property-model-animation-settings");
var elModelTextures = document.getElementById("property-model-textures");
var elModelOriginalTextures = document.getElementById("property-model-original-textures");
var elModelShapeType = document.getElementById("property-model-shape");
var elTextSections = document.querySelectorAll(".text-section");
var elTextText = document.getElementById("property-text-text");
@ -258,6 +259,7 @@
elModelAnimationSettings.value = properties.animationSettings;
elModelTextures.value = properties.textures;
elModelOriginalTextures.value = properties.originalTextures;
elModelShapeType.value = properties.shapeType;
}
if (properties.type != "Text") {
@ -404,6 +406,7 @@
elModelAnimationFrame.addEventListener('change', createEmitNumberPropertyUpdateFunction('animationFrameIndex'));
elModelAnimationSettings.addEventListener('change', createEmitTextPropertyUpdateFunction('animationSettings'));
elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures'));
elModelShapeType.addEventListener('change', createEmitNumberPropertyUpdateFunction('shapeType'));
elTextText.addEventListener('change', createEmitTextPropertyUpdateFunction('text'));
elTextLineHeight.addEventListener('change', createEmitNumberPropertyUpdateFunction('lineHeight'));
@ -664,6 +667,17 @@
</div>
</div>
<div class="model-section property">
<div class="label">Shape Type</div>
<div class="value">
<select name="SelectShapeType" id="property-model-shape" name="SelectShapeType">
<option value=0>None</option>
<option value=1>Box</option>
<option value=2>Sphere</option>
</select>
</div>
</div>
<div class="text-section property">
<div class="label">Text</div>

View file

@ -30,8 +30,8 @@
elPosY.value = origin.y.toFixed(2);
}
if (data.minorGridSpacing !== undefined) {
elMinorSpacing.value = data.minorGridSpacing;
if (data.minorGridWidth !== undefined) {
elMinorSpacing.value = data.minorGridWidth;
}
if (data.majorGridEvery !== undefined) {
@ -57,7 +57,7 @@
origin: {
y: elPosY.value,
},
minorGridSpacing: elMinorSpacing.value,
minorGridWidth: elMinorSpacing.value,
majorGridEvery: elMajorSpacing.value,
gridColor: gridColor,
colorIndex: gridColorIndex,

View file

@ -21,7 +21,7 @@ modelUploader = (function () {
//svoBuffer,
mapping,
geometry,
API_URL = "https://data.highfidelity.io/api/v1/models",
API_URL = "https://metaverse.highfidelity.io/api/v1/models",
MODEL_URL = "http://public.highfidelity.io/models/content",
NAME_FIELD = "name",
SCALE_FIELD = "scale",

View file

@ -152,7 +152,7 @@ var places = {};
function changeLobbyTextures() {
var req = new XMLHttpRequest();
req.open("GET", "https://data.highfidelity.io/api/v1/places?limit=21", false);
req.open("GET", "https://metaverse.highfidelity.io/api/v1/places?limit=21", false);
req.send();
places = JSON.parse(req.responseText).data.places;

View file

@ -516,7 +516,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
void Application::aboutToQuit() {
_aboutToQuit = true;
setFullscreen(false); // if you exit while in full screen, you'll get bad behavior when you restart.
}
Application::~Application() {

View file

@ -24,7 +24,8 @@
MainWindow::MainWindow(QWidget* parent) :
QMainWindow(parent),
_windowGeometry("WindowGeometry")
_windowGeometry("WindowGeometry"),
_windowState("WindowState", 0)
{
}
@ -34,13 +35,24 @@ void MainWindow::restoreGeometry() {
QRect geometry = _windowGeometry.get(qApp->desktop()->availableGeometry());
move(geometry.topLeft());
resize(geometry.size());
// Restore to maximized or full screen after restoring to windowed so that going windowed goes to good position and sizes.
Qt::WindowStates state = (Qt::WindowStates)_windowState.get(Qt::WindowNoState);
if (state != Qt::WindowNoState) {
setWindowState(state);
}
}
void MainWindow::saveGeometry() {
// Did not use geometry() on purpose,
// see http://doc.qt.io/qt-5/qsettings.html#restoring-the-state-of-a-gui-application
QRect geometry(pos(), size());
_windowGeometry.set(geometry);
_windowState.set((int)windowState());
// Save position and size only if windowed so that have good values for windowed after starting maximized or full screen.
if (windowState() == Qt::WindowNoState) {
QRect geometry(pos(), size());
_windowGeometry.set(geometry);
}
}
void MainWindow::moveEvent(QMoveEvent* event) {

View file

@ -38,6 +38,7 @@ protected:
private:
Setting::Handle<QRect> _windowGeometry;
Setting::Handle<int> _windowState;
};
#endif /* defined(__hifi__MainWindow__) */

View file

@ -760,19 +760,19 @@ void SkeletonModel::buildShapes() {
Shape::Type type = joint.shapeType;
int parentIndex = joint.parentIndex;
if (parentIndex == -1 || radius < EPSILON) {
type = SHAPE_TYPE_UNKNOWN;
} else if (type == SHAPE_TYPE_CAPSULE && halfHeight < EPSILON) {
type = INVALID_SHAPE;
} else if (type == CAPSULE_SHAPE && halfHeight < EPSILON) {
// this shape is forced to be a sphere
type = SHAPE_TYPE_SPHERE;
type = SPHERE_SHAPE;
}
Shape* shape = NULL;
if (type == SHAPE_TYPE_SPHERE) {
if (type == SPHERE_SHAPE) {
shape = new VerletSphereShape(radius, &(points[i]));
shape->setEntity(this);
float mass = massScale * glm::max(MIN_JOINT_MASS, DENSITY_OF_WATER * shape->getVolume());
points[i].setMass(mass);
totalMass += mass;
} else if (type == SHAPE_TYPE_CAPSULE) {
} else if (type == CAPSULE_SHAPE) {
assert(parentIndex != -1);
shape = new VerletCapsuleShape(radius, &(points[parentIndex]), &(points[i]));
shape->setEntity(this);

View file

@ -788,13 +788,12 @@ void ApplicationOverlay::renderAudioMeter() {
// Audio VU Meter and Mute Icon
const int MUTE_ICON_SIZE = 24;
const int AUDIO_METER_INSET = 2;
const int MUTE_ICON_PADDING = 10;
const int AUDIO_METER_WIDTH = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - AUDIO_METER_INSET - MUTE_ICON_PADDING;
const int AUDIO_METER_SCALE_WIDTH = AUDIO_METER_WIDTH - 2 * AUDIO_METER_INSET;
const int AUDIO_METER_WIDTH = MIRROR_VIEW_WIDTH - MUTE_ICON_SIZE - MUTE_ICON_PADDING;
const int AUDIO_METER_SCALE_WIDTH = AUDIO_METER_WIDTH - 2 ;
const int AUDIO_METER_HEIGHT = 8;
const int AUDIO_METER_GAP = 5;
const int AUDIO_METER_X = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_INSET + AUDIO_METER_GAP;
const int AUDIO_METER_X = MIRROR_VIEW_LEFT_PADDING + MUTE_ICON_SIZE + AUDIO_METER_GAP;
int audioMeterY;
bool smallMirrorVisible = Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !OculusManager::isConnected();
@ -847,7 +846,7 @@ void ApplicationOverlay::renderAudioMeter() {
// Draw audio meter background Quad
DependencyManager::get<GeometryCache>()->renderQuad(AUDIO_METER_X, audioMeterY, AUDIO_METER_WIDTH, AUDIO_METER_HEIGHT,
glm::vec4(0, 0, 0, 1));
glm::vec4(0.298f, 0.757f, 0.722f, 1));
if (audioLevel > AUDIO_RED_START) {
glm::vec4 quadColor;
@ -857,10 +856,10 @@ void ApplicationOverlay::renderAudioMeter() {
quadColor = glm::vec4(1, 1, 1, 1);
}
// Draw Red Quad
DependencyManager::get<GeometryCache>()->renderQuad(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_RED_START,
audioMeterY + AUDIO_METER_INSET,
DependencyManager::get<GeometryCache>()->renderQuad(AUDIO_METER_X + AUDIO_RED_START,
audioMeterY,
audioLevel - AUDIO_RED_START,
AUDIO_METER_HEIGHT - AUDIO_METER_INSET, quadColor,
AUDIO_METER_HEIGHT, quadColor,
_audioRedQuad);
audioLevel = AUDIO_RED_START;
@ -874,26 +873,28 @@ void ApplicationOverlay::renderAudioMeter() {
quadColor = glm::vec4(1, 1, 1, 1);
}
// Draw Green Quad
DependencyManager::get<GeometryCache>()->renderQuad(AUDIO_METER_X + AUDIO_METER_INSET + AUDIO_GREEN_START,
audioMeterY + AUDIO_METER_INSET,
DependencyManager::get<GeometryCache>()->renderQuad(AUDIO_METER_X + AUDIO_GREEN_START,
audioMeterY,
audioLevel - AUDIO_GREEN_START,
AUDIO_METER_HEIGHT - AUDIO_METER_INSET, quadColor,
AUDIO_METER_HEIGHT, quadColor,
_audioGreenQuad);
audioLevel = AUDIO_GREEN_START;
}
// Draw Blue Quad
glm::vec4 quadColor;
if (!isClipping) {
quadColor = AUDIO_METER_BLUE;
} else {
quadColor = glm::vec4(1, 1, 1, 1);
if (audioLevel >= 0) {
glm::vec4 quadColor;
if (!isClipping) {
quadColor = AUDIO_METER_BLUE;
} else {
quadColor = glm::vec4(1, 1, 1, 1);
}
// Draw Blue (low level) quad
DependencyManager::get<GeometryCache>()->renderQuad(AUDIO_METER_X,
audioMeterY,
audioLevel, AUDIO_METER_HEIGHT, quadColor,
_audioBlueQuad);
}
// Draw Blue (low level) quad
DependencyManager::get<GeometryCache>()->renderQuad(AUDIO_METER_X + AUDIO_METER_INSET,
audioMeterY + AUDIO_METER_INSET,
audioLevel, AUDIO_METER_HEIGHT - AUDIO_METER_INSET, quadColor,
_audioBlueQuad);
}
void ApplicationOverlay::renderStatsAndLogs() {

View file

@ -22,7 +22,7 @@
#include "ui_loginDialog.h"
#include "LoginDialog.h"
const QString FORGOT_PASSWORD_URL = "https://data.highfidelity.io/users/password/new";
const QString FORGOT_PASSWORD_URL = "https://metaverse.highfidelity.io/users/password/new";
LoginDialog::LoginDialog(QWidget* parent) :
FramelessDialog(parent, 0, FramelessDialog::POSITION_TOP),

View file

@ -37,7 +37,6 @@ using namespace std;
const int STATS_PELS_PER_LINE = 20;
const int STATS_GENERAL_MIN_WIDTH = 165;
const int STATS_BANDWIDTH_MIN_WIDTH = 250;
const int STATS_PING_MIN_WIDTH = 190;
const int STATS_GEO_MIN_WIDTH = 240;
const int STATS_OCTREE_MIN_WIDTH = 410;
@ -52,7 +51,6 @@ Stats::Stats():
_recentMaxPackets(0),
_resetRecentMaxPacketsSoon(true),
_generalStatsWidth(STATS_GENERAL_MIN_WIDTH),
_bandwidthStatsWidth(STATS_BANDWIDTH_MIN_WIDTH),
_pingStatsWidth(STATS_PING_MIN_WIDTH),
_geoStatsWidth(STATS_GEO_MIN_WIDTH),
_octreeStatsWidth(STATS_OCTREE_MIN_WIDTH),
@ -133,7 +131,6 @@ void Stats::resetWidth(int width, int horizontalOffset) {
auto glCanvas = DependencyManager::get<GLCanvas>();
int extraSpace = glCanvas->width() - horizontalOffset -2
- STATS_GENERAL_MIN_WIDTH
- STATS_BANDWIDTH_MIN_WIDTH
- (Menu::getInstance()->isOptionChecked(MenuOption::TestPing) ? STATS_PING_MIN_WIDTH -1 : 0)
- STATS_GEO_MIN_WIDTH
- STATS_OCTREE_MIN_WIDTH;
@ -141,7 +138,6 @@ void Stats::resetWidth(int width, int horizontalOffset) {
int panels = 4;
_generalStatsWidth = STATS_GENERAL_MIN_WIDTH;
_bandwidthStatsWidth = STATS_BANDWIDTH_MIN_WIDTH;
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
_pingStatsWidth = STATS_PING_MIN_WIDTH;
} else {
@ -153,13 +149,12 @@ void Stats::resetWidth(int width, int horizontalOffset) {
if (extraSpace > panels) {
_generalStatsWidth += (int) extraSpace / panels;
_bandwidthStatsWidth += (int) extraSpace / panels;
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
_pingStatsWidth += (int) extraSpace / panels;
}
_geoStatsWidth += (int) extraSpace / panels;
_octreeStatsWidth += glCanvas->width() -
(_generalStatsWidth + _bandwidthStatsWidth + _pingStatsWidth + _geoStatsWidth + 3);
(_generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3);
}
}
@ -231,14 +226,14 @@ void Stats::display(
int totalAvatars = DependencyManager::get<AvatarManager>()->size() - 1;
int totalServers = DependencyManager::get<NodeList>()->size();
lines = _expanded ? 5 : 3;
lines = 5;
int columnOneWidth = _generalStatsWidth;
PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) {
columnOneWidth = _generalStatsWidth + _bandwidthStatsWidth + _pingStatsWidth + _geoStatsWidth; // 4 columns wide...
columnOneWidth = _generalStatsWidth + _pingStatsWidth + _geoStatsWidth; // 3 columns wide...
// we will also include room for 1 line per timing record and a header of 4 lines
lines += 4;
@ -267,6 +262,17 @@ void Stats::display(
drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarNodes.toUtf8().constData(), color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, framesPerSecond.toUtf8().constData(), color);
QString packetsPerSecondString = QString("Packets In/Out: %1/%2").arg(inPacketsPerSecond).arg(outPacketsPerSecond);
QString averageMegabitsPerSecond = QString("Mbps In/Out: %1/%2").
arg((float)inKbitsPerSecond * 1.0f / 1000.0f).
arg((float)outKbitsPerSecond * 1.0f / 1000.0f);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, packetsPerSecondString.toUtf8().constData(), color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, averageMegabitsPerSecond.toUtf8().constData(), color);
// TODO: the display of these timing details should all be moved to JavaScript
if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) {
@ -309,30 +315,9 @@ void Stats::display(
}
verticalOffset = 0;
horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + 1;
if (columnOneWidth == _generalStatsWidth) {
drawBackground(backgroundColor, horizontalOffset, 0, _bandwidthStatsWidth, lines * STATS_PELS_PER_LINE + 10);
}
horizontalOffset += 5;
QString packetsPerSecondString = QString("Packets In/Out: %1/%2").arg(inPacketsPerSecond).arg(outPacketsPerSecond);
QString averageMegabitsPerSecond = QString("Mbps In/Out: %1/%2").
arg((float)inKbitsPerSecond * 1.0f / 1000.0f).
arg((float)outKbitsPerSecond * 1.0f / 1000.0f);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, packetsPerSecondString.toUtf8().constData(), color);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, averageMegabitsPerSecond.toUtf8().constData(), color);
verticalOffset = 0;
horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _bandwidthStatsWidth +1;
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
int pingAudio = -1, pingAvatar = -1, pingVoxel = -1, pingOctreeMax = -1;
@ -412,7 +397,7 @@ void Stats::display(
}
verticalOffset = 0;
horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _bandwidthStatsWidth + _pingStatsWidth + 2;
horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + 2;
}
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
@ -492,7 +477,7 @@ void Stats::display(
}
verticalOffset = 0;
horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _bandwidthStatsWidth + _pingStatsWidth + _geoStatsWidth + 3;
horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3;
lines = _expanded ? 14 : 3;

View file

@ -20,6 +20,7 @@ const xColor DEFAULT_BACKGROUND_COLOR = { 0, 0, 0 };
const float DEFAULT_BACKGROUND_ALPHA = 0.7f;
const float DEFAULT_MARGIN = 0.1f;
const int FIXED_FONT_POINT_SIZE = 40;
const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 40.0f; // this is a ratio determined through experimentation
const float LINE_SCALE_RATIO = 1.2f;
Text3DOverlay::Text3DOverlay() :
@ -104,8 +105,6 @@ void Text3DOverlay::render(RenderArgs* args) {
glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND);
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, quadColor);
const int FIXED_FONT_SCALING_RATIO = FIXED_FONT_POINT_SIZE * 40.0f; // this is a ratio determined through experimentation
// Same font properties as textSize()
TextRenderer* textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE);
float maxHeight = (float)textRenderer->computeExtent("Xy").y * LINE_SCALE_RATIO;
@ -229,22 +228,12 @@ Text3DOverlay* Text3DOverlay::createClone() const {
}
QSizeF Text3DOverlay::textSize(const QString& text) const {
auto textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE);
auto extents = textRenderer->computeExtent(text);
QFont font(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE); // Same font properties as render()
QFontMetrics fontMetrics(font);
const float TEXT_SCALE_ADJUST = 1.025f; // Experimentally detemined for the specified font
const int TEXT_HEIGHT_ADJUST = -10;
float scaleFactor = _lineHeight * TEXT_SCALE_ADJUST * LINE_SCALE_RATIO / (float)FIXED_FONT_POINT_SIZE;
float maxHeight = (float)textRenderer->computeExtent("Xy").y * LINE_SCALE_RATIO;
float pointToWorldScale = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight;
QStringList lines = text.split(QRegExp("\r\n|\r|\n"));
float width = 0.0f;
for (int i = 0; i < lines.count(); i += 1) {
width = std::max(width, scaleFactor * (float)fontMetrics.width(qPrintable(lines[i])));
}
float height = lines.count() * scaleFactor * (float)(fontMetrics.height() + TEXT_HEIGHT_ADJUST);
return QSizeF(width, height);
return QSizeF(extents.x, extents.y) * pointToWorldScale;
}

View file

@ -165,19 +165,8 @@ QScriptValue TextOverlay::getProperty(const QString& property) {
}
QSizeF TextOverlay::textSize(const QString& text) const {
auto textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT);
auto extents = textRenderer->computeExtent(text);
QFont font(SANS_FONT_FAMILY, _fontSize, DEFAULT_FONT_WEIGHT); // Same font properties as render()
QFontMetrics fontMetrics(font);
const int TEXT_HEIGHT_ADJUST = -2; // Experimentally determined for the specified font
QStringList lines = text.split(QRegExp("\r\n|\r|\n"));
int width = 0;
for (int i = 0; i < lines.count(); i += 1) {
width = std::max(width, fontMetrics.width(qPrintable(lines[i])));
}
int height = lines.count() * (fontMetrics.height() + TEXT_HEIGHT_ADJUST);
return QSizeF(width, height);
return QSizeF(extents.x, extents.y);
}

View file

@ -136,7 +136,7 @@
<string>&lt;style type=&quot;text/css&quot;&gt;
a { text-decoration: none; color: #267077;}
&lt;/style&gt;
Invalid username or password. &lt;a href=&quot;https://data.highfidelity.io/password/new&quot;&gt;Recover?&lt;/a&gt;</string>
Invalid username or password. &lt;a href=&quot;https://metaverse.highfidelity.io/password/new&quot;&gt;Recover?&lt;/a&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
@ -458,7 +458,7 @@ border-radius: 4px; padding-top: 1px;</string>
<string>&lt;style type=&quot;text/css&quot;&gt;
a { text-decoration: none; color: #267077;}
&lt;/style&gt;
&lt;a href=&quot;https://data.highfidelity.io/password/new&quot;&gt;Recover password?&lt;/a&gt;</string>
&lt;a href=&quot;https://metaverse.highfidelity.io/password/new&quot;&gt;Recover password?&lt;/a&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>

View file

@ -13,6 +13,7 @@
#include <gpu/GPUConfig.h>
#include <DeferredLightingEffect.h>
#include <GeometryCache.h>
#include <PerfStat.h>
#include <TextRenderer.h>
@ -49,7 +50,12 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND);
glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND);
// TODO: Determine if we want these entities to have the deferred lighting effect? I think we do, so that the color
// used for a sphere, or box have the same look as those used on a text entity.
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram();
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, glm::vec4(toGlm(getBackgroundColorX()), alpha));
DependencyManager::get<DeferredLightingEffect>()->releaseSimpleProgram();
TextRenderer* textRenderer = TextRenderer::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE / 2.0f);

View file

@ -96,11 +96,6 @@ void BoxEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst
APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, getColor());
}
void BoxEntityItem::computeShapeInfo(ShapeInfo& info) const {
glm::vec3 halfExtents = 0.5f * getDimensionsInMeters();
info.setBox(halfExtents);
}
void BoxEntityItem::debugDump() const {
quint64 now = usecTimestampNow();
qDebug() << " BOX EntityItem id:" << getEntityItemID() << "---------------------------------------------";

View file

@ -51,7 +51,7 @@ public:
_color[BLUE_INDEX] = value.blue;
}
void computeShapeInfo(ShapeInfo& info) const;
virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; }
virtual void debugDump() const;

View file

@ -57,8 +57,6 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) {
_collisionsWillMove = ENTITY_ITEM_DEFAULT_COLLISIONS_WILL_MOVE;
_locked = ENTITY_ITEM_DEFAULT_LOCKED;
_userData = ENTITY_ITEM_DEFAULT_USER_DATA;
recalculateCollisionShape();
}
EntityItem::EntityItem(const EntityItemID& entityItemID) {
@ -70,7 +68,6 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) {
_lastEditedFromRemote = 0;
_lastEditedFromRemoteInRemoteTime = 0;
_created = UNKNOWN_CREATED_TIME;
_physicsInfo = NULL;
_dirtyFlags = 0;
_changedOnServer = 0;
_element = NULL;
@ -86,7 +83,6 @@ EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemPropert
_lastEditedFromRemote = 0;
_lastEditedFromRemoteInRemoteTime = 0;
_created = UNKNOWN_CREATED_TIME;
_physicsInfo = NULL;
_dirtyFlags = 0;
_changedOnServer = 0;
_element = NULL;
@ -541,7 +537,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData);
recalculateCollisionShape();
if (overwriteLocalData && (getDirtyFlags() & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY))) {
// NOTE: This code is attempting to "repair" the old data we just got from the server to make it more
// closely match where the entities should be if they'd stepped forward in time to "now". The server
@ -1003,14 +998,7 @@ float EntityItem::getRadius() const {
}
void EntityItem::computeShapeInfo(ShapeInfo& info) const {
info.clear();
}
void EntityItem::recalculateCollisionShape() {
AACube entityAACube = getMinimumAACube();
entityAACube.scale(TREE_SCALE); // scale to meters
_collisionShape.setTranslation(entityAACube.calcCenter());
_collisionShape.setScale(entityAACube.getScale());
info.setParams(getShapeType(), 0.5f * getDimensionsInMeters());
}
const float MIN_POSITION_DELTA = 0.0001f;
@ -1023,7 +1011,6 @@ const float MIN_SPIN_DELTA = 0.0003f;
void EntityItem::updatePosition(const glm::vec3& value) {
if (glm::distance(_position, value) * (float)TREE_SCALE > MIN_POSITION_DELTA) {
_position = value;
recalculateCollisionShape();
_dirtyFlags |= EntityItem::DIRTY_POSITION;
}
}
@ -1032,7 +1019,6 @@ void EntityItem::updatePositionInMeters(const glm::vec3& value) {
glm::vec3 position = glm::clamp(value / (float) TREE_SCALE, 0.0f, 1.0f);
if (glm::distance(_position, position) * (float)TREE_SCALE > MIN_POSITION_DELTA) {
_position = position;
recalculateCollisionShape();
_dirtyFlags |= EntityItem::DIRTY_POSITION;
}
}
@ -1040,7 +1026,6 @@ void EntityItem::updatePositionInMeters(const glm::vec3& value) {
void EntityItem::updateDimensions(const glm::vec3& value) {
if (_dimensions != value) {
_dimensions = glm::abs(value);
recalculateCollisionShape();
_dirtyFlags |= (EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS);
}
}
@ -1049,7 +1034,6 @@ void EntityItem::updateDimensionsInMeters(const glm::vec3& value) {
glm::vec3 dimensions = glm::abs(value) / (float) TREE_SCALE;
if (_dimensions != dimensions) {
_dimensions = dimensions;
recalculateCollisionShape();
_dirtyFlags |= (EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS);
}
}
@ -1057,7 +1041,6 @@ void EntityItem::updateDimensionsInMeters(const glm::vec3& value) {
void EntityItem::updateRotation(const glm::quat& rotation) {
if (glm::dot(_rotation, rotation) < MIN_ALIGNMENT_DOT) {
_rotation = rotation;
recalculateCollisionShape();
_dirtyFlags |= EntityItem::DIRTY_POSITION;
}
}

View file

@ -16,7 +16,6 @@
#include <glm/glm.hpp>
#include <AACubeShape.h>
#include <AnimationCache.h> // for Animation, AnimationCache, and AnimationPointer classes
#include <CollisionInfo.h>
#include <Octree.h> // for EncodeBitstreamParams class
@ -150,7 +149,7 @@ public:
glm::vec3 getPositionInMeters() const { return _position * (float) TREE_SCALE; } /// get position in meters
/// set position in domain scale units (0.0 - 1.0)
void setPosition(const glm::vec3& value) { _position = value; recalculateCollisionShape(); }
void setPosition(const glm::vec3& value) { _position = value; }
void setPositionInMeters(const glm::vec3& value) /// set position in meter units (0.0 - TREE_SCALE)
{ setPosition(glm::clamp(value / (float) TREE_SCALE, 0.0f, 1.0f)); }
@ -162,13 +161,13 @@ public:
float getLargestDimension() const { return glm::length(_dimensions); } /// get the largest possible dimension
/// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately
virtual void setDimensions(const glm::vec3& value) { _dimensions = value; recalculateCollisionShape(); }
virtual void setDimensions(const glm::vec3& value) { _dimensions = value; }
/// set dimensions in meter units (0.0 - TREE_SCALE) this will also reset radius appropriately
void setDimensionsInMeters(const glm::vec3& value) { setDimensions(value / (float) TREE_SCALE); }
const glm::quat& getRotation() const { return _rotation; }
void setRotation(const glm::quat& rotation) { _rotation = rotation; recalculateCollisionShape(); }
void setRotation(const glm::quat& rotation) { _rotation = rotation; }
float getGlowLevel() const { return _glowLevel; }
void setGlowLevel(float glowLevel) { _glowLevel = glowLevel; }
@ -225,7 +224,7 @@ public:
/// registration point as ratio of entity
void setRegistrationPoint(const glm::vec3& value)
{ _registrationPoint = glm::clamp(value, 0.0f, 1.0f); recalculateCollisionShape(); }
{ _registrationPoint = glm::clamp(value, 0.0f, 1.0f); }
const glm::vec3& getAngularVelocity() const { return _angularVelocity; }
void setAngularVelocity(const glm::vec3& value) { _angularVelocity = value; }
@ -254,11 +253,12 @@ public:
// TODO: We need to get rid of these users of getRadius()...
float getRadius() const;
void applyHardCollision(const CollisionInfo& collisionInfo);
virtual const Shape& getCollisionShapeInMeters() const { return _collisionShape; }
virtual bool contains(const glm::vec3& point) const { return getAABox().contains(point); }
virtual void computeShapeInfo(ShapeInfo& info) const;
/// return preferred shape type (actual physical shape may differ)
virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; }
// updateFoo() methods to be used when changes need to be accumulated in the _dirtyFlags
void updatePosition(const glm::vec3& value);
void updatePositionInMeters(const glm::vec3& value);
@ -277,6 +277,7 @@ public:
void updateIgnoreForCollisions(bool value);
void updateCollisionsWillMove(bool value);
void updateLifetime(float value);
virtual void updateShapeType(ShapeType type) { /* do nothing */ }
uint32_t getDirtyFlags() const { return _dirtyFlags; }
void clearDirtyFlags(uint32_t mask = 0xffff) { _dirtyFlags &= ~mask; }
@ -297,7 +298,6 @@ protected:
static bool _sendPhysicsUpdates;
virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init
virtual void recalculateCollisionShape();
EntityTypes::EntityType _type;
QUuid _id;
@ -353,11 +353,9 @@ protected:
/// set radius in domain scale units (0.0 - 1.0) this will also reset dimensions to be equal for each axis
void setRadius(float value);
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
void* _physicsInfo = NULL; // only set by EntitySimulation
// 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

View file

@ -64,6 +64,7 @@ EntityItemProperties::EntityItemProperties() :
CONSTRUCT_PROPERTY(lineHeight, TextEntityItem::DEFAULT_LINE_HEIGHT),
CONSTRUCT_PROPERTY(textColor, TextEntityItem::DEFAULT_TEXT_COLOR),
CONSTRUCT_PROPERTY(backgroundColor, TextEntityItem::DEFAULT_BACKGROUND_COLOR),
CONSTRUCT_PROPERTY(shapeType, SHAPE_TYPE_NONE),
_id(UNKNOWN_ENTITY_ID),
_idSet(false),
@ -210,6 +211,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_LINE_HEIGHT, lineHeight);
CHECK_PROPERTY_CHANGE(PROP_TEXT_COLOR, textColor);
CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_COLOR, backgroundColor);
CHECK_PROPERTY_CHANGE(PROP_SHAPE_TYPE, shapeType);
return changedProperties;
}
@ -268,6 +270,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
COPY_PROPERTY_TO_QSCRIPTVALUE(lineHeight);
COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(textColor, getTextColor());
COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR_GETTER(backgroundColor, getBackgroundColor());
COPY_PROPERTY_TO_QSCRIPTVALUE(shapeType);
// Sitting properties support
QScriptValue sittingPoints = engine->newObject();
@ -347,6 +350,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(lineHeight, setLineHeight);
COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(textColor, setTextColor);
COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(backgroundColor, setBackgroundColor);
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(shapeType, setShapeType, ShapeType);
_lastEdited = usecTimestampNow();
}
@ -510,6 +514,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, appendValue, properties.getAnimationIsPlaying());
APPEND_ENTITY_PROPERTY(PROP_TEXTURES, appendValue, properties.getTextures());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_SETTINGS, appendValue, properties.getAnimationSettings());
APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, appendValue, (uint32_t)(properties.getShapeType()));
}
if (properties.getType() == EntityTypes::Light) {
@ -731,6 +736,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_PLAYING, bool, setAnimationIsPlaying);
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_TEXTURES, setTextures);
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_ANIMATION_SETTINGS, setAnimationSettings);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType);
}
if (properties.getType() == EntityTypes::Light) {
@ -820,6 +826,7 @@ void EntityItemProperties::markAllChanged() {
_lineHeightChanged = true;
_textColorChanged = true;
_backgroundColorChanged = true;
_shapeTypeChanged = true;
}
AACube EntityItemProperties::getMaximumAACubeInTreeUnits() const {

View file

@ -26,7 +26,7 @@
#include <FBXReader.h> // for SittingPoint
#include <PropertyFlags.h>
#include <OctreeConstants.h>
#include <ShapeInfo.h>
#include "EntityItemID.h"
#include "EntityItemPropertiesMacros.h"
@ -82,8 +82,10 @@ enum EntityPropertyList {
PROP_TEXTURES,
PROP_ANIMATION_SETTINGS,
PROP_USER_DATA,
PROP_LAST_ITEM = PROP_USER_DATA,
PROP_SHAPE_TYPE,
// NOTE: add new properties ABOVE this line and then modify PROP_LAST_ITEM below
PROP_LAST_ITEM = PROP_SHAPE_TYPE,
// These properties of TextEntity piggy back off of properties of ModelEntities, the type doesn't matter
// since the derived class knows how to interpret it's own properties and knows the types it expects
@ -179,6 +181,7 @@ public:
DEFINE_PROPERTY(PROP_LINE_HEIGHT, LineHeight, lineHeight, float);
DEFINE_PROPERTY_REF(PROP_TEXT_COLOR, TextColor, textColor, xColor);
DEFINE_PROPERTY_REF(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, xColor);
DEFINE_PROPERTY_REF(PROP_SHAPE_TYPE, ShapeType, shapeType, ShapeType);
public:
float getMaxDimension() const { return glm::max(_dimensions.x, _dimensions.y, _dimensions.z); }

View file

@ -180,6 +180,15 @@
#define COPY_PROPERTY_TO_QSCRIPTVALUE(P) \
properties.setProperty(#P, _##P);
#define COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(P, S, E) \
QScriptValue P = object.property(#P); \
if (P.isValid()) { \
E newValue = (E)(P.toVariant().toInt()); \
if (_defaultSettings || newValue != _##P) { \
S(newValue); \
} \
}
#define COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(P, S) \
QScriptValue P = object.property(#P); \
if (P.isValid()) { \

View file

@ -11,9 +11,6 @@
#include <glm/gtx/transform.hpp>
#include <AACubeShape.h>
#include <ShapeCollider.h>
#include <FBXReader.h>
#include <GeometryUtil.h>
@ -570,33 +567,6 @@ bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float rad
return false;
}
bool EntityTreeElement::findShapeCollisions(const Shape* shape, CollisionList& collisions) const {
bool atLeastOneCollision = false;
QList<EntityItem*>::iterator entityItr = _entityItems->begin();
QList<EntityItem*>::const_iterator entityEnd = _entityItems->end();
while(entityItr != entityEnd) {
EntityItem* entity = (*entityItr);
// entities that are set for ignore for collisions then don't consider them for collision
const Shape* otherCollisionShape = &entity->getCollisionShapeInMeters();
bool ignoreForCollisions = entity->getIgnoreForCollisions();
if (shape != otherCollisionShape && !ignoreForCollisions) {
if (ShapeCollider::collideShapes(shape, otherCollisionShape, collisions)) {
CollisionInfo* lastCollision = collisions.getLastCollision();
if (lastCollision) {
lastCollision->_extraData = entity;
atLeastOneCollision = true;
} else {
qDebug() << "UNEXPECTED - ShapeCollider::collideShapes() returned true, but no lastCollision.";
}
}
}
++entityItr;
}
return atLeastOneCollision;
}
void EntityTreeElement::updateEntityItemID(const EntityItemID& creatorTokenEntityID, const EntityItemID& knownIDEntityID) {
uint16_t numberOfEntities = _entityItems->size();
for (uint16_t i = 0; i < numberOfEntities; i++) {

View file

@ -142,8 +142,6 @@ public:
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
glm::vec3& penetration, void** penetratedObject) const;
virtual bool findShapeCollisions(const Shape* shape, CollisionList& collisions) const;
const QList<EntityItem*>& getEntities() const { return *_entityItems; }
QList<EntityItem*>& getEntities() { return *_entityItems; }
bool hasEntities() const { return _entityItems ? _entityItems->size() > 0 : false; }

View file

@ -42,16 +42,11 @@ LightEntityItem::LightEntityItem(const EntityItemID& entityItemID, const EntityI
_cutoff = PI;
setProperties(properties);
// a light is not collide-able so we make it's shape be a tiny sphere at origin
_emptyShape.setTranslation(glm::vec3(0.0f, 0.0f, 0.0f));
_emptyShape.setRadius(0.0f);
}
void LightEntityItem::setDimensions(const glm::vec3& value) {
float maxDimension = glm::max(value.x, value.y, value.z);
_dimensions = glm::vec3(maxDimension, maxDimension, maxDimension);
recalculateCollisionShape();
}

View file

@ -12,7 +12,6 @@
#ifndef hifi_LightEntityItem_h
#define hifi_LightEntityItem_h
#include <SphereShape.h>
#include "EntityItem.h"
class LightEntityItem : public EntityItem {
@ -98,13 +97,10 @@ public:
float getCutoff() const { return _cutoff; }
void setCutoff(float value) { _cutoff = value; }
virtual const Shape& getCollisionShapeInMeters() const { return _emptyShape; }
static bool getLightsArePickable() { return _lightsArePickable; }
static void setLightsArePickable(bool value) { _lightsArePickable = value; }
protected:
virtual void recalculateCollisionShape() { /* nothing to do */ }
// properties of a light
rgbColor _ambientColor;
@ -117,9 +113,6 @@ protected:
float _exponent;
float _cutoff;
// used for collision detection
SphereShape _emptyShape;
static bool _lightsArePickable;
};

View file

@ -51,6 +51,7 @@ EntityItemProperties ModelEntityItem::getProperties() const {
COPY_ENTITY_PROPERTY_TO_PROPERTIES(glowLevel, getGlowLevel);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationSettings, getAnimationSettings);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType);
return properties;
}
@ -66,6 +67,7 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationFPS, setAnimationFPS);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationSettings, setAnimationSettings);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, updateShapeType);
if (somethingChanged) {
bool wantDebug = false;
@ -116,6 +118,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
READ_ENTITY_PROPERTY_STRING(PROP_TEXTURES, setTextures);
READ_ENTITY_PROPERTY_STRING(PROP_ANIMATION_SETTINGS, setAnimationSettings);
READ_ENTITY_PROPERTY_SETTER(PROP_SHAPE_TYPE, ShapeType, updateShapeType);
return bytesRead;
}
@ -131,6 +134,7 @@ EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams&
requestedProperties += PROP_ANIMATION_PLAYING;
requestedProperties += PROP_ANIMATION_SETTINGS;
requestedProperties += PROP_TEXTURES;
requestedProperties += PROP_SHAPE_TYPE;
return requestedProperties;
}
@ -153,6 +157,7 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, appendValue, getAnimationIsPlaying());
APPEND_ENTITY_PROPERTY(PROP_TEXTURES, appendValue, getTextures());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_SETTINGS, appendValue, getAnimationSettings());
APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, appendValue, (uint32_t)getShapeType());
}
@ -245,21 +250,6 @@ bool ModelEntityItem::needsToCallUpdate() const {
return isAnimatingSomething() ? true : EntityItem::needsToCallUpdate();
}
void ModelEntityItem::computeShapeInfo(ShapeInfo& info) const {
// HACK: Default first first approximation is to boxify the entity... but only if it is small enough.
// The limit here is chosen to something that most avatars could not comfortably fit inside
// to prevent houses from getting boxified... we don't want the things inside houses to
// collide with a house as if it were a giant solid block.
const float MAX_SIZE_FOR_BOXIFICATION_HACK = 3.0f;
float diagonal = glm::length(getDimensionsInMeters());
if (diagonal < MAX_SIZE_FOR_BOXIFICATION_HACK) {
glm::vec3 halfExtents = 0.5f * getDimensionsInMeters();
info.setBox(halfExtents);
} else {
info.clear();
}
}
void ModelEntityItem::update(const quint64& now) {
// only advance the frame index if we're playing
if (getAnimationIsPlaying()) {
@ -280,6 +270,13 @@ void ModelEntityItem::debugDump() const {
qDebug() << " model URL:" << getModelURL();
}
void ModelEntityItem::updateShapeType(ShapeType type) {
if (type != _shapeType) {
_shapeType = type;
_dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS;
}
}
void ModelEntityItem::setAnimationURL(const QString& url) {
_dirtyFlags |= EntityItem::DIRTY_UPDATEABLE;
_animationURL = url;

View file

@ -46,9 +46,10 @@ public:
virtual void update(const quint64& now);
virtual bool needsToCallUpdate() const;
void computeShapeInfo(ShapeInfo& info) const;
virtual void debugDump() const;
void updateShapeType(ShapeType type);
virtual ShapeType getShapeType() const { return _shapeType; }
// TODO: Move these to subclasses, or other appropriate abstraction
// getters/setters applicable to models and particles
@ -126,6 +127,7 @@ protected:
AnimationLoop _animationLoop;
QString _animationSettings;
QString _textures;
ShapeType _shapeType = SHAPE_TYPE_NONE;
// used on client side
bool _jointMappingCompleted;

View file

@ -94,19 +94,6 @@ void SphereEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBi
APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, getColor());
}
void SphereEntityItem::recalculateCollisionShape() {
_sphereShape.setTranslation(getCenterInMeters());
glm::vec3 dimensionsInMeters = getDimensionsInMeters();
float largestDiameter = glm::max(dimensionsInMeters.x, dimensionsInMeters.y, dimensionsInMeters.z);
_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& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
void** intersectedObject, bool precisionPicking) const {

View file

@ -12,7 +12,6 @@
#ifndef hifi_SphereEntityItem_h
#define hifi_SphereEntityItem_h
#include <SphereShape.h>
#include "EntityItem.h"
class SphereEntityItem : public EntityItem {
@ -51,12 +50,10 @@ public:
_color[BLUE_INDEX] = value.blue;
}
virtual const Shape& getCollisionShapeInMeters() const { return _sphereShape; }
// TODO: implement proper contains for 3D ellipsoid
//virtual bool contains(const glm::vec3& point) const;
void computeShapeInfo(ShapeInfo& info) const;
virtual ShapeType getShapeType() const { return SHAPE_TYPE_SPHERE; }
virtual bool supportsDetailedRayIntersection() const { return true; }
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
@ -66,10 +63,8 @@ public:
virtual void debugDump() const;
protected:
virtual void recalculateCollisionShape();
rgbColor _color;
SphereShape _sphereShape;
};
#endif // hifi_SphereEntityItem_h

View file

@ -44,7 +44,6 @@ void TextEntityItem::setDimensions(const glm::vec3& value) {
// NOTE: Text Entities always have a "depth" of 1cm.
float fixedDepth = 0.01f / (float)TREE_SCALE;
_dimensions = glm::vec3(value.x, value.y, fixedDepth);
recalculateCollisionShape();
}
EntityItemProperties TextEntityItem::getProperties() const {

View file

@ -24,6 +24,7 @@ public:
/// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately
virtual void setDimensions(const glm::vec3& value);
virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; }
// methods for getting/setting all properties of an entity
virtual EntityItemProperties getProperties() const;

View file

@ -26,6 +26,7 @@
#include <GeometryUtil.h>
#include <GLMHelpers.h>
#include <OctalCode.h>
#include <Shape.h>
#include "FBXReader.h"
@ -1951,7 +1952,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping,
joint.inverseBindRotation = joint.inverseDefaultRotation;
joint.name = model.name;
joint.shapePosition = glm::vec3(0.0f);
joint.shapeType = SHAPE_TYPE_UNKNOWN;
joint.shapeType = INVALID_SHAPE;
foreach (const QString& childID, childMap.values(modelID)) {
QString type = typeFlags.value(childID);
@ -2375,10 +2376,10 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping,
if (collideLikeCapsule) {
joint.shapeRotation = rotationBetween(defaultCapsuleAxis, jointShapeInfo.boneBegin);
joint.shapePosition = 0.5f * jointShapeInfo.boneBegin;
joint.shapeType = SHAPE_TYPE_CAPSULE;
joint.shapeType = CAPSULE_SHAPE;
} else {
// collide the joint like a sphere
joint.shapeType = SHAPE_TYPE_SPHERE;
joint.shapeType = SPHERE_SHAPE;
if (jointShapeInfo.numVertices > 0) {
jointShapeInfo.averageVertex /= (float)jointShapeInfo.numVertices;
joint.shapePosition = jointShapeInfo.averageVertex;
@ -2398,8 +2399,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping,
if (distanceFromEnd > joint.distanceToParent && distanceFromBegin > joint.distanceToParent) {
// The shape is further from both joint endpoints than the endpoints are from each other
// which probably means the model has a bad transform somewhere. We disable this shape
// by setting its type to SHAPE_TYPE_UNKNOWN.
joint.shapeType = SHAPE_TYPE_UNKNOWN;
// by setting its type to INVALID_SHAPE.
joint.shapeType = INVALID_SHAPE;
}
}
}

View file

@ -18,12 +18,13 @@
#include <QVariant>
#include <QVector>
#include <Extents.h>
#include <Transform.h>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <Extents.h>
#include <Transform.h>
#include <ShapeInfo.h>
#include <model/Geometry.h>
#include <model/Material.h>
@ -53,12 +54,6 @@ public:
QVector<glm::vec3> normals;
};
enum ShapeType {
SHAPE_TYPE_SPHERE = 0,
SHAPE_TYPE_CAPSULE = 1,
SHAPE_TYPE_UNKNOWN = 2
};
/// A single joint (transformation node) extracted from an FBX document.
class FBXJoint {
public:
@ -83,7 +78,7 @@ public:
QString name;
glm::vec3 shapePosition; // in joint frame
glm::quat shapeRotation; // in joint frame
ShapeType shapeType;
quint8 shapeType;
bool isSkeletonJoint;
};

View file

@ -36,7 +36,7 @@ const char SOLO_NODE_TYPES[2] = {
NodeType::AudioMixer
};
const QUrl DEFAULT_NODE_AUTH_URL = QUrl("https://data.highfidelity.io");
const QUrl DEFAULT_NODE_AUTH_URL = QUrl("https://metaverse.highfidelity.io");
LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short dtlsListenPort) :
_sessionUUID(),

View file

@ -72,7 +72,7 @@ PacketVersion versionForPacketType(PacketType type) {
return 1;
case PacketTypeEntityAddOrEdit:
case PacketTypeEntityData:
return VERSION_ENTITIES_HAS_LAST_SIMULATED_TIME;
return VERSION_MODEL_ENTITIES_SUPPORT_SHAPE_TYPE;
case PacketTypeEntityErase:
return 2;
case PacketTypeAudioStreamStats:

View file

@ -127,6 +127,7 @@ const PacketVersion VERSION_ENTITIES_SUPPORT_DIMENSIONS = 4;
const PacketVersion VERSION_ENTITIES_MODELS_HAVE_ANIMATION_SETTINGS = 5;
const PacketVersion VERSION_ENTITIES_HAVE_USER_DATA = 6;
const PacketVersion VERSION_ENTITIES_HAS_LAST_SIMULATED_TIME = 7;
const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_SHAPE_TYPE = 8;
const PacketVersion VERSION_OCTREE_HAS_FILE_BREAKS = 1;
#endif // hifi_PacketHeaders_h

View file

@ -27,7 +27,6 @@
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <Shape.h>
#include <ShapeCollider.h>
#include "CoverageMap.h"
#include "OctreeConstants.h"
@ -856,26 +855,6 @@ bool findCapsulePenetrationOp(OctreeElement* element, void* extraData) {
return false;
}
bool findShapeCollisionsOp(OctreeElement* element, void* extraData) {
ShapeArgs* args = static_cast<ShapeArgs*>(extraData);
// coarse check against bounds
AACube cube = element->getAACube();
cube.scale(TREE_SCALE);
if (!cube.expandedContains(args->shape->getTranslation(), args->shape->getBoundingRadius())) {
return false;
}
if (element->hasContent()) {
if (element->findShapeCollisions(args->shape, args->collisions)) {
args->found = true;
return true;
}
}
if (!element->isLeaf()) {
return true; // recurse on children
}
return false;
}
uint qHash(const glm::vec3& point) {
// NOTE: TREE_SCALE = 16384 (15 bits) and multiplier is 1024 (11 bits),
// so each component (26 bits) uses more than its alloted 21 bits.
@ -947,37 +926,6 @@ bool Octree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end
return args.found;
}
bool Octree::findShapeCollisions(const Shape* shape, CollisionList& collisions,
Octree::lockType lockType, bool* accurateResult) {
ShapeArgs args = { shape, collisions, false };
bool gotLock = false;
if (lockType == Octree::Lock) {
lockForRead();
gotLock = true;
} else if (lockType == Octree::TryLock) {
gotLock = tryLockForRead();
if (!gotLock) {
if (accurateResult) {
*accurateResult = false; // if user asked to accuracy or result, let them know this is inaccurate
}
return args.found; // if we wanted to tryLock, and we couldn't then just bail...
}
}
recurseTreeWithOperation(findShapeCollisionsOp, &args);
if (gotLock) {
unlock();
}
if (accurateResult) {
*accurateResult = true; // if user asked to accuracy or result, let them know this is accurate
}
return args.found;
}
bool Octree::findContentInCube(const AACube& cube, CubeList& cubes) {
if (!tryLockForRead()) {
return false;

View file

@ -308,9 +308,6 @@ public:
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration,
Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL);
bool findShapeCollisions(const Shape* shape, CollisionList& collisions,
Octree::lockType = Octree::TryLock, bool* accurateResult = NULL);
bool findContentInCube(const AACube& cube, CubeList& cubes);
OctreeElement* getElementEnclosingPoint(const glm::vec3& point,

View file

@ -20,7 +20,6 @@
#include <NodeList.h>
#include <PerfStat.h>
#include <AACubeShape.h>
#include <ShapeCollider.h>
#include "AACube.h"
#include "OctalCode.h"
@ -1396,12 +1395,6 @@ bool OctreeElement::findSpherePenetration(const glm::vec3& center, float radius,
return _cube.findSpherePenetration(center, radius, penetration);
}
bool OctreeElement::findShapeCollisions(const Shape* shape, CollisionList& collisions) const {
AACube cube = getAACube();
cube.scale(TREE_SCALE);
return ShapeCollider::collideShapeWithAACubeLegacy(shape, cube.calcCenter(), cube.getScale(), collisions);
}
// TODO: consider removing this, or switching to using getOrCreateChildElementContaining(const AACube& box)...
OctreeElement* OctreeElement::getOrCreateChildElementAt(float x, float y, float z, float s) {
OctreeElement* child = NULL;

View file

@ -128,8 +128,6 @@ public:
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
glm::vec3& penetration, void** penetratedObject) const;
virtual bool findShapeCollisions(const Shape* shape, CollisionList& collisions) const;
// Base class methods you don't need to implement
const unsigned char* getOctalCode() const { return (_octcodePointer) ? _octalCode.pointer : &_octalCode.buffer[0]; }
OctreeElement* getChildAtIndex(int childIndex) const;

View file

@ -9,7 +9,6 @@
// 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"
@ -18,36 +17,30 @@
int ShapeInfoUtil::toBulletShapeType(int shapeInfoType) {
int bulletShapeType = INVALID_SHAPE_PROXYTYPE;
switch(shapeInfoType) {
case BOX_SHAPE:
case SHAPE_TYPE_BOX:
bulletShapeType = BOX_SHAPE_PROXYTYPE;
break;
case SPHERE_SHAPE:
case SHAPE_TYPE_SPHERE:
bulletShapeType = SPHERE_SHAPE_PROXYTYPE;
break;
case CAPSULE_SHAPE:
case SHAPE_TYPE_CAPSULE_Y:
bulletShapeType = CAPSULE_SHAPE_PROXYTYPE;
break;
case CYLINDER_SHAPE:
bulletShapeType = CYLINDER_SHAPE_PROXYTYPE;
break;
}
return bulletShapeType;
}
int ShapeInfoUtil::fromBulletShapeType(int bulletShapeType) {
int shapeInfoType = INVALID_SHAPE;
int shapeInfoType = SHAPE_TYPE_NONE;
switch(bulletShapeType) {
case BOX_SHAPE_PROXYTYPE:
shapeInfoType = BOX_SHAPE;
shapeInfoType = SHAPE_TYPE_BOX;
break;
case SPHERE_SHAPE_PROXYTYPE:
shapeInfoType = SPHERE_SHAPE;
shapeInfoType = SHAPE_TYPE_SPHERE;
break;
case CAPSULE_SHAPE_PROXYTYPE:
shapeInfoType = CAPSULE_SHAPE;
break;
case CYLINDER_SHAPE_PROXYTYPE:
shapeInfoType = CYLINDER_SHAPE;
shapeInfoType = SHAPE_TYPE_CAPSULE_Y;
break;
}
return shapeInfoType;
@ -57,29 +50,16 @@ void ShapeInfoUtil::collectInfoFromShape(const btCollisionShape* shape, ShapeInf
if (shape) {
int type = ShapeInfoUtil::fromBulletShapeType(shape->getShapeType());
switch(type) {
case BOX_SHAPE: {
case SHAPE_TYPE_BOX: {
const btBoxShape* boxShape = static_cast<const btBoxShape*>(shape);
info.setBox(bulletToGLM(boxShape->getHalfExtentsWithMargin()));
}
break;
case SPHERE_SHAPE: {
case SHAPE_TYPE_SPHERE: {
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;
@ -91,77 +71,23 @@ void ShapeInfoUtil::collectInfoFromShape(const btCollisionShape* shape, ShapeInf
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]));
case SHAPE_TYPE_BOX: {
shape = new btBoxShape(glmToBullet(info.getHalfExtents()));
}
break;
case SPHERE_SHAPE: {
float radius = data[0].z;
case SHAPE_TYPE_SPHERE: {
float radius = info.getHalfExtents().x;
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;
case SHAPE_TYPE_CAPSULE_Y: {
glm::vec3 halfExtents = info.getHalfExtents();
float radius = halfExtents.x;
float height = 2.0f * halfExtents.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;
}

View file

@ -17,8 +17,6 @@
#include <ShapeInfo.h>
#include "DoubleHashKey.h"
// translates between ShapeInfo and btShape
namespace ShapeInfoUtil {
@ -26,8 +24,6 @@ namespace ShapeInfoUtil {
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);

View file

@ -27,14 +27,17 @@ ShapeManager::~ShapeManager() {
}
btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
if (info.getType() == SHAPE_TYPE_NONE) {
return NULL;
}
// Very small or large objects are not supported.
float diagonal = glm::length2(info.getBoundingBoxDiagonal());
float diagonal = 4.0f * glm::length2(info.getHalfExtents());
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);
DoubleHashKey key = info.getHash();
ShapeReference* shapeRef = _shapeMap.find(key);
if (shapeRef) {
shapeRef->_refCount++;
@ -51,7 +54,7 @@ btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
}
bool ShapeManager::releaseShape(const ShapeInfo& info) {
DoubleHashKey key = ShapeInfoUtil::computeHash(info);
DoubleHashKey key = info.getHash();
ShapeReference* shapeRef = _shapeMap.find(key);
if (shapeRef) {
if (shapeRef->_refCount > 0) {
@ -95,7 +98,7 @@ void ShapeManager::collectGarbage() {
}
int ShapeManager::getNumReferences(const ShapeInfo& info) const {
DoubleHashKey key = ShapeInfoUtil::computeHash(info);
DoubleHashKey key = info.getHash();
const ShapeReference* shapeRef = _shapeMap.find(key);
if (shapeRef) {
return shapeRef->_refCount;

View file

@ -999,12 +999,12 @@ void GeometryCache::renderBevelCornersRect(int x, int y, int width, int height,
void GeometryCache::renderQuad(const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec4& color, int id) {
bool registered = (id != UNKNOWN_ID);
Vec2Pair key(minCorner, maxCorner);
Vec4Pair key(glm::vec4(minCorner.x, minCorner.y, maxCorner.x, maxCorner.y), color);
BatchItemDetails& details = registered ? _registeredQuad2D[id] : _quad2D[key];
// if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed
if (registered && details.isCreated) {
Vec2Pair& lastKey = _lastRegisteredQuad2D[id];
Vec4Pair & lastKey = _lastRegisteredQuad2D[id];
if (lastKey != key) {
details.clear();
_lastRegisteredQuad2D[id] = key;
@ -1082,12 +1082,14 @@ void GeometryCache::renderQuad(const glm::vec2& minCorner, const glm::vec2& maxC
const glm::vec4& color, int id) {
bool registered = (id != UNKNOWN_ID);
Vec2PairPair key(Vec2Pair(minCorner, maxCorner), Vec2Pair(texCoordMinCorner, texCoordMaxCorner));
Vec4PairVec4 key(Vec4Pair(glm::vec4(minCorner.x, minCorner.y, maxCorner.x, maxCorner.y),
glm::vec4(texCoordMinCorner.x, texCoordMinCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y)),
color);
BatchItemDetails& details = registered ? _registeredQuad2DTextures[id] : _quad2DTextures[key];
// if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed
if (registered && details.isCreated) {
Vec2PairPair& lastKey = _lastRegisteredQuad2DTexture[id];
Vec4PairVec4& lastKey = _lastRegisteredQuad2DTexture[id];
if (lastKey != key) {
details.clear();
_lastRegisteredQuad2DTexture[id] = key;
@ -1172,12 +1174,12 @@ void GeometryCache::renderQuad(const glm::vec2& minCorner, const glm::vec2& maxC
void GeometryCache::renderQuad(const glm::vec3& minCorner, const glm::vec3& maxCorner, const glm::vec4& color, int id) {
bool registered = (id != UNKNOWN_ID);
Vec3Pair key(minCorner, maxCorner);
Vec3PairVec4 key(Vec3Pair(minCorner, maxCorner), color);
BatchItemDetails& details = registered ? _registeredQuad3D[id] : _quad3D[key];
// if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed
if (registered && details.isCreated) {
Vec3Pair& lastKey = _lastRegisteredQuad3D[id];
Vec3PairVec4& lastKey = _lastRegisteredQuad3D[id];
if (lastKey != key) {
details.clear();
_lastRegisteredQuad3D[id] = key;

View file

@ -41,7 +41,9 @@ typedef QPair<Vec2Pair, Vec2Pair> Vec2PairPair;
typedef QPair<glm::vec3, glm::vec3> Vec3Pair;
typedef QPair<glm::vec4, glm::vec4> Vec4Pair;
typedef QPair<Vec3Pair, Vec2Pair> Vec3PairVec2Pair;
typedef QPair<Vec3Pair, glm::vec4> Vec3PairVec4;
typedef QPair<Vec3Pair, Vec4Pair> Vec3PairVec4Pair;
typedef QPair<Vec4Pair, glm::vec4> Vec4PairVec4;
typedef QPair<Vec4Pair, Vec4Pair> Vec4PairVec4Pair;
inline uint qHash(const glm::vec2& v, uint seed) {
@ -87,6 +89,14 @@ inline uint qHash(const Vec3PairVec2Pair& v, uint seed) {
5077 * v.second.second.x + 5081 * v.second.second.y, seed);
}
inline uint qHash(const Vec3PairVec4& v, uint seed) {
// multiply by prime numbers greater than the possible size
return qHash(v.first.first.x + 5009 * v.first.first.y + 5011 * v.first.first.z +
5021 * v.first.second.x + 5023 * v.first.second.y + 5039 * v.first.second.z +
5051 * v.second.x + 5059 * v.second.y + 5077 * v.second.z + 5081 * v.second.w, seed);
}
inline uint qHash(const Vec3PairVec4Pair& v, uint seed) {
// multiply by prime numbers greater than the possible size
return qHash(v.first.first.x + 5009 * v.first.first.y + 5011 * v.first.first.z
@ -96,6 +106,14 @@ inline uint qHash(const Vec3PairVec4Pair& v, uint seed) {
seed);
}
inline uint qHash(const Vec4PairVec4& v, uint seed) {
// multiply by prime numbers greater than the possible size
return qHash(v.first.first.x + 5009 * v.first.first.y + 5011 * v.first.first.z + 5021 * v.first.first.w
+ 5023 * v.first.second.x + 5039 * v.first.second.y + 5051 * v.first.second.z + 5059 * v.first.second.w
+ 5077 * v.second.x + 5081 * v.second.y + 5087 * v.second.z + 5099 * v.second.w,
seed);
}
inline uint qHash(const Vec4PairVec4Pair& v, uint seed) {
// multiply by prime numbers greater than the possible size
return qHash(v.first.first.x + 5009 * v.first.first.y + 5011 * v.first.first.z + 5021 * v.first.first.w
@ -236,16 +254,16 @@ private:
QHash<Vec3PairVec4Pair, BatchItemDetails> _quad3DTextures;
QHash<int, BatchItemDetails> _registeredQuad3DTextures;
QHash<int, Vec2PairPair> _lastRegisteredQuad2DTexture;
QHash<Vec2PairPair, BatchItemDetails> _quad2DTextures;
QHash<int, Vec4PairVec4> _lastRegisteredQuad2DTexture;
QHash<Vec4PairVec4, BatchItemDetails> _quad2DTextures;
QHash<int, BatchItemDetails> _registeredQuad2DTextures;
QHash<int, Vec3Pair> _lastRegisteredQuad3D;
QHash<Vec3Pair, BatchItemDetails> _quad3D;
QHash<int, Vec3PairVec4> _lastRegisteredQuad3D;
QHash<Vec3PairVec4, BatchItemDetails> _quad3D;
QHash<int, BatchItemDetails> _registeredQuad3D;
QHash<int, Vec2Pair> _lastRegisteredQuad2D;
QHash<Vec2Pair, BatchItemDetails> _quad2D;
QHash<int, Vec4Pair> _lastRegisteredQuad2D;
QHash<Vec4Pair, BatchItemDetails> _quad2D;
QHash<int, BatchItemDetails> _registeredQuad2D;
QHash<int, Vec3Pair> _lastRegisteredBevelRects;

View file

@ -136,10 +136,12 @@ public:
glm::vec2 drawString(float x, float y, const QString & str,
const glm::vec4& color, TextRenderer::EffectType effectType,
const glm::vec2& bound) const;
const glm::vec2& bound);
private:
QStringList tokenizeForWrapping(const QString & str) const;
bool _initialized;
};
static QHash<QString, Font*> LOADED_FONTS;
@ -186,7 +188,7 @@ Font* loadFont(const QString& family) {
return LOADED_FONTS[family];
}
Font::Font() {
Font::Font() : _initialized(false) {
static bool fontResourceInitComplete = false;
if (!fontResourceInitComplete) {
Q_INIT_RESOURCE(fonts);
@ -255,8 +257,6 @@ void Font::read(QIODevice& in) {
// store in the character to glyph hash
_glyphs[g.c] = g;
};
setupGL();
}
struct TextureVertex {
@ -291,6 +291,11 @@ QRectF Glyph::textureBounds(const glm::vec2 & textureSize) const {
}
void Font::setupGL() {
if (_initialized) {
return;
}
_initialized = true;
_texture = TexturePtr(
new QOpenGLTexture(_image, QOpenGLTexture::GenerateMipMaps));
_program = ProgramPtr(new QOpenGLShaderProgram());
@ -411,7 +416,9 @@ glm::vec2 Font::computeExtent(const QString & str) const {
// even without explicit line feeds.
glm::vec2 Font::drawString(float x, float y, const QString & str,
const glm::vec4& color, TextRenderer::EffectType effectType,
const glm::vec2& bounds) const {
const glm::vec2& bounds) {
setupGL();
// Stores how far we've moved from the start of the string, in DTP units
glm::vec2 advance(0, -_rowHeight - _descent);
@ -481,9 +488,12 @@ glm::vec2 Font::drawString(float x, float y, const QString & str,
}
_vao->release();
_texture->release(); // TODO: Brad & Sam, let's discuss this. Without this non-textured quads get their colors borked.
_program->release();
// FIXME, needed?
// glDisable(GL_TEXTURE_2D);
return advance;
}

View file

@ -207,7 +207,7 @@ void XMLHttpRequestClass::open(const QString& method, const QString& url, bool a
notImplemented();
}
} else {
if (url.toLower().left(33) == "https://data.highfidelity.io/api/") {
if (url.toLower().left(33) == "https://metaverse.highfidelity.io/api/") {
AccountManager& accountManager = AccountManager::getInstance();
if (accountManager.hasValidAccessToken()) {

View file

@ -1,6 +1,6 @@
//
// DoubleHashKey.cpp
// libraries/physcis/src
// libraries/shared/src
//
// Created by Andrew Meadows 2014.11.02
// Copyright 2014 High Fidelity, Inc.
@ -11,8 +11,8 @@
#include "DoubleHashKey.h"
const int NUM_PRIMES = 64;
const unsigned int PRIMES[] = {
const uint32_t NUM_PRIMES = 64;
const uint32_t 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,
@ -23,8 +23,8 @@ const unsigned int PRIMES[] = {
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);
uint32_t DoubleHashKey::hashFunction(uint32_t value, uint32_t primeIndex) {
uint32_t hash = PRIMES[primeIndex % NUM_PRIMES] * (value + 1U);
hash += ~(hash << 15);
hash ^= (hash >> 10);
hash += (hash << 3);
@ -33,11 +33,16 @@ unsigned int DoubleHashKey::hashFunction(unsigned int value, int primeIndex) {
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);
uint32_t DoubleHashKey::hashFunction2(uint32_t value) {
uint32_t hash = 0x811c9dc5U;
for (uint32_t i = 0; i < 4; i++ ) {
uint32_t byte = (value << (i * 8)) >> (24 - i * 8);
hash = ( hash ^ byte ) * 0x01000193U;
}
return hash;
}
void DoubleHashKey::computeHash(uint32_t value, uint32_t primeIndex) {
_hash = DoubleHashKey::hashFunction(value, primeIndex);
_hash2 = DoubleHashKey::hashFunction2(value);
}

View file

@ -1,6 +1,6 @@
//
// DoubleHashKey.h
// libraries/physcis/src
// libraries/shared/src
//
// Created by Andrew Meadows 2014.11.02
// Copyright 2014 High Fidelity, Inc.
@ -12,27 +12,38 @@
#ifndef hifi_DoubleHashKey_h
#define hifi_DoubleHashKey_h
#include <stdint.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);
static uint32_t hashFunction(uint32_t value, uint32_t primeIndex);
static uint32_t hashFunction2(uint32_t value);
DoubleHashKey() : _hash(0), _hash2(0) { }
DoubleHashKey(unsigned int value, int primeIndex = 0) :
DoubleHashKey(uint32_t value, uint32_t primeIndex = 0) :
_hash(hashFunction(value, primeIndex)),
_hash2(hashFunction2(value)) {
}
void clear() { _hash = 0; _hash2 = 0; }
bool isNull() const { return _hash == 0 && _hash2 == 0; }
bool equals(const DoubleHashKey& other) const {
return _hash == other._hash && _hash2 == other._hash2;
}
unsigned int getHash() const { return (unsigned int)_hash; }
void computeHash(uint32_t value, uint32_t primeIndex = 0);
uint32_t getHash() const { return _hash; }
uint32_t getHash2() const { return _hash2; }
int _hash;
int _hash2;
void setHash(uint32_t hash) { _hash = hash; }
void setHash2(uint32_t hash2) { _hash2 = hash2; }
private:
uint32_t _hash;
uint32_t _hash2;
};
#endif // hifi_DoubleHashKey_h

View file

@ -13,78 +13,82 @@
#include "SharedUtil.h" // for MILLIMETERS_PER_METER
//#include "DoubleHashKey.h"
#include "ShapeInfo.h"
void ShapeInfo::clear() {
_type = INVALID_SHAPE;
_data.clear();
_type = SHAPE_TYPE_NONE;
_halfExtents = glm::vec3(0.0f);
_doubleHashKey.clear();
_externalData = NULL;
}
void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QVector<glm::vec3>* data) {
_type = type;
switch(type) {
case SHAPE_TYPE_NONE:
_halfExtents = glm::vec3(0.0f);
break;
case SHAPE_TYPE_BOX:
_halfExtents = halfExtents;
break;
case SHAPE_TYPE_SPHERE: {
// sphere radius is max of halfExtents
float radius = glm::max(glm::max(halfExtents.x, halfExtents.y), halfExtents.z);
_halfExtents = glm::vec3(radius);
break;
}
default:
_halfExtents = halfExtents;
}
_externalData = data;
}
void ShapeInfo::setBox(const glm::vec3& halfExtents) {
_type = BOX_SHAPE;
_data.clear();
// _data[0] = < halfX, halfY, halfZ >
_data.push_back(halfExtents);
_type = SHAPE_TYPE_BOX;
_halfExtents = halfExtents;
_doubleHashKey.clear();
}
void ShapeInfo::setSphere(float radius) {
_type = SPHERE_SHAPE;
_data.clear();
// _data[0] = < radius, radius, radius >
_data.push_back(glm::vec3(radius));
_type = SHAPE_TYPE_SPHERE;
_halfExtents = glm::vec3(radius, radius, radius);
_doubleHashKey.clear();
}
void ShapeInfo::setCylinder(float radius, float halfHeight) {
_type = CYLINDER_SHAPE;
_data.clear();
// _data[0] = < radius, halfHeight, radius >
// NOTE: default cylinder has (UpAxis = 1) axis along yAxis and radius stored in X
_data.push_back(glm::vec3(radius, halfHeight, radius));
void ShapeInfo::setEllipsoid(const glm::vec3& halfExtents) {
_type = SHAPE_TYPE_ELLIPSOID;
_halfExtents = halfExtents;
_doubleHashKey.clear();
}
void ShapeInfo::setCapsule(float radius, float halfHeight) {
_type = CAPSULE_SHAPE;
_data.clear();
// _data[0] = < radius, halfHeight, radius >
_data.push_back(glm::vec3(radius, halfHeight, radius));
}
glm::vec3 ShapeInfo::getBoundingBoxDiagonal() const {
switch(_type) {
case BOX_SHAPE:
case SPHERE_SHAPE:
case CYLINDER_SHAPE:
case CAPSULE_SHAPE:
return 2.0f * _data[0];
default:
break;
}
return glm::vec3(0.0f);
void ShapeInfo::setCapsuleY(float radius, float halfHeight) {
_type = SHAPE_TYPE_CAPSULE_Y;
_halfExtents = glm::vec3(radius, halfHeight, radius);
_doubleHashKey.clear();
}
float ShapeInfo::computeVolume() const {
const float DEFAULT_VOLUME = 1.0f;
float volume = DEFAULT_VOLUME;
switch(_type) {
case BOX_SHAPE: {
// factor of 8.0 because the components of _data[0] are all halfExtents
volume = 8.0f * _data[0].x * _data[0].y * _data[0].z;
case SHAPE_TYPE_BOX: {
// factor of 8.0 because the components of _halfExtents are all halfExtents
volume = 8.0f * _halfExtents.x * _halfExtents.y * _halfExtents.z;
break;
}
case SPHERE_SHAPE: {
float radius = _data[0].x;
case SHAPE_TYPE_SPHERE: {
float radius = _halfExtents.x;
volume = 4.0f * PI * radius * radius * radius / 3.0f;
break;
}
case CYLINDER_SHAPE: {
float radius = _data[0].x;
volume = PI * radius * radius * 2.0f * _data[0].y;
case SHAPE_TYPE_CYLINDER_Y: {
float radius = _halfExtents.x;
volume = PI * radius * radius * 2.0f * _halfExtents.y;
break;
}
case CAPSULE_SHAPE: {
float radius = _data[0].x;
volume = PI * radius * radius * (2.0f * _data[0].y + 4.0f * radius / 3.0f);
case SHAPE_TYPE_CAPSULE_Y: {
float radius = _halfExtents.x;
volume = PI * radius * radius * (2.0f * _halfExtents.y + 4.0f * radius / 3.0f);
break;
}
default:
@ -93,3 +97,85 @@ float ShapeInfo::computeVolume() const {
assert(volume > 0.0f);
return volume;
}
const DoubleHashKey& ShapeInfo::getHash() const {
// NOTE: we cache the hash so we only ever need to compute it once for any valid ShapeInfo instance.
if (_doubleHashKey.isNull() && _type != SHAPE_TYPE_NONE) {
// cast this to non-const pointer so we can do our dirty work
ShapeInfo* thisPtr = const_cast<ShapeInfo*>(this);
// compute hash1
// TODO?: provide lookup table for hash/hash2 of _type rather than recompute?
uint32_t primeIndex = 0;
thisPtr->_doubleHashKey.computeHash((uint32_t)_type, primeIndex++);
const QVector<glm::vec3>* data = getData();
if (data) {
// if externalData exists we use it to continue the hash
// compute hash
uint32_t hash = _doubleHashKey.getHash();
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()
uint32_t floatHash =
DoubleHashKey::hashFunction((uint32_t)(tmpData[j] * MILLIMETERS_PER_METER + copysignf(1.0f, tmpData[j]) * 0.49f), primeIndex++);
hash ^= floatHash;
}
}
thisPtr->_doubleHashKey.setHash(hash);
// compute hash2
hash = _doubleHashKey.getHash2();
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()
uint32_t floatHash =
DoubleHashKey::hashFunction2((uint32_t)(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);
}
}
thisPtr->_doubleHashKey.setHash2(hash);
} else {
// this shape info has no external data so type+extents should be enough to generate a unique hash
// compute hash1
uint32_t hash = _doubleHashKey.getHash();
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()
uint32_t floatHash =
DoubleHashKey::hashFunction((uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f), primeIndex++);
hash ^= floatHash;
}
thisPtr->_doubleHashKey.setHash(hash);
// compute hash2
hash = _doubleHashKey.getHash2();
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()
uint32_t floatHash =
DoubleHashKey::hashFunction2((uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f));
hash += ~(floatHash << 17);
hash ^= (floatHash >> 11);
hash += (floatHash << 4);
hash ^= (floatHash >> 7);
hash += ~(floatHash << 10);
hash = (hash << 16) | (hash >> 16);
}
thisPtr->_doubleHashKey.setHash2(hash);
}
}
return _doubleHashKey;
}

View file

@ -15,28 +15,51 @@
#include <QVector>
#include <glm/glm.hpp>
#include "Shape.h"
#include "DoubleHashKey.h"
enum ShapeType {
SHAPE_TYPE_NONE,
SHAPE_TYPE_BOX,
SHAPE_TYPE_SPHERE,
SHAPE_TYPE_ELLIPSOID,
SHAPE_TYPE_HULL,
SHAPE_TYPE_PLANE,
SHAPE_TYPE_COMPOUND,
SHAPE_TYPE_CAPSULE_X,
SHAPE_TYPE_CAPSULE_Y,
SHAPE_TYPE_CAPSULE_Z,
SHAPE_TYPE_CYLINDER_X,
SHAPE_TYPE_CYLINDER_Y,
SHAPE_TYPE_CYLINDER_Z
};
class ShapeInfo {
public:
ShapeInfo() : _type(INVALID_SHAPE) {}
void clear();
void setParams(ShapeType type, const glm::vec3& halfExtents, QVector<glm::vec3>* data = NULL);
void setBox(const glm::vec3& halfExtents);
void setSphere(float radius);
void setCylinder(float radius, float halfHeight);
void setCapsule(float radius, float halfHeight);
void setEllipsoid(const glm::vec3& halfExtents);
//void setHull(); // TODO: implement this
void setCapsuleY(float radius, float halfHeight);
const int getType() const { return _type; }
const QVector<glm::vec3>& getData() const { return _data; }
glm::vec3 getBoundingBoxDiagonal() const;
const glm::vec3& getHalfExtents() const { return _halfExtents; }
void setData(const QVector<glm::vec3>* data) { _externalData = data; }
const QVector<glm::vec3>* getData() const { return _externalData; }
float computeVolume() const;
const DoubleHashKey& getHash() const;
protected:
int _type;
QVector<glm::vec3> _data;
ShapeType _type = SHAPE_TYPE_NONE;
glm::vec3 _halfExtents = glm::vec3(0.0f);
DoubleHashKey _doubleHashKey;
const QVector<glm::vec3>* _externalData = NULL;
};
#endif // hifi_ShapeInfo_h

View file

@ -24,10 +24,10 @@
void ShapeInfoTests::testHashFunctions() {
int maxTests = 10000000;
ShapeInfo info;
btHashMap<btHashInt, int> hashes;
btHashMap<btHashInt, uint32_t> hashes;
int bits[32];
unsigned int masks[32];
uint32_t bits[32];
uint32_t masks[32];
for (int i = 0; i < 32; ++i) {
bits[i] = 0;
masks[i] = 1U << i;
@ -45,26 +45,27 @@ void ShapeInfoTests::testHashFunctions() {
// test sphere
info.setSphere(radiusX);
++testCount;
DoubleHashKey key = ShapeInfoUtil::computeHash(info);
int* hashPtr = hashes.find(key._hash);
if (hashPtr && *hashPtr == key._hash2) {
DoubleHashKey key = info.getHash();
uint32_t* hashPtr = hashes.find(key.getHash());
if (hashPtr && *hashPtr == key.getHash2()) {
std::cout << testCount << " hash collision radiusX = " << radiusX
<< " h1 = 0x" << std::hex << (unsigned int)(key._hash)
<< " h2 = 0x" << std::hex << (unsigned int)(key._hash2)
<< " h1 = 0x" << std::hex << key.getHash()
<< " h2 = 0x" << std::hex << key.getHash2()
<< std::endl;
++numCollisions;
assert(false);
} else {
hashes.insert(key._hash, key._hash2);
hashes.insert(key.getHash(), key.getHash2());
}
for (int k = 0; k < 32; ++k) {
if (masks[k] & key._hash2) {
if (masks[k] & key.getHash2()) {
++bits[k];
}
}
for (int y = 1; y < numSteps && testCount < maxTests; ++y) {
float radiusY = (float)y * deltaLength;
/* TODO: reimplement Cylinder and Capsule shapes
// test cylinder and capsule
int types[] = { CYLINDER_SHAPE_PROXYTYPE, CAPSULE_SHAPE_PROXYTYPE };
for (int i = 0; i < 2; ++i) {
@ -74,58 +75,59 @@ void ShapeInfoTests::testHashFunctions() {
break;
}
case CAPSULE_SHAPE_PROXYTYPE: {
info.setCapsule(radiusX, radiusY);
info.setCapsuleY(radiusX, radiusY);
break;
}
}
++testCount;
key = ShapeInfoUtil::computeHash(info);
hashPtr = hashes.find(key._hash);
if (hashPtr && *hashPtr == key._hash2) {
key = info.getHash();
hashPtr = hashes.find(key.getHash());
if (hashPtr && *hashPtr == key.getHash2()) {
std::cout << testCount << " hash collision radiusX = " << radiusX << " radiusY = " << radiusY
<< " h1 = 0x" << std::hex << (unsigned int)(key._hash)
<< " h2 = 0x" << std::hex << (unsigned int)(key._hash2)
<< " h1 = 0x" << std::hex << key.getHash()
<< " h2 = 0x" << std::hex << key.getHash2()
<< std::endl;
++numCollisions;
assert(false);
} else {
hashes.insert(key._hash, key._hash2);
hashes.insert(key.getHash(), key.getHash2());
}
for (int k = 0; k < 32; ++k) {
if (masks[k] & key._hash2) {
if (masks[k] & key.getHash2()) {
++bits[k];
}
}
}
*/
for (int z = 1; z < numSteps && testCount < maxTests; ++z) {
float radiusZ = (float)z * deltaLength;
// test box
info.setBox(glm::vec3(radiusX, radiusY, radiusZ));
++testCount;
DoubleHashKey key = ShapeInfoUtil::computeHash(info);
hashPtr = hashes.find(key._hash);
if (hashPtr && *hashPtr == key._hash2) {
DoubleHashKey key = info.getHash();
hashPtr = hashes.find(key.getHash());
if (hashPtr && *hashPtr == key.getHash2()) {
std::cout << testCount << " hash collision radiusX = " << radiusX
<< " radiusY = " << radiusY << " radiusZ = " << radiusZ
<< " h1 = 0x" << std::hex << (unsigned int)(key._hash)
<< " h2 = 0x" << std::hex << (unsigned int)(key._hash2)
<< " h1 = 0x" << std::hex << key.getHash()
<< " h2 = 0x" << std::hex << key.getHash2()
<< std::endl;
++numCollisions;
assert(false);
} else {
hashes.insert(key._hash, key._hash2);
hashes.insert(key.getHash(), key.getHash2());
}
for (int k = 0; k < 32; ++k) {
if (masks[k] & key._hash2) {
if (masks[k] & key.getHash2()) {
++bits[k];
}
}
}
}
}
unsigned long int msec = timer.getTimeMilliseconds();
uint64_t msec = timer.getTimeMilliseconds();
std::cout << msec << " msec with " << numCollisions << " collisions out of " << testCount << " hashes" << std::endl;
// print out distribution of bits
@ -138,7 +140,7 @@ void ShapeInfoTests::testBoxShape() {
ShapeInfo info;
glm::vec3 halfExtents(1.23f, 4.56f, 7.89f);
info.setBox(halfExtents);
DoubleHashKey key = ShapeInfoUtil::computeHash(info);
DoubleHashKey key = info.getHash();
btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info);
if (!shape) {
@ -148,15 +150,15 @@ void ShapeInfoTests::testBoxShape() {
ShapeInfo otherInfo;
ShapeInfoUtil::collectInfoFromShape(shape, otherInfo);
DoubleHashKey otherKey = ShapeInfoUtil::computeHash(otherInfo);
if (key._hash != otherKey._hash) {
DoubleHashKey otherKey = otherInfo.getHash();
if (key.getHash() != otherKey.getHash()) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: expected Box shape hash = " << key._hash << " but found hash = " << otherKey._hash << std::endl;
<< " ERROR: expected Box shape hash = " << key.getHash() << " but found hash = " << otherKey.getHash() << std::endl;
}
if (key._hash2 != otherKey._hash2) {
if (key.getHash2() != otherKey.getHash2()) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: expected Box shape hash2 = " << key._hash2 << " but found hash2 = " << otherKey._hash2 << std::endl;
<< " ERROR: expected Box shape hash2 = " << key.getHash2() << " but found hash2 = " << otherKey.getHash2() << std::endl;
}
delete shape;
@ -166,74 +168,78 @@ void ShapeInfoTests::testSphereShape() {
ShapeInfo info;
float radius = 1.23f;
info.setSphere(radius);
DoubleHashKey key = ShapeInfoUtil::computeHash(info);
DoubleHashKey key = info.getHash();
btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info);
ShapeInfo otherInfo;
ShapeInfoUtil::collectInfoFromShape(shape, otherInfo);
DoubleHashKey otherKey = ShapeInfoUtil::computeHash(otherInfo);
if (key._hash != otherKey._hash) {
DoubleHashKey otherKey = otherInfo.getHash();
if (key.getHash() != otherKey.getHash()) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: expected Sphere shape hash = " << key._hash << " but found hash = " << otherKey._hash << std::endl;
<< " ERROR: expected Sphere shape hash = " << key.getHash() << " but found hash = " << otherKey.getHash() << std::endl;
}
if (key._hash2 != otherKey._hash2) {
if (key.getHash2() != otherKey.getHash2()) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: expected Sphere shape hash2 = " << key._hash2 << " but found hash2 = " << otherKey._hash2 << std::endl;
<< " ERROR: expected Sphere shape hash2 = " << key.getHash2() << " but found hash2 = " << otherKey.getHash2() << std::endl;
}
delete shape;
}
void ShapeInfoTests::testCylinderShape() {
/* TODO: reimplement Cylinder shape
ShapeInfo info;
float radius = 1.23f;
float height = 4.56f;
info.setCylinder(radius, height);
DoubleHashKey key = ShapeInfoUtil::computeHash(info);
DoubleHashKey key = info.getHash();
btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info);
ShapeInfo otherInfo;
ShapeInfoUtil::collectInfoFromShape(shape, otherInfo);
DoubleHashKey otherKey = ShapeInfoUtil::computeHash(otherInfo);
if (key._hash != otherKey._hash) {
DoubleHashKey otherKey = otherInfo.getHash();
if (key.getHash() != otherKey.getHash()) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: expected Cylinder shape hash = " << key._hash << " but found hash = " << otherKey._hash << std::endl;
<< " ERROR: expected Cylinder shape hash = " << key.getHash() << " but found hash = " << otherKey.getHash() << std::endl;
}
if (key._hash2 != otherKey._hash2) {
if (key.getHash2() != otherKey.getHash2()) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: expected Cylinder shape hash2 = " << key._hash2 << " but found hash2 = " << otherKey._hash2 << std::endl;
<< " ERROR: expected Cylinder shape hash2 = " << key.getHash2() << " but found hash2 = " << otherKey.getHash2() << std::endl;
}
delete shape;
*/
}
void ShapeInfoTests::testCapsuleShape() {
/* TODO: reimplement Capsule shape
ShapeInfo info;
float radius = 1.23f;
float height = 4.56f;
info.setCapsule(radius, height);
DoubleHashKey key = ShapeInfoUtil::computeHash(info);
DoubleHashKey key = info.getHash();
btCollisionShape* shape = ShapeInfoUtil::createShapeFromInfo(info);
ShapeInfo otherInfo;
ShapeInfoUtil::collectInfoFromShape(shape, otherInfo);
DoubleHashKey otherKey = ShapeInfoUtil::computeHash(otherInfo);
if (key._hash != otherKey._hash) {
DoubleHashKey otherKey = otherInfo.getHash();
if (key.getHash() != otherKey.getHash()) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: expected Capsule shape hash = " << key._hash << " but found hash = " << otherKey._hash << std::endl;
<< " ERROR: expected Capsule shape hash = " << key.getHash() << " but found hash = " << otherKey.getHash() << std::endl;
}
if (key._hash2 != otherKey._hash2) {
if (key.getHash2() != otherKey.getHash2()) {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: expected Capsule shape hash2 = " << key._hash2 << " but found hash2 = " << otherKey._hash2 << std::endl;
<< " ERROR: expected Capsule shape hash2 = " << key.getHash2() << " but found hash2 = " << otherKey.getHash2() << std::endl;
}
delete shape;
*/
}
void ShapeInfoTests::runAllTests() {

View file

@ -188,6 +188,7 @@ void ShapeManagerTests::addSphereShape() {
}
void ShapeManagerTests::addCylinderShape() {
/* TODO: reimplement Cylinder shape
ShapeInfo info;
float radius = 1.23f;
float height = 4.56f;
@ -204,9 +205,11 @@ void ShapeManagerTests::addCylinderShape() {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: Cylinder ShapeInfo --> shape --> ShapeInfo --> shape did not work" << std::endl;
}
*/
}
void ShapeManagerTests::addCapsuleShape() {
/* TODO: reimplement Capsule shape
ShapeInfo info;
float radius = 1.23f;
float height = 4.56f;
@ -223,6 +226,7 @@ void ShapeManagerTests::addCapsuleShape() {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: Capsule ShapeInfo --> shape --> ShapeInfo --> shape did not work" << std::endl;
}
*/
}
void ShapeManagerTests::runAllTests() {