3
0
Fork 0
mirror of https://github.com/lubosz/overte.git synced 2025-04-26 21:35:45 +02:00

Merge remote-tracking branch 'upstream/master' into pull_mode_audio_output

This commit is contained in:
wangyix 2014-08-04 17:58:31 -07:00
commit 332e75453e
13 changed files with 506 additions and 94 deletions

View file

@ -1,3 +1,4 @@
</div>
<script src='/js/jquery-2.0.3.min.js'></script>
<script src='/js/bootstrap.min.js'></script>
<script src='/js/bootstrap.min.js'></script>
<script src='/js/domain-server.js'></script>

View file

@ -8,4 +8,33 @@
<link href="/css/style.css" rel="stylesheet" media="screen">
</head>
<body>
<nav class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">domain-server</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="/">Nodes</a></li>
<li><a href="/settings/">Settings</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Assignments <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="/assignment">New Assignment</a></li>
</ul>
</li>
</ul>
</div>
</div><!-- /.container-fluid -->
</nav>
<div class="container">

View file

@ -0,0 +1,10 @@
$(document).ready(function(){
var url = window.location;
// Will only work if string in href matches with location
$('ul.nav a[href="'+ url +'"]').parent().addClass('active');
// Will also work for relative and absolute hrefs
$('ul.nav a').filter(function() {
return this.href == url;
}).parent().addClass('active');
});

View file

@ -28,33 +28,5 @@
"default": ""
}
}
},
"voxels": {
"label": "Voxels",
"assignment-types": [2,3],
"settings": {
"voxel-wallet": {
"label": "Destination Wallet ID",
"help": "Wallet to be paid for voxel changes",
"placeholder": "00000000-0000-0000-0000-000000000000",
"default": ""
},
"per-voxel-credits": {
"type": "double",
"label": "Per Voxel Cost",
"help": "Credit cost to change each voxel",
"placeholder": "0.0",
"default": "0.0",
"input_addon": "₵"
},
"per-meter-cubed-credits": {
"type": "double",
"label": "Per Meter Cubed Cost",
"help": "Credit cost to change each cubed meter of voxel space",
"placeholder": "0.0",
"default": "0.0",
"input_addon": "₵"
}
}
}
}

View file

