From 87e33397918d1148684210c93fe2692feb04e07f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 21 Jan 2015 12:28:05 -0800 Subject: [PATCH 01/23] Fix for normal selection with degenerate triangles. --- interface/src/MetavoxelSystem.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index eeb1c84a4e..ea4941ee33 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1498,6 +1498,17 @@ const NormalIndex& IndexVector::get(int y) const { return (relative >= 0 && relative < size()) ? at(relative) : invalidIndex; } +static inline glm::vec3 getNormal(const QVector& vertices, const NormalIndex& i0, + const NormalIndex& i1, const NormalIndex& i2, const NormalIndex& i3) { + // check both triangles in case one is degenerate + const glm::vec3& v0 = vertices.at(i0.indices[0]).vertex; + glm::vec3 normal = glm::cross(vertices.at(i1.indices[0]).vertex - v0, vertices.at(i2.indices[0]).vertex - v0); + if (glm::length(normal) > EPSILON) { + return normal; + } + return glm::cross(vertices.at(i2.indices[0]).vertex - v0, vertices.at(i3.indices[0]).vertex - v0); +} + void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, bool cursor) { if (!node->getHeight()) { @@ -2174,10 +2185,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ - 1), indices.size()); quadIndices.insert(qRgb(reclampedX, y, reclampedZ - 1), indices.size()); } - const glm::vec3& first = vertices.at(index.indices[0]).vertex; - glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, - vertices.at(index3.indices[0]).vertex - first); - + glm::vec3 normal = getNormal(vertices, index, index1, index2, index3); if (alpha0 == 0) { // quad faces negative x indices.append(index3.getClosestIndex(normal = -normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); @@ -2206,10 +2214,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g if (reclampedZ > 0) { quadIndices.insert(qRgb(reclampedX, y, reclampedZ - 1), indices.size()); } - const glm::vec3& first = vertices.at(index.indices[0]).vertex; - glm::vec3 normal = glm::cross(vertices.at(index3.indices[0]).vertex - first, - vertices.at(index1.indices[0]).vertex - first); - + glm::vec3 normal = getNormal(vertices, index, index3, index2, index1); if (alpha0 == 0) { // quad faces negative y indices.append(index3.getClosestIndex(normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); @@ -2235,10 +2240,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ), indices.size()); - const glm::vec3& first = vertices.at(index.indices[0]).vertex; - glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first, - vertices.at(index3.indices[0]).vertex - first); - + glm::vec3 normal = getNormal(vertices, index, index1, index2, index3); if (alpha0 == 0) { // quad faces negative z indices.append(index1.getClosestIndex(normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices)); From 8636f0c48e80374ea3614789a8a9e3f5c6d951f4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 21 Jan 2015 12:57:24 -0800 Subject: [PATCH 02/23] Simplification. --- interface/src/MetavoxelSystem.cpp | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index ea4941ee33..719326f628 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1857,11 +1857,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g if (!(corners & (1 << i))) { continue; } - int offsetX = (i & X_MAXIMUM_FLAG) ? 1 : 0; - int offsetZ = (i & Y_MAXIMUM_FLAG) ? 1 : 0; - const quint16* height = heightLineSrc + offsetZ * width + offsetX; - float heightValue = *height * voxelScale; - if (heightValue >= y && heightValue < y + 1) { + const EdgeCrossing& cornerCrossing = cornerCrossings[i]; + if (cornerCrossing.point.y >= y && cornerCrossing.point.y < y + 1) { crossedCorners |= (1 << i); } } @@ -1901,30 +1898,24 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g if (!(corners & (1 << i))) { continue; } - int offsetX = (i & X_MAXIMUM_FLAG) ? 1 : 0; - int offsetZ = (i & Y_MAXIMUM_FLAG) ? 1 : 0; - const quint16* height = heightLineSrc + offsetZ * width + offsetX; - float heightValue = *height * voxelScale; int nextIndex = NEXT_CORNERS[i]; if (!(corners & (1 << nextIndex))) { continue; } - int nextOffsetX = (nextIndex & X_MAXIMUM_FLAG) ? 1 : 0; - int nextOffsetZ = (nextIndex & Y_MAXIMUM_FLAG) ? 1 : 0; - const quint16* nextHeight = heightLineSrc + nextOffsetZ * width + nextOffsetX; - float nextHeightValue = *nextHeight * voxelScale; - float divisor = (nextHeightValue - heightValue); + const EdgeCrossing& cornerCrossing = cornerCrossings[i]; + const EdgeCrossing& nextCornerCrossing = cornerCrossings[nextIndex]; + float divisor = (nextCornerCrossing.point.y - cornerCrossing.point.y); if (divisor == 0.0f) { continue; } - float t1 = (y - heightValue) / divisor; - float t2 = (y + 1 - heightValue) / divisor; + float t1 = (y - cornerCrossing.point.y) / divisor; + float t2 = (y + 1 - cornerCrossing.point.y) / divisor; if (t1 >= 0.0f && t1 <= 1.0f) { - crossings[crossingCount++].mix(cornerCrossings[i], cornerCrossings[nextIndex], t1); + crossings[crossingCount++].mix(cornerCrossing, nextCornerCrossing, t1); crossings[crossingCount - 1].point.y -= y; } if (t2 >= 0.0f && t2 <= 1.0f) { - crossings[crossingCount++].mix(cornerCrossings[i], cornerCrossings[nextIndex], t2); + crossings[crossingCount++].mix(cornerCrossing, nextCornerCrossing, t2); crossings[crossingCount - 1].point.y -= y; } } From 53a70c43e1b6f2cbd4d99cc522945749b6f2f6d1 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 21 Jan 2015 15:08:38 -0800 Subject: [PATCH 03/23] Add width and height to NetworkTexture --- libraries/render-utils/src/TextureCache.cpp | 6 +++++- libraries/render-utils/src/TextureCache.h | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/TextureCache.cpp b/libraries/render-utils/src/TextureCache.cpp index 3bd05a14ee..4c9abc74a1 100644 --- a/libraries/render-utils/src/TextureCache.cpp +++ b/libraries/render-utils/src/TextureCache.cpp @@ -385,7 +385,9 @@ Texture::~Texture() { NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content) : Resource(url, !content.isEmpty()), _type(type), - _translucent(false) { + _translucent(false), + _width(0), + _height(0) { if (!url.isValid()) { _loaded = true; @@ -532,6 +534,8 @@ void NetworkTexture::loadContent(const QByteArray& content) { void NetworkTexture::setImage(const QImage& image, bool translucent, const QColor& averageColor) { _translucent = translucent; _averageColor = averageColor; + _width = image.width(); + _height = image.height(); finishedLoading(true); imageLoaded(image); diff --git a/libraries/render-utils/src/TextureCache.h b/libraries/render-utils/src/TextureCache.h index 54c98a61cb..efcccc4b8c 100644 --- a/libraries/render-utils/src/TextureCache.h +++ b/libraries/render-utils/src/TextureCache.h @@ -150,6 +150,9 @@ public: /// Returns the lazily-computed average texture color. const QColor& getAverageColor() const { return _averageColor; } + int getWidth() const { return _width; } + int getHeight() const { return _height; } + protected: virtual void downloadFinished(QNetworkReply* reply); @@ -163,6 +166,8 @@ private: TextureType _type; bool _translucent; QColor _averageColor; + int _width; + int _height; }; /// Caches derived, dilated textures. From ba2f7d88ce3cdd7c8ad372457fc5cf9c7c709345 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 21 Jan 2015 15:09:09 -0800 Subject: [PATCH 04/23] Update ImageOverlay to use TextureCache --- interface/src/ui/overlays/ImageOverlay.cpp | 51 ++++++++-------------- interface/src/ui/overlays/ImageOverlay.h | 7 +-- 2 files changed, 19 insertions(+), 39 deletions(-) diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index 1ecfc74e44..164b3916db 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -22,9 +22,7 @@ #include "ImageOverlay.h" ImageOverlay::ImageOverlay() : - _textureID(0), _renderImage(false), - _textureBound(false), _wantClipFromImage(false) { _isLoaded = false; @@ -32,63 +30,48 @@ ImageOverlay::ImageOverlay() : ImageOverlay::ImageOverlay(const ImageOverlay* imageOverlay) : Overlay2D(imageOverlay), + _texture(imageOverlay->_texture), _imageURL(imageOverlay->_imageURL), _textureImage(imageOverlay->_textureImage), - _textureID(0), - _fromImage(), + _fromImage(imageOverlay->_fromImage), _renderImage(imageOverlay->_renderImage), - _textureBound(false), - _wantClipFromImage(false) + _wantClipFromImage(imageOverlay->_wantClipFromImage) { } ImageOverlay::~ImageOverlay() { - if (_parent && _textureID) { - // do we need to call this? - //_parent->deleteTexture(_textureID); - } } // TODO: handle setting image multiple times, how do we manage releasing the bound texture? void ImageOverlay::setImageURL(const QUrl& url) { _imageURL = url; _isLoaded = false; - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url)); - connect(reply, &QNetworkReply::finished, this, &ImageOverlay::replyFinished); -} - -void ImageOverlay::replyFinished() { - QNetworkReply* reply = static_cast(sender()); - - // replace our byte array with the downloaded data - QByteArray rawData = reply->readAll(); - _textureImage.loadFromData(rawData); - _renderImage = true; - _isLoaded = true; - reply->deleteLater(); } void ImageOverlay::render(RenderArgs* args) { - if (!_visible || !_isLoaded) { - return; // do nothing if we're not visible + if (!_isLoaded && !_imageURL.isEmpty()) { + _isLoaded = true; + _renderImage = true; + qDebug() << "Now loding texture for ImageOverlay"; + _texture = DependencyManager::get()->getTexture(_imageURL); } - if (_renderImage && !_textureBound) { - _textureID = _parent->bindTexture(_textureImage); - _textureBound = true; + + if (!_visible || !_isLoaded || !_texture || !_texture->isLoaded()) { + return; } if (_renderImage) { + qDebug() << "Rendering: " << _imageURL << ", " << _texture->getWidth() << ", " << _texture->getHeight(); glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, _textureID); + glBindTexture(GL_TEXTURE_2D, _texture->getID()); } const float MAX_COLOR = 255.0f; xColor color = getColor(); float alpha = getAlpha(); glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - float imageWidth = _textureImage.width(); - float imageHeight = _textureImage.height(); + float imageWidth = _texture->getWidth(); + float imageHeight = _texture->getHeight(); QRect fromImage; if (_wantClipFromImage) { @@ -111,8 +94,8 @@ void ImageOverlay::render(RenderArgs* args) { glm::vec2 topLeft(left, top); glm::vec2 bottomRight(right, bottom); - glm::vec2 texCoordTopLeft(x, 1.0f - y); - glm::vec2 texCoordBottomRight(x + w, 1.0f - (y + h)); + glm::vec2 texCoordTopLeft(x, y); + glm::vec2 texCoordBottomRight(x + w, y + h); if (_renderImage) { DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight); diff --git a/interface/src/ui/overlays/ImageOverlay.h b/interface/src/ui/overlays/ImageOverlay.h index cff557654f..e167c4e755 100644 --- a/interface/src/ui/overlays/ImageOverlay.h +++ b/interface/src/ui/overlays/ImageOverlay.h @@ -24,6 +24,7 @@ #include #include +#include #include "Overlay.h" #include "Overlay2D.h" @@ -49,18 +50,14 @@ public: virtual ImageOverlay* createClone() const; -private slots: - void replyFinished(); // we actually want to hide this... - private: QUrl _imageURL; QImage _textureImage; - GLuint _textureID; + NetworkTexturePointer _texture; QRect _fromImage; // where from in the image to sample bool _renderImage; // is there an image associated with this overlay, or is it just a colored rectangle - bool _textureBound; // has the texture been bound bool _wantClipFromImage; }; From 8bd944e32e038738a95690e8af63ae630a0680f5 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 21 Jan 2015 16:10:00 -0800 Subject: [PATCH 05/23] Incremental improvement to stitching. --- interface/src/MetavoxelSystem.cpp | 49 +++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 719326f628..a0d6a8d72e 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1427,6 +1427,8 @@ public: void setColorMaterial(const StackArray::Entry& entry) { color = entry.color; material = entry.material; } void mix(const EdgeCrossing& first, const EdgeCrossing& second, float t); + + VoxelPoint createPoint(int clampedX, int clampedZ, float step) const; }; void EdgeCrossing::mix(const EdgeCrossing& first, const EdgeCrossing& second, float t) { @@ -1437,6 +1439,16 @@ void EdgeCrossing::mix(const EdgeCrossing& first, const EdgeCrossing& second, fl material = (t < 0.5f) ? first.material : second.material; } +VoxelPoint EdgeCrossing::createPoint(int clampedX, int clampedZ, float step) const { + VoxelPoint voxelPoint = { glm::vec3(clampedX + point.x, point.y, clampedZ + point.z) * step, + { (quint8)qRed(color), (quint8)qGreen(color), (quint8)qBlue(color) }, + { (char)(normal.x * numeric_limits::max()), (char)(normal.y * numeric_limits::max()), + (char)(normal.z * numeric_limits::max()) }, + { (quint8)material, 0, 0, 0 }, + { numeric_limits::max(), 0, 0, 0 } }; + return voxelPoint; +} + const int MAX_NORMALS_PER_VERTEX = 4; class NormalIndex { @@ -1509,6 +1521,19 @@ static inline glm::vec3 getNormal(const QVector& vertices, const Nor return glm::cross(vertices.at(i2.indices[0]).vertex - v0, vertices.at(i3.indices[0]).vertex - v0); } +static inline void appendTriangle(const EdgeCrossing& e0, const EdgeCrossing& e1, const EdgeCrossing& e2, + int clampedX, int clampedZ, float step, QVector& vertices, QVector& indices, + QMultiHash& quadIndices) { + int firstIndex = vertices.size(); + vertices.append(e0.createPoint(clampedX, clampedZ, step)); + vertices.append(e1.createPoint(clampedX, clampedZ, step)); + vertices.append(e2.createPoint(clampedX, clampedZ, step)); + indices.append(firstIndex); + indices.append(firstIndex + 1); + indices.append(firstIndex + 2); + indices.append(firstIndex + 2); +} + void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, bool cursor) { if (!node->getHeight()) { @@ -1865,7 +1890,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g switch (crossedCorners) { case UPPER_LEFT_CORNER: case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER: - case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER: + case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER | UPPER_LEFT_CORNER: case UPPER_LEFT_CORNER | LOWER_RIGHT_CORNER: crossings[crossingCount++] = cornerCrossings[0]; crossings[crossingCount - 1].point.y -= y; @@ -1873,22 +1898,34 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g case UPPER_RIGHT_CORNER: case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER: - case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER: case UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER: crossings[crossingCount++] = cornerCrossings[1]; crossings[crossingCount - 1].point.y -= y; break; - + + case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER: + crossings[crossingCount++] = cornerCrossings[1]; + crossings[crossingCount - 1].point.y -= y; + appendTriangle(cornerCrossings[1], cornerCrossings[0], cornerCrossings[2], + clampedX, clampedZ, step, vertices, indices, quadIndices); + break; + case LOWER_LEFT_CORNER: case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: - case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER | UPPER_LEFT_CORNER: crossings[crossingCount++] = cornerCrossings[2]; crossings[crossingCount - 1].point.y -= y; break; - + + case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: + crossings[crossingCount++] = cornerCrossings[2]; + crossings[crossingCount - 1].point.y -= y; + appendTriangle(cornerCrossings[2], cornerCrossings[3], cornerCrossings[1], + clampedX, clampedZ, step, vertices, indices, quadIndices); + break; + case LOWER_RIGHT_CORNER: case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER: - case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: + case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER: crossings[crossingCount++] = cornerCrossings[3]; crossings[crossingCount - 1].point.y -= y; break; From 0d8380a28839d846c5146e9c76f404802d4114c5 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 21 Jan 2015 17:47:13 -0800 Subject: [PATCH 06/23] Put the indices for our extra triangles into the spatial hash. --- interface/src/MetavoxelSystem.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index a0d6a8d72e..2161887b91 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1532,6 +1532,12 @@ static inline void appendTriangle(const EdgeCrossing& e0, const EdgeCrossing& e1 indices.append(firstIndex + 1); indices.append(firstIndex + 2); indices.append(firstIndex + 2); + + int minimumY = qMin((int)e0.point.y, qMin((int)e1.point.y, (int)e2.point.y)); + int maximumY = qMax((int)e0.point.y, qMax((int)e1.point.y, (int)e2.point.y)); + for (int y = minimumY; y <= maximumY; y++) { + quadIndices.insert(qRgb(clampedX, y, clampedZ), firstIndex); + } } void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const glm::vec3& translation, @@ -1895,7 +1901,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g crossings[crossingCount++] = cornerCrossings[0]; crossings[crossingCount - 1].point.y -= y; break; - + case UPPER_RIGHT_CORNER: case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER: case UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER: @@ -1909,7 +1915,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g appendTriangle(cornerCrossings[1], cornerCrossings[0], cornerCrossings[2], clampedX, clampedZ, step, vertices, indices, quadIndices); break; - + case LOWER_LEFT_CORNER: case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: crossings[crossingCount++] = cornerCrossings[2]; @@ -1922,7 +1928,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g appendTriangle(cornerCrossings[2], cornerCrossings[3], cornerCrossings[1], clampedX, clampedZ, step, vertices, indices, quadIndices); break; - + case LOWER_RIGHT_CORNER: case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER: case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER: From 52193fce387e1d4511838d4d5691de25e8c62771 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 08:35:38 -0800 Subject: [PATCH 07/23] Remove qDebug --- interface/src/ui/overlays/ImageOverlay.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index 164b3916db..b5c235d828 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -52,7 +52,6 @@ void ImageOverlay::render(RenderArgs* args) { if (!_isLoaded && !_imageURL.isEmpty()) { _isLoaded = true; _renderImage = true; - qDebug() << "Now loding texture for ImageOverlay"; _texture = DependencyManager::get()->getTexture(_imageURL); } From 0e9e77f1667c15a2ab032ed362c00c5a54e46914 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 08:58:55 -0800 Subject: [PATCH 08/23] Fix broken support for color-only ImageOverlay --- interface/src/ui/overlays/ImageOverlay.cpp | 63 +++++++++++++--------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index b5c235d828..e18f99072f 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -22,10 +22,10 @@ #include "ImageOverlay.h" ImageOverlay::ImageOverlay() : + _imageURL(), _renderImage(false), _wantClipFromImage(false) { - _isLoaded = false; } ImageOverlay::ImageOverlay(const ImageOverlay* imageOverlay) : @@ -45,47 +45,39 @@ ImageOverlay::~ImageOverlay() { // TODO: handle setting image multiple times, how do we manage releasing the bound texture? void ImageOverlay::setImageURL(const QUrl& url) { _imageURL = url; - _isLoaded = false; + + if (url.isEmpty()) { + _isLoaded = true; + _renderImage = false; + _texture.clear(); + } else { + _isLoaded = false; + _renderImage = true; + } } void ImageOverlay::render(RenderArgs* args) { - if (!_isLoaded && !_imageURL.isEmpty()) { + if (!_isLoaded && _renderImage) { _isLoaded = true; - _renderImage = true; _texture = DependencyManager::get()->getTexture(_imageURL); } - if (!_visible || !_isLoaded || !_texture || !_texture->isLoaded()) { + // If we are not visible or loaded, return. If we are trying to render an + // image but the texture hasn't loaded, return. + if (!_visible || !_isLoaded || (_renderImage && !_texture->isLoaded())) { return; } if (_renderImage) { - qDebug() << "Rendering: " << _imageURL << ", " << _texture->getWidth() << ", " << _texture->getHeight(); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, _texture->getID()); } + const float MAX_COLOR = 255.0f; xColor color = getColor(); float alpha = getAlpha(); glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - float imageWidth = _texture->getWidth(); - float imageHeight = _texture->getHeight(); - - QRect fromImage; - if (_wantClipFromImage) { - fromImage = _fromImage; - } else { - fromImage.setX(0); - fromImage.setY(0); - fromImage.setWidth(imageWidth); - fromImage.setHeight(imageHeight); - } - float x = fromImage.x() / imageWidth; - float y = fromImage.y() / imageHeight; - float w = fromImage.width() / imageWidth; // ?? is this what we want? not sure - float h = fromImage.height() / imageHeight; - int left = _bounds.left(); int right = _bounds.right() + 1; int top = _bounds.top(); @@ -93,10 +85,29 @@ void ImageOverlay::render(RenderArgs* args) { glm::vec2 topLeft(left, top); glm::vec2 bottomRight(right, bottom); - glm::vec2 texCoordTopLeft(x, y); - glm::vec2 texCoordBottomRight(x + w, y + h); if (_renderImage) { + float imageWidth = _texture->getWidth(); + float imageHeight = _texture->getHeight(); + + QRect fromImage; + if (_wantClipFromImage) { + fromImage = _fromImage; + } else { + fromImage.setX(0); + fromImage.setY(0); + fromImage.setWidth(imageWidth); + fromImage.setHeight(imageHeight); + } + + float x = fromImage.x() / imageWidth; + float y = fromImage.y() / imageHeight; + float w = fromImage.width() / imageWidth; // ?? is this what we want? not sure + float h = fromImage.height() / imageHeight; + + glm::vec2 texCoordTopLeft(x, y); + glm::vec2 texCoordBottomRight(x + w, y + h); + DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight); } else { DependencyManager::get()->renderQuad(topLeft, bottomRight); @@ -135,7 +146,7 @@ void ImageOverlay::setProperties(const QScriptValue& properties) { subImageRect.setHeight(oldSubImageRect.height()); } setClipFromSource(subImageRect); - } + } QScriptValue imageURL = properties.property("imageURL"); if (imageURL.isValid()) { From 3997b916c1bcccb640f6f2ce8b11cc4426feef4a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 22 Jan 2015 09:17:51 -0800 Subject: [PATCH 09/23] remove ground collision hack for physics testing --- libraries/physics/src/PhysicsEngine.cpp | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 7f2b139058..c374f03705 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -186,22 +186,6 @@ void PhysicsEngine::init(EntityEditPacketSender* packetSender) { // default gravity of the world is zero, so each object must specify its own gravity // TODO: set up gravity zones _dynamicsWorld->setGravity(btVector3(0.0f, 0.0f, 0.0f)); - - // GROUND HACK: add a big planar floor (and walls for testing) to catch falling objects - btTransform groundTransform; - groundTransform.setIdentity(); - for (int i = 0; i < 3; ++i) { - btVector3 normal(0.0f, 0.0f, 0.0f); - normal[i] = 1.0f; - btCollisionShape* plane = new btStaticPlaneShape(normal, 0.0f); - - btCollisionObject* groundObject = new btCollisionObject(); - groundObject->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT); - groundObject->setCollisionShape(plane); - - groundObject->setWorldTransform(groundTransform); - _dynamicsWorld->addCollisionObject(groundObject); - } } assert(packetSender); From 45a84895e10a0ef649ddcdaed5297356a2e93b96 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 22 Jan 2015 12:44:07 -0800 Subject: [PATCH 10/23] Paddle ball game, first version --- examples/controllers/hydra/paddleBall.js | 164 +++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 examples/controllers/hydra/paddleBall.js diff --git a/examples/controllers/hydra/paddleBall.js b/examples/controllers/hydra/paddleBall.js new file mode 100644 index 0000000000..e5d409bd8a --- /dev/null +++ b/examples/controllers/hydra/paddleBall.js @@ -0,0 +1,164 @@ +// PaddleBall.js +// +// Created by Philip Rosedale on January 21, 2015 +// Copyright 2014 High Fidelity, Inc. +// +// Move your hand with the hydra controller, and hit the ball with the paddle. +// Click 'X' button to turn off this script. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var BALL_SIZE = 0.08; +var PADDLE_SIZE = 0.20; +var PADDLE_THICKNESS = 0.06; +var PADDLE_COLOR = { red: 184, green: 134, blue: 11 }; +var BALL_COLOR = { red: 255, green: 0, blue: 0 }; +var LINE_COLOR = { red: 255, green: 255, blue: 0 }; +var PADDLE_OFFSET = { x: 0.05, y: 0.0, z: 0.0 }; +var GRAVITY = 0.0; +var SPRING_FORCE = 15.0; +var lastSoundTime = 0; +var controllerID = 1; +var gameOn = false; + +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; +hitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-ballhitsandcatches/billiards/collision1.wav"); + +var screenSize = Controller.getViewportDimensions(); +var offButton = Overlays.addOverlay("image", { + x: screenSize.x - 48, + y: 96, + width: 32, + height: 32, + imageURL: HIFI_PUBLIC_BUCKET + "images/close.png", + color: { red: 255, green: 255, blue: 255}, + alpha: 1 + }); + +var ball, paddle, paddleModel, line; + +function createEntities() { + ball = Entities.addEntity( + { type: "Sphere", + position: Controller.getSpatialControlPosition(controllerID), + dimensions: { x: BALL_SIZE, y: BALL_SIZE, z: BALL_SIZE }, + color: BALL_COLOR, + gravity: { x: 0, y: GRAVITY, z: 0 }, + ignoreCollisions: false, + damping: 0.50, + collisionsWillMove: true }); + + paddle = Entities.addEntity( + { type: "Box", + position: Controller.getSpatialControlPosition(controllerID), + dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 0.80 }, + color: PADDLE_COLOR, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: false, + damping: 0.10, + visible: false, + rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), + collisionsWillMove: false }); + + modelURL = "http://public.highfidelity.io/models/attachments/pong_paddle.fbx"; + paddleModel = Entities.addEntity( + { type: "Model", + position: Vec3.sum(Controller.getSpatialControlPosition(controllerID), PADDLE_OFFSET), + dimensions: { x: PADDLE_SIZE * 1.5, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 1.25 }, + color: PADDLE_COLOR, + gravity: { x: 0, y: 0, z: 0 }, + ignoreCollisions: true, + modelURL: modelURL, + damping: 0.10, + rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), + collisionsWillMove: false }); + + line = Overlays.addOverlay("line3d", { + start: { x: 0, y: 0, z: 0 }, + end: { x: 0, y: 0, z: 0 }, + color: LINE_COLOR, + alpha: 1, + visible: true, + lineWidth: 2 }); +} + +function deleteEntities() { + Entities.deleteEntity(ball); + Entities.deleteEntity(paddle); + Entities.deleteEntity(paddleModel); + Overlays.deleteOverlay(line); +} + +function update(deltaTime) { + var palmPosition = Controller.getSpatialControlPosition(controllerID); + var controllerActive = (Vec3.length(palmPosition) > 0); + + if (!gameOn && controllerActive) { + createEntities(); + gameOn = true; + } else if (gameOn && !controllerActive) { + deleteEntities(); + gameOn = false; + } + if (!gameOn || !controllerActive) { + return; + } + + if (!paddle.isKnownID) { + paddle = Entities.identifyEntity(paddle); + } + if (!ball.isKnownID) { + ball = Entities.identifyEntity(ball); + } else { + var props = Entities.getEntityProperties(ball); + var spring = Vec3.subtract(palmPosition, props.position); + var paddleWorldOrientation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)); + var springLength = Vec3.length(spring); + spring = Vec3.normalize(spring); + var ballVelocity = Vec3.sum(props.velocity, Vec3.multiply(springLength * SPRING_FORCE * deltaTime, spring)); + Entities.editEntity(ball, { velocity: ballVelocity }); + Overlays.editOverlay(line, { start: props.position, end: palmPosition }); + Entities.editEntity(paddle, { position: palmPosition, + velocity: Controller.getSpatialControlVelocity(controllerID), + rotation: paddleWorldOrientation }); + Entities.editEntity(paddleModel, { position: Vec3.sum(palmPosition, Vec3.multiplyQbyV(paddleWorldOrientation, PADDLE_OFFSET)), + velocity: Controller.getSpatialControlVelocity(controllerID), + rotation: paddleWorldOrientation }); + } +} + +function entityCollisionWithEntity(entity1, entity2, collision) { + if ((entity1.id == ball.id) || (entity2.id ==ball.id)) { + var props1 = Entities.getEntityProperties(entity1); + var props2 = Entities.getEntityProperties(entity2); + var dVel = Vec3.length(Vec3.subtract(props1.velocity, props2.velocity)); + var currentTime = new Date().getTime(); + var MIN_MSECS_BETWEEN_BOUNCE_SOUNDS = 100; + var MIN_VELOCITY_FOR_SOUND_IMPACT = 0.25; + if ((dVel > MIN_VELOCITY_FOR_SOUND_IMPACT) && (currentTime - lastSoundTime) > MIN_MSECS_BETWEEN_BOUNCE_SOUNDS) { + Audio.playSound(hitSound, { position: props1.position, volume: Math.min(dVel, 1.0) }); + lastSoundTime = new Date().getTime(); + } + } +} + +function mousePressEvent(event) { + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + if (clickedOverlay == offButton) { + Script.stop(); + } +} + +function scriptEnding() { + if (gameOn) { + deleteEntities(); + } + Overlays.deleteOverlay(offButton); +} + +Entities.entityCollisionWithEntity.connect(entityCollisionWithEntity); +Controller.mousePressEvent.connect(mousePressEvent); +Script.scriptEnding.connect(scriptEnding); +Script.update.connect(update); From 5efc046316d658bca622c585c7983b839734076f Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Thu, 22 Jan 2015 15:03:13 -0600 Subject: [PATCH 11/23] Adding http monitoring for ICE server --- ice-server/CMakeLists.txt | 16 +++++++++++++++- ice-server/src/IceServer.cpp | 36 +++++++++++++++++++++++++++++++++++- ice-server/src/IceServer.h | 14 ++++++++++---- 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/ice-server/CMakeLists.txt b/ice-server/CMakeLists.txt index 24e780f9aa..a7b2a206e4 100644 --- a/ice-server/CMakeLists.txt +++ b/ice-server/CMakeLists.txt @@ -4,6 +4,20 @@ set(TARGET_NAME ice-server) setup_hifi_project(Network) # link the shared hifi libraries -link_hifi_libraries(networking shared) +link_hifi_libraries(embedded-webserver networking shared) + +# find OpenSSL +find_package(OpenSSL REQUIRED) + +if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include") +# this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto +message(WARNING "The found version of OpenSSL is the OS X system version. This will produce deprecation warnings." +"\nWe recommend you install a newer version (at least 1.0.1h) in a different directory and set OPENSSL_ROOT_DIR in your env so Cmake can find it.") +endif () + +include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") + +# append OpenSSL to our list of libraries to link +target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES}) include_dependency_includes() \ No newline at end of file diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index c06bb0fc88..531dd4ea22 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -20,14 +20,19 @@ const int CLEAR_INACTIVE_PEERS_INTERVAL_MSECS = 1 * 1000; const int PEER_SILENCE_THRESHOLD_MSECS = 5 * 1000; +const quint16 ICE_SERVER_MONITORING_PORT = 40110; + IceServer::IceServer(int argc, char* argv[]) : QCoreApplication(argc, argv), _id(QUuid::createUuid()), _serverSocket(), - _activePeers() + _activePeers(), + _httpManager(ICE_SERVER_MONITORING_PORT, QString("%1/web/").arg(QCoreApplication::applicationDirPath()), this), + _httpsManager(NULL) { // start the ice-server socket qDebug() << "ice-server socket is listening on" << ICE_SERVER_DEFAULT_PORT; + qDebug() << "monitoring http endpoint is listening on " << ICE_SERVER_MONITORING_PORT; _serverSocket.bind(QHostAddress::AnyIPv4, ICE_SERVER_DEFAULT_PORT); // call our process datagrams slot when the UDP socket has packets ready @@ -165,3 +170,32 @@ void IceServer::clearInactivePeers() { } } } + +bool IceServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) { + // + // We need an HTTP handler in order to monitor the health of the ice server + // The correct functioning of the ICE server will first be determined by its HTTP availability, + // and then by the existence of a minimum number of peers in the list, matching the minimum number of + // domains in production by High Fidelity. + // + + int MINIMUM_PEERS = 3; + bool IS_HEALTHY = false; + + IS_HEALTHY = _activePeers.size() >= MINIMUM_PEERS ? true : false; + + if (connection->requestOperation() == QNetworkAccessManager::GetOperation) { + if (url.path() == "/status") { + if (IS_HEALTHY) { + connection->respond(HTTPConnection::StatusCode200, QByteArray::number(_activePeers.size())); + } else { + connection->respond(HTTPConnection::StatusCode404, QByteArray::number(_activePeers.size())); + } + } + } + return true; +} + +bool IceServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url, bool skipSubHandler) { + return true; +} diff --git a/ice-server/src/IceServer.h b/ice-server/src/IceServer.h index e15bda1211..effc8b8154 100644 --- a/ice-server/src/IceServer.h +++ b/ice-server/src/IceServer.h @@ -12,17 +12,21 @@ #ifndef hifi_IceServer_h #define hifi_IceServer_h -#include -#include -#include +#include +#include +#include #include +#include typedef QHash NetworkPeerHash; -class IceServer : public QCoreApplication { +class IceServer : public QCoreApplication, public HTTPSRequestHandler { + Q_OBJECT public: IceServer(int argc, char* argv[]); + bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false); + bool handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url, bool skipSubHandler = false); private slots: void processDatagrams(); void clearInactivePeers(); @@ -34,6 +38,8 @@ private: QUdpSocket _serverSocket; NetworkPeerHash _activePeers; QHash > _currentConnections; + HTTPManager _httpManager; + HTTPSManager* _httpsManager; }; #endif // hifi_IceServer_h \ No newline at end of file From 162c2f031bd16d5e1a5c33b2397405d6c36463ae Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Thu, 22 Jan 2015 15:33:37 -0600 Subject: [PATCH 12/23] Remove https unneeded crust --- ice-server/CMakeLists.txt | 14 -------------- ice-server/src/IceServer.cpp | 7 +------ ice-server/src/IceServer.h | 4 +--- 3 files changed, 2 insertions(+), 23 deletions(-) diff --git a/ice-server/CMakeLists.txt b/ice-server/CMakeLists.txt index a7b2a206e4..6a8ca5bd9f 100644 --- a/ice-server/CMakeLists.txt +++ b/ice-server/CMakeLists.txt @@ -6,18 +6,4 @@ setup_hifi_project(Network) # link the shared hifi libraries link_hifi_libraries(embedded-webserver networking shared) -# find OpenSSL -find_package(OpenSSL REQUIRED) - -if (APPLE AND ${OPENSSL_INCLUDE_DIR} STREQUAL "/usr/include") -# this is a user on OS X using system OpenSSL, which is going to throw warnings since they're deprecating for their common crypto -message(WARNING "The found version of OpenSSL is the OS X system version. This will produce deprecation warnings." -"\nWe recommend you install a newer version (at least 1.0.1h) in a different directory and set OPENSSL_ROOT_DIR in your env so Cmake can find it.") -endif () - -include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") - -# append OpenSSL to our list of libraries to link -target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES}) - include_dependency_includes() \ No newline at end of file diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index 531dd4ea22..4648656e87 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -27,8 +27,7 @@ IceServer::IceServer(int argc, char* argv[]) : _id(QUuid::createUuid()), _serverSocket(), _activePeers(), - _httpManager(ICE_SERVER_MONITORING_PORT, QString("%1/web/").arg(QCoreApplication::applicationDirPath()), this), - _httpsManager(NULL) + _httpManager(ICE_SERVER_MONITORING_PORT, QString("%1/web/").arg(QCoreApplication::applicationDirPath()), this) { // start the ice-server socket qDebug() << "ice-server socket is listening on" << ICE_SERVER_DEFAULT_PORT; @@ -195,7 +194,3 @@ bool IceServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, b } return true; } - -bool IceServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url, bool skipSubHandler) { - return true; -} diff --git a/ice-server/src/IceServer.h b/ice-server/src/IceServer.h index effc8b8154..5367786d01 100644 --- a/ice-server/src/IceServer.h +++ b/ice-server/src/IceServer.h @@ -21,12 +21,11 @@ typedef QHash NetworkPeerHash; -class IceServer : public QCoreApplication, public HTTPSRequestHandler { +class IceServer : public QCoreApplication, public HTTPRequestHandler { Q_OBJECT public: IceServer(int argc, char* argv[]); bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false); - bool handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url, bool skipSubHandler = false); private slots: void processDatagrams(); void clearInactivePeers(); @@ -39,7 +38,6 @@ private: NetworkPeerHash _activePeers; QHash > _currentConnections; HTTPManager _httpManager; - HTTPSManager* _httpsManager; }; #endif // hifi_IceServer_h \ No newline at end of file From 001d6896464cf0f6c3123f99c8b6308b816f96eb Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Thu, 22 Jan 2015 15:37:58 -0600 Subject: [PATCH 13/23] Fixing includes --- ice-server/src/IceServer.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ice-server/src/IceServer.h b/ice-server/src/IceServer.h index 5367786d01..be6d298e3d 100644 --- a/ice-server/src/IceServer.h +++ b/ice-server/src/IceServer.h @@ -17,7 +17,8 @@ #include #include -#include +#include +#include typedef QHash NetworkPeerHash; From 59f79fd4368bba983f120d076d63ab4baadede8a Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 22 Jan 2015 13:42:19 -0800 Subject: [PATCH 14/23] Add handedness choice, default to right handed --- examples/controllers/hydra/paddleBall.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/examples/controllers/hydra/paddleBall.js b/examples/controllers/hydra/paddleBall.js index e5d409bd8a..85b025e4cd 100644 --- a/examples/controllers/hydra/paddleBall.js +++ b/examples/controllers/hydra/paddleBall.js @@ -20,8 +20,16 @@ var PADDLE_OFFSET = { x: 0.05, y: 0.0, z: 0.0 }; var GRAVITY = 0.0; var SPRING_FORCE = 15.0; var lastSoundTime = 0; -var controllerID = 1; var gameOn = false; +var leftHanded = false; +var controllerID; + +if (leftHanded) { + controllerID = 1; +} else { + controllerID = 3; +} + HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; hitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-ballhitsandcatches/billiards/collision1.wav"); From 805d5ace13cf8d5411689776071b734edc8be27a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 22 Jan 2015 14:55:14 -0800 Subject: [PATCH 15/23] Another slight stitching improvement. --- interface/src/MetavoxelSystem.cpp | 87 ++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 20 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 2161887b91..e91ff89270 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1540,6 +1540,50 @@ static inline void appendTriangle(const EdgeCrossing& e0, const EdgeCrossing& e1 } } +const int CORNER_COUNT = 4; + +static inline StackArray::Entry getEntry(const StackArray* lineSrc, int stackWidth, int y, float heightfieldHeight, + EdgeCrossing cornerCrossings[CORNER_COUNT], int cornerIndex) { + const EdgeCrossing& cornerCrossing = cornerCrossings[cornerIndex]; + if (cornerCrossing.point.y == 0.0f) { + int offsetX = (cornerIndex & X_MAXIMUM_FLAG) ? 1 : 0; + int offsetZ = (cornerIndex & Y_MAXIMUM_FLAG) ? 1 : 0; + return lineSrc[offsetZ * stackWidth + offsetX].getEntry(y, heightfieldHeight); + } + StackArray::Entry entry; + bool set = false; + if (cornerCrossing.point.y >= y) { + entry.color = cornerCrossing.color; + entry.material = cornerCrossing.material; + set = true; + + if (cornerCrossing.point.y < y + 1) { + entry.setHermiteY(cornerCrossing.normal, cornerCrossing.point.y - y); + } + } else { + entry.material = entry.color = 0; + } + if (!(cornerIndex & X_MAXIMUM_FLAG)) { + const EdgeCrossing& nextCornerCrossingX = cornerCrossings[cornerIndex | X_MAXIMUM_FLAG]; + if (nextCornerCrossingX.point.y != 0.0f && (nextCornerCrossingX.point.y >= y) != set) { + float t = (y - cornerCrossing.point.y) / (nextCornerCrossingX.point.y - cornerCrossing.point.y); + if (t >= 0.0f && t <= 1.0f) { + entry.setHermiteX(glm::normalize(glm::mix(cornerCrossing.normal, nextCornerCrossingX.normal, t)), t); + } + } + } + if (!(cornerIndex & Y_MAXIMUM_FLAG)) { + const EdgeCrossing& nextCornerCrossingZ = cornerCrossings[cornerIndex | Y_MAXIMUM_FLAG]; + if (nextCornerCrossingZ.point.y != 0.0f && (nextCornerCrossingZ.point.y >= y) != set) { + float t = (y - cornerCrossing.point.y) / (nextCornerCrossingZ.point.y - cornerCrossing.point.y); + if (t >= 0.0f && t <= 1.0f) { + entry.setHermiteZ(glm::normalize(glm::mix(cornerCrossing.normal, nextCornerCrossingZ.normal, t)), t); + } + } + } + return entry; +} + void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale, bool cursor) { if (!node->getHeight()) { @@ -1748,7 +1792,6 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g const int LOWER_RIGHT_CORNER = 8; const int NO_CORNERS = 0; const int ALL_CORNERS = UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER | LOWER_RIGHT_CORNER; - const int CORNER_COUNT = 4; const int NEXT_CORNERS[] = { 1, 3, 0, 2 }; int corners = NO_CORNERS; if (heightfieldHeight != 0.0f) { @@ -1823,7 +1866,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g indicesZ[x].position = position; indicesZ[x].resize(count); for (int y = position, end = position + count; y < end; y++) { - const StackArray::Entry& entry = lineSrc->getEntry(y, heightfieldHeight); + StackArray::Entry entry = getEntry(lineSrc, stackWidth, y, heightfieldHeight, cornerCrossings, 0); if (displayHermite && x != 0 && z != 0 && !lineSrc->isEmpty() && y >= lineSrc->getPosition()) { glm::vec3 normal; if (entry.hermiteX != 0) { @@ -1969,10 +2012,13 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g // the terrifying conditional code that follows checks each cube edge for a crossing, gathering // its properties (color, material, normal) if one is present; as before, boundary edges are excluded if (crossingCount == 0) { - const StackArray::Entry& nextEntryY = lineSrc->getEntry(y + 1, heightfieldHeight); + StackArray::Entry nextEntryY = getEntry(lineSrc, stackWidth, y + 1, + heightfieldHeight, cornerCrossings, 0); if (middleX) { - const StackArray::Entry& nextEntryX = lineSrc[1].getEntry(y, nextHeightfieldHeightX); - const StackArray::Entry& nextEntryXY = lineSrc[1].getEntry(y + 1, nextHeightfieldHeightX); + StackArray::Entry nextEntryX = getEntry(lineSrc, stackWidth, y, nextHeightfieldHeightX, + cornerCrossings, 1); + StackArray::Entry nextEntryXY = getEntry(lineSrc, stackWidth, y + 1, nextHeightfieldHeightX, + cornerCrossings, 1); if (alpha0 != alpha1) { EdgeCrossing& crossing = crossings[crossingCount++]; crossing.point = glm::vec3(entry.getHermiteX(crossing.normal), 0.0f, 0.0f); @@ -1989,12 +2035,12 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g crossing.setColorMaterial(alpha2 == 0 ? nextEntryXY : nextEntryY); } if (middleZ) { - const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y, - nextHeightfieldHeightZ); - const StackArray::Entry& nextEntryXZ = lineSrc[stackWidth + 1].getEntry( - y, nextHeightfieldHeightXZ); - const StackArray::Entry& nextEntryXYZ = lineSrc[stackWidth + 1].getEntry( - y + 1, nextHeightfieldHeightXZ); + StackArray::Entry nextEntryZ = getEntry(lineSrc, stackWidth, y, nextHeightfieldHeightZ, + cornerCrossings, 2); + StackArray::Entry nextEntryXZ = getEntry(lineSrc, stackWidth, y, nextHeightfieldHeightXZ, + cornerCrossings, 3); + StackArray::Entry nextEntryXYZ = getEntry(lineSrc, stackWidth, y + 1, + nextHeightfieldHeightXZ, cornerCrossings, 3); if (alpha1 != alpha5) { EdgeCrossing& crossing = crossings[crossingCount++]; crossing.point = glm::vec3(1.0f, 0.0f, nextEntryX.getHermiteZ(crossing.normal)); @@ -2002,8 +2048,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } if (alpha3 != alpha7) { EdgeCrossing& crossing = crossings[crossingCount++]; - const StackArray::Entry& nextEntryXY = lineSrc[1].getEntry(y + 1, - nextHeightfieldHeightX); + StackArray::Entry nextEntryXY = getEntry(lineSrc, stackWidth, y + 1, + nextHeightfieldHeightX, cornerCrossings, 1); crossing.point = glm::vec3(1.0f, 1.0f, nextEntryXY.getHermiteZ(crossing.normal)); crossing.setColorMaterial(alpha3 == 0 ? nextEntryXYZ : nextEntryXY); } @@ -2014,15 +2060,15 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } if (alpha5 != alpha7) { EdgeCrossing& crossing = crossings[crossingCount++]; - const StackArray::Entry& nextEntryXZ = lineSrc[stackWidth + 1].getEntry( - y, nextHeightfieldHeightXZ); + StackArray::Entry nextEntryXZ = getEntry(lineSrc, stackWidth, y, + nextHeightfieldHeightXZ, cornerCrossings, 3); crossing.point = glm::vec3(1.0f, nextEntryXZ.getHermiteY(crossing.normal), 1.0f); crossing.setColorMaterial(alpha5 == 0 ? nextEntryXYZ : nextEntryXZ); } if (alpha6 != alpha7) { EdgeCrossing& crossing = crossings[crossingCount++]; - const StackArray::Entry& nextEntryYZ = lineSrc[stackWidth].getEntry( - y + 1, nextHeightfieldHeightZ); + StackArray::Entry nextEntryYZ = getEntry(lineSrc, stackWidth, y + 1, + nextHeightfieldHeightZ, cornerCrossings, 2); crossing.point = glm::vec3(nextEntryYZ.getHermiteX(crossing.normal), 1.0f, 1.0f); crossing.setColorMaterial(alpha6 == 0 ? nextEntryXYZ : nextEntryYZ); } @@ -2034,9 +2080,10 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g crossing.setColorMaterial(alpha0 == 0 ? nextEntryY : entry); } if (middleZ) { - const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y, nextHeightfieldHeightZ); - const StackArray::Entry& nextEntryYZ = lineSrc[stackWidth].getEntry(y + 1, - nextHeightfieldHeightZ); + StackArray::Entry nextEntryZ = getEntry(lineSrc, stackWidth, y, + nextHeightfieldHeightZ, cornerCrossings, 2); + StackArray::Entry nextEntryYZ = getEntry(lineSrc, stackWidth, y + 1, + nextHeightfieldHeightZ, cornerCrossings, 2); if (alpha0 != alpha4) { EdgeCrossing& crossing = crossings[crossingCount++]; crossing.point = glm::vec3(0.0f, 0.0f, entry.getHermiteZ(crossing.normal)); From 0e6f2d7252f8943897ae1032457a9a45b27494a8 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 22 Jan 2015 16:22:36 -0800 Subject: [PATCH 16/23] Update runningScriptsWidgets px -> pt --- interface/ui/runningScriptsWidget.ui | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/interface/ui/runningScriptsWidget.ui b/interface/ui/runningScriptsWidget.ui index 3d7db5064a..ec078681e3 100644 --- a/interface/ui/runningScriptsWidget.ui +++ b/interface/ui/runningScriptsWidget.ui @@ -70,7 +70,7 @@ Helvetica,Arial,sans-serif - -1 + 16 75 false true @@ -78,7 +78,7 @@ color: #0e7077; -font: bold 16px; +font: bold 16pt; @@ -192,7 +192,7 @@ font: bold 16px; - font: 14px; color: #5f5f5f; margin: 2px; + font: 14pt; color: #5f5f5f; margin: 2px; There are no scripts running. @@ -245,8 +245,8 @@ font: bold 16px; 0 0 - 334 - 20 + 328 + 18 @@ -259,7 +259,7 @@ font: bold 16px; Qt::LeftToRight - font-size: 14px; + font-size: 14pt; @@ -301,7 +301,7 @@ font: bold 16px; - font: 14px; color: #5f5f5f; margin: 2px; + font: 14pt; color: #5f5f5f; margin: 2px; Tip @@ -370,7 +370,7 @@ font: bold 16px; color: #0e7077; -font: bold 16px; +font: bold 16pt; Load Scripts From e65090bab917d781637b2fad748914d759288928 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 22 Jan 2015 21:08:48 -0800 Subject: [PATCH 17/23] Another slight stitching improvement. --- interface/src/MetavoxelSystem.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index e91ff89270..7edabb0beb 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1857,6 +1857,15 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g } minimumY = qMin(minimumY, cornerMinimumY); maximumY = qMax(maximumY, cornerMaximumY); + + if (corners == (LOWER_LEFT_CORNER | UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER)) { + appendTriangle(cornerCrossings[1], cornerCrossings[0], cornerCrossings[2], + clampedX, clampedZ, step, vertices, indices, quadIndices); + + } else if (corners == (UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER)) { + appendTriangle(cornerCrossings[2], cornerCrossings[3], cornerCrossings[1], + clampedX, clampedZ, step, vertices, indices, quadIndices); + } } int position = minimumY; int count = maximumY - minimumY + 1; @@ -1948,28 +1957,16 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g case UPPER_RIGHT_CORNER: case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER: case UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER: - crossings[crossingCount++] = cornerCrossings[1]; - crossings[crossingCount - 1].point.y -= y; - break; - case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER: crossings[crossingCount++] = cornerCrossings[1]; crossings[crossingCount - 1].point.y -= y; - appendTriangle(cornerCrossings[1], cornerCrossings[0], cornerCrossings[2], - clampedX, clampedZ, step, vertices, indices, quadIndices); break; case LOWER_LEFT_CORNER: case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: - crossings[crossingCount++] = cornerCrossings[2]; - crossings[crossingCount - 1].point.y -= y; - break; - case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: crossings[crossingCount++] = cornerCrossings[2]; crossings[crossingCount - 1].point.y -= y; - appendTriangle(cornerCrossings[2], cornerCrossings[3], cornerCrossings[1], - clampedX, clampedZ, step, vertices, indices, quadIndices); break; case LOWER_RIGHT_CORNER: From c124dffd6ab1f3f3988decc684c1457ab3974dbb Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 22 Jan 2015 21:26:08 -0800 Subject: [PATCH 18/23] Another slight improvement. --- interface/src/MetavoxelSystem.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 7edabb0beb..1d2caa6c2c 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1556,29 +1556,25 @@ static inline StackArray::Entry getEntry(const StackArray* lineSrc, int stackWid entry.color = cornerCrossing.color; entry.material = cornerCrossing.material; set = true; + entry.setHermiteY(cornerCrossing.normal, glm::clamp(cornerCrossing.point.y - y, 0.0f, 1.0f)); - if (cornerCrossing.point.y < y + 1) { - entry.setHermiteY(cornerCrossing.normal, cornerCrossing.point.y - y); - } } else { entry.material = entry.color = 0; } if (!(cornerIndex & X_MAXIMUM_FLAG)) { const EdgeCrossing& nextCornerCrossingX = cornerCrossings[cornerIndex | X_MAXIMUM_FLAG]; if (nextCornerCrossingX.point.y != 0.0f && (nextCornerCrossingX.point.y >= y) != set) { - float t = (y - cornerCrossing.point.y) / (nextCornerCrossingX.point.y - cornerCrossing.point.y); - if (t >= 0.0f && t <= 1.0f) { - entry.setHermiteX(glm::normalize(glm::mix(cornerCrossing.normal, nextCornerCrossingX.normal, t)), t); - } + float t = glm::clamp((y - cornerCrossing.point.y) / + (nextCornerCrossingX.point.y - cornerCrossing.point.y), 0.0f, 1.0f); + entry.setHermiteX(glm::normalize(glm::mix(cornerCrossing.normal, nextCornerCrossingX.normal, t)), t); } } if (!(cornerIndex & Y_MAXIMUM_FLAG)) { const EdgeCrossing& nextCornerCrossingZ = cornerCrossings[cornerIndex | Y_MAXIMUM_FLAG]; if (nextCornerCrossingZ.point.y != 0.0f && (nextCornerCrossingZ.point.y >= y) != set) { - float t = (y - cornerCrossing.point.y) / (nextCornerCrossingZ.point.y - cornerCrossing.point.y); - if (t >= 0.0f && t <= 1.0f) { - entry.setHermiteZ(glm::normalize(glm::mix(cornerCrossing.normal, nextCornerCrossingZ.normal, t)), t); - } + float t = glm::clamp((y - cornerCrossing.point.y) / + (nextCornerCrossingZ.point.y - cornerCrossing.point.y), 0.0f, 1.0f); + entry.setHermiteZ(glm::normalize(glm::mix(cornerCrossing.normal, nextCornerCrossingZ.normal, t)), t); } } return entry; From b8a15f0726c90943f07bbc521ab7c37a1fa0bb24 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 22 Jan 2015 22:04:10 -0800 Subject: [PATCH 19/23] Another tweak. --- interface/src/MetavoxelSystem.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 1d2caa6c2c..80c7c3f41f 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1542,13 +1542,21 @@ static inline void appendTriangle(const EdgeCrossing& e0, const EdgeCrossing& e1 const int CORNER_COUNT = 4; -static inline StackArray::Entry getEntry(const StackArray* lineSrc, int stackWidth, int y, float heightfieldHeight, +static StackArray::Entry getEntry(const StackArray* lineSrc, int stackWidth, int y, float heightfieldHeight, EdgeCrossing cornerCrossings[CORNER_COUNT], int cornerIndex) { + int offsetX = (cornerIndex & X_MAXIMUM_FLAG) ? 1 : 0; + int offsetZ = (cornerIndex & Y_MAXIMUM_FLAG) ? 1 : 0; + const StackArray& src = lineSrc[offsetZ * stackWidth + offsetX]; + int count = src.getEntryCount(); + if (count > 0) { + int relative = y - src.getPosition(); + if (relative < count && (relative >= 0 || heightfieldHeight == 0.0f)) { + return src.getEntry(y, heightfieldHeight); + } + } const EdgeCrossing& cornerCrossing = cornerCrossings[cornerIndex]; if (cornerCrossing.point.y == 0.0f) { - int offsetX = (cornerIndex & X_MAXIMUM_FLAG) ? 1 : 0; - int offsetZ = (cornerIndex & Y_MAXIMUM_FLAG) ? 1 : 0; - return lineSrc[offsetZ * stackWidth + offsetX].getEntry(y, heightfieldHeight); + return src.getEntry(y, heightfieldHeight); } StackArray::Entry entry; bool set = false; From d173afaa70dc2bc68cdad9d6b7cfefb4c21afa81 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 23 Jan 2015 11:01:46 -0800 Subject: [PATCH 20/23] add support for non-physical kinematic movement --- libraries/entities/src/EntityItem.cpp | 23 +++- libraries/physics/src/EntityMotionState.cpp | 41 ++++--- libraries/physics/src/EntityMotionState.h | 5 +- libraries/physics/src/ObjectMotionState.cpp | 26 +++-- libraries/physics/src/ObjectMotionState.h | 14 ++- libraries/physics/src/PhysicsEngine.cpp | 119 +++++++++++++++++--- libraries/physics/src/PhysicsEngine.h | 9 +- 7 files changed, 176 insertions(+), 61 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 7e3e982fb8..c830236287 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -520,7 +520,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData); recalculateCollisionShape(); - if (overwriteLocalData && (getDirtyFlags() & EntityItem::DIRTY_POSITION)) { + if (overwriteLocalData && (getDirtyFlags() & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY))) { _lastSimulated = now; } } @@ -770,6 +770,9 @@ void EntityItem::simulateSimpleKinematicMotion(float timeElapsed) { float angularSpeed = glm::length(_angularVelocity); const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.1f; // if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) { + if (angularSpeed > 0.0f) { + _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; + } setAngularVelocity(ENTITY_ITEM_ZERO_VEC3); } else { // NOTE: angularSpeed is currently in degrees/sec!!! @@ -799,10 +802,18 @@ void EntityItem::simulateSimpleKinematicMotion(float timeElapsed) { // "ground" plane of the domain, but for now it's what we've got velocity += getGravity() * timeElapsed; } - - // NOTE: the simulation should NOT set any DirtyFlags on this entity - setPosition(position); // this will automatically recalculate our collision shape - setVelocity(velocity); + + float speed = glm::length(velocity); + const float EPSILON_LINEAR_VELOCITY_LENGTH = 0.001f; // 1mm/sec + if (speed < EPSILON_LINEAR_VELOCITY_LENGTH) { + setVelocity(ENTITY_ITEM_ZERO_VEC3); + if (speed > 0.0f) { + _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; + } + } else { + setPosition(position); // this will automatically recalculate our collision shape + setVelocity(velocity); + } } } @@ -886,7 +897,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { if (_created != UNKNOWN_CREATED_TIME) { setLastEdited(now); } - if (getDirtyFlags() & EntityItem::DIRTY_POSITION) { + if (getDirtyFlags() & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY)) { _lastSimulated = now; } } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 8b6fb1ea9f..b4aa9d0e7e 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -14,7 +14,7 @@ #include "BulletUtil.h" #include "EntityMotionState.h" -#include "SimpleEntityKinematicController.h" +#include "PhysicsEngine.h" QSet* _outgoingEntityList; @@ -41,8 +41,6 @@ EntityMotionState::~EntityMotionState() { assert(_entity); _entity->setPhysicsInfo(NULL); _entity = NULL; - delete _kinematicController; - _kinematicController = NULL; } MotionType EntityMotionState::computeMotionType() const { @@ -52,13 +50,15 @@ MotionType EntityMotionState::computeMotionType() const { return _entity->isMoving() ? MOTION_TYPE_KINEMATIC : MOTION_TYPE_STATIC; } -void EntityMotionState::addKinematicController() { - if (!_kinematicController) { - _kinematicController = new SimpleEntityKinematicController(_entity); - _kinematicController->start(); - } else { - _kinematicController->start(); - } +void EntityMotionState::updateKinematicState(uint32_t substep) { + setKinematic(_entity->isMoving(), substep); +} + +void EntityMotionState::stepKinematicSimulation(uint32_t substep) { + assert(_isKinematic); + float dt = (substep - _lastKinematicSubstep) * PHYSICS_ENGINE_FIXED_SUBSTEP; + _entity->simulateSimpleKinematicMotion(dt); + _lastKinematicSubstep = substep; } // This callback is invoked by the physics simulation in two cases: @@ -67,8 +67,11 @@ void EntityMotionState::addKinematicController() { // (2) at the beginning of each simulation frame for KINEMATIC RigidBody's -- // it is an opportunity for outside code to update the object's simulation position void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { - if (_kinematicController && _kinematicController->isRunning()) { - _kinematicController->stepForward(); + if (_isKinematic) { + uint32_t substep = PhysicsEngine::getNumSubsteps(); + // remove const-ness so we can actually update this instance + EntityMotionState* thisMotion = const_cast(this); + thisMotion->stepKinematicSimulation(substep); } worldTrans.setOrigin(glmToBullet(_entity->getPositionInMeters() - ObjectMotionState::getWorldOffset())); worldTrans.setRotation(glmToBullet(_entity->getRotation())); @@ -229,12 +232,14 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ uint32_t EntityMotionState::getIncomingDirtyFlags() const { uint32_t dirtyFlags = _entity->getDirtyFlags(); - // we add DIRTY_MOTION_TYPE if the body's motion type disagrees with entity velocity settings - int bodyFlags = _body->getCollisionFlags(); - bool isMoving = _entity->isMoving(); - if (((bodyFlags & btCollisionObject::CF_STATIC_OBJECT) && isMoving) || - (bodyFlags & btCollisionObject::CF_KINEMATIC_OBJECT && !isMoving)) { - dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; + if (_body) { + // we add DIRTY_MOTION_TYPE if the body's motion type disagrees with entity velocity settings + int bodyFlags = _body->getCollisionFlags(); + bool isMoving = _entity->isMoving(); + if (((bodyFlags & btCollisionObject::CF_STATIC_OBJECT) && isMoving) || + (bodyFlags & btCollisionObject::CF_KINEMATIC_OBJECT && !isMoving)) { + dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; + } } return dirtyFlags; } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 8eb639688a..192ac166b4 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -14,7 +14,6 @@ #include -#include "KinematicController.h" #include "ObjectMotionState.h" class EntityItem; @@ -39,8 +38,8 @@ public: /// \return MOTION_TYPE_DYNAMIC or MOTION_TYPE_STATIC based on params set in EntityItem MotionType computeMotionType() const; - // virtual override for ObjectMotionState - void addKinematicController(); + void updateKinematicState(uint32_t substep); + void stepKinematicSimulation(uint32_t substep); // this relays incoming position/rotation to the RigidBody void getWorldTransform(btTransform& worldTrans) const; diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index cab36b8370..dfa059d47f 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -12,7 +12,6 @@ #include #include "BulletUtil.h" -#include "KinematicController.h" #include "ObjectMotionState.h" #include "PhysicsEngine.h" @@ -56,10 +55,6 @@ ObjectMotionState::ObjectMotionState() : ObjectMotionState::~ObjectMotionState() { // NOTE: you MUST remove this MotionState from the world before you call the dtor. assert(_body == NULL); - if (_kinematicController) { - delete _kinematicController; - _kinematicController = NULL; - } } void ObjectMotionState::setFriction(float friction) { @@ -108,6 +103,15 @@ bool ObjectMotionState::doesNotNeedToSendUpdate() const { bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) { assert(_body); + // if we've never checked before, our _sentFrame will be 0, and we need to initialize our state + if (_sentFrame == 0) { + _sentPosition = bulletToGLM(_body->getWorldTransform().getOrigin()); + _sentVelocity = bulletToGLM(_body->getLinearVelocity()); + _sentAngularVelocity = bulletToGLM(_body->getAngularVelocity()); + _sentFrame = simulationFrame; + return false; + } + float dt = (float)(simulationFrame - _sentFrame) * PHYSICS_ENGINE_FIXED_SUBSTEP; _sentFrame = simulationFrame; bool isActive = _body->isActive(); @@ -164,13 +168,6 @@ bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) { return (fabsf(glm::dot(actualRotation, _sentRotation)) < MIN_ROTATION_DOT); } -void ObjectMotionState::removeKinematicController() { - if (_kinematicController) { - delete _kinematicController; - _kinematicController = NULL; - } -} - void ObjectMotionState::setRigidBody(btRigidBody* body) { // give the body a (void*) back-pointer to this ObjectMotionState if (_body != body) { @@ -183,3 +180,8 @@ void ObjectMotionState::setRigidBody(btRigidBody* body) { } } } + +void ObjectMotionState::setKinematic(bool kinematic, uint32_t substep) { + _isKinematic = kinematic; + _lastKinematicSubstep = substep; +} diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 223664ceec..424a7fb680 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -46,7 +46,6 @@ const uint32_t OUTGOING_DIRTY_PHYSICS_FLAGS = EntityItem::DIRTY_POSITION | Entit class OctreeEditPacketSender; -class KinematicController; class ObjectMotionState : public btMotionState { public: @@ -93,11 +92,15 @@ public: virtual MotionType computeMotionType() const = 0; - virtual void addKinematicController() = 0; - virtual void removeKinematicController(); + virtual void updateKinematicState(uint32_t substep) = 0; btRigidBody* getRigidBody() const { return _body; } + bool isKinematic() const { return _isKinematic; } + + void setKinematic(bool kinematic, uint32_t substep); + virtual void stepKinematicSimulation(uint32_t substep) = 0; + friend class PhysicsEngine; protected: void setRigidBody(btRigidBody* body); @@ -114,6 +117,9 @@ protected: btRigidBody* _body; + bool _isKinematic = false; + uint32_t _lastKinematicSubstep = 0; + bool _sentMoving; // true if last update was moving int _numNonMovingUpdates; // RELIABLE_SEND_HACK for "not so reliable" resends of packets for non-moving objects @@ -124,8 +130,6 @@ protected: glm::vec3 _sentVelocity; glm::vec3 _sentAngularVelocity; // radians per second glm::vec3 _sentAcceleration; - - KinematicController* _kinematicController = NULL; }; #endif // hifi_ObjectMotionState_h diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 02304b3b8d..dbeea165c4 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -62,7 +62,13 @@ void PhysicsEngine::addEntityInternal(EntityItem* entity) { entity->setPhysicsInfo(static_cast(motionState)); _entityMotionStates.insert(motionState); addObject(shapeInfo, shape, motionState); - } else { + } else if (entity->isMoving()) { + EntityMotionState* motionState = new EntityMotionState(entity); + entity->setPhysicsInfo(static_cast(motionState)); + _entityMotionStates.insert(motionState); + + motionState->setKinematic(true, _numSubsteps); + _nonPhysicalKinematicObjects.insert(motionState); // We failed to add the entity to the simulation. Probably because we couldn't create a shape for it. //qDebug() << "failed to add entity " << entity->getEntityItemID() << " to physics engine"; } @@ -74,10 +80,16 @@ void PhysicsEngine::removeEntityInternal(EntityItem* entity) { void* physicsInfo = entity->getPhysicsInfo(); if (physicsInfo) { EntityMotionState* motionState = static_cast(physicsInfo); - removeObject(motionState); + if (motionState->getRigidBody()) { + removeObject(motionState); + } else { + // only need to hunt in this list when there is no RigidBody + _nonPhysicalKinematicObjects.remove(motionState); + } _entityMotionStates.remove(motionState); _incomingChanges.remove(motionState); _outgoingPackets.remove(motionState); + // NOTE: EntityMotionState dtor will remove its backpointer from EntityItem delete motionState; } } @@ -117,6 +129,7 @@ void PhysicsEngine::clearEntitiesInternal() { delete (*stateItr); } _entityMotionStates.clear(); + _nonPhysicalKinematicObjects.clear(); _incomingChanges.clear(); _outgoingPackets.clear(); } @@ -127,19 +140,75 @@ void PhysicsEngine::relayIncomingChangesToSimulation() { QSet::iterator stateItr = _incomingChanges.begin(); while (stateItr != _incomingChanges.end()) { ObjectMotionState* motionState = *stateItr; + ++stateItr; uint32_t flags = motionState->getIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS; + bool removeMotionState = false; btRigidBody* body = motionState->getRigidBody(); if (body) { if (flags & HARD_DIRTY_PHYSICS_FLAGS) { // a HARD update requires the body be pulled out of physics engine, changed, then reinserted // but it also handles all EASY changes - updateObjectHard(body, motionState, flags); + bool success = updateObjectHard(body, motionState, flags); + if (!success) { + // NOTE: since updateObjectHard() failed we know that motionState has been removed + // from simulation and body has been deleted. Depending on what else has changed + // we might need to remove motionState altogether... + if (flags & EntityItem::DIRTY_VELOCITY) { + motionState->updateKinematicState(_numSubsteps); + if (motionState->isKinematic()) { + // all is NOT lost, we still need to move this object around kinematically + _nonPhysicalKinematicObjects.insert(motionState); + } else { + // no need to keep motionState around + removeMotionState = true; + } + } else { + // no need to keep motionState around + removeMotionState = true; + } + } } else if (flags) { // an EASY update does NOT require that the body be pulled out of physics engine // hence the MotionState has all the knowledge and authority to perform the update. motionState->updateObjectEasy(flags, _numSubsteps); } + } else { + // the only way we should ever get here (motionState exists but no body) is when the object + // is undergoing non-physical kinematic motion. + assert(_nonPhysicalKinematicObjects.contains(motionState)); + + // it is possible that the changes are such that the object can now be added to the physical simulation + if (flags & EntityItem::DIRTY_SHAPE) { + ShapeInfo shapeInfo; + motionState->computeShapeInfo(shapeInfo); + btCollisionShape* shape = _shapeManager.getShape(shapeInfo); + if (shape) { + addObject(shapeInfo, shape, motionState); + _nonPhysicalKinematicObjects.remove(motionState); + } else if (flags & EntityItem::DIRTY_VELOCITY) { + // although we couldn't add the object to the simulation, might need to update kinematic motion... + motionState->updateKinematicState(_numSubsteps); + if (!motionState->isKinematic()) { + _nonPhysicalKinematicObjects.remove(motionState); + removeMotionState = true; + } + } + } else if (flags & EntityItem::DIRTY_VELOCITY) { + // although we still can't add to physics simulation, might need to update kinematic motion... + motionState->updateKinematicState(_numSubsteps); + if (!motionState->isKinematic()) { + _nonPhysicalKinematicObjects.remove(motionState); + removeMotionState = true; + } + } + } + if (removeMotionState) { + // if we get here then there is no need to keep this motionState around (no physics or kinematics) + _outgoingPackets.remove(motionState); + // NOTE: motionState will clean up its own backpointers in the Object + delete motionState; + continue; } // NOTE: the grand order of operations is: @@ -152,7 +221,6 @@ void PhysicsEngine::relayIncomingChangesToSimulation() { // outgoing changes at this point. motionState->clearOutgoingPacketFlags(flags); // clear outgoing flags that were trumped motionState->clearIncomingDirtyFlags(flags); // clear incoming flags that were processed - ++stateItr; } _incomingChanges.clear(); } @@ -229,6 +297,7 @@ void PhysicsEngine::stepSimulation() { // This is step (2). int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP); _numSubsteps += (uint32_t)numSubsteps; + stepNonPhysicalKinematics(); unlock(); // This is step (3) which is done outside of stepSimulation() so we can lock _entityTree. @@ -248,6 +317,18 @@ void PhysicsEngine::stepSimulation() { computeCollisionEvents(); } +// TODO: need to update non-physical kinematic objects +void PhysicsEngine::stepNonPhysicalKinematics() { + QSet::iterator stateItr = _nonPhysicalKinematicObjects.begin(); + while (stateItr != _nonPhysicalKinematicObjects.end()) { + ObjectMotionState* motionState = *stateItr; + motionState->stepKinematicSimulation(_numSubsteps); + ++stateItr; + } +} + +// TODO?: need to occasionally scan for stopped non-physical kinematics objects + void PhysicsEngine::computeCollisionEvents() { // update all contacts int numManifolds = _collisionDispatcher->getNumManifolds(); @@ -322,7 +403,7 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap body->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT); body->updateInertiaTensor(); motionState->setRigidBody(body); - motionState->addKinematicController(); + motionState->setKinematic(true, _numSubsteps); const float KINEMATIC_LINEAR_VELOCITY_THRESHOLD = 0.01f; // 1 cm/sec const float KINEMATIC_ANGULAR_VELOCITY_THRESHOLD = 0.01f; // ~1 deg/sec body->setSleepingThresholds(KINEMATIC_LINEAR_VELOCITY_THRESHOLD, KINEMATIC_ANGULAR_VELOCITY_THRESHOLD); @@ -334,6 +415,7 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap body = new btRigidBody(mass, motionState, shape, inertia); body->updateInertiaTensor(); motionState->setRigidBody(body); + motionState->setKinematic(false, _numSubsteps); motionState->updateObjectVelocities(); // NOTE: Bullet will deactivate any object whose velocity is below these thresholds for longer than 2 seconds. // (the 2 seconds is determined by: static btRigidBody::gDeactivationTime @@ -348,6 +430,7 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap body->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT); body->updateInertiaTensor(); motionState->setRigidBody(body); + motionState->setKinematic(false, _numSubsteps); break; } } @@ -358,7 +441,7 @@ void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shap _dynamicsWorld->addRigidBody(body); } -bool PhysicsEngine::removeObject(ObjectMotionState* motionState) { +void PhysicsEngine::removeObject(ObjectMotionState* motionState) { assert(motionState); btRigidBody* body = motionState->getRigidBody(); if (body) { @@ -369,16 +452,14 @@ bool PhysicsEngine::removeObject(ObjectMotionState* motionState) { _shapeManager.releaseShape(shapeInfo); delete body; motionState->setRigidBody(NULL); - motionState->removeKinematicController(); + motionState->setKinematic(false, _numSubsteps); removeContacts(motionState); - return true; } - return false; } // private -void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags) { +bool PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags) { MotionType newType = motionState->computeMotionType(); // pull body out of physics engine @@ -393,7 +474,16 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio ShapeInfo shapeInfo; motionState->computeShapeInfo(shapeInfo); btCollisionShape* newShape = _shapeManager.getShape(shapeInfo); - if (newShape != oldShape) { + if (!newShape) { + // FAIL! we are unable to support these changes! + _shapeManager.releaseShape(oldShape); + + delete body; + motionState->setRigidBody(NULL); + motionState->setKinematic(false, _numSubsteps); + removeContacts(motionState); + return false; + } else if (newShape != oldShape) { // BUG: if shape doesn't change but density does then we won't compute new mass properties // TODO: fix this BUG by replacing DIRTY_MASS with DIRTY_DENSITY and then fix logic accordingly. body->setCollisionShape(newShape); @@ -426,7 +516,7 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio body->setMassProps(0.0f, btVector3(0.0f, 0.0f, 0.0f)); body->updateInertiaTensor(); - motionState->addKinematicController(); + motionState->setKinematic(true, _numSubsteps); break; } case MOTION_TYPE_DYNAMIC: { @@ -443,7 +533,7 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio body->updateInertiaTensor(); } body->forceActivationState(ACTIVE_TAG); - motionState->removeKinematicController(); + motionState->setKinematic(false, _numSubsteps); break; } default: { @@ -458,7 +548,7 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio body->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f)); body->setAngularVelocity(btVector3(0.0f, 0.0f, 0.0f)); - motionState->removeKinematicController(); + motionState->setKinematic(false, _numSubsteps); break; } } @@ -467,4 +557,5 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio _dynamicsWorld->addRigidBody(body); body->activate(); + return true; } diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 73a02607e8..9ae9f88e7e 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -67,6 +67,7 @@ public: virtual void init(EntityEditPacketSender* packetSender); void stepSimulation(); + void stepNonPhysicalKinematics(); void computeCollisionEvents(); @@ -81,15 +82,16 @@ public: void addObject(const ShapeInfo& shapeInfo, btCollisionShape* shape, ObjectMotionState* motionState); /// \param motionState pointer to Object's MotionState - /// \return true if Object removed - bool removeObject(ObjectMotionState* motionState); + void removeObject(ObjectMotionState* motionState); /// process queue of changed from external sources void relayIncomingChangesToSimulation(); private: void removeContacts(ObjectMotionState* motionState); - void updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); + + // return 'true' of update was successful + bool updateObjectHard(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); void updateObjectEasy(btRigidBody* body, ObjectMotionState* motionState, uint32_t flags); btClock _clock; @@ -104,6 +106,7 @@ private: // EntitySimulation stuff QSet _entityMotionStates; // all entities that we track + QSet _nonPhysicalKinematicObjects; // not in physics simulation, but still need kinematic simulation QSet _incomingChanges; // entities with pending physics changes by script or packet QSet _outgoingPackets; // MotionStates with pending changes that need to be sent over wire From eefd32b42b173392150c560b01ece0d57ca6ac6f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 23 Jan 2015 11:04:02 -0800 Subject: [PATCH 21/23] remove KinematicController classes --- libraries/physics/src/KinematicController.cpp | 22 ----------- libraries/physics/src/KinematicController.h | 36 ------------------ .../src/SimpleEntityKinematicController.cpp | 21 ---------- .../src/SimpleEntityKinematicController.h | 38 ------------------- 4 files changed, 117 deletions(-) delete mode 100644 libraries/physics/src/KinematicController.cpp delete mode 100644 libraries/physics/src/KinematicController.h delete mode 100644 libraries/physics/src/SimpleEntityKinematicController.cpp delete mode 100644 libraries/physics/src/SimpleEntityKinematicController.h diff --git a/libraries/physics/src/KinematicController.cpp b/libraries/physics/src/KinematicController.cpp deleted file mode 100644 index 354b285bc1..0000000000 --- a/libraries/physics/src/KinematicController.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// -// KinematicController.cpp -// libraries/physcis/src -// -// Created by Andrew Meadows 2015.01.13 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "KinematicController.h" -#include "PhysicsEngine.h" - -KinematicController::KinematicController() { - _lastSubstep = PhysicsEngine::getNumSubsteps(); -} - -void KinematicController::start() { - _enabled = true; - _lastSubstep = PhysicsEngine::getNumSubsteps(); -} diff --git a/libraries/physics/src/KinematicController.h b/libraries/physics/src/KinematicController.h deleted file mode 100644 index 60b8548607..0000000000 --- a/libraries/physics/src/KinematicController.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// KinematicController.h -// libraries/physcis/src -// -// Created by Andrew Meadows 2015.01.13 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_KinematicController_h -#define hifi_KinematicController_h - -#include - -/// KinematicController defines an API for derived classes. - -class KinematicController { -public: - KinematicController(); - - virtual ~KinematicController() {} - - virtual void stepForward() = 0; - - void start(); - void stop() { _enabled = false; } - bool isRunning() const { return _enabled; } - -protected: - bool _enabled = false; - uint32_t _lastSubstep; -}; - -#endif // hifi_KinematicController_h diff --git a/libraries/physics/src/SimpleEntityKinematicController.cpp b/libraries/physics/src/SimpleEntityKinematicController.cpp deleted file mode 100644 index e834d4e91b..0000000000 --- a/libraries/physics/src/SimpleEntityKinematicController.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// -// SimpleEntityKinematicController.cpp -// libraries/physcis/src -// -// Created by Andrew Meadows 2015.01.13 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "PhysicsEngine.h" -#include "SimpleEntityKinematicController.h" - -void SimpleEntityKinematicController:: stepForward() { - uint32_t substep = PhysicsEngine::getNumSubsteps(); - float dt = (substep - _lastSubstep) * PHYSICS_ENGINE_FIXED_SUBSTEP; - _entity->simulateSimpleKinematicMotion(dt); - _lastSubstep = substep; -} - diff --git a/libraries/physics/src/SimpleEntityKinematicController.h b/libraries/physics/src/SimpleEntityKinematicController.h deleted file mode 100644 index 1edfaf8d2c..0000000000 --- a/libraries/physics/src/SimpleEntityKinematicController.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// SimpleEntityKinematicController.h -// libraries/physcis/src -// -// Created by Andrew Meadows 2015.01.13 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_SimpleEntityKinematicController_h -#define hifi_SimpleEntityKinematicController_h - -/// SimpleKinematicConstroller performs simple exrapolation of velocities. - -#include -#include - -#include - -#include "KinematicController.h" - -class SimpleEntityKinematicController : public KinematicController { -public: - SimpleEntityKinematicController() = delete; // prevent compiler from making a default ctor - - SimpleEntityKinematicController(EntityItem* entity) : KinematicController(), _entity(entity) { assert(entity); } - - ~SimpleEntityKinematicController() { _entity = NULL; } - - void stepForward(); - -private: - EntityItem* _entity; -}; - -#endif // hifi_SimpleEntityKinematicController_h From f2bcdfa2b479cbe917f0afd1d6a127f34694f421 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 23 Jan 2015 13:10:41 -0800 Subject: [PATCH 22/23] update _lastSimulated for kinematic motion --- libraries/entities/src/EntityItem.cpp | 101 ++++---------------- libraries/entities/src/EntityItem.h | 4 +- libraries/physics/src/EntityMotionState.cpp | 20 ++-- libraries/physics/src/EntityMotionState.h | 2 +- libraries/physics/src/ObjectMotionState.h | 2 +- libraries/physics/src/PhysicsEngine.cpp | 7 +- libraries/physics/src/PhysicsEngine.h | 2 +- 7 files changed, 39 insertions(+), 99 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index c830236287..c1fc24fdc7 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -607,11 +607,6 @@ bool EntityItem::isRestingOnSurface() const { } void EntityItem::simulate(const quint64& now) { - if (_physicsInfo) { - // we rely on bullet for simulation, so bail - return; - } - bool wantDebug = false; if (_lastSimulated == 0) { @@ -661,9 +656,13 @@ void EntityItem::simulate(const quint64& now) { qDebug() << " ********** EntityItem::simulate() .... SETTING _lastSimulated=" << _lastSimulated; } - if (hasAngularVelocity()) { - glm::quat rotation = getRotation(); + simulateKinematicMotion(timeElapsed); + _lastSimulated = now; +} +void EntityItem::simulateKinematicMotion(float timeElapsed) { + bool wantDebug = false; + if (hasAngularVelocity()) { // angular damping glm::vec3 angularVelocity = getAngularVelocity(); if (_angularDamping > 0.0f) { @@ -679,6 +678,9 @@ void EntityItem::simulate(const quint64& now) { const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.1f; // if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) { + if (angularSpeed > 0.0f) { + _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; + } setAngularVelocity(ENTITY_ITEM_ZERO_VEC3); } else { // NOTE: angularSpeed is currently in degrees/sec!!! @@ -686,7 +688,7 @@ void EntityItem::simulate(const quint64& now) { float angle = timeElapsed * glm::radians(angularSpeed); glm::vec3 axis = _angularVelocity / angularSpeed; glm::quat dQ = glm::angleAxis(angle, axis); - rotation = glm::normalize(dQ * rotation); + glm::quat rotation = glm::normalize(dQ * getRotation()); setRotation(rotation); } } @@ -722,80 +724,6 @@ void EntityItem::simulate(const quint64& now) { position = newPosition; - // handle bounces off the ground... We bounce at the distance to the bottom of our entity - if (position.y <= getDistanceToBottomOfEntity()) { - velocity = velocity * glm::vec3(1,-1,1); - position.y = getDistanceToBottomOfEntity(); - } - - // apply gravity - if (hasGravity()) { - // handle resting on surface case, this is definitely a bit of a hack, and it only works on the - // "ground" plane of the domain, but for now it's what we've got - if (isRestingOnSurface()) { - velocity.y = 0.0f; - position.y = getDistanceToBottomOfEntity(); - } else { - velocity += getGravity() * timeElapsed; - } - } - - // NOTE: we don't zero out very small velocities --> we rely on a remote Bullet simulation - // to tell us when the entity has stopped. - - // NOTE: the simulation should NOT set any DirtyFlags on this entity - setPosition(position); // this will automatically recalculate our collision shape - setVelocity(velocity); - - if (wantDebug) { - qDebug() << " new position:" << position; - qDebug() << " new velocity:" << velocity; - qDebug() << " new AACube:" << getMaximumAACube(); - qDebug() << " old getAABox:" << getAABox(); - } - } - - _lastSimulated = now; -} - -void EntityItem::simulateSimpleKinematicMotion(float timeElapsed) { - if (hasAngularVelocity()) { - // angular damping - glm::vec3 angularVelocity = getAngularVelocity(); - if (_angularDamping > 0.0f) { - angularVelocity *= powf(1.0f - _angularDamping, timeElapsed); - setAngularVelocity(angularVelocity); - } - - float angularSpeed = glm::length(_angularVelocity); - const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.1f; // - if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) { - if (angularSpeed > 0.0f) { - _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; - } - setAngularVelocity(ENTITY_ITEM_ZERO_VEC3); - } else { - // NOTE: angularSpeed is currently in degrees/sec!!! - // TODO: Andrew to convert to radians/sec - float angle = timeElapsed * glm::radians(angularSpeed); - glm::vec3 axis = _angularVelocity / angularSpeed; - glm::quat dQ = glm::angleAxis(angle, axis); - glm::quat rotation = getRotation(); - rotation = glm::normalize(dQ * rotation); - setRotation(rotation); - } - } - - if (hasVelocity()) { - // linear damping - glm::vec3 velocity = getVelocity(); - if (_damping > 0.0f) { - velocity *= powf(1.0f - _damping, timeElapsed); - } - - // integrate position forward - glm::vec3 position = getPosition() + (velocity * timeElapsed); - // apply gravity if (hasGravity()) { // handle resting on surface case, this is definitely a bit of a hack, and it only works on the @@ -811,9 +739,16 @@ void EntityItem::simulateSimpleKinematicMotion(float timeElapsed) { _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; } } else { - setPosition(position); // this will automatically recalculate our collision shape + setPosition(position); setVelocity(velocity); } + + if (wantDebug) { + qDebug() << " new position:" << position; + qDebug() << " new velocity:" << velocity; + qDebug() << " new AACube:" << getMaximumAACube(); + qDebug() << " old getAABox:" << getAABox(); + } } } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index a9a82c5209..d266a30f62 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -82,6 +82,7 @@ public: void recordCreationTime(); // set _created to 'now' quint64 getLastSimulated() const { return _lastSimulated; } /// Last simulated time of this entity universal usecs + void setLastSimulated(quint64 now) { _lastSimulated = now; } /// Last edited time of this entity universal usecs quint64 getLastEdited() const { return _lastEdited; } @@ -128,9 +129,8 @@ public: // perform linear extrapolation for SimpleEntitySimulation void simulate(const quint64& now); + void simulateKinematicMotion(float timeElapsed); - void simulateSimpleKinematicMotion(float timeElapsed); - virtual bool needsToCallUpdate() const { return false; } virtual void debugDump() const; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index b4aa9d0e7e..f22487b8ea 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -54,11 +54,12 @@ void EntityMotionState::updateKinematicState(uint32_t substep) { setKinematic(_entity->isMoving(), substep); } -void EntityMotionState::stepKinematicSimulation(uint32_t substep) { +void EntityMotionState::stepKinematicSimulation(quint64 now) { assert(_isKinematic); - float dt = (substep - _lastKinematicSubstep) * PHYSICS_ENGINE_FIXED_SUBSTEP; - _entity->simulateSimpleKinematicMotion(dt); - _lastKinematicSubstep = substep; + // NOTE: this is non-physical kinematic motion which steps to real run-time (now) + // which is different from physical kinematic motion (inside getWorldTransform()) + // which steps in physics simulation time. + _entity->simulate(now); } // This callback is invoked by the physics simulation in two cases: @@ -68,10 +69,15 @@ void EntityMotionState::stepKinematicSimulation(uint32_t substep) { // it is an opportunity for outside code to update the object's simulation position void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { if (_isKinematic) { + // This is physical kinematic motion which steps strictly by the subframe count + // of the physics simulation. uint32_t substep = PhysicsEngine::getNumSubsteps(); - // remove const-ness so we can actually update this instance - EntityMotionState* thisMotion = const_cast(this); - thisMotion->stepKinematicSimulation(substep); + float dt = (substep - _lastKinematicSubstep) * PHYSICS_ENGINE_FIXED_SUBSTEP; + _entity->simulateKinematicMotion(dt); + _entity->setLastSimulated(usecTimestampNow()); + + // bypass const-ness so we can remember the substep + const_cast(this)->_lastKinematicSubstep = substep; } worldTrans.setOrigin(glmToBullet(_entity->getPositionInMeters() - ObjectMotionState::getWorldOffset())); worldTrans.setRotation(glmToBullet(_entity->getRotation())); diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 192ac166b4..5d98e545d9 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -39,7 +39,7 @@ public: MotionType computeMotionType() const; void updateKinematicState(uint32_t substep); - void stepKinematicSimulation(uint32_t substep); + void stepKinematicSimulation(quint64 now); // this relays incoming position/rotation to the RigidBody void getWorldTransform(btTransform& worldTrans) const; diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 424a7fb680..ceeea219cf 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -99,7 +99,7 @@ public: bool isKinematic() const { return _isKinematic; } void setKinematic(bool kinematic, uint32_t substep); - virtual void stepKinematicSimulation(uint32_t substep) = 0; + virtual void stepKinematicSimulation(quint64 now) = 0; friend class PhysicsEngine; protected: diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index dbeea165c4..62693e3c9a 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -297,7 +297,7 @@ void PhysicsEngine::stepSimulation() { // This is step (2). int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP); _numSubsteps += (uint32_t)numSubsteps; - stepNonPhysicalKinematics(); + stepNonPhysicalKinematics(usecTimestampNow()); unlock(); // This is step (3) which is done outside of stepSimulation() so we can lock _entityTree. @@ -317,12 +317,11 @@ void PhysicsEngine::stepSimulation() { computeCollisionEvents(); } -// TODO: need to update non-physical kinematic objects -void PhysicsEngine::stepNonPhysicalKinematics() { +void PhysicsEngine::stepNonPhysicalKinematics(const quint64& now) { QSet::iterator stateItr = _nonPhysicalKinematicObjects.begin(); while (stateItr != _nonPhysicalKinematicObjects.end()) { ObjectMotionState* motionState = *stateItr; - motionState->stepKinematicSimulation(_numSubsteps); + motionState->stepKinematicSimulation(now); ++stateItr; } } diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 9ae9f88e7e..1dbfe2646e 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -67,7 +67,7 @@ public: virtual void init(EntityEditPacketSender* packetSender); void stepSimulation(); - void stepNonPhysicalKinematics(); + void stepNonPhysicalKinematics(const quint64& now); void computeCollisionEvents(); From 36c20c24fe7f3d90c3bb7d5fd25bfa77edcd6b5d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 23 Jan 2015 13:20:12 -0800 Subject: [PATCH 23/23] fix for velocity in units of domain size --- libraries/entities/src/EntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index c1fc24fdc7..1ebde85d65 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -732,7 +732,7 @@ void EntityItem::simulateKinematicMotion(float timeElapsed) { } float speed = glm::length(velocity); - const float EPSILON_LINEAR_VELOCITY_LENGTH = 0.001f; // 1mm/sec + const float EPSILON_LINEAR_VELOCITY_LENGTH = 0.001f / (float)TREE_SCALE; // 1mm/sec if (speed < EPSILON_LINEAR_VELOCITY_LENGTH) { setVelocity(ENTITY_ITEM_ZERO_VEC3); if (speed > 0.0f) {