From f27263cc6a66ac123582c0a82a5bf9cd1aa9af17 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 1 Mar 2024 13:24:28 -0800 Subject: [PATCH 01/23] clean up geometrycache and remove _glColor4f --- .../src/RenderableMaterialEntityItem.cpp | 13 +- .../src/RenderableMaterialEntityItem.h | 1 + .../src/RenderableShapeEntityItem.cpp | 16 +- .../src/RenderableShapeEntityItem.h | 2 + .../gpu-gl-common/src/gpu/gl/GLBackend.cpp | 18 - .../gpu-gl-common/src/gpu/gl/GLBackend.h | 6 - .../src/gpu/gl/GLBackendInput.cpp | 16 - .../gpu-gl/src/gpu/gl41/GL41BackendInput.cpp | 13 - .../gpu-gl/src/gpu/gl45/GL45BackendInput.cpp | 13 - libraries/gpu/src/gpu/Batch.cpp | 9 - libraries/gpu/src/gpu/Batch.h | 4 - libraries/gpu/src/gpu/FrameIOKeys.h | 2 - libraries/graphics/src/graphics/Geometry.cpp | 18 +- libraries/graphics/src/graphics/Geometry.h | 6 +- libraries/render-utils/src/GeometryCache.cpp | 948 +++++------------- libraries/render-utils/src/GeometryCache.h | 90 +- .../render-utils/src/MeshPartPayload.cpp | 7 +- 17 files changed, 316 insertions(+), 866 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index b086f42d72..fd7f710bb3 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -337,21 +337,26 @@ void MaterialEntityRenderer::doRender(RenderArgs* args) { } // Draw! - DependencyManager::get()->renderSphere(batch); + auto compactColor = 0xFFFFFFFF; + _colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); + DependencyManager::get()->renderShape(batch, GeometryCache::Shape::Sphere, _colorBuffer); } else { auto proceduralDrawMaterial = std::static_pointer_cast(drawMaterial); glm::vec4 outColor = glm::vec4(drawMaterial->getAlbedo(), drawMaterial->getOpacity()); outColor = proceduralDrawMaterial->getColor(outColor); proceduralDrawMaterial->prepare(batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(outColor.a < 1.0f)); + + auto compactColor = GeometryCache::toCompactColor(glm::vec4(outColor)); + _colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); if (render::ShapeKey(args->_globalShapeKey).isWireframe() || _primitiveMode == PrimitiveMode::LINES) { - DependencyManager::get()->renderWireSphere(batch, outColor); + DependencyManager::get()->renderWireShape(batch, GeometryCache::Shape::Sphere, _colorBuffer); } else { - DependencyManager::get()->renderSphere(batch, outColor); + DependencyManager::get()->renderShape(batch, GeometryCache::Shape::Sphere, _colorBuffer); } } - args->_details._trianglesRendered += (int)DependencyManager::get()->getSphereTriangleCount(); + args->_details._trianglesRendered += (int)DependencyManager::get()->getShapeTriangleCount(GeometryCache::Shape::Sphere); } void MaterialEntityRenderer::setCurrentMaterialName(const std::string& currentMaterialName) { diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.h b/libraries/entities-renderer/src/RenderableMaterialEntityItem.h index ad1f9771a5..efded3aab3 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.h +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.h @@ -67,6 +67,7 @@ private: std::shared_ptr _appliedMaterial; std::string _currentMaterialName; + gpu::BufferPointer _colorBuffer { std::make_shared() }; }; } } diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 1355885625..ba141a96f6 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -131,10 +131,12 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { procedural->prepare(batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(outColor.a < 1.0f)); }); + auto compactColor = GeometryCache::toCompactColor(glm::vec4(outColor)); + _colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); if (wireframe) { - geometryCache->renderWireShape(batch, geometryShape, outColor); + geometryCache->renderWireShape(batch, geometryShape, _colorBuffer); } else { - geometryCache->renderShape(batch, geometryShape, outColor); + geometryCache->renderShape(batch, geometryShape, _colorBuffer); } } else if (pipelineType == Pipeline::SIMPLE) { // FIXME, support instanced multi-shape rendering using multidraw indirect @@ -149,10 +151,12 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { geometryCache->renderSolidShapeInstance(args, batch, geometryShape, outColor, pipeline); } } else { + auto compactColor = GeometryCache::toCompactColor(glm::vec4(outColor)); + _colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); if (wireframe) { - geometryCache->renderWireShape(batch, geometryShape, outColor); + geometryCache->renderWireShape(batch, geometryShape, _colorBuffer); } else { - geometryCache->renderShape(batch, geometryShape, outColor); + geometryCache->renderShape(batch, geometryShape, _colorBuffer); } } } else { @@ -160,7 +164,9 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { args->_details._materialSwitches++; } - geometryCache->renderShape(batch, geometryShape); + auto compactColor = GeometryCache::toCompactColor(glm::vec4(outColor)); + _colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); + geometryCache->renderShape(batch, geometryShape, _colorBuffer); } const auto triCount = geometryCache->getShapeTriangleCount(geometryShape); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 686014f4de..fd7bd4795b 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -42,6 +42,8 @@ private: std::shared_ptr _material { std::make_shared() }; glm::vec3 _color { NAN }; float _alpha { NAN }; + + gpu::BufferPointer _colorBuffer { std::make_shared() }; }; } } diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index af39458f17..47861d4f80 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -100,8 +100,6 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_glUniformMatrix3fv), (&::gpu::gl::GLBackend::do_glUniformMatrix4fv), - (&::gpu::gl::GLBackend::do_glColor4f), - (&::gpu::gl::GLBackend::do_pushProfileRange), (&::gpu::gl::GLBackend::do_popProfileRange), }; @@ -838,22 +836,6 @@ void GLBackend::do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) { (void)CHECK_GL_ERROR(); } -void GLBackend::do_glColor4f(const Batch& batch, size_t paramOffset) { - - glm::vec4 newColor( - batch._params[paramOffset + 3]._float, - batch._params[paramOffset + 2]._float, - batch._params[paramOffset + 1]._float, - batch._params[paramOffset + 0]._float); - - if (_input._colorAttribute != newColor) { - _input._colorAttribute = newColor; - glVertexAttrib4fv(gpu::Stream::COLOR, &_input._colorAttribute.r); - _input._hasColorAttribute = true; - } - (void)CHECK_GL_ERROR(); -} - void GLBackend::releaseBuffer(GLuint id, Size size) const { Lock lock(_trashMutex); _currentFrameTrash.buffersTrash.push_back({ id, size }); diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h index 2947649ce7..73e99ce1ac 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h @@ -241,8 +241,6 @@ public: virtual void do_glUniformMatrix3fv(const Batch& batch, size_t paramOffset) final; virtual void do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) final; - virtual void do_glColor4f(const Batch& batch, size_t paramOffset) final; - // The State setters called by the GLState::Commands when a new state is assigned virtual void do_setStateFillMode(int32 mode) final; virtual void do_setStateCullMode(int32 mode) final; @@ -350,8 +348,6 @@ protected: struct InputStageState { bool _invalidFormat { true }; bool _lastUpdateStereoState { false }; - bool _hasColorAttribute { false }; - bool _hadColorAttribute { false }; FormatReference _format { GPU_REFERENCE_INIT_VALUE }; std::string _formatKey; @@ -368,8 +364,6 @@ protected: std::array _bufferStrides; std::array _bufferVBOs; - glm::vec4 _colorAttribute { 1.0f }; - BufferReference _indexBuffer; Offset _indexBufferOffset { 0 }; Type _indexBufferType { UINT32 }; diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendInput.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendInput.cpp index 4efd5f9941..e5b0ead027 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendInput.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendInput.cpp @@ -103,9 +103,6 @@ void GLBackend::resetInputStage() { reset(_input._format); _input._formatKey.clear(); _input._invalidFormat = false; - _input._hasColorAttribute = false; - _input._hadColorAttribute = false; - _input._colorAttribute = vec4(1.0f); _input._attributeActivation.reset(); for (uint32_t i = 0; i < _input._buffers.size(); i++) { @@ -163,8 +160,6 @@ void GLBackend::updateInput() { #endif _input._lastUpdateStereoState = isStereoNow; - bool hasColorAttribute = _input._hasColorAttribute; - if (_input._invalidFormat) { InputStageState::ActivationCache newActivation; @@ -194,8 +189,6 @@ void GLBackend::updateInput() { GLenum perLocationSize = attrib._element.getLocationSize(); - hasColorAttribute |= slot == Stream::COLOR; - for (GLuint locNum = 0; locNum < locationCount; ++locNum) { GLuint attriNum = (GLuint)(slot + locNum); newActivation.set(attriNum); @@ -226,12 +219,6 @@ void GLBackend::updateInput() { glVertexBindingDivisor(bufferChannelNum, frequency); #endif } - - if (!hasColorAttribute && _input._hadColorAttribute) { - // The previous input stage had a color attribute but this one doesn't, so reset the color to pure white. - _input._colorAttribute = glm::vec4(1.0f); - glVertexAttrib4fv(Stream::COLOR, &_input._colorAttribute.r); - } } // Manage Activation what was and what is expected now @@ -253,9 +240,6 @@ void GLBackend::updateInput() { _stats._ISNumFormatChanges++; } - _input._hadColorAttribute = hasColorAttribute; - _input._hasColorAttribute = false; - if (_input._invalidBuffers.any()) { auto vbo = _input._bufferVBOs.data(); auto offset = _input._bufferOffsets.data(); diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendInput.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendInput.cpp index 188b4a1084..bdb37f6319 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendInput.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendInput.cpp @@ -33,8 +33,6 @@ void GL41Backend::updateInput() { #endif _input._lastUpdateStereoState = isStereoNow; - bool hasColorAttribute = _input._hasColorAttribute; - if (_input._invalidFormat || _input._invalidBuffers.any()) { auto format = acquire(_input._format); @@ -110,8 +108,6 @@ void GL41Backend::updateInput() { uintptr_t pointer = (uintptr_t)(attrib._offset + offsets[bufferNum]); GLboolean isNormalized = attrib._element.isNormalized(); - hasColorAttribute |= slot == Stream::COLOR; - for (size_t locNum = 0; locNum < locationCount; ++locNum) { if (attrib._element.isInteger()) { glVertexAttribIPointer(slot + (GLuint)locNum, count, type, stride, @@ -131,17 +127,8 @@ void GL41Backend::updateInput() { } } } - - if (!hasColorAttribute && _input._hadColorAttribute) { - // The previous input stage had a color attribute but this one doesn't, so reset the color to pure white. - _input._colorAttribute = glm::vec4(1.0f); - glVertexAttrib4fv(Stream::COLOR, &_input._colorAttribute.r); - } } // everything format related should be in sync now _input._invalidFormat = false; } - - _input._hadColorAttribute = hasColorAttribute; - _input._hasColorAttribute = false; } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendInput.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendInput.cpp index 8add4a9296..7513ff7caf 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendInput.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendInput.cpp @@ -35,8 +35,6 @@ void GL45Backend::updateInput() { #endif _input._lastUpdateStereoState = isStereoNow; - bool hasColorAttribute = _input._hasColorAttribute; - if (_input._invalidFormat) { InputStageState::ActivationCache newActivation; @@ -66,8 +64,6 @@ void GL45Backend::updateInput() { GLenum perLocationSize = attrib._element.getLocationSize(); - hasColorAttribute |= slot == Stream::COLOR; - for (GLuint locNum = 0; locNum < locationCount; ++locNum) { GLuint attriNum = (GLuint)(slot + locNum); newActivation.set(attriNum); @@ -98,12 +94,6 @@ void GL45Backend::updateInput() { glVertexBindingDivisor(bufferChannelNum, frequency); #endif } - - if (!hasColorAttribute && _input._hadColorAttribute) { - // The previous input stage had a color attribute but this one doesn't, so reset the color to pure white. - _input._colorAttribute = glm::vec4(1.0f); - glVertexAttrib4fv(Stream::COLOR, &_input._colorAttribute.r); - } } // Manage Activation what was and what is expected now @@ -125,9 +115,6 @@ void GL45Backend::updateInput() { _stats._ISNumFormatChanges++; } - _input._hadColorAttribute = hasColorAttribute; - _input._hasColorAttribute = false; - if (_input._invalidBuffers.any()) { auto vbo = _input._bufferVBOs.data(); auto offset = _input._bufferOffsets.data(); diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index e6217cc600..3654dfca5d 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -687,15 +687,6 @@ void Batch::_glUniformMatrix4fv(int32 location, int count, uint8 transpose, cons _params.emplace_back(location); } -void Batch::_glColor4f(float red, float green, float blue, float alpha) { - ADD_COMMAND(glColor4f); - - _params.emplace_back(alpha); - _params.emplace_back(blue); - _params.emplace_back(green); - _params.emplace_back(red); -} - void Batch::finishFrame(BufferUpdates& updates) { PROFILE_RANGE(render_gpu, __FUNCTION__); diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 0a438ea148..be04f08e41 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -287,8 +287,6 @@ public: _glUniformMatrix3fv(location, 1, false, glm::value_ptr(v)); } - void _glColor4f(float red, float green, float blue, float alpha); - // Maybe useful but shoudln't be public. Please convince me otherwise // Well porting to gles i need it... void runLambda(std::function f); @@ -363,8 +361,6 @@ public: COMMAND_glUniformMatrix3fv, COMMAND_glUniformMatrix4fv, - COMMAND_glColor4f, - COMMAND_pushProfileRange, COMMAND_popProfileRange, diff --git a/libraries/gpu/src/gpu/FrameIOKeys.h b/libraries/gpu/src/gpu/FrameIOKeys.h index 1a98d0decd..4cb7c6085d 100644 --- a/libraries/gpu/src/gpu/FrameIOKeys.h +++ b/libraries/gpu/src/gpu/FrameIOKeys.h @@ -201,8 +201,6 @@ constexpr const char* COMMAND_NAMES[] = { "glUniformMatrix3fv", "glUniformMatrix4fv", - "glColor4f", - "pushProfileRange", "popProfileRange", }; diff --git a/libraries/graphics/src/graphics/Geometry.cpp b/libraries/graphics/src/graphics/Geometry.cpp index 0fb2a0eb51..4b3cbfe9bc 100644 --- a/libraries/graphics/src/graphics/Geometry.cpp +++ b/libraries/graphics/src/graphics/Geometry.cpp @@ -19,6 +19,8 @@ Mesh::Mesh() : _vertexBuffer(gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)), _indexBuffer(gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::INDEX)), _partBuffer(gpu::Element(gpu::VEC4, gpu::UINT32, gpu::PART)) { + auto compactColor = 0xFFFFFFFF; + _colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); } Mesh::Mesh(const Mesh& mesh) : @@ -26,7 +28,8 @@ Mesh::Mesh(const Mesh& mesh) : _vertexBuffer(mesh._vertexBuffer), _attributeBuffers(mesh._attributeBuffers), _indexBuffer(mesh._indexBuffer), - _partBuffer(mesh._partBuffer) { + _partBuffer(mesh._partBuffer), + _colorBuffer(mesh._colorBuffer) { } Mesh::~Mesh() { @@ -39,6 +42,13 @@ void Mesh::setVertexFormatAndStream(const gpu::Stream::FormatPointer& vf, const auto attrib = _vertexFormat->getAttribute(gpu::Stream::POSITION); _vertexBuffer = BufferView(vbs->getBuffers()[attrib._channel], vbs->getOffsets()[attrib._channel], vbs->getBuffers()[attrib._channel]->getSize(), (gpu::uint16) vbs->getStrides()[attrib._channel], attrib._element); + + // We require meshes to have a color attribute. If they don't, we default to white. + if (!_vertexFormat->hasAttribute(gpu::Stream::COLOR)) { + int channelNum = _vertexStream.getNumBuffers(); + _vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); + _vertexStream.addBuffer(_colorBuffer, channelNum, _vertexFormat->getChannels().at(channelNum)._stride); + } } void Mesh::setVertexBuffer(const BufferView& buffer) { @@ -98,6 +108,12 @@ void Mesh::evalVertexStream() { _vertexStream.addBuffer(view._buffer, view._offset, stride); channelNum++; } + + // We require meshes to have a color attribute. If they don't, we default to white. + if (!_vertexFormat->hasAttribute(gpu::Stream::COLOR)) { + _vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); + _vertexStream.addBuffer(_colorBuffer, channelNum, _vertexFormat->getChannels().at(channelNum)._stride); + } } void Mesh::setIndexBuffer(const BufferView& buffer) { diff --git a/libraries/graphics/src/graphics/Geometry.h b/libraries/graphics/src/graphics/Geometry.h index fe1981c0e9..dcaeaad271 100644 --- a/libraries/graphics/src/graphics/Geometry.h +++ b/libraries/graphics/src/graphics/Geometry.h @@ -15,6 +15,7 @@ #include +#include #include #include @@ -28,7 +29,6 @@ typedef glm::vec3 Vec3; class Mesh; using MeshPointer = std::shared_ptr< Mesh >; - class Mesh { public: const static Index PRIMITIVE_RESTART_INDEX = -1; @@ -142,6 +142,8 @@ public: std::string modelName; std::string displayName; + gpu::BufferPointer getColorBuffer() const { return _colorBuffer; } + protected: gpu::Stream::FormatPointer _vertexFormat; @@ -154,6 +156,8 @@ protected: BufferView _partBuffer; + gpu::BufferPointer _colorBuffer { std::make_shared() }; + void evalVertexFormat(); void evalVertexStream(); diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 4ac097e31d..7af922c303 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -99,14 +99,9 @@ static const gpu::Element NORMAL_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ }; static const gpu::Element TEXCOORD0_ELEMENT { gpu::VEC2, gpu::FLOAT, gpu::UV }; static const gpu::Element TANGENT_ELEMENT { gpu::VEC3, gpu::FLOAT, gpu::XYZ }; static const gpu::Element COLOR_ELEMENT { gpu::VEC4, gpu::NUINT8, gpu::RGBA }; -static const gpu::Element TEXCOORD4_ELEMENT { gpu::VEC4, gpu::FLOAT, gpu::XYZW }; static gpu::Stream::FormatPointer SOLID_STREAM_FORMAT; -static gpu::Stream::FormatPointer INSTANCED_SOLID_STREAM_FORMAT; -static gpu::Stream::FormatPointer INSTANCED_SOLID_FADE_STREAM_FORMAT; static gpu::Stream::FormatPointer WIRE_STREAM_FORMAT; -static gpu::Stream::FormatPointer INSTANCED_WIRE_STREAM_FORMAT; -static gpu::Stream::FormatPointer INSTANCED_WIRE_FADE_STREAM_FORMAT; static const uint SHAPE_VERTEX_STRIDE = sizeof(GeometryCache::ShapeVertex); // position, normal, texcoords, tangent static const uint SHAPE_NORMALS_OFFSET = offsetof(GeometryCache::ShapeVertex, normal); @@ -259,14 +254,6 @@ size_t GeometryCache::getShapeTriangleCount(Shape shape) { return _shapes[shape]._indicesView.getNumElements() / VERTICES_PER_TRIANGLE; } -size_t GeometryCache::getSphereTriangleCount() { - return getShapeTriangleCount(Sphere); -} - -size_t GeometryCache::getCubeTriangleCount() { - return getShapeTriangleCount(Cube); -} - using IndexPair = uint64_t; using IndexPairs = std::unordered_set; @@ -647,83 +634,24 @@ gpu::Stream::FormatPointer& getSolidStreamFormat() { SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD0, gpu::Stream::TEXCOORD0, TEXCOORD0_ELEMENT); SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::TANGENT, gpu::Stream::TANGENT, TANGENT_ELEMENT); + SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); } return SOLID_STREAM_FORMAT; } -gpu::Stream::FormatPointer& getInstancedSolidStreamFormat() { - if (!INSTANCED_SOLID_STREAM_FORMAT) { - INSTANCED_SOLID_STREAM_FORMAT = std::make_shared(); // 1 for everyone - INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); - INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); - INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD0, gpu::Stream::TEXCOORD0, TEXCOORD0_ELEMENT); - INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::TANGENT, gpu::Stream::TANGENT, TANGENT_ELEMENT); - INSTANCED_SOLID_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); - } - return INSTANCED_SOLID_STREAM_FORMAT; -} - -gpu::Stream::FormatPointer& getInstancedSolidFadeStreamFormat() { - if (!INSTANCED_SOLID_FADE_STREAM_FORMAT) { - INSTANCED_SOLID_FADE_STREAM_FORMAT = std::make_shared(); // 1 for everyone - INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); - INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); - INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD0, gpu::Stream::TEXCOORD0, TEXCOORD0_ELEMENT); - INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TANGENT, gpu::Stream::TANGENT, TANGENT_ELEMENT); - INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); - INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD2, gpu::Stream::TEXCOORD2, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); - INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD3, gpu::Stream::TEXCOORD3, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); - INSTANCED_SOLID_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD4, gpu::Stream::TEXCOORD4, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); - } - return INSTANCED_SOLID_FADE_STREAM_FORMAT; -} - gpu::Stream::FormatPointer& getWireStreamFormat() { if (!WIRE_STREAM_FORMAT) { WIRE_STREAM_FORMAT = std::make_shared(); // 1 for everyone WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); + WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); } return WIRE_STREAM_FORMAT; } -gpu::Stream::FormatPointer& getInstancedWireStreamFormat() { - if (!INSTANCED_WIRE_STREAM_FORMAT) { - INSTANCED_WIRE_STREAM_FORMAT = std::make_shared(); // 1 for everyone - INSTANCED_WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); - INSTANCED_WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); - INSTANCED_WIRE_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); - } - return INSTANCED_WIRE_STREAM_FORMAT; -} - -gpu::Stream::FormatPointer& getInstancedWireFadeStreamFormat() { - if (!INSTANCED_WIRE_FADE_STREAM_FORMAT) { - INSTANCED_WIRE_FADE_STREAM_FORMAT = std::make_shared(); // 1 for everyone - INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, POSITION_ELEMENT); - INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, NORMAL_ELEMENT); - INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, COLOR_ELEMENT, 0, gpu::Stream::PER_INSTANCE); - INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD2, gpu::Stream::TEXCOORD2, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); - INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD3, gpu::Stream::TEXCOORD3, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); - INSTANCED_WIRE_FADE_STREAM_FORMAT->setAttribute(gpu::Stream::TEXCOORD4, gpu::Stream::TEXCOORD4, TEXCOORD4_ELEMENT, 0, gpu::Stream::PER_INSTANCE); - } - return INSTANCED_WIRE_FADE_STREAM_FORMAT; -} - -QHash GeometryCache::_simplePrograms; - -gpu::ShaderPointer GeometryCache::_simpleShader; -gpu::ShaderPointer GeometryCache::_transparentShader; -gpu::ShaderPointer GeometryCache::_unlitShader; -gpu::ShaderPointer GeometryCache::_simpleFadeShader; -gpu::ShaderPointer GeometryCache::_unlitFadeShader; -gpu::ShaderPointer GeometryCache::_forwardSimpleShader; -gpu::ShaderPointer GeometryCache::_forwardTransparentShader; -gpu::ShaderPointer GeometryCache::_forwardUnlitShader; -gpu::ShaderPointer GeometryCache::_forwardSimpleFadeShader; -gpu::ShaderPointer GeometryCache::_forwardUnlitFadeShader; - +std::map, gpu::ShaderPointer> GeometryCache::_shapeShaders; std::map, render::ShapePipelinePointer> GeometryCache::_shapePipelines; +QHash GeometryCache::_simplePrograms; GeometryCache::GeometryCache() : _nextID(0) { @@ -809,103 +737,35 @@ render::ShapePipelinePointer GeometryCache::getFadingShapePipeline(bool textured ); } -void GeometryCache::renderShape(gpu::Batch& batch, Shape shape) { - batch.setInputFormat(getSolidStreamFormat()); - _shapes[shape].draw(batch); -} - -void GeometryCache::renderWireShape(gpu::Batch& batch, Shape shape) { - batch.setInputFormat(getWireStreamFormat()); - _shapes[shape].drawWire(batch); -} - -void GeometryCache::renderShape(gpu::Batch& batch, Shape shape, const glm::vec4& color) { - batch.setInputFormat(getSolidStreamFormat()); - batch._glColor4f(color.r, color.g, color.b, color.a); - _shapes[shape].draw(batch); -} - -void GeometryCache::renderWireShape(gpu::Batch& batch, Shape shape, const glm::vec4& color) { - batch.setInputFormat(getWireStreamFormat()); - batch._glColor4f(color.r, color.g, color.b, color.a); - _shapes[shape].drawWire(batch); -} - -void setupBatchInstance(gpu::Batch& batch, gpu::BufferPointer colorBuffer) { +void setupColorInputBuffer(gpu::Batch& batch, gpu::BufferPointer colorBuffer) { gpu::BufferView colorView(colorBuffer, COLOR_ELEMENT); batch.setInputBuffer(gpu::Stream::COLOR, colorView); } +void GeometryCache::renderShape(gpu::Batch& batch, Shape shape, gpu::BufferPointer& colorBuffer) { + batch.setInputFormat(getSolidStreamFormat()); + setupColorInputBuffer(batch, colorBuffer); + _shapes[shape].draw(batch); +} + +void GeometryCache::renderWireShape(gpu::Batch& batch, Shape shape, gpu::BufferPointer& colorBuffer) { + batch.setInputFormat(getWireStreamFormat()); + setupColorInputBuffer(batch, colorBuffer); + _shapes[shape].drawWire(batch); +} + void GeometryCache::renderShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer) { - batch.setInputFormat(getInstancedSolidStreamFormat()); - setupBatchInstance(batch, colorBuffer); + batch.setInputFormat(getSolidStreamFormat()); + setupColorInputBuffer(batch, colorBuffer); _shapes[shape].drawInstances(batch, count); } void GeometryCache::renderWireShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer) { - batch.setInputFormat(getInstancedWireStreamFormat()); - setupBatchInstance(batch, colorBuffer); + batch.setInputFormat(getWireStreamFormat()); + setupColorInputBuffer(batch, colorBuffer); _shapes[shape].drawWireInstances(batch, count); } -void setupBatchFadeInstance(gpu::Batch& batch, gpu::BufferPointer colorBuffer, - gpu::BufferPointer fadeBuffer1, gpu::BufferPointer fadeBuffer2, gpu::BufferPointer fadeBuffer3) { - gpu::BufferView colorView(colorBuffer, COLOR_ELEMENT); - gpu::BufferView texCoord2View(fadeBuffer1, TEXCOORD4_ELEMENT); - gpu::BufferView texCoord3View(fadeBuffer2, TEXCOORD4_ELEMENT); - gpu::BufferView texCoord4View(fadeBuffer3, TEXCOORD4_ELEMENT); - batch.setInputBuffer(gpu::Stream::COLOR, colorView); - batch.setInputBuffer(gpu::Stream::TEXCOORD2, texCoord2View); - batch.setInputBuffer(gpu::Stream::TEXCOORD3, texCoord3View); - batch.setInputBuffer(gpu::Stream::TEXCOORD4, texCoord4View); -} - -void GeometryCache::renderFadeShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer, - gpu::BufferPointer& fadeBuffer1, gpu::BufferPointer& fadeBuffer2, gpu::BufferPointer& fadeBuffer3) { - batch.setInputFormat(getInstancedSolidFadeStreamFormat()); - setupBatchFadeInstance(batch, colorBuffer, fadeBuffer1, fadeBuffer2, fadeBuffer3); - _shapes[shape].drawInstances(batch, count); -} - -void GeometryCache::renderWireFadeShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer, - gpu::BufferPointer& fadeBuffer1, gpu::BufferPointer& fadeBuffer2, gpu::BufferPointer& fadeBuffer3) { - batch.setInputFormat(getInstancedWireFadeStreamFormat()); - setupBatchFadeInstance(batch, colorBuffer, fadeBuffer1, fadeBuffer2, fadeBuffer3); - _shapes[shape].drawWireInstances(batch, count); -} - -void GeometryCache::renderCube(gpu::Batch& batch) { - renderShape(batch, Cube); -} - -void GeometryCache::renderWireCube(gpu::Batch& batch) { - renderWireShape(batch, Cube); -} - -void GeometryCache::renderCube(gpu::Batch& batch, const glm::vec4& color) { - renderShape(batch, Cube, color); -} - -void GeometryCache::renderWireCube(gpu::Batch& batch, const glm::vec4& color) { - renderWireShape(batch, Cube, color); -} - -void GeometryCache::renderSphere(gpu::Batch& batch) { - renderShape(batch, Sphere); -} - -void GeometryCache::renderWireSphere(gpu::Batch& batch) { - renderWireShape(batch, Sphere); -} - -void GeometryCache::renderSphere(gpu::Batch& batch, const glm::vec4& color) { - renderShape(batch, Sphere, color); -} - -void GeometryCache::renderWireSphere(gpu::Batch& batch, const glm::vec4& color) { - renderWireShape(batch, Sphere, color); -} - void GeometryCache::renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, int majorRows, int majorCols, float majorEdge, int minorRows, int minorCols, float minorEdge, const glm::vec4& color, bool forward, int id) { @@ -963,33 +823,30 @@ void GeometryCache::updateVertices(int id, const QVector& points, con #endif // def WANT_DEBUG } - const int FLOATS_PER_VERTEX = 2 + 3; // vertices + normals - const int NUM_POS_COORDS = 2; - const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float); + const int FLOATS_PER_VERTEX = 2; // vertices details.isCreated = true; details.vertices = points.size(); details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); + auto normalBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; + details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0); - details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); - // TODO: circle3D overlays use this to define their vertices, so they need tex coords - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); + details.streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); + details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); - details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - - details.vertices = points.size(); - details.vertexSize = FLOATS_PER_VERTEX; + details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); + details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::NORMAL)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX]; float* vertex = vertexData; @@ -997,7 +854,6 @@ void GeometryCache::updateVertices(int id, const QVector& points, con int* colorData = new int[details.vertices]; int* colorDataAt = colorData; - const glm::vec3 NORMAL(0.0f, -1.0f, 0.0f); auto pointCount = points.size(); auto colorCount = colors.size(); int compactColor = 0; @@ -1005,19 +861,16 @@ void GeometryCache::updateVertices(int id, const QVector& points, con const auto& point = points[i]; *(vertex++) = point.x; *(vertex++) = point.y; - *(vertex++) = NORMAL.x; - *(vertex++) = NORMAL.y; - *(vertex++) = NORMAL.z; if (i < colorCount) { const auto& color = colors[i]; - compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); + compactColor = GeometryCache::toCompactColor(color); } *(colorDataAt++) = compactColor; } + details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData); + const glm::vec3 NORMAL(0.0f, -1.0f, 0.0f); + details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); details.colorBuffer->append(sizeof(int) * details.vertices, (gpu::Byte*) colorData); delete[] vertexData; delete[] colorData; @@ -1040,32 +893,30 @@ void GeometryCache::updateVertices(int id, const QVector& points, con #endif // def WANT_DEBUG } - const int FLOATS_PER_VERTEX = 3 + 3; // vertices + normals - const int NUM_POS_COORDS = 3; - const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float); + const int FLOATS_PER_VERTEX = 3; // vertices details.isCreated = true; details.vertices = points.size(); details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); + auto normalBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; + details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + details.streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); + details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); - details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - - details.vertices = points.size(); - details.vertexSize = FLOATS_PER_VERTEX; + details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); + details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::NORMAL)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); // Default to white int compactColor = 0xFFFFFFFF; @@ -1075,28 +926,23 @@ void GeometryCache::updateVertices(int id, const QVector& points, con int* colorData = new int[details.vertices]; int* colorDataAt = colorData; - const glm::vec3 NORMAL(0.0f, -1.0f, 0.0f); auto pointCount = points.size(); auto colorCount = colors.size(); for (auto i = 0; i < pointCount; i++) { const glm::vec3& point = points[i]; - if (i < colorCount) { - const glm::vec4& color = colors[i]; - compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); - } *(vertex++) = point.x; *(vertex++) = point.y; *(vertex++) = point.z; - *(vertex++) = NORMAL.x; - *(vertex++) = NORMAL.y; - *(vertex++) = NORMAL.z; + if (i < colorCount) { + const glm::vec4& color = colors[i]; + compactColor = GeometryCache::toCompactColor(color); + } *(colorDataAt++) = compactColor; } details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData); + const glm::vec3 NORMAL(0.0f, -1.0f, 0.0f); + details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); details.colorBuffer->append(sizeof(int) * details.vertices, (gpu::Byte*) colorData); delete[] vertexData; delete[] colorData; @@ -1120,69 +966,55 @@ void GeometryCache::updateVertices(int id, const QVector& points, con #endif // def WANT_DEBUG } - const int FLOATS_PER_VERTEX = 3 + 3 + 2; // vertices + normals + tex coords + const int FLOATS_PER_VERTEX = 3 + 2; // vertices + tex coords const int NUM_POS_COORDS = 3; - const int NUM_NORMAL_COORDS = 3; - const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float); - const int VERTEX_TEX_OFFSET = VERTEX_NORMAL_OFFSET + NUM_NORMAL_COORDS * sizeof(float); + const int VERTEX_TEX_OFFSET = NUM_POS_COORDS * sizeof(float); details.isCreated = true; details.vertices = points.size(); details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); + auto normalBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; + details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); + details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); details.streamFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), VERTEX_TEX_OFFSET); - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + details.streamFormat->setAttribute(gpu::Stream::NORMAL, 1, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); + details.streamFormat->setAttribute(gpu::Stream::COLOR, 2, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); + details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(1)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(2)._stride); assert(points.size() == texCoords.size()); - details.vertices = points.size(); - details.vertexSize = FLOATS_PER_VERTEX; - - int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); - float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX]; float* vertex = vertexData; - int* colorData = new int[details.vertices]; - int* colorDataAt = colorData; - - const glm::vec3 NORMAL(0.0f, -1.0f, 0.0f); for (int i = 0; i < points.size(); i++) { glm::vec3 point = points[i]; glm::vec2 texCoord = texCoords[i]; *(vertex++) = point.x; *(vertex++) = point.y; *(vertex++) = point.z; - *(vertex++) = NORMAL.x; - *(vertex++) = NORMAL.y; - *(vertex++) = NORMAL.z; *(vertex++) = texCoord.x; *(vertex++) = texCoord.y; - - *(colorDataAt++) = compactColor; } - details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData); - details.colorBuffer->append(sizeof(int) * details.vertices, (gpu::Byte*) colorData); + details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*)vertexData); + const glm::vec3 NORMAL(0.0f, -1.0f, 0.0f); + details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); + int compactColor = GeometryCache::toCompactColor(color); + details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); delete[] vertexData; - delete[] colorData; #ifdef WANT_DEBUG qCDebug(renderutils) << "new registered linestrip buffer made -- _registeredVertices.size():" << _registeredVertices.size(); @@ -1239,12 +1071,11 @@ void GeometryCache::renderBevelCornersRect(gpu::Batch& batch, int x, int y, int details.streamFormat = streamFormat; details.stream = stream; - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); - - details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); + details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); + details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); + details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); float vertexBuffer[NUM_FLOATS]; // only vertices, no normals because we're a 2D quad int vertexPoint = 0; @@ -1283,16 +1114,9 @@ void GeometryCache::renderBevelCornersRect(gpu::Batch& batch, int x, int y, int vertexBuffer[vertexPoint++] = x + width; vertexBuffer[vertexPoint++] = y + bevelDistance; - int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); - int colors[NUM_VERTICES] = { compactColor, compactColor, compactColor, compactColor, - compactColor, compactColor, compactColor, compactColor }; - - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); - details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); + int compactColor = GeometryCache::toCompactColor(color); + details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); } batch.setInputFormat(details.streamFormat); @@ -1322,52 +1146,46 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co #endif // def WANT_DEBUG } - const int FLOATS_PER_VERTEX = 2 + 3; // vertices + normals - const int VERTICES = 4; // 1 quad = 4 vertices - const int NUM_POS_COORDS = 2; - const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float); - if (!details.isCreated) { + static const int FLOATS_PER_VERTEX = 2; // vertices + static const int VERTICES = 4; // 1 quad = 4 vertices details.isCreated = true; details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); + auto normalBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; + details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0); - details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); + details.streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); + details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); - details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); + details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); + details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::NORMAL)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); - - const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { - minCorner.x, minCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, - maxCorner.x, minCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, - minCorner.x, maxCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, - maxCorner.x, maxCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, + minCorner.x, minCorner.y, + maxCorner.x, minCorner.y, + minCorner.x, maxCorner.y, + maxCorner.x, maxCorner.y, }; - const int NUM_COLOR_SCALARS_PER_QUAD = 4; - int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); - int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); - details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); + const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); + details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); + int compactColor = GeometryCache::toCompactColor(color); + details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); } batch.setInputFormat(details.streamFormat); @@ -1409,59 +1227,49 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co #endif // def WANT_DEBUG } - const int FLOATS_PER_VERTEX = 2 + 3 + 2; // vertices + normals + tex coords - const int VERTICES = 4; // 1 quad = 4 vertices - const int NUM_POS_COORDS = 2; - const int NUM_NORMAL_COORDS = 3; - const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float); - const int VERTEX_TEXCOORD_OFFSET = VERTEX_NORMAL_OFFSET + NUM_NORMAL_COORDS * sizeof(float); - if (!details.isCreated) { + static const int FLOATS_PER_VERTEX = 2 + 2; // vertices + tex coords + static const int VERTICES = 4; // 1 quad = 4 vertices + static const int NUM_POS_COORDS = 2; + static const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); details.isCreated = true; details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); + auto normalBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); - auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; + details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; - details.streamFormat = streamFormat; details.stream = stream; - // zzmp: fix the normal across all renderQuad - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ), 0); - details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); + details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); details.streamFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), VERTEX_TEXCOORD_OFFSET); - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + details.streamFormat->setAttribute(gpu::Stream::NORMAL, 1, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); + details.streamFormat->setAttribute(gpu::Stream::COLOR, 2, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); + details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(1)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(2)._stride); - - const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { - minCorner.x, minCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, texCoordMinCorner.x, texCoordMinCorner.y, - maxCorner.x, minCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, texCoordMaxCorner.x, texCoordMinCorner.y, - minCorner.x, maxCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, texCoordMinCorner.x, texCoordMaxCorner.y, - maxCorner.x, maxCorner.y, NORMAL.x, NORMAL.y, NORMAL.z, texCoordMaxCorner.x, texCoordMaxCorner.y, + minCorner.x, minCorner.y, texCoordMinCorner.x, texCoordMinCorner.y, + maxCorner.x, minCorner.y, texCoordMaxCorner.x, texCoordMinCorner.y, + minCorner.x, maxCorner.y, texCoordMinCorner.x, texCoordMaxCorner.y, + maxCorner.x, maxCorner.y, texCoordMaxCorner.x, texCoordMaxCorner.y, }; - - const int NUM_COLOR_SCALARS_PER_QUAD = 4; - int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); - int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); - details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); + const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); + details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); + int compactColor = GeometryCache::toCompactColor(color); + details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); } batch.setInputFormat(details.streamFormat); @@ -1491,54 +1299,46 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, co #endif // def WANT_DEBUG } - const int FLOATS_PER_VERTEX = 3 + 3; // vertices + normals - const int VERTICES = 4; // 1 quad = 4 vertices - const int NUM_POS_COORDS = 3; - const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float); - if (!details.isCreated) { + static const int FLOATS_PER_VERTEX = 3; // vertices + static const int VERTICES = 4; // 1 quad = 4 vertices details.isCreated = true; details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; auto verticesBuffer = std::make_shared(); + auto normalBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); - auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; + details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; - details.streamFormat = streamFormat; details.stream = stream; - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + details.streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); + details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); - details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); + details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); + details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::NORMAL)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); - - const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { - minCorner.x, minCorner.y, minCorner.z, NORMAL.x, NORMAL.y, NORMAL.z, - maxCorner.x, minCorner.y, minCorner.z, NORMAL.x, NORMAL.y, NORMAL.z, - minCorner.x, maxCorner.y, maxCorner.z, NORMAL.x, NORMAL.y, NORMAL.z, - maxCorner.x, maxCorner.y, maxCorner.z, NORMAL.x, NORMAL.y, NORMAL.z, + minCorner.x, minCorner.y, minCorner.z, + maxCorner.x, minCorner.y, minCorner.z, + minCorner.x, maxCorner.y, maxCorner.z, + maxCorner.x, maxCorner.y, maxCorner.z, }; - const int NUM_COLOR_SCALARS_PER_QUAD = 4; - int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); - int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); - details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); + const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); + details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); + int compactColor = GeometryCache::toCompactColor(color); + details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); } batch.setInputFormat(details.streamFormat); @@ -1587,56 +1387,49 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons #endif // def WANT_DEBUG } - const int FLOATS_PER_VERTEX = 3 + 3 + 2; // vertices + normals + tex coords - const int VERTICES = 4; // 1 quad = 4 vertices - const int NUM_POS_COORDS = 3; - const int NUM_NORMAL_COORDS = 3; - const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float); - const int VERTEX_TEXCOORD_OFFSET = VERTEX_NORMAL_OFFSET + NUM_NORMAL_COORDS * sizeof(float); - - if (!details.isCreated) { + static const int FLOATS_PER_VERTEX = 3 + 2; // vertices + tex coords + static const int VERTICES = 4; // 1 quad = 4 vertices + static const int NUM_POS_COORDS = 3; + static const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); details.isCreated = true; details.vertices = VERTICES; details.vertexSize = FLOATS_PER_VERTEX; // NOTE: this isn't used for BatchItemDetails maybe we can get rid of it auto verticesBuffer = std::make_shared(); + auto normalBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); - details.verticesBuffer = verticesBuffer; + details.normalBuffer = normalBuffer; + details.colorBuffer = colorBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); + details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); details.streamFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), VERTEX_TEXCOORD_OFFSET); - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + details.streamFormat->setAttribute(gpu::Stream::NORMAL, 1, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); + details.streamFormat->setAttribute(gpu::Stream::COLOR, 2, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); + details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(1)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(2)._stride); - - const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); float vertexBuffer[VERTICES * FLOATS_PER_VERTEX] = { - bottomLeft.x, bottomLeft.y, bottomLeft.z, NORMAL.x, NORMAL.y, NORMAL.z, texCoordBottomLeft.x, texCoordBottomLeft.y, - bottomRight.x, bottomRight.y, bottomRight.z, NORMAL.x, NORMAL.y, NORMAL.z, texCoordBottomRight.x, texCoordBottomRight.y, - topLeft.x, topLeft.y, topLeft.z, NORMAL.x, NORMAL.y, NORMAL.z, texCoordTopLeft.x, texCoordTopLeft.y, - topRight.x, topRight.y, topRight.z, NORMAL.x, NORMAL.y, NORMAL.z, texCoordTopRight.x, texCoordTopRight.y, + bottomLeft.x, bottomLeft.y, bottomLeft.z, texCoordBottomLeft.x, texCoordBottomLeft.y, + bottomRight.x, bottomRight.y, bottomRight.z, texCoordBottomRight.x, texCoordBottomRight.y, + topLeft.x, topLeft.y, topLeft.z, texCoordTopLeft.x, texCoordTopLeft.y, + topRight.x, topRight.y, topRight.z, texCoordTopRight.x, texCoordTopRight.y, }; - const int NUM_COLOR_SCALARS_PER_QUAD = 4; - int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); - int colors[NUM_COLOR_SCALARS_PER_QUAD] = { compactColor, compactColor, compactColor, compactColor }; - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); - details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); + const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); + details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); + int compactColor = GeometryCache::toCompactColor(color); + details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); } batch.setInputFormat(details.streamFormat); @@ -1644,6 +1437,88 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons batch.draw(gpu::TRIANGLE_STRIP, 4, 0); } +void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, + const glm::vec4& color1, const glm::vec4& color2, int id) { + + bool registered = (id != UNKNOWN_ID); + Vec3Pair key(p1, p2); + + BatchItemDetails& details = _registeredLine3DVBOs[id]; + + // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed + if (registered && details.isCreated) { + Vec3Pair& lastKey = _lastRegisteredLine3D[id]; + if (lastKey != key) { + details.clear(); + _lastRegisteredLine3D[id] = key; +#ifdef WANT_DEBUG + qCDebug(renderutils) << "renderLine() 3D ... RELEASING REGISTERED line"; +#endif // def WANT_DEBUG + } +#ifdef WANT_DEBUG + else { + qCDebug(renderutils) << "renderLine() 3D ... REUSING PREVIOUSLY REGISTERED line"; + } +#endif // def WANT_DEBUG + } + + if (!details.isCreated) { + static const int FLOATS_PER_VERTEX = 3; // vertices + static const int vertices = 2; + + details.isCreated = true; + details.vertices = vertices; + details.vertexSize = FLOATS_PER_VERTEX; + + auto verticesBuffer = std::make_shared(); + auto normalBuffer = std::make_shared(); + auto colorBuffer = std::make_shared(); + auto streamFormat = std::make_shared(); + auto stream = std::make_shared(); + + details.verticesBuffer = verticesBuffer; + details.normalBuffer = normalBuffer; + details.colorBuffer = colorBuffer; + details.streamFormat = streamFormat; + details.stream = stream; + + details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + details.streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); + details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + + details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); + details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::NORMAL)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); + + float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { + p1.x, p1.y, p1.z, + p2.x, p2.y, p2.z + }; + + const int NUM_COLOR_SCALARS = 2; + int compactColor1 = GeometryCache::toCompactColor(color1); + int compactColor2 = GeometryCache::toCompactColor(color2); + int colors[NUM_COLOR_SCALARS] = { compactColor1, compactColor2 }; + + details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); + const glm::vec3 NORMAL(0.0f, 0.0f, 1.0f); + details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); + details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); + +#ifdef WANT_DEBUG + if (id == UNKNOWN_ID) { + qCDebug(renderutils) << "new renderLine() 3D VBO made -- _line3DVBOs.size():" << _line3DVBOs.size(); + } else { + qCDebug(renderutils) << "new registered renderLine() 3D VBO made -- _registeredLine3DVBOs.size():" << _registeredLine3DVBOs.size(); + } +#endif + } + + batch.setInputFormat(details.streamFormat); + batch.setInputStream(0, *details.stream); + batch.draw(gpu::LINES, 2, 0); +} + void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, const float dash_length, const float gap_length, int id) { @@ -1664,11 +1539,6 @@ void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, if (!details.isCreated) { - int compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); - // draw each line segment with appropriate gaps const float SEGMENT_LENGTH = dash_length + gap_length; float length = glm::distance(start, end); @@ -1679,77 +1549,60 @@ void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, glm::vec3 dashVector = segmentVector / SEGMENT_LENGTH * dash_length; glm::vec3 gapVector = segmentVector / SEGMENT_LENGTH * gap_length; - const int FLOATS_PER_VERTEX = 3 + 3; // vertices + normals - const int NUM_POS_COORDS = 3; - const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float); + const int FLOATS_PER_VERTEX = 3; // vertices + details.isCreated = true; details.vertices = (segmentCountFloor + 1) * 2; details.vertexSize = FLOATS_PER_VERTEX; - details.isCreated = true; auto verticesBuffer = std::make_shared(); + auto normalBuffer = std::make_shared(); auto colorBuffer = std::make_shared(); auto streamFormat = std::make_shared(); auto stream = std::make_shared(); details.verticesBuffer = verticesBuffer; + details.normalBuffer = normalBuffer; details.colorBuffer = colorBuffer; details.streamFormat = streamFormat; details.stream = stream; - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); + details.streamFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + details.streamFormat->setAttribute(gpu::Stream::NORMAL, gpu::Stream::NORMAL, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0, gpu::Stream::PER_INSTANCE); + details.streamFormat->setAttribute(gpu::Stream::COLOR, gpu::Stream::COLOR, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); - details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - - int* colorData = new int[details.vertices]; - int* colorDataAt = colorData; + details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::POSITION)._stride); + details.stream->addBuffer(details.normalBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::NORMAL)._stride); + details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(gpu::Stream::COLOR)._stride); float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX]; float* vertex = vertexData; - const glm::vec3 NORMAL(1.0f, 0.0f, 0.0f); glm::vec3 point = start; *(vertex++) = point.x; *(vertex++) = point.y; *(vertex++) = point.z; - *(vertex++) = NORMAL.x; - *(vertex++) = NORMAL.y; - *(vertex++) = NORMAL.z; - *(colorDataAt++) = compactColor; for (int i = 0; i < segmentCountFloor; i++) { point += dashVector; *(vertex++) = point.x; *(vertex++) = point.y; *(vertex++) = point.z; - *(vertex++) = NORMAL.x; - *(vertex++) = NORMAL.y; - *(vertex++) = NORMAL.z; - *(colorDataAt++) = compactColor; point += gapVector; *(vertex++) = point.x; *(vertex++) = point.y; *(vertex++) = point.z; - *(vertex++) = NORMAL.x; - *(vertex++) = NORMAL.y; - *(vertex++) = NORMAL.z; - *(colorDataAt++) = compactColor; } *(vertex++) = end.x; *(vertex++) = end.y; *(vertex++) = end.z; - *(vertex++) = NORMAL.x; - *(vertex++) = NORMAL.y; - *(vertex++) = NORMAL.z; - *(colorDataAt++) = compactColor; details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData); - details.colorBuffer->append(sizeof(int) * details.vertices, (gpu::Byte*) colorData); + const glm::vec3 NORMAL(1.0f, 0.0f, 0.0f); + details.normalBuffer->append(3 * sizeof(float), (gpu::Byte*) glm::value_ptr(NORMAL)); + int compactColor = GeometryCache::toCompactColor(color); + details.colorBuffer->append(sizeof(compactColor), (gpu::Byte*) &compactColor); delete[] vertexData; - delete[] colorData; #ifdef WANT_DEBUG if (registered) { @@ -1770,6 +1623,7 @@ int GeometryCache::BatchItemDetails::population = 0; GeometryCache::BatchItemDetails::BatchItemDetails() : verticesBuffer(NULL), +normalBuffer(NULL), colorBuffer(NULL), streamFormat(NULL), stream(NULL), @@ -1784,6 +1638,7 @@ isCreated(false) { GeometryCache::BatchItemDetails::BatchItemDetails(const GeometryCache::BatchItemDetails& other) : verticesBuffer(other.verticesBuffer), +normalBuffer(other.normalBuffer), colorBuffer(other.colorBuffer), streamFormat(other.streamFormat), stream(other.stream), @@ -1808,184 +1663,12 @@ void GeometryCache::BatchItemDetails::clear() { isCreated = false; uniformBuffer.reset(); verticesBuffer.reset(); + normalBuffer.reset(); colorBuffer.reset(); streamFormat.reset(); stream.reset(); } -void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, - const glm::vec4& color1, const glm::vec4& color2, int id) { - - bool registered = (id != UNKNOWN_ID); - Vec3Pair key(p1, p2); - - BatchItemDetails& details = _registeredLine3DVBOs[id]; - - int compactColor1 = ((int(color1.x * 255.0f) & 0xFF)) | - ((int(color1.y * 255.0f) & 0xFF) << 8) | - ((int(color1.z * 255.0f) & 0xFF) << 16) | - ((int(color1.w * 255.0f) & 0xFF) << 24); - - int compactColor2 = ((int(color2.x * 255.0f) & 0xFF)) | - ((int(color2.y * 255.0f) & 0xFF) << 8) | - ((int(color2.z * 255.0f) & 0xFF) << 16) | - ((int(color2.w * 255.0f) & 0xFF) << 24); - - - // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed - if (registered && details.isCreated) { - Vec3Pair& lastKey = _lastRegisteredLine3D[id]; - if (lastKey != key) { - details.clear(); - _lastRegisteredLine3D[id] = key; -#ifdef WANT_DEBUG - qCDebug(renderutils) << "renderLine() 3D ... RELEASING REGISTERED line"; -#endif // def WANT_DEBUG - } -#ifdef WANT_DEBUG - else { - qCDebug(renderutils) << "renderLine() 3D ... REUSING PREVIOUSLY REGISTERED line"; - } -#endif // def WANT_DEBUG - } - - const int FLOATS_PER_VERTEX = 3 + 3; // vertices + normals - const int NUM_POS_COORDS = 3; - const int VERTEX_NORMAL_OFFSET = NUM_POS_COORDS * sizeof(float); - const int vertices = 2; - if (!details.isCreated) { - - details.isCreated = true; - details.vertices = vertices; - details.vertexSize = FLOATS_PER_VERTEX; - - auto verticesBuffer = std::make_shared(); - auto colorBuffer = std::make_shared(); - auto streamFormat = std::make_shared(); - auto stream = std::make_shared(); - - details.verticesBuffer = verticesBuffer; - details.colorBuffer = colorBuffer; - details.streamFormat = streamFormat; - details.stream = stream; - - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - details.streamFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), VERTEX_NORMAL_OFFSET); - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); - - details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - - const glm::vec3 NORMAL(1.0f, 0.0f, 0.0f); - float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { - p1.x, p1.y, p1.z, NORMAL.x, NORMAL.y, NORMAL.z, - p2.x, p2.y, p2.z, NORMAL.x, NORMAL.y, NORMAL.z }; - - const int NUM_COLOR_SCALARS = 2; - int colors[NUM_COLOR_SCALARS] = { compactColor1, compactColor2 }; - - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); - details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); - -#ifdef WANT_DEBUG - if (id == UNKNOWN_ID) { - qCDebug(renderutils) << "new renderLine() 3D VBO made -- _line3DVBOs.size():" << _line3DVBOs.size(); - } else { - qCDebug(renderutils) << "new registered renderLine() 3D VBO made -- _registeredLine3DVBOs.size():" << _registeredLine3DVBOs.size(); - } -#endif - } - - // this is what it takes to render a quad - batch.setInputFormat(details.streamFormat); - batch.setInputStream(0, *details.stream); - batch.draw(gpu::LINES, 2, 0); -} - -void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, - const glm::vec4& color1, const glm::vec4& color2, int id) { - - bool registered = (id != UNKNOWN_ID); - Vec2Pair key(p1, p2); - - BatchItemDetails& details = _registeredLine2DVBOs[id]; - - int compactColor1 = ((int(color1.x * 255.0f) & 0xFF)) | - ((int(color1.y * 255.0f) & 0xFF) << 8) | - ((int(color1.z * 255.0f) & 0xFF) << 16) | - ((int(color1.w * 255.0f) & 0xFF) << 24); - - int compactColor2 = ((int(color2.x * 255.0f) & 0xFF)) | - ((int(color2.y * 255.0f) & 0xFF) << 8) | - ((int(color2.z * 255.0f) & 0xFF) << 16) | - ((int(color2.w * 255.0f) & 0xFF) << 24); - - - // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed - if (registered && details.isCreated) { - Vec2Pair& lastKey = _lastRegisteredLine2D[id]; - if (lastKey != key) { - details.clear(); - _lastRegisteredLine2D[id] = key; -#ifdef WANT_DEBUG - qCDebug(renderutils) << "renderLine() 2D ... RELEASING REGISTERED line"; -#endif // def WANT_DEBUG - } -#ifdef WANT_DEBUG - else { - qCDebug(renderutils) << "renderLine() 2D ... REUSING PREVIOUSLY REGISTERED line"; - } -#endif // def WANT_DEBUG - } - - const int FLOATS_PER_VERTEX = 2; - const int vertices = 2; - if (!details.isCreated) { - - details.isCreated = true; - details.vertices = vertices; - details.vertexSize = FLOATS_PER_VERTEX; - - auto verticesBuffer = std::make_shared(); - auto colorBuffer = std::make_shared(); - auto streamFormat = std::make_shared(); - auto stream = std::make_shared(); - - details.verticesBuffer = verticesBuffer; - details.colorBuffer = colorBuffer; - details.streamFormat = streamFormat; - details.stream = stream; - - details.streamFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - details.streamFormat->setAttribute(gpu::Stream::COLOR, 1, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); - - details.stream->addBuffer(details.verticesBuffer, 0, details.streamFormat->getChannels().at(0)._stride); - details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - - - float vertexBuffer[vertices * FLOATS_PER_VERTEX] = { p1.x, p1.y, p2.x, p2.y }; - - const int NUM_COLOR_SCALARS = 2; - int colors[NUM_COLOR_SCALARS] = { compactColor1, compactColor2 }; - - details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); - details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); - -#ifdef WANT_DEBUG - if (id == UNKNOWN_ID) { - qCDebug(renderutils) << "new renderLine() 2D VBO made -- _line3DVBOs.size():" << _line2DVBOs.size(); - } else { - qCDebug(renderutils) << "new registered renderLine() 2D VBO made -- _registeredLine2DVBOs.size():" << _registeredLine2DVBOs.size(); - } -#endif - } - - // this is what it takes to render a quad - batch.setInputFormat(details.streamFormat); - batch.setInputStream(0, *details.stream); - batch.draw(gpu::LINES, 2, 0); -} - void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { static std::once_flag once; std::call_once(once, [&]() { @@ -2175,24 +1858,24 @@ gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transp std::call_once(once, [&]() { using namespace shader::render_utils::program; - _forwardSimpleShader = gpu::Shader::createProgram(simple_forward); - _forwardTransparentShader = gpu::Shader::createProgram(simple_translucent_forward); - _forwardUnlitShader = gpu::Shader::createProgram(simple_unlit_forward); + _shapeShaders[std::make_tuple(false, false, true, false)] = gpu::Shader::createProgram(simple_forward); + _shapeShaders[std::make_tuple(true, false, true, false)] = gpu::Shader::createProgram(simple_translucent_forward); + _shapeShaders[std::make_tuple(false, true, true, false)] = gpu::Shader::createProgram(simple_unlit_forward); - _simpleShader = gpu::Shader::createProgram(simple); - _transparentShader = gpu::Shader::createProgram(simple_translucent); - _unlitShader = gpu::Shader::createProgram(simple_unlit); + _shapeShaders[std::make_tuple(false, false, false, false)] = gpu::Shader::createProgram(simple); + _shapeShaders[std::make_tuple(true, false, false, false)] = gpu::Shader::createProgram(simple_translucent); + _shapeShaders[std::make_tuple(false, true, false, false)] = gpu::Shader::createProgram(simple_unlit); }); } else { static std::once_flag once; std::call_once(once, [&]() { using namespace shader::render_utils::program; // Fading is currently disabled during forward rendering - _forwardSimpleFadeShader = gpu::Shader::createProgram(simple_forward); - _forwardUnlitFadeShader = gpu::Shader::createProgram(simple_unlit_forward); + _shapeShaders[std::make_tuple(false, false, true, true)] = gpu::Shader::createProgram(simple_forward); + _shapeShaders[std::make_tuple(false, true, true, true)] = gpu::Shader::createProgram(simple_unlit_forward); - _simpleFadeShader = gpu::Shader::createProgram(simple_fade); - _unlitFadeShader = gpu::Shader::createProgram(simple_unlit_fade); + _shapeShaders[std::make_tuple(false, false, false, true)] = gpu::Shader::createProgram(simple_fade); + _shapeShaders[std::make_tuple(false, true, false, true)] = gpu::Shader::createProgram(simple_unlit_fade); }); } @@ -2220,20 +1903,13 @@ gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transp PrepareStencil::testMaskDrawShapeNoAA(*state); } - gpu::ShaderPointer program; - if (config.isForward()) { - program = (config.isUnlit()) ? (config.isFading() ? _forwardUnlitFadeShader : _forwardUnlitShader) : - (config.isFading() ? _forwardSimpleFadeShader : (config.isTransparent() ? _forwardTransparentShader : _forwardSimpleShader)); - } else { - program = (config.isUnlit()) ? (config.isFading() ? _unlitFadeShader : _unlitShader) : - (config.isFading() ? _simpleFadeShader : (config.isTransparent() ? _transparentShader : _simpleShader)); - } + gpu::ShaderPointer program = _shapeShaders[std::make_tuple(config.isTransparent(), config.isUnlit(), config.isForward(), config.isFading())]; gpu::PipelinePointer pipeline = gpu::Pipeline::create(program, state); _simplePrograms.insert(config, pipeline); return pipeline; } -uint32_t toCompactColor(const glm::vec4& color) { +uint32_t GeometryCache::toCompactColor(const glm::vec4& color) { uint32_t compactColor = ((int(color.x * 255.0f) & 0xFF)) | ((int(color.y * 255.0f) & 0xFF) << 8) | ((int(color.z * 255.0f) & 0xFF) << 16) | @@ -2251,7 +1927,7 @@ void renderInstances(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color // Add color to named buffer { gpu::BufferPointer instanceColorBuffer = batch.getNamedBuffer(instanceName, INSTANCE_COLOR_BUFFER); - auto compactColor = toCompactColor(color); + auto compactColor = GeometryCache::toCompactColor(color); instanceColorBuffer->append(compactColor); } @@ -2268,55 +1944,6 @@ void renderInstances(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color }); } -static const size_t INSTANCE_FADE_BUFFER1 = 1; -static const size_t INSTANCE_FADE_BUFFER2 = 2; -static const size_t INSTANCE_FADE_BUFFER3 = 3; - -void renderFadeInstances(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, int fadeCategory, float fadeThreshold, - const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize, bool isWire, - const render::ShapePipelinePointer& pipeline, GeometryCache::Shape shape) { - // Add pipeline to name - std::string instanceName = (isWire ? "wire_shapes_" : "solid_shapes_") + std::to_string(shape) + "_" + std::to_string(std::hash()(pipeline)); - - // Add color to named buffer - { - gpu::BufferPointer instanceColorBuffer = batch.getNamedBuffer(instanceName, INSTANCE_COLOR_BUFFER); - auto compactColor = toCompactColor(color); - instanceColorBuffer->append(compactColor); - } - // Add fade parameters to named buffers - { - gpu::BufferPointer fadeBuffer1 = batch.getNamedBuffer(instanceName, INSTANCE_FADE_BUFFER1); - gpu::BufferPointer fadeBuffer2 = batch.getNamedBuffer(instanceName, INSTANCE_FADE_BUFFER2); - gpu::BufferPointer fadeBuffer3 = batch.getNamedBuffer(instanceName, INSTANCE_FADE_BUFFER3); - // Pack parameters in 3 vec4s - glm::vec4 fadeData1; - glm::vec4 fadeData2; - glm::vec4 fadeData3; - FadeEffect::packToAttributes(fadeCategory, fadeThreshold, fadeNoiseOffset, fadeBaseOffset, fadeBaseInvSize, - fadeData1, fadeData2, fadeData3); - fadeBuffer1->append(fadeData1); - fadeBuffer2->append(fadeData2); - fadeBuffer3->append(fadeData3); - } - - // Add call to named buffer - batch.setupNamedCalls(instanceName, [args, isWire, pipeline, shape](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { - auto& buffers = data.buffers; - batch.setPipeline(pipeline->pipeline); - pipeline->prepare(batch, args); - - if (isWire) { - DependencyManager::get()->renderWireFadeShapeInstances(batch, shape, data.count(), - buffers[INSTANCE_COLOR_BUFFER], buffers[INSTANCE_FADE_BUFFER1], buffers[INSTANCE_FADE_BUFFER2], buffers[INSTANCE_FADE_BUFFER3]); - } - else { - DependencyManager::get()->renderFadeShapeInstances(batch, shape, data.count(), - buffers[INSTANCE_COLOR_BUFFER], buffers[INSTANCE_FADE_BUFFER1], buffers[INSTANCE_FADE_BUFFER2], buffers[INSTANCE_FADE_BUFFER3]); - } - }); -} - void GeometryCache::renderSolidShapeInstance(RenderArgs* args, gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); renderInstances(args, batch, color, false, pipeline, shape); @@ -2327,74 +1954,11 @@ void GeometryCache::renderWireShapeInstance(RenderArgs* args, gpu::Batch& batch, renderInstances(args, batch, color, true, pipeline, shape); } -void GeometryCache::renderSolidFadeShapeInstance(RenderArgs* args, gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, - int fadeCategory, float fadeThreshold, const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize, - const render::ShapePipelinePointer& pipeline) { - assert(pipeline != nullptr); - renderFadeInstances(args, batch, color, fadeCategory, fadeThreshold, fadeNoiseOffset, fadeBaseOffset, fadeBaseInvSize, false, pipeline, shape); -} - -void GeometryCache::renderWireFadeShapeInstance(RenderArgs* args, gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, - int fadeCategory, float fadeThreshold, const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize, - const render::ShapePipelinePointer& pipeline) { - assert(pipeline != nullptr); - renderFadeInstances(args, batch, color, fadeCategory, fadeThreshold, fadeNoiseOffset, fadeBaseOffset, fadeBaseInvSize, true, pipeline, shape); -} - void GeometryCache::renderSolidSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); renderInstances(args, batch, color, false, pipeline, GeometryCache::Sphere); } -void GeometryCache::renderWireSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { - assert(pipeline != nullptr); - renderInstances(args, batch, color, true, pipeline, GeometryCache::Sphere); -} - -// Enable this in a debug build to cause 'box' entities to iterate through all the -// available shape types, both solid and wireframes -//#define DEBUG_SHAPES - - -void GeometryCache::renderSolidCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { - assert(pipeline != nullptr); -#ifdef DEBUG_SHAPES - static auto startTime = usecTimestampNow(); - renderInstances(INSTANCE_NAME, batch, color, pipeline, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { - - auto usecs = usecTimestampNow(); - usecs -= startTime; - auto msecs = usecs / USECS_PER_MSEC; - float seconds = msecs; - seconds /= MSECS_PER_SECOND; - float fractionalSeconds = seconds - floor(seconds); - int shapeIndex = (int)seconds; - - // Every second we flip to the next shape. - static const int SHAPE_COUNT = 5; - GeometryCache::Shape shapes[SHAPE_COUNT] = { - GeometryCache::Cube, - GeometryCache::Tetrahedron, - GeometryCache::Sphere, - GeometryCache::Icosahedron, - GeometryCache::Line, - }; - - shapeIndex %= SHAPE_COUNT; - GeometryCache::Shape shape = shapes[shapeIndex]; - - // For the first half second for a given shape, show the wireframe, for the second half, show the solid. - if (fractionalSeconds > 0.5f) { - renderInstances(INSTANCE_NAME, batch, color, true, pipeline, shape); - } else { - renderInstances(INSTANCE_NAME, batch, color, false, pipeline, shape); - } - }); -#else - renderInstances(args, batch, color, false, pipeline, GeometryCache::Cube); -#endif -} - void GeometryCache::renderWireCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { static const std::string INSTANCE_NAME = __FUNCTION__; assert(pipeline != nullptr); diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 179d49c076..3f06a6b1a3 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -180,31 +180,10 @@ public: void renderShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer); void renderWireShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer); - void renderFadeShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer, - gpu::BufferPointer& fadeBuffer1, gpu::BufferPointer& fadeBuffer2, gpu::BufferPointer& fadeBuffer3); - void renderWireFadeShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer, - gpu::BufferPointer& fadeBuffer1, gpu::BufferPointer& fadeBuffer2, gpu::BufferPointer& fadeBuffer3); - void renderSolidShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec4& color, - const render::ShapePipelinePointer& pipeline); - void renderSolidShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec3& color, - const render::ShapePipelinePointer& pipeline) { - renderSolidShapeInstance(args, batch, shape, glm::vec4(color, 1.0f), pipeline); - } - + const render::ShapePipelinePointer& pipeline); void renderWireShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec4& color, const render::ShapePipelinePointer& pipeline); - void renderWireShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec3& color, - const render::ShapePipelinePointer& pipeline) { - renderWireShapeInstance(args, batch, shape, glm::vec4(color, 1.0f), pipeline); - } - - void renderSolidFadeShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec4& color, int fadeCategory, float fadeThreshold, - const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize, - const render::ShapePipelinePointer& pipeline); - void renderWireFadeShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec4& color, int fadeCategory, float fadeThreshold, - const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize, - const render::ShapePipelinePointer& pipeline); void renderSolidSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline); @@ -213,20 +192,6 @@ public: renderSolidSphereInstance(args, batch, glm::vec4(color, 1.0f), pipeline); } - void renderWireSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, - const render::ShapePipelinePointer& pipeline); - void renderWireSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec3& color, - const render::ShapePipelinePointer& pipeline) { - renderWireSphereInstance(args, batch, glm::vec4(color, 1.0f), pipeline); - } - - void renderSolidCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, - const render::ShapePipelinePointer& pipeline); - void renderSolidCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec3& color, - const render::ShapePipelinePointer& pipeline) { - renderSolidCubeInstance(args, batch, glm::vec4(color, 1.0f), pipeline); - } - void renderWireCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline); void renderWireCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec3& color, @@ -235,24 +200,10 @@ public: } // Dynamic geometry - void renderShape(gpu::Batch& batch, Shape shape); - void renderWireShape(gpu::Batch& batch, Shape shape); - void renderShape(gpu::Batch& batch, Shape shape, const glm::vec4& color); - void renderWireShape(gpu::Batch& batch, Shape shape, const glm::vec4& color); + void renderShape(gpu::Batch& batch, Shape shape, gpu::BufferPointer& colorBuffer); + void renderWireShape(gpu::Batch& batch, Shape shape, gpu::BufferPointer& colorBuffer); size_t getShapeTriangleCount(Shape shape); - void renderCube(gpu::Batch& batch); - void renderWireCube(gpu::Batch& batch); - void renderCube(gpu::Batch& batch, const glm::vec4& color); - void renderWireCube(gpu::Batch& batch, const glm::vec4& color); - size_t getCubeTriangleCount(); - - void renderSphere(gpu::Batch& batch); - void renderWireSphere(gpu::Batch& batch); - void renderSphere(gpu::Batch& batch, const glm::vec4& color); - void renderWireSphere(gpu::Batch& batch, const glm::vec4& color); - size_t getSphereTriangleCount(); - void renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, int majorRows, int majorCols, float majorEdge, int minorRows, int minorCols, float minorEdge, @@ -262,10 +213,6 @@ public: void renderUnitQuad(gpu::Batch& batch, const glm::vec4& color, int id); - void renderUnitQuad(gpu::Batch& batch, int id) { - renderUnitQuad(batch, glm::vec4(1), id); - } - void renderQuad(gpu::Batch& batch, int x, int y, int width, int height, const glm::vec4& color, int id) { renderQuad(batch, glm::vec2(x,y), glm::vec2(x + width, y + height), color, id); } @@ -307,19 +254,6 @@ public: void renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, const float dash_length, const float gap_length, int id); - void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec3& color, int id) - { renderLine(batch, p1, p2, glm::vec4(color, 1.0f), id); } - - void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec4& color, int id) - { renderLine(batch, p1, p2, color, color, id); } - - void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, - const glm::vec3& color1, const glm::vec3& color2, int id) - { renderLine(batch, p1, p2, glm::vec4(color1, 1.0f), glm::vec4(color2, 1.0f), id); } - - void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, - const glm::vec4& color1, const glm::vec4& color2, int id); - void updateVertices(int id, const QVector& points, const glm::vec4& color); void updateVertices(int id, const QVector& points, const QVector& colors); void updateVertices(int id, const QVector& points, const glm::vec4& color); @@ -364,6 +298,8 @@ public: graphics::MeshPointer meshFromShape(Shape geometryShape, glm::vec3 color); + static uint32_t toCompactColor(const glm::vec4& color); + private: GeometryCache(); @@ -397,6 +333,7 @@ private: public: static int population; gpu::BufferPointer verticesBuffer; + gpu::BufferPointer normalBuffer; gpu::BufferPointer colorBuffer; gpu::BufferPointer uniformBuffer; gpu::Stream::FormatPointer streamFormat; @@ -445,18 +382,9 @@ private: QHash _lastRegisteredGridBuffer; QHash _registeredGridBuffers; - // FIXME: clean these up - static gpu::ShaderPointer _simpleShader; - static gpu::ShaderPointer _transparentShader; - static gpu::ShaderPointer _unlitShader; - static gpu::ShaderPointer _simpleFadeShader; - static gpu::ShaderPointer _unlitFadeShader; - static gpu::ShaderPointer _forwardSimpleShader; - static gpu::ShaderPointer _forwardTransparentShader; - static gpu::ShaderPointer _forwardUnlitShader; - static gpu::ShaderPointer _forwardSimpleFadeShader; - static gpu::ShaderPointer _forwardUnlitFadeShader; - + // transparent, unlit, forward, fade + static std::map, gpu::ShaderPointer> _shapeShaders; + // transparent, unlit, forward static std::map, render::ShapePipelinePointer> _shapePipelines; static QHash _simplePrograms; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index f6e4b3b460..ad76b818eb 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -354,12 +354,17 @@ void ModelMeshPartPayload::render(RenderArgs* args) { outColor = procedural->getColor(outColor); procedural->prepare(batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(outColor.a < 1.0f, _shapeKey.isDeformed(), _shapeKey.isDualQuatSkinned())); - batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a); + + auto compactColor = GeometryCache::toCompactColor(glm::vec4(outColor)); + _drawMesh->getColorBuffer()->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); } else { // apply material properties if (RenderPipelines::bindMaterials(_drawMaterials, batch, args->_renderMode, args->_enableTexturing)) { args->_details._materialSwitches++; } + + auto compactColor = 0xFFFFFFFF; + _drawMesh->getColorBuffer()->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); } // Draw! From cd8ad23dd1860e7118a4da42d53381574c72f3a7 Mon Sep 17 00:00:00 2001 From: ksuprynowicz Date: Tue, 5 Mar 2024 01:09:57 +0100 Subject: [PATCH 02/23] Fix invalid reference --- assignment-client/src/avatars/ScriptableAvatar.cpp | 9 +++++---- libraries/ui/src/QmlWindowClass.cpp | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index c919d2a2d7..8a15f05bf1 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -138,7 +138,8 @@ static AnimPose composeAnimPose(const HFMJoint& joint, const glm::quat rotation, void ScriptableAvatar::update(float deltatime) { // Run animation - if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0 && !_bind.isNull() && _bind->isLoaded()) { + auto frames = _animation->getFrames(); + if (_animation && _animation->isLoaded() && frames.size() > 0 && !_bind.isNull() && _bind->isLoaded()) { if (!_animSkeleton) { _animSkeleton = std::make_shared(_bind->getHFMModel()); } @@ -157,9 +158,9 @@ void ScriptableAvatar::update(float deltatime) { _jointData.resize(nJoints); } - const int frameCount = _animation->getFrames().size(); - const HFMAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(currentFrame) % frameCount); - const HFMAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(currentFrame) % frameCount); + const int frameCount = frames.size(); + const HFMAnimationFrame& floorFrame = frames.at((int)glm::floor(currentFrame) % frameCount); + const HFMAnimationFrame& ceilFrame = frames.at((int)glm::ceil(currentFrame) % frameCount); const float frameFraction = glm::fract(currentFrame); std::vector poses = _animSkeleton->getRelativeDefaultPoses(); diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index 474a8f467d..74734fdc43 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -279,6 +279,7 @@ bool QmlWindowClass::isVisible() { return quickItem->isVisible(); } else { qDebug() << "QmlWindowClass::isVisible: asQuickItem() returned NULL"; + return false; } } From 805cd78728b975e01e8d88c7c7fbc03652934563 Mon Sep 17 00:00:00 2001 From: ksuprynowicz Date: Wed, 6 Mar 2024 00:35:19 +0100 Subject: [PATCH 03/23] Switched to animation frames reference --- assignment-client/src/avatars/ScriptableAvatar.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 8a15f05bf1..f321e3c48f 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -138,7 +138,9 @@ static AnimPose composeAnimPose(const HFMJoint& joint, const glm::quat rotation, void ScriptableAvatar::update(float deltatime) { // Run animation - auto frames = _animation->getFrames(); + Q_ASSERT(QThread::currentThread() == thread()); + Q_ASSERT(thread() == _animation->thread()); + auto frames = _animation->getFramesReference(); if (_animation && _animation->isLoaded() && frames.size() > 0 && !_bind.isNull() && _bind->isLoaded()) { if (!_animSkeleton) { _animSkeleton = std::make_shared(_bind->getHFMModel()); From 78978c709722b30021a2bdd5caaff50cafdb568c Mon Sep 17 00:00:00 2001 From: ksuprynowicz Date: Fri, 8 Mar 2024 17:28:07 +0100 Subject: [PATCH 04/23] Initial fix for animations --- assignment-client/src/Agent.cpp | 2 + .../src/avatars/ScriptableAvatar.cpp | 145 +++++++++++------- .../src/avatars/ScriptableAvatar.h | 12 +- 3 files changed, 98 insertions(+), 61 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 246fd22cbb..3ac8eaaff7 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -108,6 +108,7 @@ Agent::Agent(ReceivedMessage& message) : DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); // Needed to ensure the creation of the DebugDraw instance on the main thread DebugDraw::getInstance(); @@ -848,6 +849,7 @@ void Agent::aboutToFinish() { DependencyManager::destroy(); DependencyManager::destroy(); + DependencyManager::destroy(); DependencyManager::destroy(); diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index f321e3c48f..3a3b2cf517 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -32,6 +32,10 @@ ScriptableAvatar::ScriptableAvatar(): _scriptEngine(newScriptEngine()) { _clientTraitsHandler.reset(new ClientTraitsHandler(this)); + static std::once_flag once; + std::call_once(once, [] { + qRegisterMetaType("HFMModel::Pointer"); + }); } QByteArray ScriptableAvatar::toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking) { @@ -52,6 +56,7 @@ void ScriptableAvatar::startAnimation(const QString& url, float fps, float prior _animation = DependencyManager::get()->getAnimation(url); _animationDetails = AnimationDetails("", QUrl(url), fps, 0, loop, hold, false, firstFrame, lastFrame, true, firstFrame, false); _maskedJoints = maskedJoints; + _isAnimationRigValid = false; } void ScriptableAvatar::stopAnimation() { @@ -89,11 +94,12 @@ QStringList ScriptableAvatar::getJointNames() const { } void ScriptableAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { - _bind.reset(); - _animSkeleton.reset(); + _avatarAnimSkeleton.reset(); + _geometryResource.reset(); AvatarData::setSkeletonModelURL(skeletonModelURL); updateJointMappings(); + _isRigValid = false; } int ScriptableAvatar::sendAvatarDataPacket(bool sendAll) { @@ -137,68 +143,90 @@ static AnimPose composeAnimPose(const HFMJoint& joint, const glm::quat rotation, } void ScriptableAvatar::update(float deltatime) { + // TODO: the current decision to use geometry resource results in loading textures, but it works way better + // than previous choice of loading avatar as animation, which was missing data such as joint names hash, + // and also didn't support glTF models. Optimizing this will be left fot future PR. + if (!_geometryResource && !_skeletonModelFilenameURL.isEmpty()) { // AvatarData will parse the .fst, but not get the .fbx skeleton. + _geometryResource = DependencyManager::get()->getGeometryResource(_skeletonModelFilenameURL); + } + // Run animation Q_ASSERT(QThread::currentThread() == thread()); - Q_ASSERT(thread() == _animation->thread()); - auto frames = _animation->getFramesReference(); - if (_animation && _animation->isLoaded() && frames.size() > 0 && !_bind.isNull() && _bind->isLoaded()) { - if (!_animSkeleton) { - _animSkeleton = std::make_shared(_bind->getHFMModel()); - } - float currentFrame = _animationDetails.currentFrame + deltatime * _animationDetails.fps; - if (_animationDetails.loop || currentFrame < _animationDetails.lastFrame) { - while (currentFrame >= _animationDetails.lastFrame) { - currentFrame -= (_animationDetails.lastFrame - _animationDetails.firstFrame); + if (_animation && _animation->isLoaded()) { + Q_ASSERT(thread() == _animation->thread()); + auto frames = _animation->getFramesReference(); + if (frames.size() > 0 && _geometryResource && _geometryResource->isHFMModelLoaded()) { + if (!_isRigValid) { + _rig.reset(_geometryResource->getHFMModel()); + _isRigValid = true; } - _animationDetails.currentFrame = currentFrame; - - const QVector& modelJoints = _bind->getHFMModel().joints; - QStringList animationJointNames = _animation->getJointNames(); - - const int nJoints = modelJoints.size(); - if (_jointData.size() != nJoints) { - _jointData.resize(nJoints); + if (!_isAnimationRigValid) { + _animationRig.reset(_animation->getHFMModel()); + _isAnimationRigValid = true; } - - const int frameCount = frames.size(); - const HFMAnimationFrame& floorFrame = frames.at((int)glm::floor(currentFrame) % frameCount); - const HFMAnimationFrame& ceilFrame = frames.at((int)glm::ceil(currentFrame) % frameCount); - const float frameFraction = glm::fract(currentFrame); - std::vector poses = _animSkeleton->getRelativeDefaultPoses(); - - const float UNIT_SCALE = 0.01f; - - for (int i = 0; i < animationJointNames.size(); i++) { - const QString& name = animationJointNames[i]; - // As long as we need the model preRotations anyway, let's get the jointIndex from the bind skeleton rather than - // trusting the .fst (which is sometimes not updated to match changes to .fbx). - int mapping = _bind->getHFMModel().getJointIndex(name); - if (mapping != -1 && !_maskedJoints.contains(name)) { - - AnimPose floorPose = composeAnimPose(modelJoints[mapping], floorFrame.rotations[i], floorFrame.translations[i] * UNIT_SCALE); - AnimPose ceilPose = composeAnimPose(modelJoints[mapping], ceilFrame.rotations[i], floorFrame.translations[i] * UNIT_SCALE); - blend(1, &floorPose, &ceilPose, frameFraction, &poses[mapping]); - } + if (!_avatarAnimSkeleton) { + _avatarAnimSkeleton = std::make_shared(_geometryResource->getHFMModel()); } - - std::vector absPoses = poses; - _animSkeleton->convertRelativePosesToAbsolute(absPoses); - for (int i = 0; i < nJoints; i++) { - JointData& data = _jointData[i]; - AnimPose& absPose = absPoses[i]; - if (data.rotation != absPose.rot()) { - data.rotation = absPose.rot(); - data.rotationIsDefaultPose = false; + float currentFrame = _animationDetails.currentFrame + deltatime * _animationDetails.fps; + if (_animationDetails.loop || currentFrame < _animationDetails.lastFrame) { + while (currentFrame >= _animationDetails.lastFrame) { + currentFrame -= (_animationDetails.lastFrame - _animationDetails.firstFrame); } - AnimPose& relPose = poses[i]; - if (data.translation != relPose.trans()) { - data.translation = relPose.trans(); - data.translationIsDefaultPose = false; - } - } + _animationDetails.currentFrame = currentFrame; - } else { - _animation.clear(); + const QVector& modelJoints = _geometryResource->getHFMModel().joints; + QStringList animationJointNames = _animation->getJointNames(); + + const int nJoints = modelJoints.size(); + if (_jointData.size() != nJoints) { + _jointData.resize(nJoints); + } + + const int frameCount = frames.size(); + const HFMAnimationFrame& floorFrame = frames.at((int)glm::floor(currentFrame) % frameCount); + const HFMAnimationFrame& ceilFrame = frames.at((int)glm::ceil(currentFrame) % frameCount); + const float frameFraction = glm::fract(currentFrame); + std::vector poses = _avatarAnimSkeleton->getRelativeDefaultPoses(); + + // TODO: this needs more testing, it's possible that we need not only scale but also rotation and translation + // According to tests with unmatching avatar and animation armatures, sometimes bones are not rotated correctly. + // Matching armatures already work very well now. + const float UNIT_SCALE = _animationRig.GetScaleFactorGeometryToUnscaledRig() / _rig.GetScaleFactorGeometryToUnscaledRig(); + + for (int i = 0; i < animationJointNames.size(); i++) { + const QString& name = animationJointNames[i]; + // As long as we need the model preRotations anyway, let's get the jointIndex from the bind skeleton rather than + // trusting the .fst (which is sometimes not updated to match changes to .fbx). + int mapping = _geometryResource->getHFMModel().getJointIndex(name); + if (mapping != -1 && !_maskedJoints.contains(name)) { + AnimPose floorPose = composeAnimPose(modelJoints[mapping], floorFrame.rotations[i], + floorFrame.translations[i] * UNIT_SCALE); + AnimPose ceilPose = composeAnimPose(modelJoints[mapping], ceilFrame.rotations[i], + ceilFrame.translations[i] * UNIT_SCALE); + blend(1, &floorPose, &ceilPose, frameFraction, &poses[mapping]); + } + } + + std::vector absPoses = poses; + Q_ASSERT(_avatarAnimSkeleton != nullptr); + _avatarAnimSkeleton->convertRelativePosesToAbsolute(absPoses); + for (int i = 0; i < nJoints; i++) { + JointData& data = _jointData[i]; + AnimPose& absPose = absPoses[i]; + if (data.rotation != absPose.rot()) { + data.rotation = absPose.rot(); + data.rotationIsDefaultPose = false; + } + AnimPose& relPose = poses[i]; + if (data.translation != relPose.trans()) { + data.translation = relPose.trans(); + data.translationIsDefaultPose = false; + } + } + + } else { + _animation.clear(); + } } } @@ -248,6 +276,7 @@ void ScriptableAvatar::setJointMappingsFromNetworkReply() { networkReply->deleteLater(); return; } + // TODO: this works only with .fst files currently, not directly with FBX and GLB models { QWriteLocker writeLock(&_jointDataLock); QByteArray line; @@ -256,7 +285,7 @@ void ScriptableAvatar::setJointMappingsFromNetworkReply() { if (line.startsWith("filename")) { int filenameIndex = line.indexOf('=') + 1; if (filenameIndex > 0) { - _skeletonFBXURL = _skeletonModelURL.resolved(QString(line.mid(filenameIndex).trimmed())); + _skeletonModelFilenameURL = _skeletonModelURL.resolved(QString(line.mid(filenameIndex).trimmed())); } } if (!line.startsWith("jointIndex")) { diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index 703a0a9f64..192de31a26 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -19,6 +19,8 @@ #include #include #include +#include "model-networking/ModelCache.h" +#include "Rig.h" /*@jsdoc * The Avatar API is used to manipulate scriptable avatars on the domain. This API is a subset of the @@ -217,11 +219,15 @@ private: AnimationPointer _animation; AnimationDetails _animationDetails; QStringList _maskedJoints; - AnimationPointer _bind; // a sleazy way to get the skeleton, given the various library/cmake dependencies - std::shared_ptr _animSkeleton; + GeometryResource::Pointer _geometryResource; + Rig _rig; + bool _isRigValid{false}; + Rig _animationRig; + bool _isAnimationRigValid{false}; + std::shared_ptr _avatarAnimSkeleton; QHash _fstJointIndices; ///< 1-based, since zero is returned for missing keys QStringList _fstJointNames; ///< in order of depth-first traversal - QUrl _skeletonFBXURL; + QUrl _skeletonModelFilenameURL; // This contains URL from filename field in fst file mutable ScriptEnginePointer _scriptEngine; std::map _entities; From dac3006d8b51d4b9cb174ba4f75e0b3b0be945cc Mon Sep 17 00:00:00 2001 From: Edgar Date: Mon, 11 Mar 2024 20:55:56 +0100 Subject: [PATCH 05/23] :wrench: Disable building electron screenshare app on windows --- CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa85f13c24..8437d265bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -252,9 +252,6 @@ else() endif() set(SCREENSHARE 0) -if (WIN32) - set(SCREENSHARE 1) -endif() if (APPLE AND NOT CLIENT_ONLY) # Don't include Screenshare in OSX client-only builds. set(SCREENSHARE 1) From e27696802be556c8c328df9df20e4444f2aebb17 Mon Sep 17 00:00:00 2001 From: Edgar Date: Tue, 12 Mar 2024 22:12:03 +0100 Subject: [PATCH 06/23] :bug: Fixed avatar bounding box being inverted --- libraries/physics/src/CharacterController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 4beeb5b22f..25cb1404f0 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -587,7 +587,7 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const } // it's ok to change offset immediately -- there are no thread safety issues here - _shapeLocalOffset = minCorner + 0.5f * scale; + _shapeLocalOffset = glm::vec3((minCorner + 0.5f * scale).x, (minCorner + 0.5f * scale).y, -(minCorner + 0.5f * scale).z); if (_rigidBody) { // update CCD with new _radius From be95d324ccea6b2be1627fa8cd4cbca8e42d79d1 Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Tue, 12 Mar 2024 21:36:59 -0700 Subject: [PATCH 07/23] fix auto casts, offset --- .../entities-renderer/src/RenderableMaterialEntityItem.cpp | 4 ++-- .../entities-renderer/src/RenderableShapeEntityItem.cpp | 6 +++--- libraries/graphics/src/graphics/Geometry.cpp | 6 +++--- libraries/render-utils/src/GeometryCache.cpp | 2 +- libraries/render-utils/src/MeshPartPayload.cpp | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index fd7f710bb3..49962decc4 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -337,7 +337,7 @@ void MaterialEntityRenderer::doRender(RenderArgs* args) { } // Draw! - auto compactColor = 0xFFFFFFFF; + const uint32_t compactColor = 0xFFFFFFFF; _colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); DependencyManager::get()->renderShape(batch, GeometryCache::Shape::Sphere, _colorBuffer); } else { @@ -347,7 +347,7 @@ void MaterialEntityRenderer::doRender(RenderArgs* args) { proceduralDrawMaterial->prepare(batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(outColor.a < 1.0f)); - auto compactColor = GeometryCache::toCompactColor(glm::vec4(outColor)); + const uint32_t compactColor = GeometryCache::toCompactColor(glm::vec4(outColor)); _colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); if (render::ShapeKey(args->_globalShapeKey).isWireframe() || _primitiveMode == PrimitiveMode::LINES) { DependencyManager::get()->renderWireShape(batch, GeometryCache::Shape::Sphere, _colorBuffer); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index ba141a96f6..02491105f5 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -131,7 +131,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { procedural->prepare(batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(outColor.a < 1.0f)); }); - auto compactColor = GeometryCache::toCompactColor(glm::vec4(outColor)); + const uint32_t compactColor = GeometryCache::toCompactColor(glm::vec4(outColor)); _colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); if (wireframe) { geometryCache->renderWireShape(batch, geometryShape, _colorBuffer); @@ -151,7 +151,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { geometryCache->renderSolidShapeInstance(args, batch, geometryShape, outColor, pipeline); } } else { - auto compactColor = GeometryCache::toCompactColor(glm::vec4(outColor)); + const uint32_t compactColor = GeometryCache::toCompactColor(glm::vec4(outColor)); _colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); if (wireframe) { geometryCache->renderWireShape(batch, geometryShape, _colorBuffer); @@ -164,7 +164,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { args->_details._materialSwitches++; } - auto compactColor = GeometryCache::toCompactColor(glm::vec4(outColor)); + const uint32_t compactColor = GeometryCache::toCompactColor(glm::vec4(outColor)); _colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); geometryCache->renderShape(batch, geometryShape, _colorBuffer); } diff --git a/libraries/graphics/src/graphics/Geometry.cpp b/libraries/graphics/src/graphics/Geometry.cpp index 4b3cbfe9bc..3984863f1c 100644 --- a/libraries/graphics/src/graphics/Geometry.cpp +++ b/libraries/graphics/src/graphics/Geometry.cpp @@ -19,7 +19,7 @@ Mesh::Mesh() : _vertexBuffer(gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)), _indexBuffer(gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::INDEX)), _partBuffer(gpu::Element(gpu::VEC4, gpu::UINT32, gpu::PART)) { - auto compactColor = 0xFFFFFFFF; + const uint32_t compactColor = 0xFFFFFFFF; _colorBuffer->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); } @@ -47,7 +47,7 @@ void Mesh::setVertexFormatAndStream(const gpu::Stream::FormatPointer& vf, const if (!_vertexFormat->hasAttribute(gpu::Stream::COLOR)) { int channelNum = _vertexStream.getNumBuffers(); _vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); - _vertexStream.addBuffer(_colorBuffer, channelNum, _vertexFormat->getChannels().at(channelNum)._stride); + _vertexStream.addBuffer(_colorBuffer, 0, _vertexFormat->getChannels().at(channelNum)._stride); } } @@ -112,7 +112,7 @@ void Mesh::evalVertexStream() { // We require meshes to have a color attribute. If they don't, we default to white. if (!_vertexFormat->hasAttribute(gpu::Stream::COLOR)) { _vertexFormat->setAttribute(gpu::Stream::COLOR, channelNum, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), 0, gpu::Stream::PER_INSTANCE); - _vertexStream.addBuffer(_colorBuffer, channelNum, _vertexFormat->getChannels().at(channelNum)._stride); + _vertexStream.addBuffer(_colorBuffer, 0, _vertexFormat->getChannels().at(channelNum)._stride); } } diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 7af922c303..4d4f9c0680 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1927,7 +1927,7 @@ void renderInstances(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color // Add color to named buffer { gpu::BufferPointer instanceColorBuffer = batch.getNamedBuffer(instanceName, INSTANCE_COLOR_BUFFER); - auto compactColor = GeometryCache::toCompactColor(color); + const uint32_t compactColor = GeometryCache::toCompactColor(color); instanceColorBuffer->append(compactColor); } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index ad76b818eb..e095d9b15d 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -355,7 +355,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) { procedural->prepare(batch, transform.getTranslation(), transform.getScale(), transform.getRotation(), _created, ProceduralProgramKey(outColor.a < 1.0f, _shapeKey.isDeformed(), _shapeKey.isDualQuatSkinned())); - auto compactColor = GeometryCache::toCompactColor(glm::vec4(outColor)); + const uint32_t compactColor = GeometryCache::toCompactColor(glm::vec4(outColor)); _drawMesh->getColorBuffer()->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); } else { // apply material properties @@ -363,7 +363,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) { args->_details._materialSwitches++; } - auto compactColor = 0xFFFFFFFF; + const uint32_t compactColor = 0xFFFFFFFF; _drawMesh->getColorBuffer()->setData(sizeof(compactColor), (const gpu::Byte*) &compactColor); } From 6f8f32d8023423385d560e0a45183f124c5bb473 Mon Sep 17 00:00:00 2001 From: ksuprynowicz Date: Wed, 13 Mar 2024 22:40:17 +0100 Subject: [PATCH 08/23] Fix collisions on glTF avatars. Co-authored-by: Edgar --- .../model-serializers/src/GLTFSerializer.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/libraries/model-serializers/src/GLTFSerializer.cpp b/libraries/model-serializers/src/GLTFSerializer.cpp index 73e2bf965c..1c6076809f 100644 --- a/libraries/model-serializers/src/GLTFSerializer.cpp +++ b/libraries/model-serializers/src/GLTFSerializer.cpp @@ -30,6 +30,8 @@ #include #include +#include + #include #include #include @@ -460,6 +462,7 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& // Increment the triangle indices by the current mesh vertex count so each mesh part can all reference the same buffers within the mesh int prevMeshVerticesCount = mesh.vertices.count(); + // For each vertex (stride is WEIGHTS_PER_VERTEX), it contains index of the cluster that given weight belongs to. QVector clusterJoints; QVector clusterWeights; @@ -940,18 +943,17 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& } else { mesh.clusterWeights[prevMeshClusterWeightCount + j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF); } - for (int clusterIndex = 0; clusterIndex < mesh.clusters.size() - 1; ++clusterIndex) { + for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) { + int clusterIndex = mesh.clusterIndices[prevMeshClusterIndexCount + k]; ShapeVertices& points = hfmModel.shapeVertices.at(clusterIndex); glm::vec3 globalMeshScale = extractScale(globalTransforms[nodeIndex]); const glm::mat4 meshToJoint = glm::scale(glm::mat4(), globalMeshScale) * jointInverseBindTransforms[clusterIndex]; - // TODO: The entire clustering is probably broken and detailed collision shapes fail to generate due to it. const uint16_t EXPANSION_WEIGHT_THRESHOLD = UINT16_MAX/4; // Equivalent of 0.25f? - if (mesh.clusterWeights[j] >= EXPANSION_WEIGHT_THRESHOLD) { - // TODO: fix transformed vertices being pushed back - auto& vertex = mesh.vertices[i]; - const glm::mat4 vertexTransform = meshToJoint * (glm::translate(glm::mat4(), vertex)); - glm::vec3 transformedVertex = hfmModel.joints[clusterIndex].translation * (extractTranslation(vertexTransform)); + if (mesh.clusterWeights[prevMeshClusterWeightCount + k] >= EXPANSION_WEIGHT_THRESHOLD) { + auto& vertex = mesh.vertices[prevMeshVerticesCount + i]; + const glm::mat4 vertexTransform = meshToJoint * glm::translate(vertex); + glm::vec3 transformedVertex = extractTranslation(vertexTransform); points.push_back(transformedVertex); } } From 2e7b19ea403046828fc798c9a6ea3b0379fcc310 Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Thu, 14 Mar 2024 22:35:40 +0100 Subject: [PATCH 09/23] Change the hash salt from Vircadia to Overte --- libraries/networking/src/FingerprintUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/FingerprintUtils.cpp b/libraries/networking/src/FingerprintUtils.cpp index cab8cde832..5bb530d332 100644 --- a/libraries/networking/src/FingerprintUtils.cpp +++ b/libraries/networking/src/FingerprintUtils.cpp @@ -42,7 +42,7 @@ static const int HASH_ITERATIONS = 65535; // Salt string for the hardware ID, makes our IDs unique to our project. // Changing this results in different hardware IDs being computed. -static const QByteArray HASH_SALT{"Vircadia"}; +static const QByteArray HASH_SALT{"Overte"}; static const QString FALLBACK_FINGERPRINT_KEY = "fallbackFingerprint"; From 65f55dd5fb3a17a23b7bb8f64e4db3c79c834f06 Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Thu, 14 Mar 2024 22:36:38 +0100 Subject: [PATCH 10/23] Change Vircadia launcher directory to Overte --- launchers/qt/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launchers/qt/CMakeLists.txt b/launchers/qt/CMakeLists.txt index 12cf7f08d4..0e5ddd6990 100644 --- a/launchers/qt/CMakeLists.txt +++ b/launchers/qt/CMakeLists.txt @@ -281,7 +281,7 @@ if (APPLE) set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME}) - set(DMG_SUBFOLDER_NAME "Vircadia") + set(DMG_SUBFOLDER_NAME "Overte") set(ESCAPED_DMG_SUBFOLDER_NAME "") set(DMG_SUBFOLDER_ICON "${CMAKE_SOURCE_DIR}/cmake/installer/install-folder.rsrc") From 40761fb545bfa31670b5d81ca323770ad9625f03 Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Sat, 16 Mar 2024 00:40:33 +0100 Subject: [PATCH 11/23] Fix warnings as errors for MSVC --- CMakeLists.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8437d265bc..8332db6151 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -184,8 +184,13 @@ if(OVERTE_WARNINGS_WHITELIST) endif() if(OVERTE_WARNINGS_AS_ERRORS) - set(ENV{CXXFLAGS} "$ENV{CXXFLAGS} -Werror") - set(ENV{CFLAGS} "$ENV{CXXFLAGS} -Werror") + if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC" OR (CMAKE_CXX_COMPILER_ID MATCHES "" AND WIN32)) + set(ENV{CXXFLAGS} "$ENV{CXXFLAGS} /WX") + set(ENV{CFLAGS} "$ENV{CXXFLAGS} /WX") + else() + set(ENV{CXXFLAGS} "$ENV{CXXFLAGS} -Werror") + set(ENV{CFLAGS} "$ENV{CXXFLAGS} -Werror") + endif() endif() From f50101db8a717ae97e736d8e0a2df99fd861fdd0 Mon Sep 17 00:00:00 2001 From: Alezia Kurdis <60075796+AleziaKurdis@users.noreply.github.com> Date: Sat, 16 Mar 2024 14:04:24 -0400 Subject: [PATCH 12/23] Add "Import" Tab Add "Import" Tab --- scripts/system/create/qml/EditTabView.qml | 19 +++++++++++++++++ .../system/create/qml/EditToolsTabView.qml | 21 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/scripts/system/create/qml/EditTabView.qml b/scripts/system/create/qml/EditTabView.qml index 96e66c109e..2db23ec659 100644 --- a/scripts/system/create/qml/EditTabView.qml +++ b/scripts/system/create/qml/EditTabView.qml @@ -301,6 +301,22 @@ TabBar { } } + EditTabButton { + title: "IMPORT" + active: true + enabled: true + property string originalUrl: "" + + property Component visualItem: Component { + WebView { + id: advancedImportWebView + url: Qt.resolvedUrl("../importEntities/html/importEntities.html") + enabled: true + blurOnCtrlShift: false + } + } + } + function fromScript(message) { switch (message.method) { case 'selectTab': @@ -333,6 +349,9 @@ TabBar { case 'grid': editTabView.currentIndex = 3; break; + case 'import': + editTabView.currentIndex = 4; + break; default: console.warn('Attempt to switch to invalid tab:', id); } diff --git a/scripts/system/create/qml/EditToolsTabView.qml b/scripts/system/create/qml/EditToolsTabView.qml index 998c3a3aac..1000724458 100644 --- a/scripts/system/create/qml/EditToolsTabView.qml +++ b/scripts/system/create/qml/EditToolsTabView.qml @@ -291,6 +291,22 @@ TabBar { } } + EditTabButton { + title: "IMPORT" + active: true + enabled: true + property string originalUrl: "" + + property Component visualItem: Component { + WebView { + id: advancedImportWebView + url: Qt.resolvedUrl("../importEntities/html/importEntities.html") + enabled: true + blurOnCtrlShift: false + } + } + } + function fromScript(message) { switch (message.method) { case 'selectTab': @@ -304,7 +320,7 @@ TabBar { // Changes the current tab based on tab index or title as input function selectTab(id) { if (typeof id === 'number') { - if (id >= tabIndex.create && id <= tabIndex.grid) { + if (id >= tabIndex.create && id <= tabIndex.import) { editTabView.currentIndex = id; } else { console.warn('Attempt to switch to invalid tab:', id); @@ -320,6 +336,9 @@ TabBar { case 'grid': editTabView.currentIndex = tabIndex.grid; break; + case 'import': + editTabView.currentIndex = tabIndex.import; + break; default: console.warn('Attempt to switch to invalid tab:', id); } From a509f84a42013e0135db3f14452085fea97e98fc Mon Sep 17 00:00:00 2001 From: Alezia Kurdis <60075796+AleziaKurdis@users.noreply.github.com> Date: Sat, 16 Mar 2024 14:05:14 -0400 Subject: [PATCH 13/23] Add "Import" Tab Add "Import" Tab --- scripts/system/create/edit.js | 81 ++++++- .../html/css/importEntities.css | 160 +++++++++++++ .../importEntities/html/importEntities.html | 77 +++++++ .../html/js/importEntitiesUi.js | 217 ++++++++++++++++++ 4 files changed, 533 insertions(+), 2 deletions(-) create mode 100644 scripts/system/create/importEntities/html/css/importEntities.css create mode 100644 scripts/system/create/importEntities/html/importEntities.html create mode 100644 scripts/system/create/importEntities/html/js/importEntitiesUi.js diff --git a/scripts/system/create/edit.js b/scripts/system/create/edit.js index 5d3e924ccf..1d7f4fc05e 100644 --- a/scripts/system/create/edit.js +++ b/scripts/system/create/edit.js @@ -121,6 +121,16 @@ var copiedPosition; var copiedRotation; + var importUiPersistedData = { + "elJsonUrl": "", + "elImportAtAvatar": true, + "elImportAtSpecificPosition": false, + "elPositionX": 0, + "elPositionY": 0, + "elPositionZ": 0, + "elEntityHostTypeDomain": true, + "elEntityHostTypeAvatar": false + }; var cameraManager = new CameraManager(); @@ -2009,7 +2019,8 @@ return position; } - function importSVO(importURL) { + function importSVO(importURL, importEntityHostType) { + importEntityHostType = importEntityHostType || "domain"; if (!Entities.canRez() && !Entities.canRezTmp()) { Window.notifyEditError(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG); return; @@ -2032,7 +2043,7 @@ position = createApp.getPositionToCreateEntity(Clipboard.getClipboardContentsLargestDimension() / 2); } if (position !== null && position !== undefined) { - var pastedEntityIDs = Clipboard.pasteEntities(position); + var pastedEntityIDs = Clipboard.pasteEntities(position, importEntityHostType); if (!isLargeImport) { // The first entity in Clipboard gets the specified position with the rest being relative to it. Therefore, move // entities after they're imported so that they're all the correct distance in front of and with geometric mean @@ -2792,6 +2803,72 @@ type: 'zoneListRequest', zones: getExistingZoneList() }); + } else if (data.type === "importUiBrowse") { + let fileToImport = Window.browse("Select .json to Import", "", "*.json"); + if (fileToImport !== null) { + emitScriptEvent({ + type: 'importUi_SELECTED_FILE', + file: fileToImport + }); + } else { + audioFeedback.rejection(); + } + } else if (data.type === "importUiImport") { + if ((data.entityHostType === "domain" && Entities.canAdjustLocks() && Entities.canRez()) || + (data.entityHostType === "avatar" && Entities.canRezAvatarEntities())) { + if (data.positioningMode === "avatar") { + importSVO(data.jsonURL, data.entityHostType); + } else { + if (Clipboard.importEntities(data.jsonURL)) { + let importedPastedEntities = Clipboard.pasteEntities(data.position, data.entityHostType); + if (importedPastedEntities.length === 0) { + emitScriptEvent({ + type: 'importUi_IMPORT_ERROR', + reason: "No Entity has been imported." + }); + } else { + if (isActive) { + selectionManager.setSelections(importedPastedEntities, this); + } + emitScriptEvent({type: 'importUi_IMPORT_CONFIRMATION'}); + } + } else { + emitScriptEvent({ + type: 'importUi_IMPORT_ERROR', + reason: "Import Entities has failed." + }); + } + } + } else { + emitScriptEvent({ + type: 'importUi_IMPORT_ERROR', + reason: "You don't have permission to create in this domain." + }); + } + } else if (data.type === "importUiGoBack") { + if (location.canGoBack()) { + location.goBack(); + } else { + audioFeedback.rejection(); + } + } else if (data.type === "importUiGoTutorial") { + Window.location = "file:///~/serverless/tutorial.json"; + } else if (data.type === "importUiGetCopiedPosition") { + if (copiedPosition !== undefined) { + emitScriptEvent({ + type: 'importUi_POSITION_TO_PASTE', + position: copiedPosition + }); + } else { + audioFeedback.rejection(); + } + } else if (data.type === "importUiPersistData") { + importUiPersistedData = data.importUiPersistedData; + } else if (data.type === "importUiGetPersistData") { + emitScriptEvent({ + type: 'importUi_LOAD_DATA', + importUiPersistedData: importUiPersistedData + }); } }; diff --git a/scripts/system/create/importEntities/html/css/importEntities.css b/scripts/system/create/importEntities/html/css/importEntities.css new file mode 100644 index 0000000000..61c75dabb3 --- /dev/null +++ b/scripts/system/create/importEntities/html/css/importEntities.css @@ -0,0 +1,160 @@ +/* +// importEntities.css +// +// Created by Alezia Kurdis on March 13th, 2024 +// Copyright 2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +*/ + +@font-face { + font-family: FiraSans-SemiBold; + src: url(../../../../../../resources/fonts/FiraSans-SemiBold.ttf), /* Windows production */ + url(../../../../../../fonts/FiraSans-SemiBold.ttf); /* OSX production */ +} + +@font-face { + font-family: FiraSans-Regular; + src: url(../../../../../../resources/fonts/FiraSans-Regular.ttf), /* Windows production */ + url(../../../../../../fonts/FiraSans-Regular.ttf); /* OSX production */ +} + +@font-face { + font-family: Raleway-Bold; + src: url(../../../../../../resources/fonts/Raleway-Bold.ttf), /* Windows production */ + url(../../../../../../fonts/Raleway-Bold.ttf); /* OSX production */ +} + +html { + width: 100%; + height: 100%; +} +input[type="text"] { + font-family: FiraSans-SemiBold; + color: #BBBBBB; + background-color: #222222; + border: 0; + padding: 4px; + margin: 1px; +} + +input[type="number"] { + font-family: FiraSans-SemiBold; + color: #BBBBBB; + background-color: #222222; + border: 0; + padding: 4px; + margin: 1px; + width: 90px; +} + +h2 { + font-size: 18px; + color: #FFFFFF; +} +body { + background: #404040; + font-family: FiraSans-Regular; + font-size: 14px; + color: #BBBBBB; + text-decoration: none; + font-style: normal; + font-variant: normal; + text-transform: none; +} + +#importAtSpecificPositionContainer { + display: none; + width: 100%; +} + +#jsonUrl { + width:90%; +} +#browseBtn { + font-family: FiraSans-SemiBold; +} +#browseBtn:hover { + +} + +label { + font-family: FiraSans-SemiBold; + color: #DDDDDD; +} +font.red { + font-family: FiraSans-SemiBold; + color: #e83333; +} +font.green { + font-family: FiraSans-SemiBold; + color: #0db518; +} +font.blue { + font-family: FiraSans-SemiBold; + color: #447ef2; +} +#importBtn { + color: #ffffff; + background-color: #1080b8; + background: linear-gradient(#00b4ef 20%, #1080b8 100%); + font-family: Raleway-Bold; + font-size: 13px; + text-transform: uppercase; + vertical-align: top; + height: 28px; + min-width: 70px; + padding: 0 18px; + margin: 3px 3px 12px 3px; + border-radius: 5px; + border: 0; + cursor: pointer; +} +#importBtn:hover { + background: linear-gradient(#00b4ef, #00b4ef); + border: none; +} +input:focus { + outline: none; + color: #FFFFFF; +} +button:focus { + outline: none; +} +div.explicative { + width: 96%; + padding: 7px; + font-family: FiraSans-SemiBold; + font-size: 12px; + text-decoration: none; + color: #BBBBBB; +} +button.black { + font-family: Raleway-Bold; + font-size: 10px; + text-transform: uppercase; + vertical-align: top; + height: 18px; + min-width: 60px; + padding: 0 14px; + margin: 5px; + border-radius: 4px; + border: none; + color: #fff; + background-color: #000; + background: linear-gradient(#343434 20%, #000 100%); + cursor: pointer; +} +button.black:hover { + background: linear-gradient(#000, #000); + border: none; +} +#messageContainer { + font-family: FiraSans-SemiBold; + width: 100%; +} +#testContainer { + border: 1px solid #AAAAAA; + padding: 0px; +} diff --git a/scripts/system/create/importEntities/html/importEntities.html b/scripts/system/create/importEntities/html/importEntities.html new file mode 100644 index 0000000000..a1550a642e --- /dev/null +++ b/scripts/system/create/importEntities/html/importEntities.html @@ -0,0 +1,77 @@ + + + + Import Entities + + + + + + +