@ -1280,6 +1280,9 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
const QByteArray HTTP_COOKIE_HEADER_KEY = "Cookie";
const QString ADMIN_USERS_CONFIG_KEY = "admin-users";
const QString ADMIN_ROLES_CONFIG_KEY = "admin-roles";
const QString BASIC_AUTH_CONFIG_KEY = "basic-auth";
const QByteArray UNAUTHENTICATED_BODY = "You do not have permission to access this domain-server.";
if (!_oauthProviderURL.isEmpty()
&& (_argumentVariantMap.contains(ADMIN_USERS_CONFIG_KEY) || _argumentVariantMap.contains(ADMIN_ROLES_CONFIG_KEY))) {
@ -1293,6 +1296,11 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
cookieUUID = cookieUUIDRegex.cap(1);
}
if (_argumentVariantMap.contains(BASIC_AUTH_CONFIG_KEY)) {
qDebug() << "Config file contains web admin settings for OAuth and basic HTTP authentication."
<< "These cannot be combined - using OAuth for authentication.";
}
if (!cookieUUID.isNull() && _cookieSessionHash.contains(cookieUUID)) {
// pull the QJSONObject for the user with this cookie UUID
DomainServerWebSessionData sessionData = _cookieSessionHash.value(cookieUUID);
@ -1315,8 +1323,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
}
}
QString unauthenticatedRequest = "You do not have permission to access this domain-server.";
connection->respond(HTTPConnection::StatusCode401, unauthenticatedRequest.toUtf8());
connection->respond(HTTPConnection::StatusCode401, UNAUTHENTICATED_BODY);
// the user does not have allowed username or role, return 401
return false;
@ -1340,6 +1347,59 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
// we don't know about this user yet, so they are not yet authenticated
return false;
}
} else if (_argumentVariantMap.contains(BASIC_AUTH_CONFIG_KEY)) {
// config file contains username and password combinations for basic auth
const QByteArray BASIC_AUTH_HEADER_KEY = "Authorization";
// check if a username and password have been provided with the request
QString basicAuthString = connection->requestHeaders().value(BASIC_AUTH_HEADER_KEY);
if (!basicAuthString.isEmpty()) {
QStringList splitAuthString = basicAuthString.split(' ');
QString base64String = splitAuthString.size() == 2 ? splitAuthString[1] : "";
QString credentialString = QByteArray::fromBase64(base64String.toLocal8Bit());
if (!credentialString.isEmpty()) {
QStringList credentialList = credentialString.split(':');
if (credentialList.size() == 2) {
QString username = credentialList[0];
QString password = credentialList[1];
// we've pulled a username and password - now check if there is a match in our basic auth hash
QJsonObject basicAuthObject = _argumentVariantMap.value(BASIC_AUTH_CONFIG_KEY).toJsonValue().toObject();
if (basicAuthObject.contains(username)) {
const QString BASIC_AUTH_USER_PASSWORD_KEY = "password";
QJsonObject userObject = basicAuthObject.value(username).toObject();
if (userObject.contains(BASIC_AUTH_USER_PASSWORD_KEY)
&& userObject.value(BASIC_AUTH_USER_PASSWORD_KEY).toString() == password) {
// this is username / password match - let this user in
return true;
}
}
}
}
}
// basic HTTP auth being used but no username and password are present
// or the username and password are not correct
// send back a 401 and ask for basic auth
const QByteArray HTTP_AUTH_REQUEST_HEADER_KEY = "WWW-Authenticate";
static QString HTTP_AUTH_REALM_STRING = QString("Basic realm='%1 %2'")
.arg(_hostname.isEmpty() ? "localhost" : _hostname)
.arg("domain-server");
Headers basicAuthHeader;
basicAuthHeader.insert(HTTP_AUTH_REQUEST_HEADER_KEY, HTTP_AUTH_REALM_STRING.toUtf8());
connection->respond(HTTPConnection::StatusCode401, UNAUTHENTICATED_BODY,
HTTPConnection::DefaultContentType, basicAuthHeader);
// not authenticated, bubble up false
return false;
} else {
// we don't have an OAuth URL + admin roles/usernames, so all users are authenticated
return true;

View file

@ -1051,6 +1051,11 @@ function checkController(deltaTime) {
var numberOfTriggers = Controller.getNumberOfTriggers();
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
if (!isActive) {
// So that we hide the lasers bellow and keep updating the overlays position
numberOfButtons = 0;
}
// this is expected for hydras
if (numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2) {
@ -1072,11 +1077,21 @@ function checkController(deltaTime) {
moveOverlays();
}
var isActive = false;
var active;
var newModel;
var browser;
function initToolBar() {
toolBar = new ToolBar(0, 0, ToolBar.VERTICAL);
// New Model
active = toolBar.addTool({
imageURL: toolIconUrl + "models-tool.svg",
subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
width: toolWidth, height: toolHeight,
visible: true,
alpha: 0.9
}, true, false);
newModel = toolBar.addTool({
imageURL: toolIconUrl + "add-model-tool.svg",
subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
@ -1194,6 +1209,11 @@ function mousePressEvent(event) {
modelSelected = false;
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
if (active == toolBar.clicked(clickedOverlay)) {
isActive = !isActive;
return;
}
if (newModel == toolBar.clicked(clickedOverlay)) {
var url = Window.prompt("Model URL", modelURLs[Math.floor(Math.random() * modelURLs.length)]);
if (url == null || url == "") {
@ -1229,6 +1249,11 @@ function mousePressEvent(event) {
}
} else {
// If we aren't active and didn't click on an overlay: quit
if (!isActive) {
return;
}
var pickRay = Camera.computePickRay(event.x, event.y);
Vec3.print("[Mouse] Looking at: ", pickRay.origin);
var foundIntersection = Models.findRayIntersection(pickRay);
@ -1313,7 +1338,7 @@ var oldModifier = 0;
var modifier = 0;
var wasShifted = false;
function mouseMoveEvent(event) {
if (event.isAlt) {
if (event.isAlt || !isActive) {
return;
}
@ -1456,7 +1481,7 @@ function mouseMoveEvent(event) {
function mouseReleaseEvent(event) {
if (event.isAlt) {
if (event.isAlt || !isActive) {
return;
}

View file

@ -108,6 +108,12 @@ int RenderVisitor::visit(MetavoxelInfo& info) {
}
void MetavoxelSystem::render() {
// update the frustum
ViewFrustum* viewFrustum = Application::getInstance()->getViewFrustum();
_frustum.set(viewFrustum->getFarTopLeft(), viewFrustum->getFarTopRight(), viewFrustum->getFarBottomLeft(),
viewFrustum->getFarBottomRight(), viewFrustum->getNearTopLeft(), viewFrustum->getNearTopRight(),
viewFrustum->getNearBottomLeft(), viewFrustum->getNearBottomRight());
RenderVisitor renderVisitor(getLOD());
guideToAugmented(renderVisitor);
}
@ -628,13 +634,31 @@ public:
SpannerRenderVisitor(const MetavoxelLOD& lod);
virtual int visit(MetavoxelInfo& info);
virtual bool visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize);
private:
int _containmentDepth;
};
SpannerRenderVisitor::SpannerRenderVisitor(const MetavoxelLOD& lod) :
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute(),
QVector<AttributePointer>(), QVector<AttributePointer>(), QVector<AttributePointer>(),
lod, encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())) {
lod, encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())),
_containmentDepth(INT_MAX) {
}
int SpannerRenderVisitor::visit(MetavoxelInfo& info) {
if (_containmentDepth >= _depth) {
Frustum::IntersectionType intersection = Application::getInstance()->getMetavoxels()->getFrustum().getIntersectionType(
info.getBounds());
if (intersection == Frustum::NO_INTERSECTION) {
return STOP_RECURSION;
}
_containmentDepth = (intersection == Frustum::CONTAINS_INTERSECTION) ? _depth : INT_MAX;
}
return SpannerVisitor::visit(info);
}
bool SpannerRenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) {
@ -652,14 +676,24 @@ public:
private:
int _order;
int _containmentDepth;
};
BufferRenderVisitor::BufferRenderVisitor(const AttributePointer& attribute, const MetavoxelLOD& lod) :
MetavoxelVisitor(QVector<AttributePointer>() << attribute, QVector<AttributePointer>(), lod),
_order(encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())) {
_order(encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())),
_containmentDepth(INT_MAX) {
}
int BufferRenderVisitor::visit(MetavoxelInfo& info) {
if (_containmentDepth >= _depth) {
Frustum::IntersectionType intersection = Application::getInstance()->getMetavoxels()->getFrustum().getIntersectionType(
info.getBounds());
if (intersection == Frustum::NO_INTERSECTION) {
return STOP_RECURSION;
}
_containmentDepth = (intersection == Frustum::CONTAINS_INTERSECTION) ? _depth : INT_MAX;
}
BufferDataPointer buffer = info.inputValues.at(0).getInlineValue<BufferDataPointer>();
if (buffer) {
buffer->render();

View file

@ -34,6 +34,8 @@ public:
virtual void init();
virtual MetavoxelLOD getLOD();
const Frustum& getFrustum() const { return _frustum; }
const AttributePointer& getPointBufferAttribute() { return _pointBufferAttribute; }
const AttributePointer& getHeightfieldBufferAttribute() { return _heightfieldBufferAttribute; }
@ -56,6 +58,7 @@ private:
MetavoxelLOD _lod;
QReadWriteLock _lodLock;
Frustum _frustum;
};
/// Describes contents of a point in a point buffer.

View file

@ -26,6 +26,7 @@
#include <QPushButton>
#include <QRunnable>
#include <QScrollArea>
#include <QSpinBox>
#include <QThreadPool>
#include <QVBoxLayout>
@ -116,6 +117,7 @@ MetavoxelEditor::MetavoxelEditor() :
addTool(new ClearSpannersTool(this));
addTool(new SetSpannerTool(this));
addTool(new ImportHeightfieldTool(this));
addTool(new EraseHeightfieldTool(this));
updateAttributes();
@ -895,40 +897,68 @@ void SetSpannerTool::applyEdit(const AttributePointer& attribute, const SharedOb
spannerData->getVoxelizationGranularity(), directionImages));
}
ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) :
MetavoxelTool(editor, "Import Heightfield", false) {
HeightfieldTool::HeightfieldTool(MetavoxelEditor* editor, const QString& name) :
MetavoxelTool(editor, name, false) {
QWidget* widget = new QWidget();
QFormLayout* form = new QFormLayout();
widget->setLayout(form);
widget->setLayout(_form = new QFormLayout());
layout()->addWidget(widget);
form->addRow("Translation:", _translation = new Vec3Editor(widget));
form->addRow("Scale:", _scale = new QDoubleSpinBox());
_form->addRow("Translation:", _translation = new Vec3Editor(widget));
_form->addRow("Scale:", _scale = new QDoubleSpinBox());
_scale->setMinimum(-FLT_MAX);
_scale->setMaximum(FLT_MAX);
_scale->setPrefix("2^");
_scale->setValue(3.0);
form->addRow("Height:", _height = new QPushButton());
connect(_height, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectHeightFile);
form->addRow("Color:", _color = new QPushButton());
connect(_color, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectColorFile);
QPushButton* applyButton = new QPushButton("Apply");
layout()->addWidget(applyButton);
connect(applyButton, &QAbstractButton::clicked, this, &ImportHeightfieldTool::apply);
connect(applyButton, &QAbstractButton::clicked, this, &HeightfieldTool::apply);
}
bool ImportHeightfieldTool::appliesTo(const AttributePointer& attribute) const {
bool HeightfieldTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("HeightfieldAttribute");
}
void ImportHeightfieldTool::render() {
void HeightfieldTool::render() {
float scale = pow(2.0, _scale->value());
_translation->setSingleStep(scale);
glm::vec3 quantizedTranslation = scale * glm::floor(_translation->getValue() / scale);
_translation->setValue(quantizedTranslation);
_preview.render(quantizedTranslation, scale);
}
ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) :
HeightfieldTool(editor, "Import Heightfield") {
_form->addRow("Height:", _height = new QPushButton());
connect(_height, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectHeightFile);
_form->addRow("Color:", _color = new QPushButton());
connect(_color, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectColorFile);
}
void ImportHeightfieldTool::render() {
HeightfieldTool::render();
_preview.render(_translation->getValue(), _translation->getSingleStep());
}
void ImportHeightfieldTool::apply() {
float scale = _translation->getSingleStep();
foreach (const BufferDataPointer& bufferData, _preview.getBuffers()) {
HeightfieldBuffer* buffer = static_cast<HeightfieldBuffer*>(bufferData.data());
MetavoxelData data;
data.setSize(scale);
HeightfieldDataPointer heightPointer(new HeightfieldData(buffer->getHeight()));
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldAttribute(), new MetavoxelNode(AttributeValue(
AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer))));
if (!buffer->getColor().isEmpty()) {
HeightfieldDataPointer colorPointer(new HeightfieldData(buffer->getColor()));
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(AttributeValue(
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer))));
}
MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit(
_translation->getValue() + buffer->getTranslation() * scale, data)) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}
}
void ImportHeightfieldTool::selectHeightFile() {
@ -959,26 +989,6 @@ void ImportHeightfieldTool::selectColorFile() {
updatePreview();
}
void ImportHeightfieldTool::apply() {
float scale = pow(2.0, _scale->value());
foreach (const BufferDataPointer& bufferData, _preview.getBuffers()) {
HeightfieldBuffer* buffer = static_cast<HeightfieldBuffer*>(bufferData.data());
MetavoxelData data;
data.setSize(scale);
HeightfieldDataPointer heightPointer(new HeightfieldData(buffer->getHeight()));
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldAttribute(), new MetavoxelNode(AttributeValue(
AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer))));
if (!buffer->getColor().isEmpty()) {
HeightfieldDataPointer colorPointer(new HeightfieldData(buffer->getColor()));
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(AttributeValue(
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer))));
}
MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit(
_translation->getValue() + buffer->getTranslation() * scale, data)) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}
}
const int BLOCK_SIZE = 32;
const int BLOCK_ADVANCEMENT = BLOCK_SIZE - 1;
@ -1018,3 +1028,49 @@ void ImportHeightfieldTool::updatePreview() {
}
_preview.setBuffers(buffers);
}
EraseHeightfieldTool::EraseHeightfieldTool(MetavoxelEditor* editor) :
HeightfieldTool(editor, "Erase Heightfield") {
_form->addRow("Width:", _width = new QSpinBox());
_width->setMinimum(1);
_width->setMaximum(INT_MAX);
_form->addRow("Length:", _length = new QSpinBox());
_length->setMinimum(1);
_length->setMaximum(INT_MAX);
}
void EraseHeightfieldTool::render() {
HeightfieldTool::render();
glColor3f(1.0f, 0.0f, 0.0f);
glLineWidth(4.0f);
glPushMatrix();
glm::vec3 translation = _translation->getValue();
glTranslatef(translation.x, translation.y, translation.z);
float scale = _translation->getSingleStep();
glScalef(scale * _width->value(), scale, scale * _length->value());
glTranslatef(0.5f, 0.5f, 0.5f);
glutWireCube(1.0);
glPopMatrix();
glLineWidth(1.0f);
}
void EraseHeightfieldTool::apply() {
// clear the heightfield
float scale = _translation->getSingleStep();
BoxSetEdit edit(Box(_translation->getValue(), _translation->getValue() +
glm::vec3(_width->value() * scale, scale, _length->value() * scale)), scale,
OwnedAttributeValue(AttributeRegistry::getInstance()->getHeightfieldAttribute()));
MetavoxelEditMessage message = { QVariant::fromValue(edit) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
// and the color
edit.value = OwnedAttributeValue(AttributeRegistry::getInstance()->getHeightfieldColorAttribute());
message.edit = QVariant::fromValue(edit);
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}

View file

@ -24,6 +24,7 @@ class QGroupBox;
class QListWidget;
class QPushButton;
class QScrollArea;
class QSpinBox;
class MetavoxelTool;
class Vec3Editor;
@ -225,30 +226,52 @@ protected:
virtual void applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner);
};
/// Base class for heightfield tools.
class HeightfieldTool : public MetavoxelTool {
Q_OBJECT
public:
HeightfieldTool(MetavoxelEditor* editor, const QString& name);
virtual bool appliesTo(const AttributePointer& attribute) const;
virtual void render();
protected slots:
virtual void apply() = 0;
protected:
QFormLayout* _form;
Vec3Editor* _translation;
QDoubleSpinBox* _scale;
};
/// Allows importing a heightfield.
class ImportHeightfieldTool : public MetavoxelTool {
class ImportHeightfieldTool : public HeightfieldTool {
Q_OBJECT
public:
ImportHeightfieldTool(MetavoxelEditor* editor);
virtual bool appliesTo(const AttributePointer& attribute) const;
virtual void render();
protected:
virtual void apply();
private slots:
void selectHeightFile();
void selectColorFile();
void apply();
private:
void updatePreview();
Vec3Editor* _translation;
QDoubleSpinBox* _scale;
QPushButton* _height;
QPushButton* _color;
@ -258,4 +281,24 @@ private:
HeightfieldPreview _preview;
};
// Allows clearing heighfield blocks.
class EraseHeightfieldTool : public HeightfieldTool {
Q_OBJECT
public:
EraseHeightfieldTool(MetavoxelEditor* editor);
virtual void render();
protected:
virtual void apply();
private:
QSpinBox* _width;
QSpinBox* _length;
};
#endif // hifi_MetavoxelEditor_h

View file

@ -315,6 +315,136 @@ QDebug& operator<<(QDebug& dbg, const Box& box) {
return dbg.nospace() << "{type='Box', minimum=" << box.minimum << ", maximum=" << box.maximum << "}";
}
AxisExtents::AxisExtents(const glm::vec3& first0, const glm::vec3& first1, const glm::vec3& first2, const glm::vec3& second) :
axis(glm::cross(first2 - first1, first0 - first1)),
minimum(glm::dot(first1, axis)),
maximum(glm::dot(second, axis)) {
}
AxisExtents::AxisExtents(const glm::vec3& axis, float minimum, float maximum) :
axis(axis),
minimum(minimum),
maximum(maximum) {
}
void Frustum::set(const glm::vec3& farTopLeft, const glm::vec3& farTopRight, const glm::vec3& farBottomLeft,
const glm::vec3& farBottomRight, const glm::vec3& nearTopLeft, const glm::vec3& nearTopRight,
const glm::vec3& nearBottomLeft, const glm::vec3& nearBottomRight) {
_vertices[0] = farBottomLeft;
_vertices[1] = farBottomRight;
_vertices[2] = farTopLeft;
_vertices[3] = farTopRight;
_vertices[4] = nearBottomLeft;
_vertices[5] = nearBottomRight;
_vertices[6] = nearTopLeft;
_vertices[7] = nearTopRight;
// compute the bounds
_bounds.minimum = glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX);
_bounds.maximum = glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
for (int i = 0; i < VERTEX_COUNT; i++) {
_bounds.minimum = glm::min(_bounds.minimum, _vertices[i]);
_bounds.maximum = glm::max(_bounds.maximum, _vertices[i]);
}
// compute the extents for each side
_sideExtents[0] = AxisExtents(nearBottomLeft, nearTopLeft, nearTopRight, farBottomLeft);
_sideExtents[1] = AxisExtents(nearBottomLeft, farBottomLeft, farTopLeft, farBottomRight);
_sideExtents[2] = AxisExtents(nearBottomRight, nearTopRight, farTopRight, farBottomLeft);
_sideExtents[3] = AxisExtents(nearBottomLeft, nearBottomRight, farBottomRight, farTopLeft);
_sideExtents[4] = AxisExtents(nearTopLeft, farTopLeft, farTopRight, farBottomRight);
// the other set of extents are derived from the cross products of the frustum and box edges
glm::vec3 edges[] = { nearBottomRight - nearBottomLeft, nearTopLeft - nearBottomLeft, farBottomLeft - nearBottomLeft,
farBottomRight - nearBottomRight, farTopLeft - nearTopLeft, farTopRight - nearTopRight };
const int AXIS_COUNT = 3;
for (uint i = 0, extentIndex = 0; i < sizeof(edges) / sizeof(edges[0]); i++) {
for (int j = 0; j < AXIS_COUNT; j++) {
glm::vec3 axis;
axis[j] = 1.0f;
glm::vec3 crossProduct = glm::cross(edges[i], axis);
float minimum = FLT_MAX, maximum = -FLT_MAX;
for (int k = 0; k < VERTEX_COUNT; k++) {
float projection = glm::dot(crossProduct, _vertices[k]);
minimum = glm::min(minimum, projection);
maximum = glm::max(maximum, projection);
}
_crossProductExtents[extentIndex++] = AxisExtents(crossProduct, minimum, maximum);
}
}
}
Frustum::IntersectionType Frustum::getIntersectionType(const Box& box) const {
// first check the bounds (equivalent to checking frustum vertices against box extents)
if (!_bounds.intersects(box)) {
return NO_INTERSECTION;
}
// check box vertices against side extents
bool allInside = true;
for (int i = 0; i < SIDE_EXTENT_COUNT; i++) {
const AxisExtents& extents = _sideExtents[i];
float firstProjection = glm::dot(box.getVertex(0), extents.axis);
if (firstProjection < extents.minimum) {
allInside = false;
for (int j = 1; j < Box::VERTEX_COUNT; j++) {
if (glm::dot(box.getVertex(j), extents.axis) >= extents.minimum) {
goto sideContinue;
}
}
return NO_INTERSECTION;
} else if (firstProjection > extents.maximum) {
allInside = false;
for (int j = 1; j < Box::VERTEX_COUNT; j++) {
if (glm::dot(box.getVertex(j), extents.axis) <= extents.maximum) {
goto sideContinue;
}
}
return NO_INTERSECTION;
} else if (allInside) {
for (int j = 1; j < Box::VERTEX_COUNT; j++) {
float projection = glm::dot(box.getVertex(j), extents.axis);
if (projection < extents.minimum || projection > extents.maximum) {
allInside = false;
goto sideContinue;
}
}
}
sideContinue: ;
}
if (allInside) {
return CONTAINS_INTERSECTION;
}
// check box vertices against cross product extents
for (int i = 0; i < CROSS_PRODUCT_EXTENT_COUNT; i++) {
const AxisExtents& extents = _crossProductExtents[i];
float firstProjection = glm::dot(box.getVertex(0), extents.axis);
if (firstProjection < extents.minimum) {
for (int j = 1; j < Box::VERTEX_COUNT; j++) {
if (glm::dot(box.getVertex(j), extents.axis) >= extents.minimum) {
goto crossProductContinue;
}
}
return NO_INTERSECTION;
} else if (firstProjection > extents.maximum) {
for (int j = 1; j < Box::VERTEX_COUNT; j++) {
if (glm::dot(box.getVertex(j), extents.axis) <= extents.maximum) {
goto crossProductContinue;
}
}
return NO_INTERSECTION;
}
crossProductContinue: ;
}
return PARTIAL_INTERSECTION;
}
QMetaObjectEditor::QMetaObjectEditor(QWidget* parent) : QWidget(parent) {
QVBoxLayout* layout = new QVBoxLayout();
layout->setContentsMargins(QMargins());
@ -407,6 +537,10 @@ void BaseVec3Editor::setSingleStep(double singleStep) {
_z->setSingleStep(singleStep);
}
double BaseVec3Editor::getSingleStep() const {
return _x->singleStep();
}
QDoubleSpinBox* BaseVec3Editor::createComponentBox() {
QDoubleSpinBox* box = new QDoubleSpinBox();
box->setMinimum(-FLT_MAX);

View file

@ -65,6 +65,43 @@ Box operator*(const glm::mat4& matrix, const Box& box);
QDebug& operator<<(QDebug& out, const Box& box);
/// Represents the extents along an axis.
class AxisExtents {
public:
glm::vec3 axis;
float minimum;
float maximum;
/// Creates a set of extents given three points on the first plane and one on the second.
AxisExtents(const glm::vec3& first0, const glm::vec3& first1, const glm::vec3& first2, const glm::vec3& second);
AxisExtents(const glm::vec3& axis = glm::vec3(), float minimum = 0.0f, float maximum = 0.0f);
};
/// A simple pyramidal frustum for intersection testing.
class Frustum {
public:
void set(const glm::vec3& farTopLeft, const glm::vec3& farTopRight, const glm::vec3& farBottomLeft,
const glm::vec3& farBottomRight, const glm::vec3& nearTopLeft, const glm::vec3& nearTopRight,
const glm::vec3& nearBottomLeft, const glm::vec3& nearBottomRight);
enum IntersectionType { NO_INTERSECTION, PARTIAL_INTERSECTION, CONTAINS_INTERSECTION };
IntersectionType getIntersectionType(const Box& box) const;
private:
static const int VERTEX_COUNT = 8;
static const int SIDE_EXTENT_COUNT = 5;
static const int CROSS_PRODUCT_EXTENT_COUNT = 18;
glm::vec3 _vertices[VERTEX_COUNT];
Box _bounds;
AxisExtents _sideExtents[SIDE_EXTENT_COUNT];
AxisExtents _crossProductExtents[CROSS_PRODUCT_EXTENT_COUNT];
};
/// Editor for meta-object values.
class QMetaObjectEditor : public QWidget {
Q_OBJECT
@ -154,6 +191,7 @@ public:
BaseVec3Editor(QWidget* parent);
void setSingleStep(double singleStep);
double getSingleStep() const;
protected slots:

View file

@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtCore/QJsonDocument>
@ -68,29 +69,35 @@ QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringL
int configIndex = argumentList.indexOf(CONFIG_FILE_OPTION);
QString configFilePath;
if (configIndex != -1) {
// we have a config file - try and read it
QString configFilePath = argumentList[configIndex + 1];
QFile configFile(configFilePath);
if (configFile.exists()) {
qDebug() << "Reading JSON config file at" << configFilePath;
configFile.open(QIODevice::ReadOnly);
QJsonDocument configDocument = QJsonDocument::fromJson(configFile.readAll());
QJsonObject rootObject = configDocument.object();
// enumerate the keys of the configDocument object
foreach(const QString& key, rootObject.keys()) {
if (!mergedMap.contains(key)) {
// no match in existing list, add it
mergedMap.insert(key, QVariant(rootObject[key]));
}
configFilePath = argumentList[configIndex + 1];
} else {
// no config file - try to read a file at resources/config.json
configFilePath = QCoreApplication::applicationDirPath() + "/resources/config.json";
}
QFile configFile(configFilePath);
if (configFile.exists()) {
qDebug() << "Reading JSON config file at" << configFilePath;
configFile.open(QIODevice::ReadOnly);
QJsonDocument configDocument = QJsonDocument::fromJson(configFile.readAll());
QJsonObject rootObject = configDocument.object();
// enumerate the keys of the configDocument object
foreach(const QString& key, rootObject.keys()) {
if (!mergedMap.contains(key)) {
// no match in existing list, add it
mergedMap.insert(key, QVariant(rootObject[key]));
}
} else {
qDebug() << "Could not find JSON config file at" << configFilePath;
}
} else {
qDebug() << "Could not find JSON config file at" << configFilePath;
}
return mergedMap;