// // MetavoxelSystem.cpp // interface/src // // Created by Andrzej Kapolka on 12/10/13. // Copyright 2013 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include #include #include #include #include #include #include "Application.h" #include "MetavoxelSystem.h" #include "renderer/Model.h" REGISTER_META_OBJECT(SphereRenderer) REGISTER_META_OBJECT(StaticModelRenderer) ProgramObject MetavoxelSystem::_program; int MetavoxelSystem::_pointScaleLocation; MetavoxelSystem::MetavoxelSystem() : _simulateVisitor(_points), _buffer(QOpenGLBuffer::VertexBuffer) { } void MetavoxelSystem::init() { MetavoxelClientManager::init(); if (!_program.isLinked()) { _program.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/metavoxel_point.vert"); _program.link(); _pointScaleLocation = _program.uniformLocation("pointScale"); } _buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); _buffer.create(); _pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new PointBufferAttribute()); } MetavoxelLOD MetavoxelSystem::getLOD() const { // the LOD threshold is temporarily tied to the avatar LOD parameter const float BASE_LOD_THRESHOLD = 0.01f; return MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(), BASE_LOD_THRESHOLD * Menu::getInstance()->getAvatarLODDistanceMultiplier()); } void MetavoxelSystem::simulate(float deltaTime) { // update the clients _points.clear(); _simulateVisitor.setDeltaTime(deltaTime); _simulateVisitor.setOrder(-Application::getInstance()->getViewFrustum()->getDirection()); update(); _buffer.bind(); int bytes = _points.size() * sizeof(Point); if (_buffer.size() < bytes) { _buffer.allocate(_points.constData(), bytes); } else { _buffer.write(0, _points.constData(), bytes); } _buffer.release(); } void MetavoxelSystem::render() { int viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); const int VIEWPORT_WIDTH_INDEX = 2; const int VIEWPORT_HEIGHT_INDEX = 3; float viewportWidth = viewport[VIEWPORT_WIDTH_INDEX]; float viewportHeight = viewport[VIEWPORT_HEIGHT_INDEX]; float viewportDiagonal = sqrtf(viewportWidth*viewportWidth + viewportHeight*viewportHeight); float worldDiagonal = glm::distance(Application::getInstance()->getViewFrustum()->getNearBottomLeft(), Application::getInstance()->getViewFrustum()->getNearTopRight()); _program.bind(); _program.setUniformValue(_pointScaleLocation, viewportDiagonal * Application::getInstance()->getViewFrustum()->getNearClip() / worldDiagonal); _buffer.bind(); Point* pt = 0; glVertexPointer(4, GL_FLOAT, sizeof(Point), &pt->vertex); glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Point), &pt->color); glNormalPointer(GL_BYTE, sizeof(Point), &pt->normal); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); glDrawArrays(GL_POINTS, 0, _points.size()); glDisable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); _buffer.release(); _program.release(); foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { if (node->getType() == NodeType::MetavoxelServer) { QMutexLocker locker(&node->getMutex()); MetavoxelSystemClient* client = static_cast(node->getLinkedData()); if (client) { client->guide(_renderVisitor); } } } } MetavoxelClient* MetavoxelSystem::createClient(const SharedNodePointer& node) { return new MetavoxelSystemClient(node, this); } void MetavoxelSystem::updateClient(MetavoxelClient* client) { MetavoxelClientManager::updateClient(client); client->guide(_simulateVisitor); } MetavoxelSystem::SimulateVisitor::SimulateVisitor(QVector& points) : SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), QVector(), QVector() << AttributeRegistry::getInstance()->getColorAttribute() << AttributeRegistry::getInstance()->getNormalAttribute() << AttributeRegistry::getInstance()->getSpannerColorAttribute() << AttributeRegistry::getInstance()->getSpannerNormalAttribute()), _points(points) { } bool MetavoxelSystem::SimulateVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { spanner->getRenderer()->simulate(_deltaTime); return true; } int MetavoxelSystem::SimulateVisitor::visit(MetavoxelInfo& info) { SpannerVisitor::visit(info); if (!info.isLeaf) { return _order; } QRgb color = info.inputValues.at(0).getInlineValue(); QRgb normal = info.inputValues.at(1).getInlineValue(); quint8 alpha = qAlpha(color); if (!info.isLODLeaf) { if (alpha > 0) { Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size), { quint8(qRed(color)), quint8(qGreen(color)), quint8(qBlue(color)), alpha }, { quint8(qRed(normal)), quint8(qGreen(normal)), quint8(qBlue(normal)) } }; _points.append(point); } } else { QRgb spannerColor = info.inputValues.at(2).getInlineValue(); QRgb spannerNormal = info.inputValues.at(3).getInlineValue(); quint8 spannerAlpha = qAlpha(spannerColor); if (spannerAlpha > 0) { if (alpha > 0) { Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size), { quint8(qRed(spannerColor)), quint8(qGreen(spannerColor)), quint8(qBlue(spannerColor)), spannerAlpha }, { quint8(qRed(spannerNormal)), quint8(qGreen(spannerNormal)), quint8(qBlue(spannerNormal)) } }; _points.append(point); } else { Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size), { quint8(qRed(spannerColor)), quint8(qGreen(spannerColor)), quint8(qBlue(spannerColor)), spannerAlpha }, { quint8(qRed(spannerNormal)), quint8(qGreen(spannerNormal)), quint8(qBlue(spannerNormal)) } }; _points.append(point); } } else if (alpha > 0) { Point point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size), { quint8(qRed(color)), quint8(qGreen(color)), quint8(qBlue(color)), alpha }, { quint8(qRed(normal)), quint8(qGreen(normal)), quint8(qBlue(normal)) } }; _points.append(point); } } return STOP_RECURSION; } MetavoxelSystem::RenderVisitor::RenderVisitor() : SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), QVector() << AttributeRegistry::getInstance()->getSpannerMaskAttribute()) { } bool MetavoxelSystem::RenderVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimum, float clipSize) { spanner->getRenderer()->render(1.0f, SpannerRenderer::DEFAULT_MODE, clipMinimum, clipSize); return true; } MetavoxelSystemClient::MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelSystem* system) : MetavoxelClient(node, system) { } int MetavoxelSystemClient::parseData(const QByteArray& packet) { // process through sequencer QMetaObject::invokeMethod(&_sequencer, "receivedDatagram", Q_ARG(const QByteArray&, packet)); Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::METAVOXELS).updateValue(packet.size()); return packet.size(); } class BufferBuilder : public MetavoxelVisitor { public: BufferBuilder(const MetavoxelLOD& lod); virtual int visit(MetavoxelInfo& info); virtual bool postVisit(MetavoxelInfo& info); private: QVector _depthPoints; }; BufferBuilder::BufferBuilder(const MetavoxelLOD& lod) : MetavoxelVisitor(QVector() << AttributeRegistry::getInstance()->getColorAttribute() << AttributeRegistry::getInstance()->getNormalAttribute() << Application::getInstance()->getMetavoxels()->getPointBufferAttribute(), QVector() << Application::getInstance()->getMetavoxels()->getPointBufferAttribute(), lod) { } const int ALPHA_RENDER_THRESHOLD = 0; int BufferBuilder::visit(MetavoxelInfo& info) { if (info.inputValues.at(2).getInlineValue()) { info.outputValues[0] = AttributeValue(_outputs.at(0)); } if (_depth >= _depthPoints.size()) { _depthPoints.resize(_depth + 1); } QRgb color = info.inputValues.at(0).getInlineValue(); quint8 alpha = qAlpha(color); if (alpha <= ALPHA_RENDER_THRESHOLD) { return info.isLeaf ? STOP_RECURSION : DEFAULT_ORDER; } QRgb normal = info.inputValues.at(1).getInlineValue(); BufferPoint point = { glm::vec4(info.minimum + glm::vec3(info.size, info.size, info.size) * 0.5f, info.size), { quint8(qRed(color)), quint8(qGreen(color)), quint8(qBlue(color)) }, { quint8(qRed(normal)), quint8(qGreen(normal)), quint8(qBlue(normal)) } }; if (info.isLeaf) { _depthPoints[_depth].first.append(point); return STOP_RECURSION; } _depthPoints[_depth].second.append(point); return DEFAULT_ORDER; } const int BUFFER_LEVELS = 5; bool BufferBuilder::postVisit(MetavoxelInfo& info) { if (_depth % BUFFER_LEVELS != 0) { return false; } QVector offsets; offsets.append(0); int leafCount = 0; int totalPoints = 0; int lastDepth = qMin(_depth + BUFFER_LEVELS, _depthPoints.size()); for (int i = _depth; i < lastDepth; i++) { const BufferPointVectorPair& pair = _depthPoints.at(i); offsets.append(totalPoints += ((leafCount += pair.first.size()) + pair.second.size())); } QOpenGLBuffer buffer; buffer.setUsagePattern(QOpenGLBuffer::StaticDraw); buffer.create(); buffer.bind(); buffer.allocate(totalPoints * sizeof(BufferPoint)); int offset = 0; for (int i = _depth; i < lastDepth; i++) { // write the internal nodes from the current level BufferPointVector& internal = _depthPoints[i].second; int length = internal.size() * sizeof(BufferPoint); buffer.write(offset, internal.constData(), length); offset += length; internal.clear(); // and the leaves from the top down for (int j = _depth; j <= i; j++) { const BufferPointVector& leaves = _depthPoints.at(j).first; length = leaves.size() * sizeof(BufferPoint); buffer.write(offset, leaves.constData(), length); offset += length; } } // clear the leaves now that we're done with them for (int i = _depth; i < lastDepth; i++) { _depthPoints[i].first.clear(); } buffer.release(); info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(PointBufferPointer( new PointBuffer(buffer, offsets, leafCount)))); return true; } void MetavoxelSystemClient::dataChanged(const MetavoxelData& oldData) { BufferBuilder builder(_remoteDataLOD); _data.guideToDifferent(oldData, builder); } void MetavoxelSystemClient::sendDatagram(const QByteArray& data) { NodeList::getInstance()->writeDatagram(data, _node); Application::getInstance()->getBandwidthMeter()->outputStream(BandwidthMeter::METAVOXELS).updateValue(data.size()); } PointBuffer::PointBuffer(const QOpenGLBuffer& buffer, const QVector& offsets, int lastLeafCount) : _buffer(buffer), _offsets(offsets), _lastLeafCount(lastLeafCount) { } void PointBuffer::render(int level) { _buffer.bind(); BufferPoint* point = 0; glVertexPointer(4, GL_FLOAT, sizeof(BufferPoint), &point->vertex); glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(BufferPoint), &point->color); glNormalPointer(GL_BYTE, sizeof(BufferPoint), &point->normal); int nextLevel = level + 1; if (nextLevel >= _offsets.size()) { glDrawArrays(GL_POINTS, _offsets.last() - _lastLeafCount, _lastLeafCount); } else { int first = _offsets.at(level); glDrawArrays(GL_POINTS, first, _offsets.at(nextLevel) - first); } _buffer.release(); } PointBufferAttribute::PointBufferAttribute() : InlineAttribute("pointBuffer") { } MetavoxelNode* PointBufferAttribute::createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const { return new MetavoxelNode(value, original); } bool PointBufferAttribute::merge(void*& parent, void* children[], bool postRead) const { for (int i = 0; i < MERGE_COUNT; i++) { if (decodeInline(children[i])) { return false; } } return true; } AttributeValue PointBufferAttribute::inherit(const AttributeValue& parentValue) const { return AttributeValue(parentValue.getAttribute()); } static void enableClipPlane(GLenum plane, float x, float y, float z, float w) { GLdouble coefficients[] = { x, y, z, w }; glClipPlane(plane, coefficients); glEnable(plane); } void ClippedRenderer::render(float alpha, Mode mode, const glm::vec3& clipMinimum, float clipSize) { if (clipSize == 0.0f) { renderUnclipped(alpha, mode); return; } enableClipPlane(GL_CLIP_PLANE0, -1.0f, 0.0f, 0.0f, clipMinimum.x + clipSize); enableClipPlane(GL_CLIP_PLANE1, 1.0f, 0.0f, 0.0f, -clipMinimum.x); enableClipPlane(GL_CLIP_PLANE2, 0.0f, -1.0f, 0.0f, clipMinimum.y + clipSize); enableClipPlane(GL_CLIP_PLANE3, 0.0f, 1.0f, 0.0f, -clipMinimum.y); enableClipPlane(GL_CLIP_PLANE4, 0.0f, 0.0f, -1.0f, clipMinimum.z + clipSize); enableClipPlane(GL_CLIP_PLANE5, 0.0f, 0.0f, 1.0f, -clipMinimum.z); renderUnclipped(alpha, mode); glDisable(GL_CLIP_PLANE0); glDisable(GL_CLIP_PLANE1); glDisable(GL_CLIP_PLANE2); glDisable(GL_CLIP_PLANE3); glDisable(GL_CLIP_PLANE4); glDisable(GL_CLIP_PLANE5); } SphereRenderer::SphereRenderer() { } void SphereRenderer::render(float alpha, Mode mode, const glm::vec3& clipMinimum, float clipSize) { if (clipSize == 0.0f) { renderUnclipped(alpha, mode); return; } // slight performance optimization: don't render if clip bounds are entirely within sphere Sphere* sphere = static_cast(parent()); Box clipBox(clipMinimum, clipMinimum + glm::vec3(clipSize, clipSize, clipSize)); for (int i = 0; i < Box::VERTEX_COUNT; i++) { const float CLIP_PROPORTION = 0.95f; if (glm::distance(sphere->getTranslation(), clipBox.getVertex(i)) >= sphere->getScale() * CLIP_PROPORTION) { ClippedRenderer::render(alpha, mode, clipMinimum, clipSize); return; } } } void SphereRenderer::renderUnclipped(float alpha, Mode mode) { Sphere* sphere = static_cast(parent()); const QColor& color = sphere->getColor(); glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF() * alpha); glPushMatrix(); const glm::vec3& translation = sphere->getTranslation(); glTranslatef(translation.x, translation.y, translation.z); glm::quat rotation = sphere->getRotation(); glm::vec3 axis = glm::axis(rotation); glRotatef(glm::angle(rotation), axis.x, axis.y, axis.z); glutSolidSphere(sphere->getScale(), 10, 10); glPopMatrix(); } StaticModelRenderer::StaticModelRenderer() : _model(new Model(this)) { } void StaticModelRenderer::init(Spanner* spanner) { _model->init(); StaticModel* staticModel = static_cast(spanner); applyTranslation(staticModel->getTranslation()); applyRotation(staticModel->getRotation()); applyScale(staticModel->getScale()); applyURL(staticModel->getURL()); connect(spanner, SIGNAL(translationChanged(const glm::vec3&)), SLOT(applyTranslation(const glm::vec3&))); connect(spanner, SIGNAL(rotationChanged(const glm::quat&)), SLOT(applyRotation(const glm::quat&))); connect(spanner, SIGNAL(scaleChanged(float)), SLOT(applyScale(float))); connect(spanner, SIGNAL(urlChanged(const QUrl&)), SLOT(applyURL(const QUrl&))); } void StaticModelRenderer::simulate(float deltaTime) { // update the bounds Box bounds; if (_model->isActive()) { const Extents& extents = _model->getGeometry()->getFBXGeometry().meshExtents; bounds = Box(extents.minimum, extents.maximum); } static_cast(parent())->setBounds(glm::translate(_model->getTranslation()) * glm::mat4_cast(_model->getRotation()) * glm::scale(_model->getScale()) * bounds); _model->simulate(deltaTime); } void StaticModelRenderer::renderUnclipped(float alpha, Mode mode) { switch (mode) { case DIFFUSE_MODE: _model->render(alpha, Model::DIFFUSE_RENDER_MODE); break; case NORMAL_MODE: _model->render(alpha, Model::NORMAL_RENDER_MODE); break; default: _model->render(alpha); break; } _model->render(alpha); } bool StaticModelRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& clipMinimum, float clipSize, float& distance) const { return _model->findRayIntersection(origin, direction, distance); } void StaticModelRenderer::applyTranslation(const glm::vec3& translation) { _model->setTranslation(translation); } void StaticModelRenderer::applyRotation(const glm::quat& rotation) { _model->setRotation(rotation); } void StaticModelRenderer::applyScale(float scale) { const float SCALE_MULTIPLIER = 0.0006f; _model->setScale(glm::vec3(scale, scale, scale) * SCALE_MULTIPLIER); } void StaticModelRenderer::applyURL(const QUrl& url) { _model->setURL(url); }