Import Entities (.json)

+ * URL/File (.json):
+  
+
+ + + + + +
+ Position:
+    
+    
+
+
+ X     + Y     + Z
+
+
+ Note: If you import a "serverless" json file, such data include positions. + It this case, the "Position" will act as an offset. +
+
+
+
+ + + + + +
+ Entity Host Type:
+    
+    
+
+
+
+
+
+ + + + + +
+
+ For large import, it can be wise to test it in a serverless environment before doing it in your real domain. +
+
+
+ +     + +
+
+
+ + diff --git a/scripts/system/create/importEntities/html/js/importEntitiesUi.js b/scripts/system/create/importEntities/html/js/importEntitiesUi.js new file mode 100644 index 0000000000..6e80c7f173 --- /dev/null +++ b/scripts/system/create/importEntities/html/js/importEntitiesUi.js @@ -0,0 +1,217 @@ +// importEntitiesUi.js +// +// Created by Alezia Kurdis on March 13th, 2024 +// Copyright 2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +let elJsonUrl; +let elBrowseBtn; +let elImportAtAvatar; +let elImportAtSpecificPosition; +let elImportAtSpecificPositionContainer; +let elPositionX; +let elPositionY; +let elPositionZ; +let elEntityHostTypeDomain; +let elEntityHostTypeAvatar; +let elMessageContainer; +let elImportBtn; +let elBackBtn; +let elTpTutorialBtn; +let elPastePositionBtn; + +let lockUntil; + +const LOCK_BTN_DELAY = 2000; //2 sec + +function loaded() { + lockUntil = 0; + + elJsonUrl = document.getElementById("jsonUrl"); + elBrowseBtn = document.getElementById("browseBtn"); + elImportAtAvatar = document.getElementById("importAtAvatar"); + elImportAtSpecificPosition = document.getElementById("importAtSpecificPosition"); + elImportAtSpecificPositionContainer = document.getElementById("importAtSpecificPositionContainer"); + elPositionX = document.getElementById("positionX"); + elPositionY = document.getElementById("positionY"); + elPositionZ = document.getElementById("positionZ"); + elEntityHostTypeDomain = document.getElementById("entityHostTypeDomain"); + elEntityHostTypeAvatar = document.getElementById("entityHostTypeAvatar"); + elMessageContainer = document.getElementById("messageContainer"); + elImportBtn = document.getElementById("importBtn"); + elBackBtn = document.getElementById("backBtn"); + elTpTutorialBtn = document.getElementById("tpTutorialBtn"); + elPastePositionBtn = document.getElementById("pastePositionBtn"); + + elJsonUrl.oninput = function() { + persistData(); + } + + elPositionX.oninput = function() { + persistData(); + } + + elPositionY.oninput = function() { + persistData(); + } + + elPositionZ.oninput = function() { + persistData(); + } + + elEntityHostTypeDomain.onclick = function() { + persistData(); + } + + elEntityHostTypeAvatar.onclick = function() { + persistData(); + } + + elBrowseBtn.onclick = function() { + const d = new Date(); + let time = d.getTime(); + if ((d.getTime() - lockUntil) > LOCK_BTN_DELAY) { + EventBridge.emitWebEvent(JSON.stringify({ "type": "importUiBrowse" })); + lockUntil = d.getTime() + LOCK_BTN_DELAY; + } + }; + + elImportAtAvatar.onclick = function() { + elImportAtSpecificPositionContainer.style.display = "None"; + persistData(); + }; + + elImportAtSpecificPosition.onclick = function() { + elImportAtSpecificPositionContainer.style.display = "Block"; + persistData(); + }; + + elImportBtn.onclick = function() { + const d = new Date(); + let time = d.getTime(); + if ((d.getTime() - lockUntil) > LOCK_BTN_DELAY) { + importJsonToWorld(); + lockUntil = d.getTime() + LOCK_BTN_DELAY; + } + }; + + elBackBtn.onclick = function() { + const d = new Date(); + let time = d.getTime(); + if ((d.getTime() - lockUntil) > LOCK_BTN_DELAY) { + EventBridge.emitWebEvent(JSON.stringify({ "type": "importUiGoBack" })); + lockUntil = d.getTime() + LOCK_BTN_DELAY; + } + }; + + elTpTutorialBtn.onclick = function() { + const d = new Date(); + let time = d.getTime(); + if ((d.getTime() - lockUntil) > LOCK_BTN_DELAY) { + EventBridge.emitWebEvent(JSON.stringify({ "type": "importUiGoTutorial" })); + lockUntil = d.getTime() + LOCK_BTN_DELAY; + } + }; + + elPastePositionBtn.onclick = function() { + const d = new Date(); + let time = d.getTime(); + if ((d.getTime() - lockUntil) > LOCK_BTN_DELAY) { + EventBridge.emitWebEvent(JSON.stringify({ "type": "importUiGetCopiedPosition" })); + lockUntil = d.getTime() + LOCK_BTN_DELAY; + } + }; + + EventBridge.emitWebEvent(JSON.stringify({ "type": "importUiGetPersistData" })); +} + +function persistData() { + let message = { + "type": "importUiPersistData", + "importUiPersistedData": { + "elJsonUrl": elJsonUrl.value, + "elImportAtAvatar": elImportAtAvatar.checked, + "elImportAtSpecificPosition": elImportAtSpecificPosition.checked, + "elPositionX": elPositionX.value, + "elPositionY": elPositionY.value, + "elPositionZ": elPositionZ.value, + "elEntityHostTypeDomain": elEntityHostTypeDomain.checked, + "elEntityHostTypeAvatar": elEntityHostTypeAvatar.checked + } + }; + EventBridge.emitWebEvent(JSON.stringify(message)); +} + +function loadDataInUi(importUiPersistedData) { + elJsonUrl.value = importUiPersistedData.elJsonUrl; + elImportAtAvatar.checked = importUiPersistedData.elImportAtAvatar; + elImportAtSpecificPosition.checked = importUiPersistedData.elImportAtSpecificPosition; + elPositionX.value = importUiPersistedData.elPositionX; + elPositionY.value = importUiPersistedData.elPositionY; + elPositionZ.value = importUiPersistedData.elPositionZ; + elEntityHostTypeDomain.checked = importUiPersistedData.elEntityHostTypeDomain; + elEntityHostTypeAvatar.checked = importUiPersistedData.elEntityHostTypeAvatar; + if (elImportAtSpecificPosition.checked) { + elImportAtSpecificPositionContainer.style.display = "Block"; + } +} + +function importJsonToWorld() { + elMessageContainer.innerHTML = ""; + + if (elJsonUrl.value === "") { + elMessageContainer.innerHTML = "
ERROR: 'URL/File (.json)' is required.
"; + return; + } + + let positioningMode = getRadioValue("importAtPosition"); + let entityHostType = getRadioValue("entityHostType"); + + if (positioningMode === "position" && (elPositionX.value === "" || elPositionY.value === "" || elPositionZ.value === "")) { + elMessageContainer.innerHTML = "
ERROR: 'Position' is required.
"; + return; + } + let position = {"x": parseFloat(elPositionX.value), "y": parseFloat(elPositionY.value), "z": parseFloat(elPositionZ.value)}; + let message = { + "type": "importUiImport", + "jsonURL": elJsonUrl.value, + "positioningMode": positioningMode, + "position": position, + "entityHostType": entityHostType + }; + EventBridge.emitWebEvent(JSON.stringify(message)); +} + +function getRadioValue(objectName) { + let radios = document.getElementsByName(objectName); + let i; + let selectedValue = ""; + for (i = 0; i < radios.length; i++) { + if (radios[i].checked) { + selectedValue = radios[i].value; + break; + } + } + return selectedValue; +} + +EventBridge.scriptEventReceived.connect(function(message){ + let messageObj = JSON.parse(message); + if (messageObj.type === "importUi_IMPORT_CONFIRMATION") { + elMessageContainer.innerHTML = "
IMPORT SUCCESSFUL.
"; + } else if (messageObj.type === "importUi_IMPORT_ERROR") { + elMessageContainer.innerHTML = "
IMPORT ERROR: " + messageObj.reason + "
"; + } else if (messageObj.type === "importUi_SELECTED_FILE") { + elJsonUrl.value = messageObj.file; + persistData(); + } else if (messageObj.type === "importUi_POSITION_TO_PASTE") { + elPositionX.value = messageObj.position.x; + elPositionY.value = messageObj.position.y; + elPositionZ.value = messageObj.position.z; + persistData(); + } else if (messageObj.type === "importUi_LOAD_DATA") { + loadDataInUi(messageObj.importUiPersistedData); + } +}); From ca4dde4bb19954f1c1d1ea34af56cdb8ce227ba8 Mon Sep 17 00:00:00 2001 From: Alezia Kurdis <60075796+AleziaKurdis@users.noreply.github.com> Date: Sun, 17 Mar 2024 23:24:49 -0400 Subject: [PATCH 14/23] Fix css of Tools tab Fix css of Tools tab --- scripts/system/html/edit-style.css | 2191 +++++++++++++++++++++++++ scripts/system/html/gridControls.html | 55 +- 2 files changed, 2220 insertions(+), 26 deletions(-) create mode 100644 scripts/system/html/edit-style.css diff --git a/scripts/system/html/edit-style.css b/scripts/system/html/edit-style.css new file mode 100644 index 0000000000..efa0952428 --- /dev/null +++ b/scripts/system/html/edit-style.css @@ -0,0 +1,2191 @@ +/* +// edit-style.css +// +// Created by Ryan Huffman on November 13th, 2014 +// Copyright 2014 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. +// Copyright 2022-2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +*/ + +@font-face { + font-family: Raleway-Regular; + src: url(../../../../resources/fonts/Raleway-Regular.ttf), /* Windows production */ + url(../../../../fonts/Raleway-Regular.ttf), /* OSX production */ + url(../../../../interface/resources/fonts/Raleway-Regular.ttf), /* Development, running script in /HiFi/examples */ + url(../fonts/Raleway-Regular.ttf); /* Marketplace script */ +} + +@font-face { + font-family: Raleway-Light; + src: url(../../../../resources/fonts/Raleway-Light.ttf), + url(../../../../fonts/Raleway-Light.ttf), + url(../../../../interface/resources/fonts/Raleway-Light.ttf), + url(../fonts/Raleway-Light.ttf); +} + +@font-face { + font-family: Raleway-Bold; + src: url(../../../../resources/fonts/Raleway-Bold.ttf), + url(../../../../fonts/Raleway-Bold.ttf), + url(../../../../interface/resources/fonts/Raleway-Bold.ttf), + url(../fonts/Raleway-Bold.ttf); +} + +@font-face { + font-family: Raleway-SemiBold; + src: url(../../../../resources/fonts/Raleway-SemiBold.ttf), + url(../../../../fonts/Raleway-SemiBold.ttf), + url(../../../../interface/resources/fonts/Raleway-SemiBold.ttf), + url(../fonts/Raleway-SemiBold.ttf); +} + +@font-face { + font-family: FiraSans-SemiBold; + src: url(../../../../resources/fonts/FiraSans-SemiBold.ttf), + url(../../../../fonts/FiraSans-SemiBold.ttf), + url(../../../../interface/resources/fonts/FiraSans-SemiBold.ttf), + url(../fonts/FiraSans-SemiBold.ttf); +} + +@font-face { + font-family: AnonymousPro-Regular; + src: url(../../../../resources/fonts/AnonymousPro-Regular.ttf), + url(../../../../fonts/AnonymousPro-Regular.ttf), + url(../../../../interface/resources/fonts/AnonymousPro-Regular.ttf), + url(../fonts/AnonymousPro-Regular.ttf); +} + +@font-face { + font-family: HiFi-Glyphs; + src: url(../../../../resources/fonts/hifi-glyphs.ttf), + url(../../../../fonts/hifi-glyphs.ttf), + url(../../../../interface/resources/fonts/hifi-glyphs.ttf), + url(../fonts/hifi-glyphs.ttf); +} + +@font-face { + font-family: Vircadia-Glyphs; + src: url(../../../../resources/fonts/vircadia_glyphs.ttf), + url(../../../../fonts/vircadia_glyphs.ttf), + url(../../../../interface/resources/fonts/vircadia_glyphs.ttf), + url(../fonts/vircadia_glyphs.ttf); +} + +* { + margin: 0; + padding: 0; +} + +body { + + color: #afafaf; + background-color: #404040; + font-family: Raleway-Regular; + font-size: 12px; + + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + overflow-x: hidden; + overflow-y: auto; +} + +table { + font-family: FiraSans-SemiBold; + font-size: 15px; + color: #afafaf; + border-collapse: collapse; + width: 100%; + border: 2px solid #575757; + border-radius: 7px; +} + +thead { + font-family: Raleway-Regular; + font-size: 12px; + background-color: #1c1c1c; + padding: 1px 0; + border-bottom: 1px solid #575757; + width: 100%; +} + +tbody { + width: 100%; + display: block; +} + +tfoot { + font-family: Raleway-Light; + font-size: 13px; + background-color: #1c1c1c; + border-top: 1px solid #575757; + width: 100%; +} + +tfoot tr { + background-color: #1c1cff; +} + +thead tr { + height: 26px; /* 28px with thead padding */ +} + +thead th { + height: 26px; + background-color: #1c1c1c; + border-right: 1px solid #575757; +} + +thead th:last-child { + border: none; +} + +tbody td { + height: 26px; +} + +tfoot td { + height: 18px; + width: 100%; + background-color: #1c1c1c; + margin-left: 12px; +} + +tr { + width: 100%; + cursor: pointer; +} + +tr:nth-child(odd) { + background-color: #2e2e2e; +} + +tr:nth-child(even) { + background-color: #1c1c1c; +} + +tr:focus { + outline: none; +} + +tr.selected { + color: #000000; + background-color: #00b4ef; +} + +tr.selected + tr.selected { + border-top: 1px solid #2e2e2e; +} + +tr.last-selected { + color: #000000; + background-color: #0064ef; +} + +tr.last-selected + tr.last-selected { + border-top: 1px solid #2e2e2e; +} + +th { + text-align: center; + word-wrap: nowrap; + white-space: nowrap; + padding-left: 12px; + padding-right: 12px; +} + +td { + overflow: hidden; + text-overflow: clip; + white-space: nowrap; + word-wrap: nowrap; + padding-left: 12px; + padding-right: 12px; +} + +td.hidden { + padding-left: 0; + padding-right: 0; +} + +td.url { + white-space: nowrap; + overflow: hidden; +} + + +input[type="text"], input[type="search"], input[type="number"], textarea { + margin: 0; + padding: 0 12px; + color: #afafaf; + background-color: #252525; + border: none; + font-family: FiraSans-SemiBold; + font-size: 15px; +} + +textarea { + font-family: AnonymousPro-Regular; + font-size: 16px; + padding-top: 5px; + padding-bottom: 5px; + min-height: 64px; + width: 100%; + resize: vertical; +} + +input::-webkit-input-placeholder { + font-style: italic; +} + +input:focus, textarea:focus, button:focus { + color: #fff; + background-color: #000; + outline: 1px solid #00b4ef; + outline-offset: -1px; +} + +input::selection, textarea::selection { + color: #000000; + background-color: #00b4ef; +} + +input.search { + border-radius: 14px; +} + +input.search:focus { + outline: none; + box-sizing: border-box; + height: 26px; + margin-top: 1px; + margin-bottom: 1px; + box-shadow: 0 0 0 1px #00b4ef; +} + +input:disabled, textarea:disabled, .draggable-number.text[disabled="disabled"] { + background-color: #383838; + color: #afafaf; +} + +input[type="text"] { + height: 28px; + width: 100%; +} + +input.multi-diff:not(:focus) + span.multi-diff, +textarea.multi-diff:not(:focus) + span.multi-diff, +.draggable-number.multi-diff>input:not(:focus)+span.multi-diff, +dl>dt.multi-diff:not(:focus) + span.multi-diff { + visibility: visible; + position: absolute; + display: inline-block; + z-index: 2; + top: 7.5px; + left: 20px; + max-width: 50px; + min-width: 10px; + width: 50%; + height: 13px; + background-image: linear-gradient(transparent 0%, transparent 10%, #afafaf 10%, #afafaf 20%, transparent 20%, transparent 45%, #afafaf 45%, #afafaf 55%, transparent 55%, transparent 80%, #afafaf 80%, #afafaf 90%, transparent 90%, transparent 100%); + background-repeat: no-repeat; + pointer-events: none; +} + +input.multi-diff:not(:focus)::-webkit-input-placeholder, input.multi-diff:not(:focus) { + color: transparent; +} + +.draggable-number.multi-diff .text { + color: transparent; +} + +.dropdown > span.multi-diff { + top: 5px; + left: 10px; +} + +.text, .url, .texture, .textarea { + position: relative; +} + +input[type="search"] { + height: 28px; + width: 100%; +} +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; + height: 20px; + width: 20px; + background-image: url('data:image/png;base64,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') +} + +input[type="number"] { + position: relative; + height: 28px; + width: 124px; +} +input[type=number] { + padding-right: 3px; +} +input[type=number]::-webkit-inner-spin-button { + opacity: 1.0; + display: block; + position: relative; + width: 10px; + height: 90%; + overflow: hidden; + font-family: HiFi-Glyphs; + font-size: 32px; + color: #afafaf; + cursor: pointer; + background-color: #000000; +} +input[type=number]::-webkit-inner-spin-button:before, +input[type=number]::-webkit-inner-spin-button:after { + position:absolute; + left: -19px; + line-height: 8px; + text-align: center; +} +input[type=number]::-webkit-inner-spin-button:before { + content: "6"; + top: 4px; +} +input[type=number]::-webkit-inner-spin-button:after { + content: "5"; + bottom: 4px; +} + +input[type=number].hover-up::-webkit-inner-spin-button:before, +input[type=number].hover-down::-webkit-inner-spin-button:after { + color: #ffffff; +} + +input[type=range] { + -webkit-appearance: none; + background: #2e2e2e; + height: 1.8rem; + border-radius: 1rem; +} +input[type=range]::-webkit-slider-thumb { + -webkit-appearance:none; + width: 0.6rem; + height: 1.8rem; + padding:0; + margin: 0; + background-color: #696969; + border-radius: 1rem; +} +input[type=range]::-webkit-slider-thumb:hover { + background-color: white; +} +input[type=range]:focus { + outline: none; +} + +input.no-spin::-webkit-outer-spin-button, +input.no-spin::-webkit-inner-spin-button { + display: none; + -webkit-appearance: none; + margin: 0; /* <-- Apparently some margin are still there even though it's hidden */ + padding-right: 12px; +} + +input[type=button], button.hifi-edit-button { + font-family: Raleway-Bold; + font-size: 13px; + text-transform: uppercase; + vertical-align: top; + height: 28px; + min-width: 70px; + padding: 0 18px; + margin-right: 6px; + border-radius: 5px; + border: none; + color: #fff; + background-color: #000; + background: linear-gradient(#343434 20%, #000 100%); + cursor: pointer; +} + +input[type=button].glyph, button.hifi-edit-button.glyph { + font-family: HiFi-Glyphs; + font-size: 20px; + text-transform: none; + min-width: 32px; + padding: 0; +} + +input[type=button].normal, button.hifi-edit-button.normal { + font-family: FiraSans-SemiBold; + font-size: 15px; + text-transform: none; + padding: 0; +} + +input[type=button].vglyph, button.hifi-edit-button.vglyph { + font-family: Vircadia-Glyphs; + font-size: 20px; + text-transform: none; + min-width: 32px; + padding: 0; +} + +input[type=button].red, button.hifi-edit-button.red { + color: #fff; + background-color: #94132e; + background: linear-gradient(#d42043 20%, #94132e 100%); +} +input[type=button].blue, button.hifi-edit-button.blue { + color: #fff; + background-color: #1080b8; + background: linear-gradient(#00b4ef 20%, #1080b8 100%); +} +input[type=button].orange, button.hifi-edit-button.orange { + color: #fff; + background-color: #8f5100; + background: linear-gradient(#d97b00 20%, #8f5100 100%); +} +input[type=button].green, button.hifi-edit-button.green { + color: #fff; + background-color: #078a00; + background: linear-gradient(#00cc07 20%, #078a00 100%); +} +input[type=button].white, button.hifi-edit-button.white { + color: #121212; + background-color: #afafaf; + background: linear-gradient(#fff 20%, #afafaf 100%); +} +input[type=button].secondary, button.hifi-edit-button.secondary { + font-family: Raleway-Bold; + font-size: 10px; + text-transform: uppercase; + vertical-align: top; + height: 18px; + min-width: 60px; + padding: 0 14px; + margin-right: 6px; + border-radius: 4px; + border: none; + color: #fff; + background-color: #000; + background: linear-gradient(#343434 20%, #000 100%); + cursor: pointer; +} +input[type=button].secondary_red, button.hifi-edit-button.secondary_red { + font-family: Raleway-Bold; + font-size: 10px; + text-transform: uppercase; + vertical-align: top; + height: 18px; + min-width: 60px; + padding: 0 14px; + margin-right: 6px; + border-radius: 4px; + border: none; + color: #fff; + background-color: #94132e; + background: linear-gradient(#d42043 20%, #94132e 100%); + cursor: pointer; +} +input[type=button].secondary_blue, button.hifi-edit-button.secondary_blue { + font-family: Raleway-Bold; + font-size: 10px; + text-transform: uppercase; + vertical-align: top; + height: 18px; + min-width: 60px; + padding: 0 14px; + margin-right: 6px; + border-radius: 4px; + border: none; + color: #fff; + background-color: #1080b8; + background: linear-gradient(#00b4ef 20%, #1080b8 100%); + cursor: pointer; +} +input[type=button]:enabled:hover, button.hifi-edit-button:enabled:hover { + background: linear-gradient(#000, #000); + border: none; +} +input[type=button].red:enabled:hover, button.hifi-edit-button.red:enabled:hover { + background: linear-gradient(#d42043, #d42043); + border: none; +} +input[type=button].blue:enabled:hover, button.hifi-edit-button.blue:enabled:hover { + background: linear-gradient(#00b4ef, #00b4ef); + border: none; +} +input[type=button].orange:enabled:hover, button.hifi-edit-button.orange:enabled:hover { + background: linear-gradient(#d97b00, #d97b00); + border: none; +} +input[type=button].green:enabled:hover, button.hifi-edit-button.green:enabled:hover { + background: linear-gradient(#00cc07, #00cc07); + border: none; +} +input[type=button].white:enabled:hover, button.hifi-edit-button.white:enabled:hover { + background: linear-gradient(#fff, #fff); + border: none; +} + +input[type=button]:active, button.hifi-edit-button:active { + background: linear-gradient(#343434, #343434); +} +input[type=button].red:active, button.hifi-edit-button.red:active { + background: linear-gradient(#94132e, #94132e); +} +input[type=button].blue:active, button.hifi-edit-button.blue:active { + background: linear-gradient(#1080b8, #1080b8); +} +input[type=button].orange:active, button.hifi-edit-button.orange:active { + background: linear-gradient(#8f5100, #8f5100); +} +input[type=button].green:active, button.hifi-edit-button.green:active { + background: linear-gradient(#078a00, #078a00); +} +input[type=button].white:active, button.hifi-edit-button.white:active { + background: linear-gradient(#afafaf, #afafaf); +} + +input[type=button]:disabled, button.hifi-edit-button:disabled { + color: #252525; + background: linear-gradient(#575757 20%, #252525 100%); +} + +input[type=button][pressed=pressed], button.hifi-edit-button[pressed=pressed] { + color: #00b4ef; +} + +input[type=checkbox] { + display: none; +} +input[type=checkbox] + label { + padding-left: 24px; + background-repeat: no-repeat; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACpSURBVDhPY2xoaGD68+dPMSMjY9L////VgTQjAw4AlH8PxLOPHj1azWxjY1MBVNsBFBfBpwkEgNKcQGwtJyfHyATkF0KEiQdAzYlMQEIUyicFyDD9+/ePgRxMvsb///4zkIOZ/v0HmkAGHginYjGNGAzS+BpdkAj8mun/3//92DyPD//993cG88nTJ4+Zm5p/BSZeJYb/DEJADEzNOPF7hn8Mk69cvVIPAHN5pyfo70F5AAAAAElFTkSuQmCC); + cursor: pointer; +} +input[type=checkbox]:enabled + label:hover { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAClSURBVDhPY2hoaGD6//9/6b9//64D8T8gGycASr/7+/dv5/79+1kYgIxKqDjRAKiniRFIv2JgYBAFYlLAE0aQ66AckgDjjx8/yNP44cMH8jS+fPmSPI0PHz4kT+PNmzfJ03jp0iXyNJ46dYo8jYcPHyYnAbxm+vnzZz8wLhlIwd+/f5/BrKSkdExCQuLrnz9/lIBpUAiIQekXF34PTGmTT548WQ8AokXg+rhVtPYAAAAASUVORK5CYII=); +} +input[type=checkbox]:checked + label { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFLSURBVDhPjZK9SgNBFIXvbCaQRDQq7mIhQRPBRisJKcwLWOobaCsExEaxcEEQe0trGysfwg0EwWoDsbFIJUaIBJOwus547saNP3FlPzgzzJxzL5edFbZtG77v7wkhtrXWS9gFRQC/DZ07jnOYKJfL+8ie4n7mvyIGdhpay+VyQuK8y5dPZoHuVtbpZcLi4wjJ1x4t316R9dDgBlsSi8mGu7pJjyJFzVaH+r7iqyHSELSQzVADjS0UgjlDKUUsLzVO98+9kSLGV5qaHXhjU0GWNSxk3hCIwnsfeMNCjTArLmHeUBodoLiE+R+jxuHPUZP4elGE3teonx2S/Q7lJzOUlkYQ+A4/xzyegzNhXmJpwTMXry9IFjcoa84O0r+QXpcK1cugCLREZadyoA19Ergxwf96nKjd1KqlYqmLQ540TUNwItUmRWdu3T36AODjwgpY9xqqAAAAAElFTkSuQmCC); +} +input[type=checkbox]:checked + label:hover { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEySURBVDhPnZLPSsNAEMa/XVPBCE0RhNy0OarP4Av4AD6JB0GwVBA8efBBxHsgh4CQswcRoUIpiIpVAm3zZ5M4szFSbQPBH3xkJvNNZskOer2eLIriKM/ze1JOcS1UHmdZduF5ngEKjr/fN4Z6+oKerwA2gxC4HAFPEWVLsAzgZAvYt3Q6Enw6jg7uBAaTFMNwhpnKdbXCkAJdy8ROu4XrXW2HTJIErHcFDD6nC02Mom8PwymeE2gvS0ZRBBaTlsOXEmdlrfLLOI7Bakrl/zWxCT8T/904f9QW/b06qtrCUdtFCqdjYs2Q2jAPX8c2XQd7Kr/wfV8vwIPs4Ga1ixe5Xrr/YFLTYfKIvWzM6ZtwXZdX7lxXG0L+sxXHcW5t254opRzawQ0S72+dPmjTroIgOP0CQSMt5LDn1T8AAAAASUVORK5CYII=); +} +input.multi-diff[type=checkbox] + label { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFbSURBVDhPY2xoaGD68+dPMSMjY9L////VgTQjAw4AlH8PxLOPHj1azWxjY1MBVNsBFBfBpwkEgNKcQGwtJyfHyALkF4IE34gqM9zU9WT4wicG4mIA1l/fGIyOL2EQeP8EZEAiC5AQBUlcMQ5ieMXIwfDo9SeG73/+gRXDAAsTI4Pd9wdgTVAgw/Tv3z8GEP7Jwctw78M3DE0goPr6BoPludVgdTAM1wgCv//9B9PIQOPNDYaAGxtRNIEw03+gYhDGBtSBNgVc3wiWR8dM//4DTQBidKD++jqD//X1YDlsGMWpMKD26jqD79V1GM5DxihOZQWGntqrawy+V9ZiOA0dw21k/f6JwerzHQbvS2swTMeGGfPz8l8BLRP9KizDwP0WHk+EwGum/3//94M8y/nmEdZAwIb//vs7g/nk6ZPHzE3NvwITrxLDfwYhIAamZpz4PcM/hslXrl6pBwAmfz5iaAlAuAAAAABJRU5ErkJggg==); +} +input.multi-diff[type=checkbox] + label:hover { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFPSURBVDhPjZJBSwJBHMXfrG6rWEkl1MFDGOihDn2JIIrqc3QJunbyFhUkRieD+hYepWteuxctXiJ1Q5xmdmZ3bWZTUHezfvAu/3lv3n+HRblcTrbb7fN+v/8eBMFgFpxz13Gcu3q9bqHb7V4M5/9GhatE3cIsy0o99YBKC3jliCWbBK43gK0MoDI9otfTB/vPBC9Uwu4xMC8IzSOSBsFxIYNqMTGcAIYQAlodD3j5/IqENIc5gqt1P/SNZKhaXR0a5E/5BEcrwH1xEHrGZbiuC604DpZ81AoiPJ/WROM4e4sSt3kaaRopNrg7z1FZdSLmcU2saqrX20lTXC5/RFabFmk2m+GLnBnbWJMOThJv4SV/QRqNBjNNM9UiGeQHdDiejZSSG5TSG71zjnVivyVOKlNLlEqlx+xCds7zvU31G6Z938dvEq4QjLMH27ZPvwHFVYQr3h7uHwAAAABJRU5ErkJggg==); +} + +.rgb.fstuple .color-picker.multi-diff:after { + width: 20px; + height: 20px; + content: ' '; + background: darkgray; + display: flex; + clip-path: polygon(0 0, 0 100%, 100% 100%); +} + +.icon-input input { + position: relative; + padding-left: 36px; +} +.icon-input span { + position: absolute; + left: 6px; + top: -2px; + font-family: HiFi-Glyphs; + font-size: 30px; + color: #afafaf; +} +.icon-input input:focus + span { + color: #ffffff; +} + +.icon { + font-family: HiFi-Glyphs; + color: white; +} + +#property-type-icon { + font-size: 50px; +} + +.selectable { + -webkit-touch-callout: text; + -webkit-user-select: text; + -khtml-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; + cursor: text; +} + +.color-box { + display: inline-block; + width: 15pt; + height: 15pt; + border: 0.75pt solid black; + margin: 1.5pt; + cursor: pointer; +} + +.color-box.highlight { + width: 13.5pt; + height: 13.5pt; + border: 1.5pt solid black; +} + +#properties-list { + display: flex; + flex-direction: column; + + margin-top: 16px; +} + +#properties-list .fieldset { + position: relative; + /* 0.1px on the top is to prevent margin collapsing between this and it's first child */ + margin: 0 -21px 21px -21px; + padding: 0.1px 21px 0 21px; + border: none; + border-top: 1px rgb(90,90,90) solid; + box-shadow: 0 -1px 0 rgb(37,37,37); +} + +#properties-list .fieldset.fstuple, #properties-list .fieldset.fsrow { + margin-top: 21px; + border: none; + box-shadow: none; +} + +#properties-list > .fieldset[data-collapsed="true"] + .fieldset { + margin-top: 0; +} + +#properties-list > .fieldset[data-collapsed="true"] > *:not(div.legend) { + display: none !important; +} + +.section-header { + padding: 0 16px; + border-top: 1px rgb(90,90,90) solid; + box-shadow: 1px -1px 0 rgb(37,37,37); + border-bottom: 1px solid rgb(37, 37, 37); +} + +div.section-header, hr { + display: flex; + flex-flow: row nowrap; + padding: 10px 16px; + font-family: Raleway-Regular; + font-size: 12px; + color: #afafaf; + height: 28px; + text-transform: uppercase; + outline: none; + margin-bottom: 10px; + align-items: center; +} + +div.simpleSeparator { + width: 97%; + height: 2px; + border: 0px; + margin-top: 4px; + margin-right: 8px; + margin-left: 8px; + margin-bottom: 4px; + background-color: #777777; +} + +.section.minor { + margin: 0 21px; + box-shadow: 1px -1px 0 rgb(37,37,37); + border-left: 1px solid #575757; +} + +.container.property { + padding: 0 16px; +} + +.stretch { + width: 100%; +} + +div.section-header .label { + width: 100%; +} + +.section.minor div.section-header { + border-right: 0; +} + +div.section[collapsed="true"] > .container { + display: none; +} + +div.section[collapsed="true"], div.section[collapsed="true"] > .section-header { + margin-bottom: 0; +} + +.section.major { + margin-bottom: 20px; +} + +.section.minor.last { + margin-bottom: 20px; + border-bottom: 1px solid rgb(37,37,37); +} + +.section-header { + background-color: #373737; +} + + +.section-header span { + font-size: 30px; + font-family: HiFi-Glyphs; +} + +.triple-label { + text-transform: uppercase; + text-align: center; + padding: 6px 0; + cursor: default; +} + +.triple-item { + margin-right: 10px; +} + +.triple-item.rgb.fstuple { + display: block !important; +} + +.section-header[collapsed="true"] { + margin-bottom: -21px; +} + +#properties-list .sub-section-header { + border-top: none; + box-shadow: none; + margin-top: 8px; +} + +.sub-section-header + .property { + margin-top: 0; +} + +hr { + border: none; + padding-top: 2px; +} + +.property { + min-height: 28px; +} + +.property.checkbox { + width: auto; +} + +span.indented { + padding-left: 16px; +} + +.property label, .number label { + display: table-cell; + vertical-align: middle; + font-family: Raleway-SemiBold; + font-size: 14px; +} +.property label .unit, .number label .unit { + margin-left: 8px; + font-family: Raleway-Light; + font-size: 13px; +} + +.property div.legend, .number div.legend { + display: table-cell; + vertical-align: middle; + font-family: Raleway-SemiBold; + font-size: 14px; +} +.property div.legend .unit, .number div.legend .unit { + margin-left: 8px; + font-family: Raleway-Light; + font-size: 13px; +} + +.value { + display: block; + min-height: 18px; +} +.value label { + display: inline-block; + vertical-align: top; +} +.value div.legend { + display: inline-block; + vertical-align: top; + width: 48px; +} +.value span { + font-size: 15px; + margin-right: 4px; +} + +#placeholder-property-type { + display: flex; + align-items: center; + width: auto; + margin-right: 20px; +} + +#placeholder-property-locked { + margin-left: 6px; +} + +.checkbox + .checkbox { + margin-top: 0; +} + +.checkbox-sub-props { + margin-top: 18px; +} + +.property .number { + float: left; +} +.property .number + .number { + margin-left: 10px; +} + +.property.range label{ + padding-bottom: 3px; +} +.property.range input[type=number]{ + margin-left: 0.8rem; + width: 5.4rem; + height: 1.8rem; +} + +.dropdown { + position: relative; + margin-bottom: -17px; +} + +.dropdown select { + clear: both; +} + +.dropdown dl { + clear: both; + cursor: pointer; + font-family: FiraSans-SemiBold; + font-size: 15px; + width: 292px; + height: 28px; + padding: 0 28px 0 12px; + color: #afafaf; + background: #575757; + position: relative; + display: flex; + align-items: center; +} + +.dropdown dl[dropped="true"] { + color: #404040; + background: linear-gradient(#afafaf, #afafaf); + z-index: 998; +} + +.dropdown dt { + height: 100%; + box-sizing: border-box; + border-right: 1px solid #121212; + width: 100%; +} +.dropdown dt:hover { + color: #404040; +} +.dropdown dt:focus { + outline: none; +} +.dropdown dt span:first-child { + display: inline-block; + position: relative; + top: 5px; +} +.dropdown dt span:last-child { + font-family: HiFi-Glyphs; + font-size: 42px; + float: right; + margin-right: -48px; + position: relative; + left: -12px; + top: -9px; +} + +.dropdown dd { + position: absolute; + top: 28px; + left: 3px; + display: none; +} +.dropdown dl[dropped="true"] dd { + display: block; +} + +.dropdown li { + list-style-type: none; + padding: 3px 0 1px 12px; + width: 320px; + height: auto; + font-family: FiraSans-SemiBold; + font-size: 15px; + color: #404040; + background-color: #afafaf; + z-index: 999; +} +.dropdown li:hover { + background-color: #00b4ef; +} + +.dropdown dl[disabled="disabled"], .dropdown dl[disabled="disabled"][dropped="true"] { + color: #252525; + background: linear-gradient(#575757 20%, #252525 100%); +} +.dropdown dl[disabled="disabled"] dd { + display: none; +} +.dropdown dl[disabled="disabled"] dt:hover { + color: #252525; +} + +.multiselect-box { + position: absolute; +} +.multiselect-box select { + font-family: FiraSans-SemiBold; + font-size: 15px; + color: #afafaf; + background-color: #252525; + border: none; + text-align-last: center; +} +.over-select { + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; +} +.multiselect-options { + position: absolute; + display: none; + border: none; +} +.multiselect-options span { + font-family: hifi-glyphs; + font-size: 13px; + color: #000000; +} +.multiselect-options label { + z-index: 2; + display: block; + font-family: FiraSans-SemiBold; + font-size: 11px; + color: #000000; + background-color: #afafaf; +} +.multiselect-options label:hover { + background-color: #1e90ff; +} +.multiselect-options input[type=checkbox] + label { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4goSADUOYnF4LQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAIMSURBVFjD7ZmxkqowFIZ/7mwJPen1AezV3t6hFvrQhweAHvrQ8wL2xt4HwD7ppd+tvHOvu0gCYdEZTsmAfpNzzpcTcAB84o3iD94sZuCx4+Pxwvl8dl4JcL1ef84lMQPPwBZDSgkp5XsASylBKUUYhhBCvDbw7XYDpRRKKTRNA8YYOOevC5ymKZRS/13jnHdCTwLMOW8tAc45GGNomuY1gKuq6lxFIQQopdMDXy4X5HmudW8URdMCSynBGNOG3Ww20wHf9dVWl4+wbav7a8CMsW9G+Cm22+1T2F8BzvMc1+u18z5CCJIkseNhKSX2+z2qqjLWl84zhBAURQHXde0A31Oa57nWbqSrLwDwPA9FUcD3fTtb82NKu8QOAHVda+srSRJt2E7gtpQKIXA4HH6csmzpyxj4dDo9TalSCpRS1HX9TV86RujSlxGwlBJpmnY+rJRCGIZ/s2BTX9qnZgBwHAee52mJ/l7nx+PRqr6MVtj3fZRlaVRf/5aGDX0Z17DrusiyrHfqhuqrt9aiKEIcx4OBTfU1aOMIggBlWYIQ0utP+uhr8CyxXC5RFIUxdBAE1srKePgxbcbVamWlnAZNa7rNSAhBlmWv8yLlWTPa0Nco83BbM2ZZZsUIowzwj80YxzEWi8VoB4IPGz9yb0YhBHa73agnGGtHJNd1R4ed9FVV33Awf6ebgd8b+Av9A/rq6s3hjgAAAABJRU5ErkJggg=='); + background-size: 11px 11px; + background-position: top 5px left 14px; +} +.multiselect-options input[type=checkbox]:enabled + label:hover { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4goSADUOYnF4LQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAIMSURBVFjD7ZmxkqowFIZ/7mwJPen1AezV3t6hFvrQhweAHvrQ8wL2xt4HwD7ppd+tvHOvu0gCYdEZTsmAfpNzzpcTcAB84o3iD94sZuCx4+Pxwvl8dl4JcL1ef84lMQPPwBZDSgkp5XsASylBKUUYhhBCvDbw7XYDpRRKKTRNA8YYOOevC5ymKZRS/13jnHdCTwLMOW8tAc45GGNomuY1gKuq6lxFIQQopdMDXy4X5HmudW8URdMCSynBGNOG3Ww20wHf9dVWl4+wbav7a8CMsW9G+Cm22+1T2F8BzvMc1+u18z5CCJIkseNhKSX2+z2qqjLWl84zhBAURQHXde0A31Oa57nWbqSrLwDwPA9FUcD3fTtb82NKu8QOAHVda+srSRJt2E7gtpQKIXA4HH6csmzpyxj4dDo9TalSCpRS1HX9TV86RujSlxGwlBJpmnY+rJRCGIZ/s2BTX9qnZgBwHAee52mJ/l7nx+PRqr6MVtj3fZRlaVRf/5aGDX0Z17DrusiyrHfqhuqrt9aiKEIcx4OBTfU1aOMIggBlWYIQ0utP+uhr8CyxXC5RFIUxdBAE1srKePgxbcbVamWlnAZNa7rNSAhBlmWv8yLlWTPa0Nco83BbM2ZZZsUIowzwj80YxzEWi8VoB4IPGz9yb0YhBHa73agnGGtHJNd1R4ed9FVV33Awf6ebgd8b+Av9A/rq6s3hjgAAAABJRU5ErkJggg=='); +} +.multiselect-options input[type=checkbox]:checked + label { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4goSADMveELP9QAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAIqSURBVFjD7ZmxkqowFIb/7GwJPfT6APZib+9QC33o4QGghz70vIC9sfcBsE966bPNWlxnlQTDRWc4JUT4hpPz5SQSAAofFF/4sJiBx47v+wun04m8E+B6vVbzlJiBZ2CLIYRQQgj1EcBCCEUpRRRF4Jyrtwa+Xq+glEJKia7rkKYpGGPqbYHzPFdSyn+uMcZ6oScBZowpzvmje0jTVHVd9x7ATdMoxtjTMZxzUErV5MDn81mVZak1No7jab+wEEKlaaoNGwQBmQz4pq9H8/IeNo5jMmnRpWmKeyP8FZvN5insfwEuy1JdLpfecb7vI8uy3tb2Szelu91ONU1jtP9jjKmmabRgq6qC4zh2VrpbSsuy1FqNdPUFAK7roqoqeJ6ntXH4Mk1pn9gBoG1bbX1lWaYN2wv8KKWcc+z3+z+7LFv6MgY+Ho9PUyqlBKUUbduqe33pGKFPX0bAQgiV53nvj6WUiKIIt2K0qS/tXTMAEELguq6W6H/nOQ6Hg1V9GX1hz/NIXdckCALtB7Vta1VfxnPYcRwURUEeNSGmYaqvwVqL45gkSfIysKm+Xlo4wjAkdV3D9/1BLxmir5d7ieVySaqqMoYOw3CwEV5ufkyLcbVaIUkSq2d1xt2abjH6vo+iKKwfLA5uL58Vow19jdIPPyrGoiisGGGUBv6+GJMkwWKxGO2M+dvGQ36LEZxztd1uRz0Qt7ZFchwHY8NOelQ1NAjm/+lm4M8G/gH2zx33BSr7jAAAAABJRU5ErkJggg=='); +} +.multiselect-options input[type=checkbox]:checked + label:hover { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4goSADMveELP9QAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAIqSURBVFjD7ZmxkqowFIb/7GwJPfT6APZib+9QC33o4QGghz70vIC9sfcBsE966bPNWlxnlQTDRWc4JUT4hpPz5SQSAAofFF/4sJiBx47v+wun04m8E+B6vVbzlJiBZ2CLIYRQQgj1EcBCCEUpRRRF4Jyrtwa+Xq+glEJKia7rkKYpGGPqbYHzPFdSyn+uMcZ6oScBZowpzvmje0jTVHVd9x7ATdMoxtjTMZxzUErV5MDn81mVZak1No7jab+wEEKlaaoNGwQBmQz4pq9H8/IeNo5jMmnRpWmKeyP8FZvN5insfwEuy1JdLpfecb7vI8uy3tb2Szelu91ONU1jtP9jjKmmabRgq6qC4zh2VrpbSsuy1FqNdPUFAK7roqoqeJ6ntXH4Mk1pn9gBoG1bbX1lWaYN2wv8KKWcc+z3+z+7LFv6MgY+Ho9PUyqlBKUUbduqe33pGKFPX0bAQgiV53nvj6WUiKIIt2K0qS/tXTMAEELguq6W6H/nOQ6Hg1V9GX1hz/NIXdckCALtB7Vta1VfxnPYcRwURUEeNSGmYaqvwVqL45gkSfIysKm+Xlo4wjAkdV3D9/1BLxmir5d7ieVySaqqMoYOw3CwEV5ufkyLcbVaIUkSq2d1xt2abjH6vo+iKKwfLA5uL58Vow19jdIPPyrGoiisGGGUBv6+GJMkwWKxGO2M+dvGQ36LEZxztd1uRz0Qt7ZFchwHY8NOelQ1NAjm/+lm4M8G/gH2zx33BSr7jAAAAABJRU5ErkJggg=='); +} + +.dynamic-multiselect { + position: relative; + top: 6px; + padding-bottom: 6px; +} + +div.refresh { + box-sizing: border-box; + padding-right: 44px; +} +div.refresh input[type="button"] { + float: right; + margin-right: -44px; + position: relative; + left: 10px; +} + +.color-picker { + box-sizing: border-box; + width: 26px; + height: 26px; + border: 3px solid #2B2B2B; + cursor: pointer; +} +.color-picker:focus { + outline: none; +} +.color-picker[active="true"] { + border-color: #000; +} + +.color-picker[disabled="disabled"] { + border-color: #afafaf; +} + +.colpick { + z-index: 3; +} +.colpick[disabled="disabled"] { + display: none !important; +} + +.rgb label { + float: left; + margin-top: 10px; + margin-left: 21px; +} +.rgb label + * { + clear: both; +} + +.rgb div.legend { + float: left; + margin-top: 10px; + margin-left: 21px; +} +.rgb div.legend + * { + clear: both; +} + +.draggable-number-container { + flex: 0 1 124px; +} +.draggable-number { + position: relative; + height: 28px; + flex: 0 1 124px; + display: flex; + align-items: center; +} + +.draggable-number .text { + position: absolute; + display: inline-block; + color: #afafaf; + background-color: #252525; + font-family: FiraSans-SemiBold; + font-size: 15px; + margin: 0; + padding: 0 16px; + height: 28px; + width: 100%; + line-height: 2; + box-sizing: border-box; + z-index: 1; +} +.draggable-number .text:hover { + cursor: ew-resize; +} +.draggable-number .left-arrow, .draggable-number .right-arrow { + position: absolute; + display: inline-block; + font-family: HiFi-Glyphs; + font-size: 20px; + z-index: 2; +} +.draggable-number span:hover { + cursor: default; +} +.draggable-number .left-arrow { + top: 3px; + left: 0; + transform: rotate(180deg); +} +.draggable-number .right-arrow { + top: 3px; + right: 0; +} +.draggable-number input[type=number] { + position: absolute; + right: 0; + width: 100%; +} +.draggable-number input[type=button] { + position: absolute; + top: 0; +} +.draggable-number input::-webkit-inner-spin-button { + -webkit-appearance: none; + visibility: hidden; +} +.draggable-number.fstuple { + height: 28px; + width: 124px; + left: 12px; +} +.draggable-number.fstuple + .draggable-number.fstuple { + margin-left: 28px; +} +.draggable-number.fstuple input { + right: -10px; +} +.draggable-number.fstuple .sublabel { + position: absolute; + top: 6px; + left: -16px; + font-family: FiraSans-SemiBold; + font-size: 15px; +} + +.rect .rect-row { + margin-bottom: 8px; +} + +.row .property { + width: auto; + display: inline-block; + margin-right: 6px; +} +.row .property:last-child { + margin-right: 0; +} +.row .property input { + clear: both; + float: left; +} + +.property.texture { + display: block; +} +.property.texture input { + margin: 0.4rem 0; +} +.texture-image img { + padding: 0; + margin: 0; + width: 100%; + height: 100%; + display: none; +} +.texture-image { + display: block; + position: relative; + background-repeat: no-repeat; + background-position: center; + background-size: 100% 100%; + margin-top: 0.4rem; + height:128px; + width: 128px; + background-image: url('data:image/png;base64,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'); +} +.texture-image.no-texture { + background-image: url('data:image/png;base64,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'); +} +.texture-image.no-preview { + background-image: url('data:image/png;base64,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'); +} + +.two-column { + display: table; + width: 100%; +} +.two-column > div { + display: table-cell; + width: 50%; +} + +#properties-list .fieldset .two-column { + padding-top: 10px; + display: flex; +} + +#properties-list .two-column .fieldset { + width: 50%; + margin: 0; + padding: 0; + border-top: none; + box-shadow: none; +} + +#properties-list .two-column .column { + position: relative; + top: -10px; +} + +#properties-list .two-column .fieldset div.legend { + width: 100%; + margin: 21px -21px 0 -21px; + padding: 16px 0 0 21px; + font-family: Raleway-Regular; + font-size: 12px; + color: #afafaf; + height: 10px; + text-transform: uppercase; + outline: none; +} + +#properties-list .two-column + .property { + margin-top: 6px; +} + +.fieldset .checkbox-sub-props { + margin-top: 0; +} + +.fieldset .checkbox-sub-props .property:first-child { + margin-top: 0px; +} + +.column { + vertical-align: top; +} + +.indent { + margin-left: 24px; +} + +::-webkit-scrollbar { + width: 20px; + height: 10px; +} +::-webkit-scrollbar-track { + background-color: #2e2e2e; +} +#entity-table-scroll::-webkit-scrollbar-track { + border-bottom-right-radius: 7px; +} + +::-webkit-scrollbar-thumb { + background-color: #696969; + border: 2px solid #2e2e2e; + border-radius: 8px; +} + +/* FIXME: Revisit textarea resizer/corner when move to Qt 5.6 or later: see if can get resizer/corner to always be visible and +have correct background color with and without scrollbars. */ +textarea:enabled::-webkit-resizer { + background-size: 10px 10px; + background: #252525 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAXSURBVChTY1RVVWXADZigNA4wMqUZGACS3gCD5UUtKAAAAABJRU5ErkJggg==) no-repeat bottom right; +} +textarea:focus::-webkit-resizer { + background-size: 10px 10px; + background: #000000 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACOSURBVChThdC5DQQhDAXQvyRI5LQxFdABARWQElAPogYkiqEWQhLYGe8xxzJaS5a/8AuQHwDG2n+Lvee0hBDQWlO+hRvy3mNZFjDG5vCDOOeIMaL3/guPKISAWiu9n+AVSSlhraXdF86Qcw6tNdoTvEOlFOScd6iUOv3JGEMopYQx9jNvaawnoHnNr8Z4AuRLPOq2gPgnAAAAAElFTkSuQmCC) no-repeat bottom right; +} +textarea:enabled[scrolling="true"]::-webkit-resizer { + background-size: 10px 10px; + background: #2e2e2e url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACKSURBVChTjdAxDsMgDAXQT4UYuQIzCwsSKxsSJ4YDoByDY7AwUOG2aZMQqX+xhd9gzIwxA3/k8a7LCCFgraX+Fk4UY4RSCoyxNfwgzjlyzhhjXOEvSimhtUbvB3hGUkp472m2wxUKIaD3TnOCd6jWim3bvlBrfdjJOUeolEJoZj/4PMH83bl/BXgCWSs2Z09IjgoAAAAASUVORK5CYII=) no-repeat bottom right; +} + +div#grid-section, body#entity-list-body { + padding-bottom: 0px; + margin: 0px 8px 8px 8px; +} + +#voxels-section { + padding-bottom: 0px; + margin: 8px 8px 8px 8px; +} + +#mode-section { + padding: 3px; + height: 28px; + margin: 8px 8px 8px 8px; + color: #000000; + background-color: #999999; + font-family: Raleway-Bold; + font-size: 16px; + border-radius: 5px; +} +#creationModeLabel { + padding-top: 4px; +} + + +#entity-list-header { + margin-bottom: 6px; +} + +#entity-list-header div { + display: inline-block; + width: 65px; + margin-right: 4px; +} + +#entity-list-header div input:first-child { + margin-right: 0; + float: left; + width: 33px; + border-right: 1px solid #808080; + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +#entity-list-header div input:last-child { + margin-right: 0; + float: right; + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +#delete { + float: right; + margin-right: 0; + background-color: #ff0000; +} + +#entity-list { + position: relative; /* New positioning context. */ +} + +#filter-area { + padding-right: 168px; + padding-bottom: 8px; +} + +#filter-type-multiselect-box select { + border-radius: 14.5px; + width: 107px; + height: 28px; +} +#filter-type-options { + position: absolute; + top: 48px; +} +#filter-type-options div { + position: relative; + height: 22px; +} +#filter-type-options span { + position: relative; + top: 3px; + font-family: HiFi-Glyphs; + font-size: 13px; + color: #000000; + padding-left: 6px; + padding-right: 4px; +} +#filter-type-options label { + position: absolute; + top: -20px; + z-index: 2; + height: 22px; + width: 200px; + padding-top: 1px; +} +#filter-type-options-buttons { + top: -22px; + width: 224px; + z-index: 2; + background-color: #afafaf; + padding-bottom: 6px; +} +#filter-type-options input[type=button] { + position: relative; + left: 16px; + z-index: 3; + height: 23px; + min-width: 60px; + font-size: 10px; + color: #000; + background: linear-gradient(#afafaf 20%, #808080 100%); +} +#filter-type-options input[type=button]:enabled:hover { + background: linear-gradient(#afafaf 20%, #575757 100%); +} +#filter-search-and-icon { + position: relative; + left: 118px; + width: calc(100% - 126px); +} +#filter-in-view { + position: absolute; + top: 0px; + right: 126px; +} +#filter-radius-and-unit { + position: relative; + float: right; + margin-right: -168px; + top: -28px; +} +#entity-list-menubar { + margin: 0px -8px 6px -8px; + padding: 0px 9px 0px 9px; + border: none; + background-color: #000; + background: linear-gradient(#343434 30%, #000 100%); +} +input[type=button].entity-list-menutitle { + font-family: FiraSans-SemiBold; + font-size: 15px; + text-transform: none; + vertical-align: top; + height: 28px; + min-width: 30px; + padding: 0px 8px; + margin-right: 6px; + border-radius: 5px; + border: none; + color: #fff; + background-color: #000; + background: linear-gradient(#343434 30%, #000 100%); + cursor: pointer; +} + +#voxel-edit-mode { + width: 120px; +} + +input[type=button].entity-list-menutitle:enabled:hover { + background: linear-gradient(#000, #000); + border: none; +} +input[type=button].entity-list-menutitle:active { + background: linear-gradient(#343434, #343434); +} +input[type=button].entity-list-menutitle:disabled { + color: #252525; + background: linear-gradient(#575757 30%, #252525 100%); +} +input[type=button].entity-list-menutitle[pressed=pressed] { + color: #00b4ef; +} +input[type=button].entity-list-menutitle:focus { + outline: none; + color: #000000; + background: linear-gradient(#c0c0c0, #c0c0c0); + border-radius: 5px 5px 0px 0px; +} +.icon-input-radius input { + position: relative; + padding-left: 36px; +} +.icon-input-radius span { + position: absolute; + left: 4px; + top: -4px; + font-family: Vircadia-Glyphs; + font-size: 23px; + color: #afafaf; +} +.icon-input-radius input:focus + span + label { + color: #ffffff; +} +.icon-input-radius label { + position: absolute; + margin-left: 3px; + bottom: 6px; + right: 9px; + font-style: italic; +} +#filter-radius:focus { + outline: none; + box-sizing: border-box; + height: 26px; + margin-top: 1px; + margin-bottom: 1px; + box-shadow: 0 0 0 1px #00b4ef; +} +#filter-radius-and-unit input { + width: 120px; + border-radius: 14.5px; + font-style: italic; +} +#filter-radius-and-unit input[type=number]::-webkit-inner-spin-button { + display: none; +} + +#entity-list-footer { + padding-top: 9px; +} + +#footer-text { + float: right; + padding-top: 6px; + padding-right: 22px; +} + +input[type=button]#export { + height: 26px; + width: 180px; +} + +#no-entities { + display: none; + position: absolute; + top: 80px; + padding: 12px; + font-family: FiraSans-SemiBold; + font-size: 15px; + font-style: italic; + color: #afafaf; +} + +#entity-table-columns-multiselect { + position: absolute; + top: 51px; + right: 22px; +} +#entity-table-columns-multiselect-box select { + height: 28px; + width: 20px; + background-color: #1c1c1c; + border-top-right-radius: 7px; +} +#entity-table-columns-options { + position: absolute; + top: 50px; + right: 110px; +} +#entity-table-columns-options div { + position: relative; + height: 22px; +} +#entity-table-columns-options label { + position: absolute; + top: -22px; + height: 22px; + width: 100px; + padding-top: 4px; +} +#entity-table-columns-options input[type=checkbox] + label { + padding-left: 30px; +} + +#entity-table-scroll { + /* Height is set by JavaScript. */ + width: 100%; + overflow-x: hidden; + overflow-y: auto; + box-sizing: border-box; + padding-top: 37px; /* Space for header and footer outside of scroll region. */ + margin-top: 28px; + border-left: 2px solid #575757; + border-right: 2px solid #575757; + border-bottom: 2px solid #575757; + border-bottom-left-radius: 7px; + border-bottom-right-radius: 7px; + background-color: #1c1c1c; +} + +#entity-table-scroll .glyph { + font-family: HiFi-Glyphs; + font-size: 15px; +} + +#entity-table-scroll .vglyph { + font-family: Vircadia-Glyphs; + font-size: 15px; +} + +#entity-table { + margin-top: -20px; + margin-bottom: -18px; + table-layout: fixed; + border: none; + background-color: #1c1c1c; +} + +#entity-table thead tr, #entity-table thead tr th { + background: none; +} + +#entity-table .glyph .vglyph { + margin: 0 -2px 0 -2px; + vertical-align: middle; +} + +#entity-table thead { + box-sizing: border-box; + border: 2px solid #575757; + border-top-left-radius: 7px; + border-top-right-radius: 7px; + border-bottom: 1px solid #575757; + position: absolute; + top: 49px; + left: 0; + width: 100%; + word-wrap: nowrap; + white-space: nowrap; + overflow: hidden; +} + +#entity-table th { + display: inline-block; + box-sizing: border-box; + padding: 5px 0 0 0; + vertical-align: middle; + overflow: hidden; + text-overflow: ellipsis; +} + +#entity-table th:focus { + outline: none; +} + +#entity-table th .glyph .vglyph { + position: relative; + left: 4px; +} +#entity-table th .glyph .vglyph + .sort-order { + position: relative; + left: 4px; +} + +#entity-table thead .sort-order { + display: inline-block; + width: 8px; + margin: -5px 0 -3px 0; + vertical-align: middle; +} + +#entity-table thead .resizer { + position: absolute; + top: 1px; + height: 26px; + width: 10px; + cursor: col-resize; +} + +#entity-table .dragging { + background-color: #b3ecff; +} + +#entity-table td { + box-sizing: border-box; +} +#entity-table td .glyph .vglyph { + text-align: center; + padding: 0px; +} + +#properties-base { + border-top: none !important; + box-shadow: none !important; + margin-bottom: 5px !important; +} + +#properties-base #property-type-icon { + font-family: HiFi-Glyphs; + font-size: 31px; + color: #00b4ef; + margin: -4px 12px -4px -2px; + width: auto; + display: none; +} + +#properties-base #property-type { + padding: 5px 24px 5px 0; + border-right: 1px solid #808080; + width: auto; + display: inline-block; +} + +#properties-base .checkbox label span { + font-family: HiFi-Glyphs; + font-size: 20px; + padding-right: 6px; + vertical-align: top; + position: relative; + top: -4px; +} + +#properties-base input[type=checkbox]:checked + label span { + color: #ffffff; +} + +#id label { + width: 24px; +} +#property-id { + display: inline-block; +} +#property-id::selection { + color: #000000; + background-color: #00b4ef; +} + +input#property-scale-button-rescale { + min-width: 50px; + left: 152px; +} +input#property-scale-button-reset { + margin-right: 0; + left: 250px; +} + +#property-userData-static, +#property-materialData-static { + display: none; + z-index: 99; + position: absolute; + width: 96%; + padding-left: 1%; + margin-top: 5px; + margin-bottom: 10px; + background-color: #2e2e2e; +} + +#property-userData-saved, +#property-materialData-saved { + margin-top: 5px; + font-size: 16px; + display: none; +} + + +#div-property-collisionSoundURL[style*="display: none"] + .property { + margin-top: 0; +} + +.context-menu { + display: none; + position: fixed; + color: #000000; + background-color: #afafaf; + padding: 5px 0 5px 0; + cursor: default; +} +.context-menu li { + list-style-type: none; + padding: 4px 18px 4px 18px; + margin: 0; + white-space: nowrap; +} +.context-menu li:hover { + background-color: #e3e3e3; +} +.context-menu li.separator { + border-top: 1px solid #333333; + margin: 5px 5px; + padding: 0 0; +} +.context-menu li.disabled { + color: #333333; +} +.context-menu li.separator:hover, .context-menu li.disabled:hover { + background-color: #afafaf; +} + +input.rename-entity { + height: 100%; + width: 100%; + border: none; + font-family: FiraSans-SemiBold; + font-size: 15px; + /* need this to show the text cursor when the input field is empty */ + padding-left: 2px; +} + +.create-app-tooltip { + z-index: 100; + position: absolute; + background: #6a6a6a; + border: 1px solid black; + width: 258px; + min-height: 20px; + padding: 5px; + z-index: 100; +} + +.create-app-tooltip .create-app-tooltip-description { + font-size: 12px; + font-style: italic; + color: #ffffff; +} + +.create-app-tooltip .create-app-tooltip-js-attribute { + font-family: Raleway-SemiBold; + font-size: 11px; + color: #000000; + bottom: 0; + margin-top: 5px; +} + +#toggle-space-mode::before { + font-family: HiFi-Glyphs; + font-size: 20px; + text-transform: none; + min-width: 32px; + padding-right: 4px; + vertical-align: middle; +} + +#toggle-space-mode.space-mode-local::before { + content: "m"; +} + +#toggle-space-mode.space-mode-world::before { + content: "\e02c"; +} + +.container { + display: flex; + flex-flow: row nowrap; + margin-bottom: 8px; + min-height: 28px; +} + +.container > label { + margin-top: 6px; + width: 160px; + min-width: 160px; + max-width: 160px; +} + +.container > div.checkbox { + padding-top: 6px; +} + +.container > .value { + width: 100%; +} + +.container .row { + display: flex; + flex-flow: row nowrap; +} + +.container.shrink { + width: min-content; +} + +.fstuple { + display: flex; + flex-flow: row; +} +.fstuple input { + margin-left: 4px; + margin-right: 10px; +} +.fstuple label.red, .fstuple label.r, .fstuple label.x, .fstuple label.w { + color: #C62147; +} +.fstuple label.green, .fstuple label.g, .fstuple label.y, .fstuple label.h { + color: #359D85; +} +.fstuple label.blue, .fstuple label.b, .fstuple label.z { + color: #0093C5; +} + +.xyz.fstuple, .pyr.fstuple, .vec3rgb.fstuple { + position: relative; + left: -12px; + min-width: 50px; + width: 100px; +} + +.rgb.fstuple .tuple { + display: none; +} + +input.number-slider { + background: #575757; + border-radius: 4px; + color: white; +} + +.fstuple > div { + display: flex; + align-items: center; + justify-content: left; +} + +.flex-row { + display: flex; + flex-flow: row; +} + +.flex-column { + display: flex; + flex-flow: column; +} + +.flex-center { + align-items: center; +} + +.flex-evenly-spaced { + flex: 1; +} + +#property-serverScripts-status { + font-family: Raleway-Light; + font-size: 14px; + margin: 6px 0; + cursor: default; +} + +#property-name, #property-id { + display: flex; + width: 100%; +} + +.spacemode-hidden { + display: none; +} + +#placeholder-property-type { + min-width: 0; +} + +.collapse-icon { + cursor: pointer; +} + +#property-userData-editor.error { + border: 2px solid red; +} + +#property-userData-editorStatus { + color: white; + background-color: red; + padding: 5px; + display: none; + cursor: pointer; +} + +#property-materialData-editor.error { + border: 2px solid red; +} + +#property-materialData-editorStatus { + color: white; + background-color: red; + padding: 5px; + display: none; + cursor: pointer; +} + +input[type=number].hide-spinner::-webkit-inner-spin-button { + -webkit-appearance: none; + visibility: hidden; +} + +div.jsoneditor-menu a.jsoneditor-poweredBy { + display: none; +} +td.zoneItem { + width: 100%; +} + +select.zoneSelect { + clear: both; + cursor: pointer; + font-family: FiraSans-SemiBold; + font-size: 15px; + width: 90%; + height: 28px; + padding: 0 28px 0 12px; + color: #afafaf; + background: #575757; + position: relative; + display: inline; + border: 0px; + align-items: center; + outline: none; +} + +div.multiZoneSelToolbar { + padding: 0px; +} + +div.zoneSelectorListPanel { + position: absolute; + display: none; + width: 100%; + height: 100%; + top: 0px; + left: 0px; + right: 0px; + bottom: 0px; + border-width: 0px; + background-color: #666666; + color: #dddddd; + padding: 0% 2% 0% 2%; + z-index: 2; + cursor: pointer; +} + +div.zoneSelectListHeader { + position: static; + width: 96%; + height: 6%; + top: 0px; + left: 0px; + right: 0px; + bottom: 0px; + padding-top: 4px; + cursor: pointer; + border-width: 0px; +} + +div.zoneSelectList { + position: static; + width: 96%; + height: 86%; + top: 0px; + left: 0px; + right: 0px; + bottom: 0px; + border-width: 0px; + background-color: #c0c0c0; + overflow-y: auto; + padding: 0px; + cursor: pointer; +} + +div.zoneSelectListFooter { + position: static; + width: 96%; + height: auto; + top: 0px; + left: 0px; + right: 0px; + bottom: 0px; + padding: 2px; + cursor: pointer; + border-width: 0px; + text-align: right; +} + +.forceAlignRight { + float: right; +} + +#menuBackgroundOverlay{ + background-color:transparent; + position:fixed; + width: 100%; + height: 100%; + top:0; + left:0; + right:0; + bottom:0; + display:none; +} + +div.entity-list-menu { + position: fixed; + display: none; + width: 370px; + height: 30px; + top: 27px; + left: 8px; + right: 0; + bottom: 0; + border-style: solid; + border-color: #505050; + border-width: 1px; + background-color: #c0c0c0; + z-index: 2; + cursor: pointer; +} + +div.tools-select-menu { + position: relative; + display: none; + width: 200px; + height: 0px; + top: -16px; + left: 124px; + right: 0; + bottom: 0; + border-style: solid; + border-color: #505050; + border-width: 1px; + background-color: #c0c0c0; + z-index: 2; + cursor: pointer; +} + +div.tools-help-popup { + font-family: Raleway-SemiBold; + font-size: 14px; + position: relative; + display: none; + width: 96%; + padding: 5px; + height: auto; + top: -16px; + left: 8px; + right: 0; + bottom: 0; + border: 2px solid #c0c0c0; + background-color: #333333; + z-index: 2; + cursor: pointer; +} + +div.menu-separator{ + width: 100%; + height: 2px; + background-color: #505050; + } + +button.menu-button { + font-family: FiraSans-SemiBold; + font-size: 15px; + width: 100%; + height: auto; + border-radius: 0; + padding: 6px; + text-align: left; + background-color: #c0c0c0; + border: none; +} + +button.menu-button:hover { + background-color: #919191; + border: none; +} + +button.menu-button:active { + background-color: #919191; + border: none; +} + +div.menu-item { + width: 100%; +} + +div.menu-item-caption { + float: left; +} + +div.menu-item-shortcut { + float: right; +} diff --git a/scripts/system/html/gridControls.html b/scripts/system/html/gridControls.html index f752ec4f2a..ca3c711d72 100644 --- a/scripts/system/html/gridControls.html +++ b/scripts/system/html/gridControls.html @@ -1,9 +1,9 @@