From dda7c6699e02d2d5c7eecb1b2938a755a043c8c9 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Tue, 7 Oct 2014 23:52:00 -0700 Subject: [PATCH 01/33] backup current work on TextRenderer --- interface/src/Util.cpp | 5 + interface/src/Util.h | 2 + interface/src/ui/TextRenderer.cpp | 260 +++++++++++++++++++++++++++++- interface/src/ui/TextRenderer.h | 140 ++++++++++++++++ 4 files changed, 404 insertions(+), 3 deletions(-) diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 9d7f5518d0..5399af8e75 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -197,6 +197,11 @@ static TextRenderer* textRenderer(int mono) { } } +void renderTextRenderer(int mono) { + textRenderer(mono)->executeDrawBatch(); + textRenderer(mono)->clearDrawBatch(); +} + int widthText(float scale, int mono, char const* string) { return textRenderer(mono)->computeWidth(string) * (scale / 0.10); } diff --git a/interface/src/Util.h b/interface/src/Util.h index 02cfd99f9a..acdbdb6f75 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -31,6 +31,8 @@ float widthChar(float scale, int mono, char ch); void drawText(int x, int y, float scale, float radians, int mono, char const* string, const float* color); +void renderTextRenderer(int mono); + void drawvec3(int x, int y, float scale, float radians, float thick, int mono, glm::vec3 vec, float r=1.0, float g=1.0, float b=1.0); diff --git a/interface/src/ui/TextRenderer.cpp b/interface/src/ui/TextRenderer.cpp index c4f6b328ef..5f00d84b90 100644 --- a/interface/src/ui/TextRenderer.cpp +++ b/interface/src/ui/TextRenderer.cpp @@ -100,12 +100,25 @@ int TextRenderer::draw(int x, int y, const char* str) { glTexCoord2f(ls, tt); glVertex2f(left, top); glEnd(); - +/* + const int NUM_COORDS_PER_GLYPH = 16; + float vertexBuffer[NUM_COORDS_PER_GLYPH] = { ls, bt, left, bottom, rs, bt, right, bottom, rs, tt, right, top, ls, tt, left, top }; + gpu::Buffer::Size offset = sizeof(vertexBuffer)*_numGlyphsBatched; + if ((offset + sizeof(vertexBuffer)) > _glyphsBuffer.getSize()) { + _glyphsBuffer.append(sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); + } else { + _glyphsBuffer.setSubData(offset, sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); + } + _numGlyphsBatched++; +*/ x += glyph.width(); } glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); + // executeDrawBatch(); + // clearDrawBatch(); + return maxHeight; } @@ -131,8 +144,10 @@ TextRenderer::TextRenderer(const Properties& properties) : _x(IMAGE_SIZE), _y(IMAGE_SIZE), _rowHeight(0), - _color(properties.color) { - + _color(properties.color), + _glyphsBuffer(), + _numGlyphsBatched(0) +{ _font.setKerning(false); } @@ -228,9 +243,248 @@ const Glyph& TextRenderer::getGlyph(char c) { return glyph; } +void TextRenderer::executeDrawBatch() { + if (_numGlyphsBatched<=0) { + return; + } + + glEnable(GL_TEXTURE_2D); + + GLuint textureID = 0; + glBindTexture(GL_TEXTURE_2D, textureID); + + gpu::backend::syncGPUObject(_glyphsBuffer); + GLuint vbo = _glyphsBuffer.getGLBufferObject(); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + const int NUM_POS_COORDS = 2; + const int NUM_TEX_COORDS = 2; + const int VERTEX_STRIDE = (NUM_POS_COORDS + NUM_TEX_COORDS) * sizeof(float); + const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); + glVertexPointer(2, GL_FLOAT, VERTEX_STRIDE, 0); + glTexCoordPointer(2, GL_FLOAT, VERTEX_STRIDE, (GLvoid*) VERTEX_TEXCOORD_OFFSET ); + + glDrawArrays(GL_QUADS, 0, _numGlyphsBatched * 4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + +} + +void TextRenderer::clearDrawBatch() { + _numGlyphsBatched = 0; +} + QHash TextRenderer::_instances; Glyph::Glyph(int textureID, const QPoint& location, const QRect& bounds, int width) : _textureID(textureID), _location(location), _bounds(bounds), _width(width) { } +using namespace gpu; + +Buffer::Size Buffer::Sysmem::allocateMemory(Byte** dataAllocated, Size size) { + if ( !dataAllocated ) { + qWarning() << "Buffer::Sysmem::allocateMemory() : Must have a valid dataAllocated pointer."; + return NOT_ALLOCATED; + } + + // Try to allocate if needed + Size newSize = 0; + if (size > 0) { + // Try allocating as much as the required size + one block of memory + newSize = size; + (*dataAllocated) = new Byte[newSize]; + // Failed? + if (!(*dataAllocated)) { + qWarning() << "Buffer::Sysmem::allocate() : Can't allocate a system memory buffer of " << newSize << "bytes. Fails to create the buffer Sysmem."; + return NOT_ALLOCATED; + } + } + + // Return what's actually allocated + return newSize; +} + +void Buffer::Sysmem::deallocateMemory(Byte* dataAllocated, Size size) { + if (dataAllocated) { + delete[] dataAllocated; + } +} + +Buffer::Sysmem::Sysmem() : + _data(NULL), + _size(0), + _stamp(0) +{ +} + +Buffer::Sysmem::Sysmem(Size size, const Byte* bytes) : + _data(NULL), + _size(0), + _stamp(0) +{ + if (size > 0) { + _size = allocateMemory(&_data, size); + if (_size >= size) { + if (bytes) { + memcpy(_data, bytes, size); + } + } + } +} + +Buffer::Sysmem::~Sysmem() { + deallocateMemory( _data, _size ); + _data = NULL; + _size = 0; +} + +Buffer::Size Buffer::Sysmem::allocate(Size size) { + if (size != _size) { + Byte* newData = 0; + Size newSize = 0; + if (size > 0) { + Size allocated = allocateMemory(&newData, size); + if (allocated == NOT_ALLOCATED) { + // early exit because allocation failed + return 0; + } + newSize = allocated; + } + // Allocation was successful, can delete previous data + deallocateMemory(_data, _size); + _data = newData; + _size = newSize; + _stamp++; + } + return _size; +} + +Buffer::Size Buffer::Sysmem::resize(Size size) { + if (size != _size) { + Byte* newData = 0; + Size newSize = 0; + if (size > 0) { + Size allocated = allocateMemory(&newData, size); + if (allocated == NOT_ALLOCATED) { + // early exit because allocation failed + return _size; + } + newSize = allocated; + // Restore back data from old buffer in the new one + if (_data) { + Size copySize = ((newSize < _size)? newSize: _size); + memcpy( newData, _data, copySize); + } + } + // Reallocation was successful, can delete previous data + deallocateMemory(_data, _size); + _data = newData; + _size = newSize; + _stamp++; + } + return _size; +} + +Buffer::Size Buffer::Sysmem::setData( Size size, const Byte* bytes ) { + if (allocate(size) == size) { + if (bytes) { + memcpy( _data, bytes, _size ); + _stamp++; + } + } + return _size; +} + +Buffer::Size Buffer::Sysmem::setSubData( Size offset, Size size, const Byte* bytes) { + if (((offset + size) <= getSize()) && bytes) { + memcpy( _data + offset, bytes, size ); + _stamp++; + return size; + } + return 0; +} + +Buffer::Size Buffer::Sysmem::append(Size size, const Byte* bytes) { + if (size > 0) { + Size oldSize = getSize(); + Size totalSize = oldSize + size; + if (resize(totalSize) == totalSize) { + return setSubData(oldSize, size, bytes); + } + } + return 0; +} + +Buffer::Buffer() : + _sysmem(NULL), + _gpuObject(NULL) { + _sysmem = new Sysmem(); +} + +Buffer::~Buffer() { + if (_sysmem) { + delete _sysmem; + _sysmem = 0; + } + if (_gpuObject) { + delete _gpuObject; + _gpuObject = 0; + } +} + +Buffer::Size Buffer::resize(Size size) { + return editSysmem().resize(size); +} + +Buffer::Size Buffer::setData(Size size, const Byte* data) { + return editSysmem().setData(size, data); +} + +Buffer::Size Buffer::setSubData(Size offset, Size size, const Byte* data) { + return editSysmem().setSubData( offset, size, data); +} + +Buffer::Size Buffer::append(Size size, const Byte* data) { + return editSysmem().append( size, data); +} + +namespace gpu { +namespace backend { + +void syncGPUObject(const Buffer& buffer) { + BufferObject* object = buffer.getGPUObject(); + + if (object && (object->_stamp == buffer.getSysmem().getStamp())) { + return; + } + + // need to have a gpu object? + if (!object) { + object = new BufferObject(); + glGenBuffers(1, &object->_buffer); + buffer.setGPUObject(object); + } + + // Now let's update the content of the bo with the sysmem version + //if (object->_size < buffer.getSize()) { + glBindBuffer(GL_COPY_WRITE_BUFFER, object->_buffer); + glBufferData(GL_COPY_WRITE_BUFFER, buffer.getSysmem().getSize(), buffer.getSysmem().read(), GL_STATIC_DRAW); + glBindBuffer(GL_COPY_WRITE_BUFFER, 0); + object->_stamp = buffer.getSysmem().getStamp(); + object->_size = buffer.getSysmem().getSize(); + //} +} + +}; +}; \ No newline at end of file diff --git a/interface/src/ui/TextRenderer.h b/interface/src/ui/TextRenderer.h index d3340462d5..ec1c9a088e 100644 --- a/interface/src/ui/TextRenderer.h +++ b/interface/src/ui/TextRenderer.h @@ -19,6 +19,8 @@ #include #include +#include + #include "InterfaceConfig.h" // a special "character" that renders as a solid block @@ -35,6 +37,138 @@ const char SOLID_BLOCK_CHAR = 127; class Glyph; +namespace gpu { +class Buffer; +typedef int Stamp; + +namespace backend { + + class BufferObject { + public: + Stamp _stamp; + GLuint _buffer; + GLuint _size; + + BufferObject() : + _stamp(0), + _buffer(0), + _size(0) + {} + }; + void syncGPUObject(const Buffer& buffer); +}; + +class Buffer { +public: + + typedef unsigned char Byte; + typedef unsigned int Size; + + static const Size MIN_ALLOCATION_BLOCK_SIZE = 256; + static const Size NOT_ALLOCATED = -1; + + Buffer(); + Buffer(const Buffer& buf ); + ~Buffer(); + + // The size in bytes of data stored in the buffer + inline Size getSize() const { return getSysmem().getSize(); } + inline const Byte* getData() const { return getSysmem().read(); } + + // Resize the buffer + // Keep previous data [0 to min(pSize, mSize)] + Size resize(Size pSize); + + // Assign data bytes and size (allocate for size, then copy bytes if exists) + Size setData(Size size, const Byte* data); + + // Assign data bytes and size (allocate for size, then copy bytes if exists) + Size setSubData(Size offset, Size size, const Byte* data); + + // Append new data at the end of the current buffer + // do a resize( size + getSIze) and copy the new data + // \return the number of bytes copied + Size append(Size size, const Byte* data); + + // this is a temporary hack so the current rendering code can access the underneath gl Buffer Object + // TODO: remove asap, when the backend is doing more of the gl features + inline GLuint getGLBufferObject() const { backend::syncGPUObject(*this); return getGPUObject()->_buffer; } + +protected: + + // Sysmem is the underneath cache for the data in ram. + class Sysmem { + public: + + Sysmem(); + Sysmem(Size size , const Byte* bytes); + ~Sysmem(); + + Size getSize() const { return _size; } + + // Allocate the byte array + // \param pSize The nb of bytes to allocate, if already exist, content is lost. + // \return The nb of bytes allocated, nothing if allready the appropriate size. + Size allocate(Size pSize); + + // Resize the byte array + // Keep previous data [0 to min(pSize, mSize)] + Size resize(Size pSize); + + // Assign data bytes and size (allocate for size, then copy bytes if exists) + Size setData( Size size, const Byte* bytes ); + + // Update Sub data, + // doesn't allocate and only copy size * bytes at the offset location + // only if all fits in the existing allocated buffer + Size setSubData( Size offset, Size size, const Byte* bytes); + + // Append new data at the end of the current buffer + // do a resize( size + getSIze) and copy the new data + // \return the number of bytes copied + Size append(Size size, const Byte* data); + + // Access the byte array. + // The edit version allow to map data. + inline const Byte* read() const { return _data; } + inline Byte* edit() { _stamp++; return _data; } + + template< typename T > + const T* read() const { return reinterpret_cast< T* > ( _data ); } + template< typename T > + T* edit() const { _stamp++; return reinterpret_cast< T* > ( _data ); } + + // Access the current version of the sysmem, used to compare if copies are in sync + inline Stamp getStamp() const { return _stamp; } + + static Size allocateMemory(Byte** memAllocated, Size size); + static void deallocateMemory(Byte* memDeallocated, Size size); + + private: + Sysmem(const Sysmem& sysmem) {} + Sysmem &operator=(const Sysmem &other) {return *this;} + + Byte* _data; + Size _size; + Stamp _stamp; + }; + + Sysmem* _sysmem; + + typedef backend::BufferObject GPUObject; + mutable backend::BufferObject* _gpuObject; + + inline const Sysmem& getSysmem() const { assert(_sysmem); return (*_sysmem); } + inline Sysmem& editSysmem() { assert(_sysmem); return (*_sysmem); } + + inline GPUObject* getGPUObject() const { return _gpuObject; } + inline void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; } + + friend void backend::syncGPUObject(const Buffer& buffer); +}; + +}; + class TextRenderer { public: @@ -64,6 +198,8 @@ public: int computeWidth(char ch); int computeWidth(const char* str); + void executeDrawBatch(); + void clearDrawBatch(); private: TextRenderer(const Properties& properties); @@ -100,6 +236,10 @@ private: // text color QColor _color; + // Graphics Buffer containing the current accumulated glyphs to render + gpu::Buffer _glyphsBuffer; + int _numGlyphsBatched; + static QHash _instances; }; From 1990419eebb74aa332c951fdebbd7b243081e5e5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 8 Oct 2014 15:15:49 -0700 Subject: [PATCH 02/33] first cut at correct handle hide/show behavior and rotate overlay hide/show behavior --- examples/libraries/entitySelectionTool.js | 372 +++++++++++++++++++--- 1 file changed, 323 insertions(+), 49 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index d9cf2c54fd..a1dd1d2032 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -31,6 +31,13 @@ SelectionDisplay = (function () { var handleHoverColor = { red: 224, green: 67, blue: 36 }; var handleHoverAlpha = 1.0; + var yawHandleRotation; + var pitchHandleRotation; + var rollHandleRotation; + var yawCenter; + var pitchCenter; + var rollCenter; + var rotateHandleColor = { red: 0, green: 0, blue: 0 }; var rotateHandleAlpha = 0.7; @@ -412,10 +419,6 @@ SelectionDisplay = (function () { var pitchCorner; var rollCorner; - var yawHandleRotation; - var pitchHandleRotation; - var rollHandleRotation; - // determine which bottom corner we are closest to /*------------------------------ example: @@ -448,6 +451,18 @@ SelectionDisplay = (function () { y: top + rotateHandleOffset, z: near - rotateHandleOffset}; + yawCenter = { x: center.x, + y: bottom, + z: center.z }; + + pitchCenter = { x: center.x, + y: center.y, + z: far }; + + rollCenter = { x: left, + y: center.y, + z: center.z}; + } else { yawHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 270, z: 0 }); pitchHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); @@ -465,6 +480,19 @@ SelectionDisplay = (function () { y: top + rotateHandleOffset, z: near - rotateHandleOffset}; + + yawCenter = { x: center.x, + y: bottom, + z: center.z }; + + pitchCenter = { x: left, + y: center.y, + z: center.z }; + + rollCenter = { x: center.x, + y: center.y, + z: near}; + } } else { // must be BLF or BLN @@ -485,6 +513,19 @@ SelectionDisplay = (function () { y: top + rotateHandleOffset, z: far + rotateHandleOffset}; + yawCenter = { x: center.x, + y: bottom, + z: center.z }; + + pitchCenter = { x: right, + y: center.y, + z: center.z }; + + rollCenter = { x: center.x, + y: center.y, + z: far}; + + } else { yawHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 180, z: 0 }); @@ -502,8 +543,40 @@ SelectionDisplay = (function () { rollCorner = { x: right + rotateHandleOffset, y: top + rotateHandleOffset, z: far + rotateHandleOffset}; + + yawCenter = { x: center.x, + y: bottom, + z: center.z }; + + pitchCenter = { x: center.x, + y: center.y, + z: near }; + + rollCenter = { x: right, + y: center.y, + z: center.z}; + + } } + + + print("select() with mode:" + mode); + var rotateHandlesVisible = true; + var translateHandlesVisible = true; + var stretchHandlesVisible = true; + if (mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL" || mode == "TRANSLATE_XZ") { + rotateHandlesVisible = false; + translateHandlesVisible = false; + stretchHandlesVisible = false; + } else if (mode == "TRANSLATE_UP_DOWN") { + rotateHandlesVisible = false; + stretchHandlesVisible = false; + } else if (mode != "UNKNOWN") { + // every other mode is a stretch mode... + rotateHandlesVisible = false; + translateHandlesVisible = false; + } Overlays.editOverlay(highlightBox, { visible: false }); @@ -516,37 +589,37 @@ SelectionDisplay = (function () { }); - Overlays.editOverlay(grabberMoveUp, { visible: true, position: { x: center.x, y: top + grabberMoveUpOffset, z: center.z } }); + Overlays.editOverlay(grabberMoveUp, { visible: translateHandlesVisible, position: { x: center.x, y: top + grabberMoveUpOffset, z: center.z } }); - Overlays.editOverlay(grabberLBN, { visible: true, position: { x: left, y: bottom, z: near } }); - Overlays.editOverlay(grabberRBN, { visible: true, position: { x: right, y: bottom, z: near } }); - Overlays.editOverlay(grabberLBF, { visible: true, position: { x: left, y: bottom, z: far } }); - Overlays.editOverlay(grabberRBF, { visible: true, position: { x: right, y: bottom, z: far } }); - Overlays.editOverlay(grabberLTN, { visible: true, position: { x: left, y: top, z: near } }); - Overlays.editOverlay(grabberRTN, { visible: true, position: { x: right, y: top, z: near } }); - Overlays.editOverlay(grabberLTF, { visible: true, position: { x: left, y: top, z: far } }); - Overlays.editOverlay(grabberRTF, { visible: true, position: { x: right, y: top, z: far } }); + Overlays.editOverlay(grabberLBN, { visible: stretchHandlesVisible, position: { x: left, y: bottom, z: near } }); + Overlays.editOverlay(grabberRBN, { visible: stretchHandlesVisible, position: { x: right, y: bottom, z: near } }); + Overlays.editOverlay(grabberLBF, { visible: stretchHandlesVisible, position: { x: left, y: bottom, z: far } }); + Overlays.editOverlay(grabberRBF, { visible: stretchHandlesVisible, position: { x: right, y: bottom, z: far } }); + Overlays.editOverlay(grabberLTN, { visible: stretchHandlesVisible, position: { x: left, y: top, z: near } }); + Overlays.editOverlay(grabberRTN, { visible: stretchHandlesVisible, position: { x: right, y: top, z: near } }); + Overlays.editOverlay(grabberLTF, { visible: stretchHandlesVisible, position: { x: left, y: top, z: far } }); + Overlays.editOverlay(grabberRTF, { visible: stretchHandlesVisible, position: { x: right, y: top, z: far } }); - Overlays.editOverlay(grabberTOP, { visible: true, position: { x: center.x, y: top, z: center.z } }); - Overlays.editOverlay(grabberBOTTOM, { visible: true, position: { x: center.x, y: bottom, z: center.z } }); - Overlays.editOverlay(grabberLEFT, { visible: true, position: { x: left, y: center.y, z: center.z } }); - Overlays.editOverlay(grabberRIGHT, { visible: true, position: { x: right, y: center.y, z: center.z } }); - Overlays.editOverlay(grabberNEAR, { visible: true, position: { x: center.x, y: center.y, z: near } }); - Overlays.editOverlay(grabberFAR, { visible: true, position: { x: center.x, y: center.y, z: far } }); + Overlays.editOverlay(grabberTOP, { visible: stretchHandlesVisible, position: { x: center.x, y: top, z: center.z } }); + Overlays.editOverlay(grabberBOTTOM, { visible: stretchHandlesVisible, position: { x: center.x, y: bottom, z: center.z } }); + Overlays.editOverlay(grabberLEFT, { visible: stretchHandlesVisible, position: { x: left, y: center.y, z: center.z } }); + Overlays.editOverlay(grabberRIGHT, { visible: stretchHandlesVisible, position: { x: right, y: center.y, z: center.z } }); + Overlays.editOverlay(grabberNEAR, { visible: stretchHandlesVisible, position: { x: center.x, y: center.y, z: near } }); + Overlays.editOverlay(grabberFAR, { visible: stretchHandlesVisible, position: { x: center.x, y: center.y, z: far } }); - Overlays.editOverlay(grabberEdgeTR, { visible: true, position: { x: right, y: top, z: center.z } }); - Overlays.editOverlay(grabberEdgeTL, { visible: true, position: { x: left, y: top, z: center.z } }); - Overlays.editOverlay(grabberEdgeTF, { visible: true, position: { x: center.x, y: top, z: far } }); - Overlays.editOverlay(grabberEdgeTN, { visible: true, position: { x: center.x, y: top, z: near } }); - Overlays.editOverlay(grabberEdgeBR, { visible: true, position: { x: right, y: bottom, z: center.z } }); - Overlays.editOverlay(grabberEdgeBL, { visible: true, position: { x: left, y: bottom, z: center.z } }); - Overlays.editOverlay(grabberEdgeBF, { visible: true, position: { x: center.x, y: bottom, z: far } }); - Overlays.editOverlay(grabberEdgeBN, { visible: true, position: { x: center.x, y: bottom, z: near } }); - Overlays.editOverlay(grabberEdgeNR, { visible: true, position: { x: right, y: center.y, z: near } }); - Overlays.editOverlay(grabberEdgeNL, { visible: true, position: { x: left, y: center.y, z: near } }); - Overlays.editOverlay(grabberEdgeFR, { visible: true, position: { x: right, y: center.y, z: far } }); - Overlays.editOverlay(grabberEdgeFL, { visible: true, position: { x: left, y: center.y, z: far } }); + Overlays.editOverlay(grabberEdgeTR, { visible: stretchHandlesVisible, position: { x: right, y: top, z: center.z } }); + Overlays.editOverlay(grabberEdgeTL, { visible: stretchHandlesVisible, position: { x: left, y: top, z: center.z } }); + Overlays.editOverlay(grabberEdgeTF, { visible: stretchHandlesVisible, position: { x: center.x, y: top, z: far } }); + Overlays.editOverlay(grabberEdgeTN, { visible: stretchHandlesVisible, position: { x: center.x, y: top, z: near } }); + Overlays.editOverlay(grabberEdgeBR, { visible: stretchHandlesVisible, position: { x: right, y: bottom, z: center.z } }); + Overlays.editOverlay(grabberEdgeBL, { visible: stretchHandlesVisible, position: { x: left, y: bottom, z: center.z } }); + Overlays.editOverlay(grabberEdgeBF, { visible: stretchHandlesVisible, position: { x: center.x, y: bottom, z: far } }); + Overlays.editOverlay(grabberEdgeBN, { visible: stretchHandlesVisible, position: { x: center.x, y: bottom, z: near } }); + Overlays.editOverlay(grabberEdgeNR, { visible: stretchHandlesVisible, position: { x: right, y: center.y, z: near } }); + Overlays.editOverlay(grabberEdgeNL, { visible: stretchHandlesVisible, position: { x: left, y: center.y, z: near } }); + Overlays.editOverlay(grabberEdgeFR, { visible: stretchHandlesVisible, position: { x: right, y: center.y, z: far } }); + Overlays.editOverlay(grabberEdgeFL, { visible: stretchHandlesVisible, position: { x: left, y: center.y, z: far } }); Overlays.editOverlay(baseOfEntityProjectionOverlay, @@ -561,15 +634,10 @@ SelectionDisplay = (function () { dimensions: { x: properties.dimensions.x, y: properties.dimensions.z }, rotation: properties.rotation, }); - - + Overlays.editOverlay(rotateOverlayInner, { visible: false, - position: { x: properties.position.x, - y: properties.position.y - (properties.dimensions.y / 2), - z: properties.position.z}, - size: innerRadius, innerRadius: 0.9, alpha: innerAlpha @@ -578,10 +646,6 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayOuter, { visible: false, - position: { x: properties.position.x, - y: properties.position.y - (properties.dimensions.y / 2), - z: properties.position.z}, - size: outerRadius, innerRadius: 0.9, startAt: 0, @@ -592,10 +656,6 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayCurrent, { visible: false, - position: { x: properties.position.x, - y: properties.position.y - (properties.dimensions.y / 2), - z: properties.position.z}, - size: outerRadius, startAt: 0, endAt: 0, @@ -603,9 +663,9 @@ SelectionDisplay = (function () { }); // TODO: we have not implemented the rotating handle/controls yet... so for now, these handles are hidden - Overlays.editOverlay(yawHandle, { visible: false, position: yawCorner, rotation: yawHandleRotation}); - Overlays.editOverlay(pitchHandle, { visible: false, position: pitchCorner, rotation: pitchHandleRotation}); - Overlays.editOverlay(rollHandle, { visible: false, position: rollCorner, rotation: rollHandleRotation}); + Overlays.editOverlay(yawHandle, { visible: rotateHandlesVisible, position: yawCorner, rotation: yawHandleRotation}); + Overlays.editOverlay(pitchHandle, { visible: rotateHandlesVisible, position: pitchCorner, rotation: pitchHandleRotation}); + Overlays.editOverlay(rollHandle, { visible: rotateHandlesVisible, position: rollCorner, rotation: rollHandleRotation}); Entities.editEntity(entityID, { localRenderAlpha: 0.1 }); }; @@ -1440,7 +1500,25 @@ SelectionDisplay = (function () { Entities.editEntity(currentSelection, selectedEntityProperties); tooltip.updateText(selectedEntityProperties); that.select(currentSelection, false); // TODO: this should be more than highlighted - }; + }; + + that.rotateYaw = function(event) { + if (!entitySelected || mode !== "ROTATE_YAW") { + return; // not allowed + } + }; + + that.rotatePitch = function(event) { + if (!entitySelected || mode !== "ROTATE_PITCH") { + return; // not allowed + } + }; + + that.rotateRoll = function(event) { + if (!entitySelected || mode !== "ROTATE_ROLL") { + return; // not allowed + } + }; that.checkMove = function() { if (currentSelection.isKnownID && @@ -1477,6 +1555,36 @@ SelectionDisplay = (function () { case grabberMoveUp: mode = "TRANSLATE_UP_DOWN"; somethingClicked = true; + + // in translate mode, we hide our stretch handles... + Overlays.editOverlay(grabberLBN, { visible: false }); + Overlays.editOverlay(grabberLBF, { visible: false }); + Overlays.editOverlay(grabberRBN, { visible: false }); + Overlays.editOverlay(grabberRBF, { visible: false }); + Overlays.editOverlay(grabberLTN, { visible: false }); + Overlays.editOverlay(grabberLTF, { visible: false }); + Overlays.editOverlay(grabberRTN, { visible: false }); + Overlays.editOverlay(grabberRTF, { visible: false }); + + Overlays.editOverlay(grabberTOP, { visible: false }); + Overlays.editOverlay(grabberBOTTOM, { visible: false }); + Overlays.editOverlay(grabberLEFT, { visible: false }); + Overlays.editOverlay(grabberRIGHT, { visible: false }); + Overlays.editOverlay(grabberNEAR, { visible: false }); + Overlays.editOverlay(grabberFAR, { visible: false }); + + Overlays.editOverlay(grabberEdgeTR, { visible: false }); + Overlays.editOverlay(grabberEdgeTL, { visible: false }); + Overlays.editOverlay(grabberEdgeTF, { visible: false }); + Overlays.editOverlay(grabberEdgeTN, { visible: false }); + Overlays.editOverlay(grabberEdgeBR, { visible: false }); + Overlays.editOverlay(grabberEdgeBL, { visible: false }); + Overlays.editOverlay(grabberEdgeBF, { visible: false }); + Overlays.editOverlay(grabberEdgeBN, { visible: false }); + Overlays.editOverlay(grabberEdgeNR, { visible: false }); + Overlays.editOverlay(grabberEdgeNL, { visible: false }); + Overlays.editOverlay(grabberEdgeFR, { visible: false }); + Overlays.editOverlay(grabberEdgeFL, { visible: false }); break; case grabberRBN: @@ -1559,20 +1667,128 @@ SelectionDisplay = (function () { } } + // if one of the items above was clicked, then we know we are in translate or stretch mode, and we + // should hide our rotate handles... + if (somethingClicked) { + Overlays.editOverlay(yawHandle, { visible: false }); + Overlays.editOverlay(pitchHandle, { visible: false }); + Overlays.editOverlay(rollHandle, { visible: false }); + + if (mode != "TRANSLATE_UP_DOWN") { + Overlays.editOverlay(grabberMoveUp, { visible: false }); + } + } + if (!somethingClicked) { + + print("rotate handle case..."); + // After testing our stretch handles, then check out rotate handles Overlays.editOverlay(yawHandle, { ignoreRayIntersection: false }); Overlays.editOverlay(pitchHandle, { ignoreRayIntersection: false }); Overlays.editOverlay(rollHandle, { ignoreRayIntersection: false }); var result = Overlays.findRayIntersection(pickRay); + + var overlayOrientation; + var overlayCenter; + if (result.intersects) { switch(result.overlayID) { + case yawHandle: + mode = "ROTATE_YAW"; + somethingClicked = true; + overlayOrientation = yawHandleRotation; + overlayCenter = yawCenter; + break; + + case pitchHandle: + mode = "ROTATE_PITCH"; + somethingClicked = true; + overlayOrientation = pitchHandleRotation; + overlayCenter = pitchCenter; + break; + + case rollHandle: + mode = "ROTATE_ROLL"; + somethingClicked = true; + overlayOrientation = rollHandleRotation; + overlayCenter = rollCenter; + break; + default: print("mousePressEvent()...... " + overlayNames[result.overlayID]); mode = "UNKNOWN"; break; } } + + print(" somethingClicked:" + somethingClicked); + print(" mode:" + mode); + + if (somethingClicked) { + + // TODO: need to place correctly.... + print(" attempting to show overlays:" + somethingClicked); + + Overlays.editOverlay(rotateOverlayInner, + { + visible: true, + rotation: overlayOrientation, + position: overlayCenter + }); + + Overlays.editOverlay(rotateOverlayOuter, + { + visible: true, + rotation: overlayOrientation, + position: overlayCenter + }); + + Overlays.editOverlay(rotateOverlayCurrent, + { + visible: true, + rotation: overlayOrientation, + position: overlayCenter + }); + + Overlays.editOverlay(yawHandle, { visible: false }); + Overlays.editOverlay(pitchHandle, { visible: false }); + Overlays.editOverlay(rollHandle, { visible: false }); + + + Overlays.editOverlay(yawHandle, { visible: false }); + Overlays.editOverlay(pitchHandle, { visible: false }); + Overlays.editOverlay(rollHandle, { visible: false }); + Overlays.editOverlay(grabberMoveUp, { visible: false }); + Overlays.editOverlay(grabberLBN, { visible: false }); + Overlays.editOverlay(grabberLBF, { visible: false }); + Overlays.editOverlay(grabberRBN, { visible: false }); + Overlays.editOverlay(grabberRBF, { visible: false }); + Overlays.editOverlay(grabberLTN, { visible: false }); + Overlays.editOverlay(grabberLTF, { visible: false }); + Overlays.editOverlay(grabberRTN, { visible: false }); + Overlays.editOverlay(grabberRTF, { visible: false }); + + Overlays.editOverlay(grabberTOP, { visible: false }); + Overlays.editOverlay(grabberBOTTOM, { visible: false }); + Overlays.editOverlay(grabberLEFT, { visible: false }); + Overlays.editOverlay(grabberRIGHT, { visible: false }); + Overlays.editOverlay(grabberNEAR, { visible: false }); + Overlays.editOverlay(grabberFAR, { visible: false }); + + Overlays.editOverlay(grabberEdgeTR, { visible: false }); + Overlays.editOverlay(grabberEdgeTL, { visible: false }); + Overlays.editOverlay(grabberEdgeTF, { visible: false }); + Overlays.editOverlay(grabberEdgeTN, { visible: false }); + Overlays.editOverlay(grabberEdgeBR, { visible: false }); + Overlays.editOverlay(grabberEdgeBL, { visible: false }); + Overlays.editOverlay(grabberEdgeBF, { visible: false }); + Overlays.editOverlay(grabberEdgeBN, { visible: false }); + Overlays.editOverlay(grabberEdgeNR, { visible: false }); + Overlays.editOverlay(grabberEdgeNL, { visible: false }); + Overlays.editOverlay(grabberEdgeFR, { visible: false }); + Overlays.editOverlay(grabberEdgeFL, { visible: false }); + } } if (!somethingClicked) { @@ -1614,6 +1830,15 @@ SelectionDisplay = (function () { that.mouseMoveEvent = function(event) { //print("mouseMoveEvent()... mode:" + mode); switch (mode) { + case "ROTATE_YAW": + that.rotateYaw(event); + break; + case "ROTATE_PITCH": + that.rotatePitch(event); + break; + case "ROTATE_ROLL": + that.rotateRoll(event); + break; case "TRANSLATE_UP_DOWN": that.translateUpDown(event); break; @@ -1671,6 +1896,54 @@ SelectionDisplay = (function () { }; that.mouseReleaseEvent = function(event) { + var showHandles = false; + // hide our rotation overlays..., and show our handles + if (mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL") { + Overlays.editOverlay(rotateOverlayInner, { visible: false }); + Overlays.editOverlay(rotateOverlayOuter, { visible: false }); + Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); + showHandles = true; + } + + if (mode != "UNKNOWN") { + showHandles = true; + } + + if (showHandles) { + Overlays.editOverlay(yawHandle, { visible: true }); + Overlays.editOverlay(pitchHandle, { visible: true }); + Overlays.editOverlay(rollHandle, { visible: true }); + Overlays.editOverlay(grabberMoveUp, { visible: true }); + Overlays.editOverlay(grabberLBN, { visible: true }); + Overlays.editOverlay(grabberLBF, { visible: true }); + Overlays.editOverlay(grabberRBN, { visible: true }); + Overlays.editOverlay(grabberRBF, { visible: true }); + Overlays.editOverlay(grabberLTN, { visible: true }); + Overlays.editOverlay(grabberLTF, { visible: true }); + Overlays.editOverlay(grabberRTN, { visible: true }); + Overlays.editOverlay(grabberRTF, { visible: true }); + + Overlays.editOverlay(grabberTOP, { visible: true }); + Overlays.editOverlay(grabberBOTTOM, { visible: true }); + Overlays.editOverlay(grabberLEFT, { visible: true }); + Overlays.editOverlay(grabberRIGHT, { visible: true }); + Overlays.editOverlay(grabberNEAR, { visible: true }); + Overlays.editOverlay(grabberFAR, { visible: true }); + + Overlays.editOverlay(grabberEdgeTR, { visible: true }); + Overlays.editOverlay(grabberEdgeTL, { visible: true }); + Overlays.editOverlay(grabberEdgeTF, { visible: true }); + Overlays.editOverlay(grabberEdgeTN, { visible: true }); + Overlays.editOverlay(grabberEdgeBR, { visible: true }); + Overlays.editOverlay(grabberEdgeBL, { visible: true }); + Overlays.editOverlay(grabberEdgeBF, { visible: true }); + Overlays.editOverlay(grabberEdgeBN, { visible: true }); + Overlays.editOverlay(grabberEdgeNR, { visible: true }); + Overlays.editOverlay(grabberEdgeNL, { visible: true }); + Overlays.editOverlay(grabberEdgeFR, { visible: true }); + Overlays.editOverlay(grabberEdgeFL, { visible: true }); + } + mode = "UNKNOWN"; // if something is selected, then reset the "original" properties for any potential next click+move operation @@ -1679,6 +1952,7 @@ SelectionDisplay = (function () { selectedEntityPropertiesOriginalPosition = properties.position; selectedEntityPropertiesOriginalDimensions = properties.dimensions; } + }; Controller.mousePressEvent.connect(that.mousePressEvent); From e544c06b92e1bacfce57cef3dec7d65be3451ee5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 8 Oct 2014 19:28:40 -0700 Subject: [PATCH 03/33] correctly reselect entity after property edits to make sure handles move if dimensions or position changes --- examples/libraries/entityPropertyDialogBox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js index fbac30e796..becc8c86b2 100644 --- a/examples/libraries/entityPropertyDialogBox.js +++ b/examples/libraries/entityPropertyDialogBox.js @@ -244,7 +244,7 @@ EntityPropertyDialogBox = (function () { properties.color.blue = array[index++].value; } Entities.editEntity(editModelID, properties); - selectionDisplay.highlightSelectable(editModelID, propeties); + selectionDisplay.select(editModelID, false); } modelSelected = false; }); From 18f3a208f7c7657aef816a21eaef2c32dd217969 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 8 Oct 2014 19:29:07 -0700 Subject: [PATCH 04/33] fix property edit dialog box --- examples/newEditEntities.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index a223813347..b753537631 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -570,6 +570,19 @@ function handeMenuEvent(menuItem) { } } else if (menuItem == "Edit Properties...") { // good place to put the properties dialog + + editModelID = -1; + if (entitySelected) { + print(" Edit Properties.... selectedEntityID="+ selectedEntityID); + editModelID = selectedEntityID; + } else { + print(" Edit Properties.... not holding..."); + } + if (editModelID != -1) { + print(" Edit Properties.... about to edit properties..."); + entityPropertyDialogBox.openDialog(editModelID); + } + } else if (menuItem == "Paste Models") { modelImporter.paste(); } else if (menuItem == "Export Models") { From 4558caf4315e7e791a653ca824e4e05bc3fd31b0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 8 Oct 2014 19:30:43 -0700 Subject: [PATCH 05/33] display current pitch/yaw/roll in rotation overlays --- examples/libraries/entitySelectionTool.js | 33 ++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index a1dd1d2032..69f4e4ae45 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -561,7 +561,6 @@ SelectionDisplay = (function () { } - print("select() with mode:" + mode); var rotateHandlesVisible = true; var translateHandlesVisible = true; var stretchHandlesVisible = true; @@ -1506,6 +1505,14 @@ SelectionDisplay = (function () { if (!entitySelected || mode !== "ROTATE_YAW") { return; // not allowed } + + var pickRay = Camera.computePickRay(event.x, event.y); + var result = Overlays.findRayIntersection(pickRay); + print("result.intersects:" + result.intersects); + print("result.overlayID:" + overlayNames[result.overlayID]); + print("result.distance:" + result.distance); + print("result.face:" + result.face); + Vec3.print("result.intersection:", result.intersection); }; that.rotatePitch = function(event) { @@ -1691,6 +1698,13 @@ SelectionDisplay = (function () { var overlayOrientation; var overlayCenter; + + var properties = Entities.getEntityProperties(currentSelection); + var angles = Quat.safeEulerAngles(properties.rotation); + var pitch = angles.x; + var yaw = angles.y; + var roll = angles.z; + var currentRotation; if (result.intersects) { switch(result.overlayID) { @@ -1699,6 +1713,7 @@ SelectionDisplay = (function () { somethingClicked = true; overlayOrientation = yawHandleRotation; overlayCenter = yawCenter; + currentRotation = yaw; break; case pitchHandle: @@ -1706,6 +1721,7 @@ SelectionDisplay = (function () { somethingClicked = true; overlayOrientation = pitchHandleRotation; overlayCenter = pitchCenter; + currentRotation = pitch; break; case rollHandle: @@ -1713,6 +1729,7 @@ SelectionDisplay = (function () { somethingClicked = true; overlayOrientation = rollHandleRotation; overlayCenter = rollCenter; + currentRotation = roll; break; default: @@ -1725,10 +1742,16 @@ SelectionDisplay = (function () { print(" somethingClicked:" + somethingClicked); print(" mode:" + mode); + if (somethingClicked) { + if (currentRotation < 0) { + currentRotation = currentRotation + 360; + } + // TODO: need to place correctly.... print(" attempting to show overlays:" + somethingClicked); + print(" currentRotation:" + currentRotation); Overlays.editOverlay(rotateOverlayInner, { @@ -1741,14 +1764,18 @@ SelectionDisplay = (function () { { visible: true, rotation: overlayOrientation, - position: overlayCenter + position: overlayCenter, + startAt: currentRotation, + endAt: 360 }); Overlays.editOverlay(rotateOverlayCurrent, { visible: true, rotation: overlayOrientation, - position: overlayCenter + position: overlayCenter, + startAt: 0, + endAt: currentRotation }); Overlays.editOverlay(yawHandle, { visible: false }); From 2004159778394fd0b691afd29020217ded5c5841 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 8 Oct 2014 20:29:53 -0700 Subject: [PATCH 06/33] implement basic support for planar overlay type intersections --- interface/src/ui/overlays/Circle3DOverlay.cpp | 20 +++++++++++++ interface/src/ui/overlays/Circle3DOverlay.h | 2 ++ interface/src/ui/overlays/Planar3DOverlay.cpp | 28 +++++++++++++++++++ interface/src/ui/overlays/Planar3DOverlay.h | 2 ++ 4 files changed, 52 insertions(+) diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 6ff256d48e..7cd385f963 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "Circle3DOverlay.h" #include "renderer/GlowEffect.h" @@ -291,6 +292,25 @@ void Circle3DOverlay::setProperties(const QScriptValue &properties) { } } +bool Circle3DOverlay::findRayIntersection(const glm::vec3& origin, + const glm::vec3& direction, float& distance, BoxFace& face) const { + + bool intersects = Planar3DOverlay::findRayIntersection(origin, direction, distance, face); + if (intersects) { + glm::vec3 hitAt = origin + (direction * distance); + float distanceToHit = glm::distance(hitAt, _position); + + float maxDimension = glm::max(_dimensions.x, _dimensions.y); + float innerRadius = maxDimension * getInnerRadius(); + float outerRadius = maxDimension * getOuterRadius(); + + // TODO: this really only works for circles, we should be handling the ellipse case as well... + if (distanceToHit < innerRadius || distanceToHit > outerRadius) { + intersects = false; + } + } + return intersects; +} diff --git a/interface/src/ui/overlays/Circle3DOverlay.h b/interface/src/ui/overlays/Circle3DOverlay.h index 791d951105..289781cdd7 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.h +++ b/interface/src/ui/overlays/Circle3DOverlay.h @@ -45,6 +45,8 @@ public: void setMinorTickMarksLength(float value) { _minorTickMarksLength = value; } void setMajorTickMarksColor(const xColor& value) { _majorTickMarksColor = value; } void setMinorTickMarksColor(const xColor& value) { _minorTickMarksColor = value; } + + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const; protected: float _startAt; diff --git a/interface/src/ui/overlays/Planar3DOverlay.cpp b/interface/src/ui/overlays/Planar3DOverlay.cpp index 42e059c3ca..91a3a023f7 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.cpp +++ b/interface/src/ui/overlays/Planar3DOverlay.cpp @@ -12,6 +12,8 @@ #include "InterfaceConfig.h" #include +#include +#include #include #include @@ -74,3 +76,29 @@ void Planar3DOverlay::setProperties(const QScriptValue& properties) { } } } + +bool Planar3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + float& distance, BoxFace& face) const { + + RayIntersectionInfo rayInfo; + rayInfo._rayStart = origin; + rayInfo._rayDirection = direction; + rayInfo._rayLength = std::numeric_limits::max(); + + PlaneShape plane; + + const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f); + glm::vec3 normal = _rotation * UNROTATED_NORMAL; + plane.setNormal(normal); + plane.setPoint(_position); // the position is definitely a point on our plane + + bool intersects = plane.findRayIntersection(rayInfo); + + if (intersects) { + distance = rayInfo._hitDistance; + // TODO: if it intersects, we want to check to see if the intersection point is within our dimensions + // glm::vec3 hitAt = origin + direction * distance; + // _dimensions + } + return intersects; +} diff --git a/interface/src/ui/overlays/Planar3DOverlay.h b/interface/src/ui/overlays/Planar3DOverlay.h index c1733bc0fb..ee4bb3e05a 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.h +++ b/interface/src/ui/overlays/Planar3DOverlay.h @@ -37,6 +37,8 @@ public: virtual void setProperties(const QScriptValue& properties); + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const; + protected: glm::vec2 _dimensions; }; From 458475ee64b34b729679c67b69eb57c6503c8081 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 8 Oct 2014 20:31:46 -0700 Subject: [PATCH 07/33] work on intersection with rotation overlays --- examples/libraries/entitySelectionTool.js | 30 +++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 69f4e4ae45..d88a8a52eb 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -1507,6 +1507,12 @@ SelectionDisplay = (function () { } var pickRay = Camera.computePickRay(event.x, event.y); + Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true }); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); + var result = Overlays.findRayIntersection(pickRay); print("result.intersects:" + result.intersects); print("result.overlayID:" + overlayNames[result.overlayID]); @@ -1519,12 +1525,36 @@ SelectionDisplay = (function () { if (!entitySelected || mode !== "ROTATE_PITCH") { return; // not allowed } + var pickRay = Camera.computePickRay(event.x, event.y); + Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true }); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); + var result = Overlays.findRayIntersection(pickRay); + print("result.intersects:" + result.intersects); + print("result.overlayID:" + overlayNames[result.overlayID]); + print("result.distance:" + result.distance); + print("result.face:" + result.face); + Vec3.print("result.intersection:", result.intersection); }; that.rotateRoll = function(event) { if (!entitySelected || mode !== "ROTATE_ROLL") { return; // not allowed } + var pickRay = Camera.computePickRay(event.x, event.y); + Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true }); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); + var result = Overlays.findRayIntersection(pickRay); + print("result.intersects:" + result.intersects); + print("result.overlayID:" + overlayNames[result.overlayID]); + print("result.distance:" + result.distance); + print("result.face:" + result.face); + Vec3.print("result.intersection:", result.intersection); }; that.checkMove = function() { From 9f0936de532e3a3474c4fb26b94adc8297343dce Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 9 Oct 2014 10:17:50 -0700 Subject: [PATCH 08/33] Improve the TextRenderer::draw(), first step - Introduce a managed Buffer for data to be used on the GPU. THis is the first type of resource (second will be texture) that we plan to use in the graphics engine in the long term. this is an api agnostic replacement to QGLBuggerObject It's in the new file gpu/Resource.h(.cpp) - Add two gpu::Buffers in the TextRenderer that collect all the glyph vertices (coords + texcoords + color) during the for loop on the string characters of the TextRenderer::draw(). Right now the text glyphs are then drawn in one draw call (and not one per character) at the end of the function. THe step 2 plan is to keep on collecting all the glyphs from all the TextRenderer::Draw() issued during one frame and to draw all of them in a single drawcall. We decided to split the task in 2 so it's easier to review. --- interface/CMakeLists.txt | 2 +- interface/src/Util.cpp | 5 - interface/src/Util.h | 2 - interface/src/gpu/Resource.cpp | 219 ++++++++ interface/src/gpu/Resource.h | 164 ++++++ interface/src/ui/Stats.cpp | 1 - interface/src/ui/TextRenderer.cpp | 813 ++++++++++++------------------ interface/src/ui/TextRenderer.h | 139 +---- 8 files changed, 721 insertions(+), 624 deletions(-) create mode 100644 interface/src/gpu/Resource.cpp create mode 100644 interface/src/gpu/Resource.h diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 19f8ef6d5f..1202b36a9f 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -41,7 +41,7 @@ configure_file(InterfaceVersion.h.in "${PROJECT_BINARY_DIR}/includes/InterfaceVe # grab the implementation and header files from src dirs file(GLOB INTERFACE_SRCS src/*.cpp src/*.h) -foreach(SUBDIR avatar devices renderer ui starfield location scripting voxels particles entities) +foreach(SUBDIR avatar devices renderer ui starfield location scripting voxels particles entities gpu) file(GLOB_RECURSE SUBDIR_SRCS src/${SUBDIR}/*.cpp src/${SUBDIR}/*.h) set(INTERFACE_SRCS ${INTERFACE_SRCS} "${SUBDIR_SRCS}") endforeach(SUBDIR) diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 5399af8e75..9d7f5518d0 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -197,11 +197,6 @@ static TextRenderer* textRenderer(int mono) { } } -void renderTextRenderer(int mono) { - textRenderer(mono)->executeDrawBatch(); - textRenderer(mono)->clearDrawBatch(); -} - int widthText(float scale, int mono, char const* string) { return textRenderer(mono)->computeWidth(string) * (scale / 0.10); } diff --git a/interface/src/Util.h b/interface/src/Util.h index acdbdb6f75..02cfd99f9a 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -31,8 +31,6 @@ float widthChar(float scale, int mono, char ch); void drawText(int x, int y, float scale, float radians, int mono, char const* string, const float* color); -void renderTextRenderer(int mono); - void drawvec3(int x, int y, float scale, float radians, float thick, int mono, glm::vec3 vec, float r=1.0, float g=1.0, float b=1.0); diff --git a/interface/src/gpu/Resource.cpp b/interface/src/gpu/Resource.cpp new file mode 100644 index 0000000000..890039e429 --- /dev/null +++ b/interface/src/gpu/Resource.cpp @@ -0,0 +1,219 @@ +// +// Resource.cpp +// interface/src/gpu +// +// Created by Sam Gateau on 10/8/2014. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "Resource.h" + +#include + +using namespace gpu; + +Resource::Size Resource::Sysmem::allocateMemory(Byte** dataAllocated, Size size) { + if ( !dataAllocated ) { + qWarning() << "Buffer::Sysmem::allocateMemory() : Must have a valid dataAllocated pointer."; + return NOT_ALLOCATED; + } + + // Try to allocate if needed + Size newSize = 0; + if (size > 0) { + // Try allocating as much as the required size + one block of memory + newSize = size; + (*dataAllocated) = new Byte[newSize]; + // Failed? + if (!(*dataAllocated)) { + qWarning() << "Buffer::Sysmem::allocate() : Can't allocate a system memory buffer of " << newSize << "bytes. Fails to create the buffer Sysmem."; + return NOT_ALLOCATED; + } + } + + // Return what's actually allocated + return newSize; +} + +void Resource::Sysmem::deallocateMemory(Byte* dataAllocated, Size size) { + if (dataAllocated) { + delete[] dataAllocated; + } +} + +Resource::Sysmem::Sysmem() : + _data(NULL), + _size(0), + _stamp(0) +{ +} + +Resource::Sysmem::Sysmem(Size size, const Byte* bytes) : + _data(NULL), + _size(0), + _stamp(0) +{ + if (size > 0) { + _size = allocateMemory(&_data, size); + if (_size >= size) { + if (bytes) { + memcpy(_data, bytes, size); + } + } + } +} + +Resource::Sysmem::~Sysmem() { + deallocateMemory( _data, _size ); + _data = NULL; + _size = 0; +} + +Resource::Size Resource::Sysmem::allocate(Size size) { + if (size != _size) { + Byte* newData = 0; + Size newSize = 0; + if (size > 0) { + Size allocated = allocateMemory(&newData, size); + if (allocated == NOT_ALLOCATED) { + // early exit because allocation failed + return 0; + } + newSize = allocated; + } + // Allocation was successful, can delete previous data + deallocateMemory(_data, _size); + _data = newData; + _size = newSize; + _stamp++; + } + return _size; +} + +Resource::Size Resource::Sysmem::resize(Size size) { + if (size != _size) { + Byte* newData = 0; + Size newSize = 0; + if (size > 0) { + Size allocated = allocateMemory(&newData, size); + if (allocated == NOT_ALLOCATED) { + // early exit because allocation failed + return _size; + } + newSize = allocated; + // Restore back data from old buffer in the new one + if (_data) { + Size copySize = ((newSize < _size)? newSize: _size); + memcpy( newData, _data, copySize); + } + } + // Reallocation was successful, can delete previous data + deallocateMemory(_data, _size); + _data = newData; + _size = newSize; + _stamp++; + } + return _size; +} + +Resource::Size Resource::Sysmem::setData( Size size, const Byte* bytes ) { + if (allocate(size) == size) { + if (bytes) { + memcpy( _data, bytes, _size ); + _stamp++; + } + } + return _size; +} + +Resource::Size Resource::Sysmem::setSubData( Size offset, Size size, const Byte* bytes) { + if (((offset + size) <= getSize()) && bytes) { + memcpy( _data + offset, bytes, size ); + _stamp++; + return size; + } + return 0; +} + +Resource::Size Resource::Sysmem::append(Size size, const Byte* bytes) { + if (size > 0) { + Size oldSize = getSize(); + Size totalSize = oldSize + size; + if (resize(totalSize) == totalSize) { + return setSubData(oldSize, size, bytes); + } + } + return 0; +} + +Buffer::Buffer() : + Resource(), + _sysmem(NULL), + _gpuObject(NULL) { + _sysmem = new Sysmem(); +} + +Buffer::~Buffer() { + if (_sysmem) { + delete _sysmem; + _sysmem = 0; + } + if (_gpuObject) { + delete _gpuObject; + _gpuObject = 0; + } +} + +Buffer::Size Buffer::resize(Size size) { + return editSysmem().resize(size); +} + +Buffer::Size Buffer::setData(Size size, const Byte* data) { + return editSysmem().setData(size, data); +} + +Buffer::Size Buffer::setSubData(Size offset, Size size, const Byte* data) { + return editSysmem().setSubData( offset, size, data); +} + +Buffer::Size Buffer::append(Size size, const Byte* data) { + return editSysmem().append( size, data); +} + +namespace gpu { +namespace backend { + +BufferObject::~BufferObject() { + if (_buffer!=0) { + glDeleteBuffers(1, &_buffer); + } +} + +void syncGPUObject(const Buffer& buffer) { + BufferObject* object = buffer.getGPUObject(); + + if (object && (object->_stamp == buffer.getSysmem().getStamp())) { + return; + } + + // need to have a gpu object? + if (!object) { + object = new BufferObject(); + glGenBuffers(1, &object->_buffer); + buffer.setGPUObject(object); + } + + // Now let's update the content of the bo with the sysmem version + //if (object->_size < buffer.getSize()) { + glBindBuffer(GL_ARRAY_BUFFER, object->_buffer); + glBufferData(GL_ARRAY_BUFFER, buffer.getSysmem().getSize(), buffer.getSysmem().readData(), GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + object->_stamp = buffer.getSysmem().getStamp(); + object->_size = buffer.getSysmem().getSize(); + //} +} + +}; +}; \ No newline at end of file diff --git a/interface/src/gpu/Resource.h b/interface/src/gpu/Resource.h new file mode 100644 index 0000000000..32e8e454e8 --- /dev/null +++ b/interface/src/gpu/Resource.h @@ -0,0 +1,164 @@ +// +// Resource.h +// interface/src/gpu +// +// Created by Sam Gateau on 10/8/2014. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_Resource_h +#define hifi_gpu_Resource_h + +#include +#include "interfaceconfig.h" + +namespace gpu { + +class Buffer; +typedef int Stamp; + +// TODO: move the backend namespace into dedicated files, for now we keep it close to the gpu objects definition for convenience +namespace backend { + + class BufferObject { + public: + Stamp _stamp; + GLuint _buffer; + GLuint _size; + + BufferObject() : + _stamp(0), + _buffer(0), + _size(0) + {} + + ~BufferObject(); + }; + void syncGPUObject(const Buffer& buffer); +}; + +class Resource { +public: + typedef unsigned char Byte; + typedef unsigned int Size; + + static const Size NOT_ALLOCATED = -1; + + // The size in bytes of data stored in the resource + virtual Size getSize() const = 0; + +protected: + + Resource() {} + virtual ~Resource() {} + + // Sysmem is the underneath cache for the data in ram of a resource. + class Sysmem { + public: + + Sysmem(); + Sysmem(Size size , const Byte* bytes); + ~Sysmem(); + + Size getSize() const { return _size; } + + // Allocate the byte array + // \param pSize The nb of bytes to allocate, if already exist, content is lost. + // \return The nb of bytes allocated, nothing if allready the appropriate size. + Size allocate(Size pSize); + + // Resize the byte array + // Keep previous data [0 to min(pSize, mSize)] + Size resize(Size pSize); + + // Assign data bytes and size (allocate for size, then copy bytes if exists) + Size setData( Size size, const Byte* bytes ); + + // Update Sub data, + // doesn't allocate and only copy size * bytes at the offset location + // only if all fits in the existing allocated buffer + Size setSubData( Size offset, Size size, const Byte* bytes); + + // Append new data at the end of the current buffer + // do a resize( size + getSIze) and copy the new data + // \return the number of bytes copied + Size append(Size size, const Byte* data); + + // Access the byte array. + // The edit version allow to map data. + inline const Byte* readData() const { return _data; } + inline Byte* editData() { _stamp++; return _data; } + + template< typename T > + const T* read() const { return reinterpret_cast< T* > ( _data ); } + template< typename T > + T* edit() const { _stamp++; return reinterpret_cast< T* > ( _data ); } + + // Access the current version of the sysmem, used to compare if copies are in sync + inline Stamp getStamp() const { return _stamp; } + + static Size allocateMemory(Byte** memAllocated, Size size); + static void deallocateMemory(Byte* memDeallocated, Size size); + + private: + Sysmem(const Sysmem& sysmem) {} + Sysmem &operator=(const Sysmem &other) {return *this;} + + Stamp _stamp; + Size _size; + Byte* _data; + }; + +}; + +class Buffer : public Resource { +public: + + Buffer(); + Buffer(const Buffer& buf ); + ~Buffer(); + + // The size in bytes of data stored in the buffer + inline Size getSize() const { return getSysmem().getSize(); } + inline const Byte* getData() const { return getSysmem().readData(); } + + // Resize the buffer + // Keep previous data [0 to min(pSize, mSize)] + Size resize(Size pSize); + + // Assign data bytes and size (allocate for size, then copy bytes if exists) + Size setData(Size size, const Byte* data); + + // Assign data bytes and size (allocate for size, then copy bytes if exists) + Size setSubData(Size offset, Size size, const Byte* data); + + // Append new data at the end of the current buffer + // do a resize( size + getSize) and copy the new data + // \return the number of bytes copied + Size append(Size size, const Byte* data); + + // this is a temporary hack so the current rendering code can access the underneath gl Buffer Object + // TODO: remove asap, when the backend is doing more of the gl features + inline GLuint getGLBufferObject() const { backend::syncGPUObject(*this); return getGPUObject()->_buffer; } + +protected: + + Sysmem* _sysmem; + + typedef backend::BufferObject GPUObject; + mutable backend::BufferObject* _gpuObject; + + inline const Sysmem& getSysmem() const { assert(_sysmem); return (*_sysmem); } + inline Sysmem& editSysmem() { assert(_sysmem); return (*_sysmem); } + + inline GPUObject* getGPUObject() const { return _gpuObject; } + inline void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; } + + friend void backend::syncGPUObject(const Buffer& buffer); +}; + +}; + +#endif \ No newline at end of file diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 6b7d499bf9..dd9ac67837 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -690,7 +690,6 @@ void Stats::display( drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, reflectionsStatus, color); } - } void Stats::setMetavoxelStats(int internal, int leaves, int sendProgress, diff --git a/interface/src/ui/TextRenderer.cpp b/interface/src/ui/TextRenderer.cpp index 5f00d84b90..d8d1b17da7 100644 --- a/interface/src/ui/TextRenderer.cpp +++ b/interface/src/ui/TextRenderer.cpp @@ -1,490 +1,343 @@ -// -// TextRenderer.cpp -// interface/src/ui -// -// Created by Andrzej Kapolka on 4/24/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "InterfaceConfig.h" -#include "TextRenderer.h" - -// the width/height of the cached glyph textures -const int IMAGE_SIZE = 256; - -static uint qHash(const TextRenderer::Properties& key, uint seed = 0) { - // can be switched to qHash(key.font, seed) when we require Qt 5.3+ - return qHash(key.font.family(), qHash(key.font.pointSize(), seed)); -} - -static bool operator==(const TextRenderer::Properties& p1, const TextRenderer::Properties& p2) { - return p1.font == p2.font && p1.effect == p2.effect && p1.effectThickness == p2.effectThickness && p1.color == p2.color; -} - -TextRenderer* TextRenderer::getInstance(const char* family, int pointSize, int weight, bool italic, - EffectType effect, int effectThickness, const QColor& color) { - Properties properties = { QFont(family, pointSize, weight, italic), effect, effectThickness, color }; - TextRenderer*& instance = _instances[properties]; - if (!instance) { - instance = new TextRenderer(properties); - } - return instance; -} - -TextRenderer::~TextRenderer() { - glDeleteTextures(_allTextureIDs.size(), _allTextureIDs.constData()); -} - -int TextRenderer::calculateHeight(const char* str) { - int maxHeight = 0; - for (const char* ch = str; *ch != 0; ch++) { - const Glyph& glyph = getGlyph(*ch); - if (glyph.textureID() == 0) { - continue; - } - - if (glyph.bounds().height() > maxHeight) { - maxHeight = glyph.bounds().height(); - } - } - return maxHeight; -} - -int TextRenderer::draw(int x, int y, const char* str) { - glEnable(GL_TEXTURE_2D); - - int maxHeight = 0; - for (const char* ch = str; *ch != 0; ch++) { - const Glyph& glyph = getGlyph(*ch); - if (glyph.textureID() == 0) { - x += glyph.width(); - continue; - } - - if (glyph.bounds().height() > maxHeight) { - maxHeight = glyph.bounds().height(); - } - - glBindTexture(GL_TEXTURE_2D, glyph.textureID()); - - int left = x + glyph.bounds().x(); - int right = x + glyph.bounds().x() + glyph.bounds().width(); - int bottom = y + glyph.bounds().y(); - int top = y + glyph.bounds().y() + glyph.bounds().height(); - - float scale = QApplication::desktop()->windowHandle()->devicePixelRatio() / IMAGE_SIZE; - float ls = glyph.location().x() * scale; - float rs = (glyph.location().x() + glyph.bounds().width()) * scale; - float bt = glyph.location().y() * scale; - float tt = (glyph.location().y() + glyph.bounds().height()) * scale; - - glBegin(GL_QUADS); - glTexCoord2f(ls, bt); - glVertex2f(left, bottom); - glTexCoord2f(rs, bt); - glVertex2f(right, bottom); - glTexCoord2f(rs, tt); - glVertex2f(right, top); - glTexCoord2f(ls, tt); - glVertex2f(left, top); - glEnd(); -/* - const int NUM_COORDS_PER_GLYPH = 16; - float vertexBuffer[NUM_COORDS_PER_GLYPH] = { ls, bt, left, bottom, rs, bt, right, bottom, rs, tt, right, top, ls, tt, left, top }; - gpu::Buffer::Size offset = sizeof(vertexBuffer)*_numGlyphsBatched; - if ((offset + sizeof(vertexBuffer)) > _glyphsBuffer.getSize()) { - _glyphsBuffer.append(sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); - } else { - _glyphsBuffer.setSubData(offset, sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); - } - _numGlyphsBatched++; -*/ - x += glyph.width(); - } - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); - - // executeDrawBatch(); - // clearDrawBatch(); - - return maxHeight; -} - -int TextRenderer::computeWidth(char ch) -{ - return getGlyph(ch).width(); -} - -int TextRenderer::computeWidth(const char* str) -{ - int width = 0; - for (const char* ch = str; *ch != 0; ch++) { - width += computeWidth(*ch); - } - return width; -} - -TextRenderer::TextRenderer(const Properties& properties) : - _font(properties.font), - _metrics(_font), - _effectType(properties.effect), - _effectThickness(properties.effectThickness), - _x(IMAGE_SIZE), - _y(IMAGE_SIZE), - _rowHeight(0), - _color(properties.color), - _glyphsBuffer(), - _numGlyphsBatched(0) -{ - _font.setKerning(false); -} - -const Glyph& TextRenderer::getGlyph(char c) { - Glyph& glyph = _glyphs[c]; - if (glyph.isValid()) { - return glyph; - } - // we use 'J' as a representative size for the solid block character - QChar ch = (c == SOLID_BLOCK_CHAR) ? QChar('J') : QChar(c); - QRect baseBounds = _metrics.boundingRect(ch); - if (baseBounds.isEmpty()) { - glyph = Glyph(0, QPoint(), QRect(), _metrics.width(ch)); - return glyph; - } - // grow the bounds to account for effect, if any - if (_effectType == SHADOW_EFFECT) { - baseBounds.adjust(-_effectThickness, 0, 0, _effectThickness); - - } else if (_effectType == OUTLINE_EFFECT) { - baseBounds.adjust(-_effectThickness, -_effectThickness, _effectThickness, _effectThickness); - } - - // grow the bounds to account for antialiasing - baseBounds.adjust(-1, -1, 1, 1); - - // adjust bounds for device pixel scaling - float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio(); - QRect bounds(baseBounds.x() * ratio, baseBounds.y() * ratio, baseBounds.width() * ratio, baseBounds.height() * ratio); - - if (_x + bounds.width() > IMAGE_SIZE) { - // we can't fit it on the current row; move to next - _y += _rowHeight; - _x = _rowHeight = 0; - } - if (_y + bounds.height() > IMAGE_SIZE) { - // can't fit it on current texture; make a new one - glGenTextures(1, &_currentTextureID); - _x = _y = _rowHeight = 0; - - glBindTexture(GL_TEXTURE_2D, _currentTextureID); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IMAGE_SIZE, IMAGE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - _allTextureIDs.append(_currentTextureID); - - } else { - glBindTexture(GL_TEXTURE_2D, _currentTextureID); - } - // render the glyph into an image and copy it into the texture - QImage image(bounds.width(), bounds.height(), QImage::Format_ARGB32); - if (c == SOLID_BLOCK_CHAR) { - image.fill(_color); - - } else { - image.fill(0); - QPainter painter(&image); - QFont font = _font; - if (ratio == 1.0f) { - painter.setFont(_font); - } else { - QFont enlargedFont = _font; - enlargedFont.setPointSize(_font.pointSize() * ratio); - painter.setFont(enlargedFont); - } - if (_effectType == SHADOW_EFFECT) { - for (int i = 0; i < _effectThickness * ratio; i++) { - painter.drawText(-bounds.x() - 1 - i, -bounds.y() + 1 + i, ch); - } - } else if (_effectType == OUTLINE_EFFECT) { - QPainterPath path; - QFont font = _font; - font.setStyleStrategy(QFont::ForceOutline); - path.addText(-bounds.x() - 0.5, -bounds.y() + 0.5, font, ch); - QPen pen; - pen.setWidth(_effectThickness * ratio); - pen.setJoinStyle(Qt::RoundJoin); - pen.setCapStyle(Qt::RoundCap); - painter.setPen(pen); - painter.setRenderHint(QPainter::Antialiasing); - painter.drawPath(path); - } - painter.setPen(_color); - painter.drawText(-bounds.x(), -bounds.y(), ch); - } - glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, bounds.width(), bounds.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.constBits()); - - glyph = Glyph(_currentTextureID, QPoint(_x / ratio, _y / ratio), baseBounds, _metrics.width(ch)); - _x += bounds.width(); - _rowHeight = qMax(_rowHeight, bounds.height()); - - glBindTexture(GL_TEXTURE_2D, 0); - return glyph; -} - -void TextRenderer::executeDrawBatch() { - if (_numGlyphsBatched<=0) { - return; - } - - glEnable(GL_TEXTURE_2D); - - GLuint textureID = 0; - glBindTexture(GL_TEXTURE_2D, textureID); - - gpu::backend::syncGPUObject(_glyphsBuffer); - GLuint vbo = _glyphsBuffer.getGLBufferObject(); - - glBindBuffer(GL_ARRAY_BUFFER, vbo); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - const int NUM_POS_COORDS = 2; - const int NUM_TEX_COORDS = 2; - const int VERTEX_STRIDE = (NUM_POS_COORDS + NUM_TEX_COORDS) * sizeof(float); - const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); - glVertexPointer(2, GL_FLOAT, VERTEX_STRIDE, 0); - glTexCoordPointer(2, GL_FLOAT, VERTEX_STRIDE, (GLvoid*) VERTEX_TEXCOORD_OFFSET ); - - glDrawArrays(GL_QUADS, 0, _numGlyphsBatched * 4); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); - -} - -void TextRenderer::clearDrawBatch() { - _numGlyphsBatched = 0; -} - -QHash TextRenderer::_instances; - -Glyph::Glyph(int textureID, const QPoint& location, const QRect& bounds, int width) : - _textureID(textureID), _location(location), _bounds(bounds), _width(width) { -} - -using namespace gpu; - -Buffer::Size Buffer::Sysmem::allocateMemory(Byte** dataAllocated, Size size) { - if ( !dataAllocated ) { - qWarning() << "Buffer::Sysmem::allocateMemory() : Must have a valid dataAllocated pointer."; - return NOT_ALLOCATED; - } - - // Try to allocate if needed - Size newSize = 0; - if (size > 0) { - // Try allocating as much as the required size + one block of memory - newSize = size; - (*dataAllocated) = new Byte[newSize]; - // Failed? - if (!(*dataAllocated)) { - qWarning() << "Buffer::Sysmem::allocate() : Can't allocate a system memory buffer of " << newSize << "bytes. Fails to create the buffer Sysmem."; - return NOT_ALLOCATED; - } - } - - // Return what's actually allocated - return newSize; -} - -void Buffer::Sysmem::deallocateMemory(Byte* dataAllocated, Size size) { - if (dataAllocated) { - delete[] dataAllocated; - } -} - -Buffer::Sysmem::Sysmem() : - _data(NULL), - _size(0), - _stamp(0) -{ -} - -Buffer::Sysmem::Sysmem(Size size, const Byte* bytes) : - _data(NULL), - _size(0), - _stamp(0) -{ - if (size > 0) { - _size = allocateMemory(&_data, size); - if (_size >= size) { - if (bytes) { - memcpy(_data, bytes, size); - } - } - } -} - -Buffer::Sysmem::~Sysmem() { - deallocateMemory( _data, _size ); - _data = NULL; - _size = 0; +// +// TextRenderer.cpp +// interface/src/ui +// +// Created by Andrzej Kapolka on 4/24/13. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "InterfaceConfig.h" +#include "TextRenderer.h" + +#include "glm/glm.hpp" +#include + + +// the width/height of the cached glyph textures +const int IMAGE_SIZE = 256; + +static uint qHash(const TextRenderer::Properties& key, uint seed = 0) { + // can be switched to qHash(key.font, seed) when we require Qt 5.3+ + return qHash(key.font.family(), qHash(key.font.pointSize(), seed)); } -Buffer::Size Buffer::Sysmem::allocate(Size size) { - if (size != _size) { - Byte* newData = 0; - Size newSize = 0; - if (size > 0) { - Size allocated = allocateMemory(&newData, size); - if (allocated == NOT_ALLOCATED) { - // early exit because allocation failed - return 0; - } - newSize = allocated; - } - // Allocation was successful, can delete previous data - deallocateMemory(_data, _size); - _data = newData; - _size = newSize; - _stamp++; - } - return _size; +static bool operator==(const TextRenderer::Properties& p1, const TextRenderer::Properties& p2) { + return p1.font == p2.font && p1.effect == p2.effect && p1.effectThickness == p2.effectThickness && p1.color == p2.color; } -Buffer::Size Buffer::Sysmem::resize(Size size) { - if (size != _size) { - Byte* newData = 0; - Size newSize = 0; - if (size > 0) { - Size allocated = allocateMemory(&newData, size); - if (allocated == NOT_ALLOCATED) { - // early exit because allocation failed - return _size; - } - newSize = allocated; - // Restore back data from old buffer in the new one - if (_data) { - Size copySize = ((newSize < _size)? newSize: _size); - memcpy( newData, _data, copySize); - } - } - // Reallocation was successful, can delete previous data - deallocateMemory(_data, _size); - _data = newData; - _size = newSize; - _stamp++; +TextRenderer* TextRenderer::getInstance(const char* family, int pointSize, int weight, bool italic, + EffectType effect, int effectThickness, const QColor& color) { + Properties properties = { QFont(family, pointSize, weight, italic), effect, effectThickness, color }; + TextRenderer*& instance = _instances[properties]; + if (!instance) { + instance = new TextRenderer(properties); } - return _size; + return instance; } -Buffer::Size Buffer::Sysmem::setData( Size size, const Byte* bytes ) { - if (allocate(size) == size) { - if (bytes) { - memcpy( _data, bytes, _size ); - _stamp++; - } - } - return _size; +TextRenderer::~TextRenderer() { + glDeleteTextures(_allTextureIDs.size(), _allTextureIDs.constData()); } -Buffer::Size Buffer::Sysmem::setSubData( Size offset, Size size, const Byte* bytes) { - if (((offset + size) <= getSize()) && bytes) { - memcpy( _data + offset, bytes, size ); - _stamp++; - return size; +int TextRenderer::calculateHeight(const char* str) { + int maxHeight = 0; + for (const char* ch = str; *ch != 0; ch++) { + const Glyph& glyph = getGlyph(*ch); + if (glyph.textureID() == 0) { + continue; + } + + if (glyph.bounds().height() > maxHeight) { + maxHeight = glyph.bounds().height(); + } } - return 0; -} - -Buffer::Size Buffer::Sysmem::append(Size size, const Byte* bytes) { - if (size > 0) { - Size oldSize = getSize(); - Size totalSize = oldSize + size; - if (resize(totalSize) == totalSize) { - return setSubData(oldSize, size, bytes); - } - } - return 0; -} - -Buffer::Buffer() : - _sysmem(NULL), - _gpuObject(NULL) { - _sysmem = new Sysmem(); -} - -Buffer::~Buffer() { - if (_sysmem) { - delete _sysmem; - _sysmem = 0; - } - if (_gpuObject) { - delete _gpuObject; - _gpuObject = 0; - } -} - -Buffer::Size Buffer::resize(Size size) { - return editSysmem().resize(size); -} - -Buffer::Size Buffer::setData(Size size, const Byte* data) { - return editSysmem().setData(size, data); -} - -Buffer::Size Buffer::setSubData(Size offset, Size size, const Byte* data) { - return editSysmem().setSubData( offset, size, data); -} - -Buffer::Size Buffer::append(Size size, const Byte* data) { - return editSysmem().append( size, data); -} - -namespace gpu { -namespace backend { - -void syncGPUObject(const Buffer& buffer) { - BufferObject* object = buffer.getGPUObject(); - - if (object && (object->_stamp == buffer.getSysmem().getStamp())) { - return; - } - - // need to have a gpu object? - if (!object) { - object = new BufferObject(); - glGenBuffers(1, &object->_buffer); - buffer.setGPUObject(object); - } - - // Now let's update the content of the bo with the sysmem version - //if (object->_size < buffer.getSize()) { - glBindBuffer(GL_COPY_WRITE_BUFFER, object->_buffer); - glBufferData(GL_COPY_WRITE_BUFFER, buffer.getSysmem().getSize(), buffer.getSysmem().read(), GL_STATIC_DRAW); - glBindBuffer(GL_COPY_WRITE_BUFFER, 0); - object->_stamp = buffer.getSysmem().getStamp(); - object->_size = buffer.getSysmem().getSize(); - //} -} - -}; -}; \ No newline at end of file + return maxHeight; +} + +int TextRenderer::draw(int x, int y, const char* str) { + // Grab the current color + float currentColor[4]; + glGetFloatv(GL_CURRENT_COLOR, currentColor); + int compactColor = ((int( currentColor[0] * 255.f) & 0xFF)) | + ((int( currentColor[1] * 255.f) & 0xFF) << 8) | + ((int( currentColor[2] * 255.f) & 0xFF) << 16) | + ((int( currentColor[3] * 255.f) & 0xFF) << 24); + + //glEnable(GL_TEXTURE_2D); + + int maxHeight = 0; + for (const char* ch = str; *ch != 0; ch++) { + const Glyph& glyph = getGlyph(*ch); + if (glyph.textureID() == 0) { + x += glyph.width(); + continue; + } + + if (glyph.bounds().height() > maxHeight) { + maxHeight = glyph.bounds().height(); + } + //glBindTexture(GL_TEXTURE_2D, glyph.textureID()); + + int left = x + glyph.bounds().x(); + int right = x + glyph.bounds().x() + glyph.bounds().width(); + int bottom = y + glyph.bounds().y(); + int top = y + glyph.bounds().y() + glyph.bounds().height(); + + glm::vec2 leftBottom = glm::vec2(float(left), float(bottom)); + glm::vec2 rightTop = glm::vec2(float(right), float(top)); + + float scale = QApplication::desktop()->windowHandle()->devicePixelRatio() / IMAGE_SIZE; + float ls = glyph.location().x() * scale; + float rs = (glyph.location().x() + glyph.bounds().width()) * scale; + float bt = glyph.location().y() * scale; + float tt = (glyph.location().y() + glyph.bounds().height()) * scale; +/* + glBegin(GL_QUADS); + glTexCoord2f(ls, bt); + glVertex2f(left, bottom); + glTexCoord2f(rs, bt); + glVertex2f(right, bottom); + glTexCoord2f(rs, tt); + glVertex2f(right, top); + glTexCoord2f(ls, tt); + glVertex2f(left, top); + glEnd(); +*/ + + const int NUM_COORDS_SCALARS_PER_GLYPH = 16; + float vertexBuffer[NUM_COORDS_SCALARS_PER_GLYPH] = { leftBottom.x, leftBottom.y, ls, bt, + rightTop.x, leftBottom.y, rs, bt, + rightTop.x, rightTop.y, rs, tt, + leftBottom.x, rightTop.y, ls, tt, }; + + const int NUM_COLOR_SCALARS_PER_GLYPH = 4; + unsigned int colorBuffer[NUM_COLOR_SCALARS_PER_GLYPH] = { compactColor, compactColor, compactColor, compactColor }; + + gpu::Buffer::Size offset = sizeof(vertexBuffer)*_numGlyphsBatched; + gpu::Buffer::Size colorOffset = sizeof(colorBuffer)*_numGlyphsBatched; + if ((offset + sizeof(vertexBuffer)) > _glyphsBuffer.getSize()) { + _glyphsBuffer.append(sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); + _glyphsColorBuffer.append(sizeof(colorBuffer), (gpu::Buffer::Byte*) colorBuffer); + } else { + _glyphsBuffer.setSubData(offset, sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); + _glyphsColorBuffer.setSubData(colorOffset, sizeof(colorBuffer), (gpu::Buffer::Byte*) colorBuffer); + } + _numGlyphsBatched++; + + x += glyph.width(); + } + + // TODO: remove these calls once we move to a full batched rendering of the text, for now, one draw call per draw() function call + drawBatch(); + clearBatch(); + + // glBindTexture(GL_TEXTURE_2D, 0); + // glDisable(GL_TEXTURE_2D); + + return maxHeight; +} + +int TextRenderer::computeWidth(char ch) +{ + return getGlyph(ch).width(); +} + +int TextRenderer::computeWidth(const char* str) +{ + int width = 0; + for (const char* ch = str; *ch != 0; ch++) { + width += computeWidth(*ch); + } + return width; +} + +TextRenderer::TextRenderer(const Properties& properties) : + _font(properties.font), + _metrics(_font), + _effectType(properties.effect), + _effectThickness(properties.effectThickness), + _x(IMAGE_SIZE), + _y(IMAGE_SIZE), + _rowHeight(0), + _color(properties.color), + _glyphsBuffer(), + _numGlyphsBatched(0) +{ + _font.setKerning(false); +} + +const Glyph& TextRenderer::getGlyph(char c) { + Glyph& glyph = _glyphs[c]; + if (glyph.isValid()) { + return glyph; + } + // we use 'J' as a representative size for the solid block character + QChar ch = (c == SOLID_BLOCK_CHAR) ? QChar('J') : QChar(c); + QRect baseBounds = _metrics.boundingRect(ch); + if (baseBounds.isEmpty()) { + glyph = Glyph(0, QPoint(), QRect(), _metrics.width(ch)); + return glyph; + } + // grow the bounds to account for effect, if any + if (_effectType == SHADOW_EFFECT) { + baseBounds.adjust(-_effectThickness, 0, 0, _effectThickness); + + } else if (_effectType == OUTLINE_EFFECT) { + baseBounds.adjust(-_effectThickness, -_effectThickness, _effectThickness, _effectThickness); + } + + // grow the bounds to account for antialiasing + baseBounds.adjust(-1, -1, 1, 1); + + // adjust bounds for device pixel scaling + float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio(); + QRect bounds(baseBounds.x() * ratio, baseBounds.y() * ratio, baseBounds.width() * ratio, baseBounds.height() * ratio); + + if (_x + bounds.width() > IMAGE_SIZE) { + // we can't fit it on the current row; move to next + _y += _rowHeight; + _x = _rowHeight = 0; + } + if (_y + bounds.height() > IMAGE_SIZE) { + // can't fit it on current texture; make a new one + glGenTextures(1, &_currentTextureID); + _x = _y = _rowHeight = 0; + + glBindTexture(GL_TEXTURE_2D, _currentTextureID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IMAGE_SIZE, IMAGE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + _allTextureIDs.append(_currentTextureID); + + } else { + glBindTexture(GL_TEXTURE_2D, _currentTextureID); + } + // render the glyph into an image and copy it into the texture + QImage image(bounds.width(), bounds.height(), QImage::Format_ARGB32); + if (c == SOLID_BLOCK_CHAR) { + image.fill(_color); + + } else { + image.fill(0); + QPainter painter(&image); + QFont font = _font; + if (ratio == 1.0f) { + painter.setFont(_font); + } else { + QFont enlargedFont = _font; + enlargedFont.setPointSize(_font.pointSize() * ratio); + painter.setFont(enlargedFont); + } + if (_effectType == SHADOW_EFFECT) { + for (int i = 0; i < _effectThickness * ratio; i++) { + painter.drawText(-bounds.x() - 1 - i, -bounds.y() + 1 + i, ch); + } + } else if (_effectType == OUTLINE_EFFECT) { + QPainterPath path; + QFont font = _font; + font.setStyleStrategy(QFont::ForceOutline); + path.addText(-bounds.x() - 0.5, -bounds.y() + 0.5, font, ch); + QPen pen; + pen.setWidth(_effectThickness * ratio); + pen.setJoinStyle(Qt::RoundJoin); + pen.setCapStyle(Qt::RoundCap); + painter.setPen(pen); + painter.setRenderHint(QPainter::Antialiasing); + painter.drawPath(path); + } + painter.setPen(_color); + painter.drawText(-bounds.x(), -bounds.y(), ch); + } + glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, bounds.width(), bounds.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.constBits()); + + glyph = Glyph(_currentTextureID, QPoint(_x / ratio, _y / ratio), baseBounds, _metrics.width(ch)); + _x += bounds.width(); + _rowHeight = qMax(_rowHeight, bounds.height()); + + glBindTexture(GL_TEXTURE_2D, 0); + return glyph; +} + +void TextRenderer::drawBatch() { + if (_numGlyphsBatched<=0) { + return; + } + + // TODO: Right now the drawBatch is called while calling the draw() function but in the future we'll need to apply the correct transform stack + /* + GLint matrixMode; + glGetIntegerv(GL_MATRIX_MODE, &matrixMode); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + */ + + glEnable(GL_TEXTURE_2D); + // TODO: Apply the correct font atlas texture, for now only one texture per TextRenderer so it should be good + glBindTexture(GL_TEXTURE_2D, _currentTextureID); + + GLuint vbo = _glyphsBuffer.getGLBufferObject(); + GLuint colorvbo = _glyphsColorBuffer.getGLBufferObject(); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + const int NUM_POS_COORDS = 2; + const int NUM_TEX_COORDS = 2; + const int VERTEX_STRIDE = (NUM_POS_COORDS + NUM_TEX_COORDS) * sizeof(float); + const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glVertexPointer(2, GL_FLOAT, VERTEX_STRIDE, 0); + glTexCoordPointer(2, GL_FLOAT, VERTEX_STRIDE, (GLvoid*) VERTEX_TEXCOORD_OFFSET ); + + glBindBuffer(GL_ARRAY_BUFFER, colorvbo); + glColorPointer(4, GL_UNSIGNED_BYTE, 0, (GLvoid*) 0 ); + + glDrawArrays(GL_QUADS, 0, _numGlyphsBatched * 4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + // TODO: Right now the drawBatch is called while calling the draw() function but in the future we'll need to apply the correct transform stack + /* + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(matrixMode); + */ +} + +void TextRenderer::clearBatch() { + _numGlyphsBatched = 0; +} + +QHash TextRenderer::_instances; + +Glyph::Glyph(int textureID, const QPoint& location, const QRect& bounds, int width) : + _textureID(textureID), _location(location), _bounds(bounds), _width(width) { +} diff --git a/interface/src/ui/TextRenderer.h b/interface/src/ui/TextRenderer.h index ec1c9a088e..70283eedd8 100644 --- a/interface/src/ui/TextRenderer.h +++ b/interface/src/ui/TextRenderer.h @@ -19,7 +19,7 @@ #include #include -#include +#include "gpu/Resource.h" #include "InterfaceConfig.h" @@ -37,138 +37,6 @@ const char SOLID_BLOCK_CHAR = 127; class Glyph; -namespace gpu { -class Buffer; -typedef int Stamp; - -namespace backend { - - class BufferObject { - public: - Stamp _stamp; - GLuint _buffer; - GLuint _size; - - BufferObject() : - _stamp(0), - _buffer(0), - _size(0) - {} - }; - void syncGPUObject(const Buffer& buffer); -}; - -class Buffer { -public: - - typedef unsigned char Byte; - typedef unsigned int Size; - - static const Size MIN_ALLOCATION_BLOCK_SIZE = 256; - static const Size NOT_ALLOCATED = -1; - - Buffer(); - Buffer(const Buffer& buf ); - ~Buffer(); - - // The size in bytes of data stored in the buffer - inline Size getSize() const { return getSysmem().getSize(); } - inline const Byte* getData() const { return getSysmem().read(); } - - // Resize the buffer - // Keep previous data [0 to min(pSize, mSize)] - Size resize(Size pSize); - - // Assign data bytes and size (allocate for size, then copy bytes if exists) - Size setData(Size size, const Byte* data); - - // Assign data bytes and size (allocate for size, then copy bytes if exists) - Size setSubData(Size offset, Size size, const Byte* data); - - // Append new data at the end of the current buffer - // do a resize( size + getSIze) and copy the new data - // \return the number of bytes copied - Size append(Size size, const Byte* data); - - // this is a temporary hack so the current rendering code can access the underneath gl Buffer Object - // TODO: remove asap, when the backend is doing more of the gl features - inline GLuint getGLBufferObject() const { backend::syncGPUObject(*this); return getGPUObject()->_buffer; } - -protected: - - // Sysmem is the underneath cache for the data in ram. - class Sysmem { - public: - - Sysmem(); - Sysmem(Size size , const Byte* bytes); - ~Sysmem(); - - Size getSize() const { return _size; } - - // Allocate the byte array - // \param pSize The nb of bytes to allocate, if already exist, content is lost. - // \return The nb of bytes allocated, nothing if allready the appropriate size. - Size allocate(Size pSize); - - // Resize the byte array - // Keep previous data [0 to min(pSize, mSize)] - Size resize(Size pSize); - - // Assign data bytes and size (allocate for size, then copy bytes if exists) - Size setData( Size size, const Byte* bytes ); - - // Update Sub data, - // doesn't allocate and only copy size * bytes at the offset location - // only if all fits in the existing allocated buffer - Size setSubData( Size offset, Size size, const Byte* bytes); - - // Append new data at the end of the current buffer - // do a resize( size + getSIze) and copy the new data - // \return the number of bytes copied - Size append(Size size, const Byte* data); - - // Access the byte array. - // The edit version allow to map data. - inline const Byte* read() const { return _data; } - inline Byte* edit() { _stamp++; return _data; } - - template< typename T > - const T* read() const { return reinterpret_cast< T* > ( _data ); } - template< typename T > - T* edit() const { _stamp++; return reinterpret_cast< T* > ( _data ); } - - // Access the current version of the sysmem, used to compare if copies are in sync - inline Stamp getStamp() const { return _stamp; } - - static Size allocateMemory(Byte** memAllocated, Size size); - static void deallocateMemory(Byte* memDeallocated, Size size); - - private: - Sysmem(const Sysmem& sysmem) {} - Sysmem &operator=(const Sysmem &other) {return *this;} - - Byte* _data; - Size _size; - Stamp _stamp; - }; - - Sysmem* _sysmem; - - typedef backend::BufferObject GPUObject; - mutable backend::BufferObject* _gpuObject; - - inline const Sysmem& getSysmem() const { assert(_sysmem); return (*_sysmem); } - inline Sysmem& editSysmem() { assert(_sysmem); return (*_sysmem); } - - inline GPUObject* getGPUObject() const { return _gpuObject; } - inline void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; } - - friend void backend::syncGPUObject(const Buffer& buffer); -}; - -}; - class TextRenderer { public: @@ -198,8 +66,8 @@ public: int computeWidth(char ch); int computeWidth(const char* str); - void executeDrawBatch(); - void clearDrawBatch(); + void drawBatch(); + void clearBatch(); private: TextRenderer(const Properties& properties); @@ -238,6 +106,7 @@ private: // Graphics Buffer containing the current accumulated glyphs to render gpu::Buffer _glyphsBuffer; + gpu::Buffer _glyphsColorBuffer; int _numGlyphsBatched; static QHash _instances; From adc4e8c5134b58012f4e433769e1a05e52ecc4b5 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 9 Oct 2014 10:39:28 -0700 Subject: [PATCH 09/33] Fixing unix file format issues with msdev2010 --- interface/src/gpu/Resource.cpp | 2 +- interface/src/gpu/Resource.h | 2 +- interface/src/ui/TextRenderer.cpp | 686 +++++++++++++++--------------- 3 files changed, 345 insertions(+), 345 deletions(-) diff --git a/interface/src/gpu/Resource.cpp b/interface/src/gpu/Resource.cpp index 890039e429..5e5f8d8ae4 100644 --- a/interface/src/gpu/Resource.cpp +++ b/interface/src/gpu/Resource.cpp @@ -216,4 +216,4 @@ void syncGPUObject(const Buffer& buffer) { } }; -}; \ No newline at end of file +}; diff --git a/interface/src/gpu/Resource.h b/interface/src/gpu/Resource.h index 32e8e454e8..e74f2d7697 100644 --- a/interface/src/gpu/Resource.h +++ b/interface/src/gpu/Resource.h @@ -161,4 +161,4 @@ protected: }; -#endif \ No newline at end of file +#endif diff --git a/interface/src/ui/TextRenderer.cpp b/interface/src/ui/TextRenderer.cpp index d8d1b17da7..9cb88f1f03 100644 --- a/interface/src/ui/TextRenderer.cpp +++ b/interface/src/ui/TextRenderer.cpp @@ -1,343 +1,343 @@ -// -// TextRenderer.cpp -// interface/src/ui -// -// Created by Andrzej Kapolka on 4/24/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "InterfaceConfig.h" -#include "TextRenderer.h" - -#include "glm/glm.hpp" -#include - - -// the width/height of the cached glyph textures -const int IMAGE_SIZE = 256; - -static uint qHash(const TextRenderer::Properties& key, uint seed = 0) { - // can be switched to qHash(key.font, seed) when we require Qt 5.3+ - return qHash(key.font.family(), qHash(key.font.pointSize(), seed)); -} - -static bool operator==(const TextRenderer::Properties& p1, const TextRenderer::Properties& p2) { - return p1.font == p2.font && p1.effect == p2.effect && p1.effectThickness == p2.effectThickness && p1.color == p2.color; -} - -TextRenderer* TextRenderer::getInstance(const char* family, int pointSize, int weight, bool italic, - EffectType effect, int effectThickness, const QColor& color) { - Properties properties = { QFont(family, pointSize, weight, italic), effect, effectThickness, color }; - TextRenderer*& instance = _instances[properties]; - if (!instance) { - instance = new TextRenderer(properties); - } - return instance; -} - -TextRenderer::~TextRenderer() { - glDeleteTextures(_allTextureIDs.size(), _allTextureIDs.constData()); -} - -int TextRenderer::calculateHeight(const char* str) { - int maxHeight = 0; - for (const char* ch = str; *ch != 0; ch++) { - const Glyph& glyph = getGlyph(*ch); - if (glyph.textureID() == 0) { - continue; - } - - if (glyph.bounds().height() > maxHeight) { - maxHeight = glyph.bounds().height(); - } - } - return maxHeight; -} - -int TextRenderer::draw(int x, int y, const char* str) { - // Grab the current color - float currentColor[4]; - glGetFloatv(GL_CURRENT_COLOR, currentColor); - int compactColor = ((int( currentColor[0] * 255.f) & 0xFF)) | - ((int( currentColor[1] * 255.f) & 0xFF) << 8) | - ((int( currentColor[2] * 255.f) & 0xFF) << 16) | - ((int( currentColor[3] * 255.f) & 0xFF) << 24); - - //glEnable(GL_TEXTURE_2D); - - int maxHeight = 0; - for (const char* ch = str; *ch != 0; ch++) { - const Glyph& glyph = getGlyph(*ch); - if (glyph.textureID() == 0) { - x += glyph.width(); - continue; - } - - if (glyph.bounds().height() > maxHeight) { - maxHeight = glyph.bounds().height(); - } - //glBindTexture(GL_TEXTURE_2D, glyph.textureID()); - - int left = x + glyph.bounds().x(); - int right = x + glyph.bounds().x() + glyph.bounds().width(); - int bottom = y + glyph.bounds().y(); - int top = y + glyph.bounds().y() + glyph.bounds().height(); - - glm::vec2 leftBottom = glm::vec2(float(left), float(bottom)); - glm::vec2 rightTop = glm::vec2(float(right), float(top)); - - float scale = QApplication::desktop()->windowHandle()->devicePixelRatio() / IMAGE_SIZE; - float ls = glyph.location().x() * scale; - float rs = (glyph.location().x() + glyph.bounds().width()) * scale; - float bt = glyph.location().y() * scale; - float tt = (glyph.location().y() + glyph.bounds().height()) * scale; -/* - glBegin(GL_QUADS); - glTexCoord2f(ls, bt); - glVertex2f(left, bottom); - glTexCoord2f(rs, bt); - glVertex2f(right, bottom); - glTexCoord2f(rs, tt); - glVertex2f(right, top); - glTexCoord2f(ls, tt); - glVertex2f(left, top); - glEnd(); -*/ - - const int NUM_COORDS_SCALARS_PER_GLYPH = 16; - float vertexBuffer[NUM_COORDS_SCALARS_PER_GLYPH] = { leftBottom.x, leftBottom.y, ls, bt, - rightTop.x, leftBottom.y, rs, bt, - rightTop.x, rightTop.y, rs, tt, - leftBottom.x, rightTop.y, ls, tt, }; - - const int NUM_COLOR_SCALARS_PER_GLYPH = 4; - unsigned int colorBuffer[NUM_COLOR_SCALARS_PER_GLYPH] = { compactColor, compactColor, compactColor, compactColor }; - - gpu::Buffer::Size offset = sizeof(vertexBuffer)*_numGlyphsBatched; - gpu::Buffer::Size colorOffset = sizeof(colorBuffer)*_numGlyphsBatched; - if ((offset + sizeof(vertexBuffer)) > _glyphsBuffer.getSize()) { - _glyphsBuffer.append(sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); - _glyphsColorBuffer.append(sizeof(colorBuffer), (gpu::Buffer::Byte*) colorBuffer); - } else { - _glyphsBuffer.setSubData(offset, sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); - _glyphsColorBuffer.setSubData(colorOffset, sizeof(colorBuffer), (gpu::Buffer::Byte*) colorBuffer); - } - _numGlyphsBatched++; - - x += glyph.width(); - } - - // TODO: remove these calls once we move to a full batched rendering of the text, for now, one draw call per draw() function call - drawBatch(); - clearBatch(); - - // glBindTexture(GL_TEXTURE_2D, 0); - // glDisable(GL_TEXTURE_2D); - - return maxHeight; -} - -int TextRenderer::computeWidth(char ch) -{ - return getGlyph(ch).width(); -} - -int TextRenderer::computeWidth(const char* str) -{ - int width = 0; - for (const char* ch = str; *ch != 0; ch++) { - width += computeWidth(*ch); - } - return width; -} - -TextRenderer::TextRenderer(const Properties& properties) : - _font(properties.font), - _metrics(_font), - _effectType(properties.effect), - _effectThickness(properties.effectThickness), - _x(IMAGE_SIZE), - _y(IMAGE_SIZE), - _rowHeight(0), - _color(properties.color), - _glyphsBuffer(), - _numGlyphsBatched(0) -{ - _font.setKerning(false); -} - -const Glyph& TextRenderer::getGlyph(char c) { - Glyph& glyph = _glyphs[c]; - if (glyph.isValid()) { - return glyph; - } - // we use 'J' as a representative size for the solid block character - QChar ch = (c == SOLID_BLOCK_CHAR) ? QChar('J') : QChar(c); - QRect baseBounds = _metrics.boundingRect(ch); - if (baseBounds.isEmpty()) { - glyph = Glyph(0, QPoint(), QRect(), _metrics.width(ch)); - return glyph; - } - // grow the bounds to account for effect, if any - if (_effectType == SHADOW_EFFECT) { - baseBounds.adjust(-_effectThickness, 0, 0, _effectThickness); - - } else if (_effectType == OUTLINE_EFFECT) { - baseBounds.adjust(-_effectThickness, -_effectThickness, _effectThickness, _effectThickness); - } - - // grow the bounds to account for antialiasing - baseBounds.adjust(-1, -1, 1, 1); - - // adjust bounds for device pixel scaling - float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio(); - QRect bounds(baseBounds.x() * ratio, baseBounds.y() * ratio, baseBounds.width() * ratio, baseBounds.height() * ratio); - - if (_x + bounds.width() > IMAGE_SIZE) { - // we can't fit it on the current row; move to next - _y += _rowHeight; - _x = _rowHeight = 0; - } - if (_y + bounds.height() > IMAGE_SIZE) { - // can't fit it on current texture; make a new one - glGenTextures(1, &_currentTextureID); - _x = _y = _rowHeight = 0; - - glBindTexture(GL_TEXTURE_2D, _currentTextureID); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IMAGE_SIZE, IMAGE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - _allTextureIDs.append(_currentTextureID); - - } else { - glBindTexture(GL_TEXTURE_2D, _currentTextureID); - } - // render the glyph into an image and copy it into the texture - QImage image(bounds.width(), bounds.height(), QImage::Format_ARGB32); - if (c == SOLID_BLOCK_CHAR) { - image.fill(_color); - - } else { - image.fill(0); - QPainter painter(&image); - QFont font = _font; - if (ratio == 1.0f) { - painter.setFont(_font); - } else { - QFont enlargedFont = _font; - enlargedFont.setPointSize(_font.pointSize() * ratio); - painter.setFont(enlargedFont); - } - if (_effectType == SHADOW_EFFECT) { - for (int i = 0; i < _effectThickness * ratio; i++) { - painter.drawText(-bounds.x() - 1 - i, -bounds.y() + 1 + i, ch); - } - } else if (_effectType == OUTLINE_EFFECT) { - QPainterPath path; - QFont font = _font; - font.setStyleStrategy(QFont::ForceOutline); - path.addText(-bounds.x() - 0.5, -bounds.y() + 0.5, font, ch); - QPen pen; - pen.setWidth(_effectThickness * ratio); - pen.setJoinStyle(Qt::RoundJoin); - pen.setCapStyle(Qt::RoundCap); - painter.setPen(pen); - painter.setRenderHint(QPainter::Antialiasing); - painter.drawPath(path); - } - painter.setPen(_color); - painter.drawText(-bounds.x(), -bounds.y(), ch); - } - glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, bounds.width(), bounds.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.constBits()); - - glyph = Glyph(_currentTextureID, QPoint(_x / ratio, _y / ratio), baseBounds, _metrics.width(ch)); - _x += bounds.width(); - _rowHeight = qMax(_rowHeight, bounds.height()); - - glBindTexture(GL_TEXTURE_2D, 0); - return glyph; -} - -void TextRenderer::drawBatch() { - if (_numGlyphsBatched<=0) { - return; - } - - // TODO: Right now the drawBatch is called while calling the draw() function but in the future we'll need to apply the correct transform stack - /* - GLint matrixMode; - glGetIntegerv(GL_MATRIX_MODE, &matrixMode); - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - */ - - glEnable(GL_TEXTURE_2D); - // TODO: Apply the correct font atlas texture, for now only one texture per TextRenderer so it should be good - glBindTexture(GL_TEXTURE_2D, _currentTextureID); - - GLuint vbo = _glyphsBuffer.getGLBufferObject(); - GLuint colorvbo = _glyphsColorBuffer.getGLBufferObject(); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - - const int NUM_POS_COORDS = 2; - const int NUM_TEX_COORDS = 2; - const int VERTEX_STRIDE = (NUM_POS_COORDS + NUM_TEX_COORDS) * sizeof(float); - const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); - - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glVertexPointer(2, GL_FLOAT, VERTEX_STRIDE, 0); - glTexCoordPointer(2, GL_FLOAT, VERTEX_STRIDE, (GLvoid*) VERTEX_TEXCOORD_OFFSET ); - - glBindBuffer(GL_ARRAY_BUFFER, colorvbo); - glColorPointer(4, GL_UNSIGNED_BYTE, 0, (GLvoid*) 0 ); - - glDrawArrays(GL_QUADS, 0, _numGlyphsBatched * 4); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); - - // TODO: Right now the drawBatch is called while calling the draw() function but in the future we'll need to apply the correct transform stack - /* - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glMatrixMode(matrixMode); - */ -} - -void TextRenderer::clearBatch() { - _numGlyphsBatched = 0; -} - -QHash TextRenderer::_instances; - -Glyph::Glyph(int textureID, const QPoint& location, const QRect& bounds, int width) : - _textureID(textureID), _location(location), _bounds(bounds), _width(width) { -} +// +// TextRenderer.cpp +// interface/src/ui +// +// Created by Andrzej Kapolka on 4/24/13. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "InterfaceConfig.h" +#include "TextRenderer.h" + +#include "glm/glm.hpp" +#include + + +// the width/height of the cached glyph textures +const int IMAGE_SIZE = 256; + +static uint qHash(const TextRenderer::Properties& key, uint seed = 0) { + // can be switched to qHash(key.font, seed) when we require Qt 5.3+ + return qHash(key.font.family(), qHash(key.font.pointSize(), seed)); +} + +static bool operator==(const TextRenderer::Properties& p1, const TextRenderer::Properties& p2) { + return p1.font == p2.font && p1.effect == p2.effect && p1.effectThickness == p2.effectThickness && p1.color == p2.color; +} + +TextRenderer* TextRenderer::getInstance(const char* family, int pointSize, int weight, bool italic, + EffectType effect, int effectThickness, const QColor& color) { + Properties properties = { QFont(family, pointSize, weight, italic), effect, effectThickness, color }; + TextRenderer*& instance = _instances[properties]; + if (!instance) { + instance = new TextRenderer(properties); + } + return instance; +} + +TextRenderer::~TextRenderer() { + glDeleteTextures(_allTextureIDs.size(), _allTextureIDs.constData()); +} + +int TextRenderer::calculateHeight(const char* str) { + int maxHeight = 0; + for (const char* ch = str; *ch != 0; ch++) { + const Glyph& glyph = getGlyph(*ch); + if (glyph.textureID() == 0) { + continue; + } + + if (glyph.bounds().height() > maxHeight) { + maxHeight = glyph.bounds().height(); + } + } + return maxHeight; +} + +int TextRenderer::draw(int x, int y, const char* str) { + // Grab the current color + float currentColor[4]; + glGetFloatv(GL_CURRENT_COLOR, currentColor); + int compactColor = ((int( currentColor[0] * 255.f) & 0xFF)) | + ((int( currentColor[1] * 255.f) & 0xFF) << 8) | + ((int( currentColor[2] * 255.f) & 0xFF) << 16) | + ((int( currentColor[3] * 255.f) & 0xFF) << 24); + + //glEnable(GL_TEXTURE_2D); + + int maxHeight = 0; + for (const char* ch = str; *ch != 0; ch++) { + const Glyph& glyph = getGlyph(*ch); + if (glyph.textureID() == 0) { + x += glyph.width(); + continue; + } + + if (glyph.bounds().height() > maxHeight) { + maxHeight = glyph.bounds().height(); + } + //glBindTexture(GL_TEXTURE_2D, glyph.textureID()); + + int left = x + glyph.bounds().x(); + int right = x + glyph.bounds().x() + glyph.bounds().width(); + int bottom = y + glyph.bounds().y(); + int top = y + glyph.bounds().y() + glyph.bounds().height(); + + glm::vec2 leftBottom = glm::vec2(float(left), float(bottom)); + glm::vec2 rightTop = glm::vec2(float(right), float(top)); + + float scale = QApplication::desktop()->windowHandle()->devicePixelRatio() / IMAGE_SIZE; + float ls = glyph.location().x() * scale; + float rs = (glyph.location().x() + glyph.bounds().width()) * scale; + float bt = glyph.location().y() * scale; + float tt = (glyph.location().y() + glyph.bounds().height()) * scale; +/* + glBegin(GL_QUADS); + glTexCoord2f(ls, bt); + glVertex2f(left, bottom); + glTexCoord2f(rs, bt); + glVertex2f(right, bottom); + glTexCoord2f(rs, tt); + glVertex2f(right, top); + glTexCoord2f(ls, tt); + glVertex2f(left, top); + glEnd(); +*/ + + const int NUM_COORDS_SCALARS_PER_GLYPH = 16; + float vertexBuffer[NUM_COORDS_SCALARS_PER_GLYPH] = { leftBottom.x, leftBottom.y, ls, bt, + rightTop.x, leftBottom.y, rs, bt, + rightTop.x, rightTop.y, rs, tt, + leftBottom.x, rightTop.y, ls, tt, }; + + const int NUM_COLOR_SCALARS_PER_GLYPH = 4; + unsigned int colorBuffer[NUM_COLOR_SCALARS_PER_GLYPH] = { compactColor, compactColor, compactColor, compactColor }; + + gpu::Buffer::Size offset = sizeof(vertexBuffer)*_numGlyphsBatched; + gpu::Buffer::Size colorOffset = sizeof(colorBuffer)*_numGlyphsBatched; + if ((offset + sizeof(vertexBuffer)) > _glyphsBuffer.getSize()) { + _glyphsBuffer.append(sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); + _glyphsColorBuffer.append(sizeof(colorBuffer), (gpu::Buffer::Byte*) colorBuffer); + } else { + _glyphsBuffer.setSubData(offset, sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); + _glyphsColorBuffer.setSubData(colorOffset, sizeof(colorBuffer), (gpu::Buffer::Byte*) colorBuffer); + } + _numGlyphsBatched++; + + x += glyph.width(); + } + + // TODO: remove these calls once we move to a full batched rendering of the text, for now, one draw call per draw() function call + drawBatch(); + clearBatch(); + + // glBindTexture(GL_TEXTURE_2D, 0); + // glDisable(GL_TEXTURE_2D); + + return maxHeight; +} + +int TextRenderer::computeWidth(char ch) +{ + return getGlyph(ch).width(); +} + +int TextRenderer::computeWidth(const char* str) +{ + int width = 0; + for (const char* ch = str; *ch != 0; ch++) { + width += computeWidth(*ch); + } + return width; +} + +TextRenderer::TextRenderer(const Properties& properties) : + _font(properties.font), + _metrics(_font), + _effectType(properties.effect), + _effectThickness(properties.effectThickness), + _x(IMAGE_SIZE), + _y(IMAGE_SIZE), + _rowHeight(0), + _color(properties.color), + _glyphsBuffer(), + _numGlyphsBatched(0) +{ + _font.setKerning(false); +} + +const Glyph& TextRenderer::getGlyph(char c) { + Glyph& glyph = _glyphs[c]; + if (glyph.isValid()) { + return glyph; + } + // we use 'J' as a representative size for the solid block character + QChar ch = (c == SOLID_BLOCK_CHAR) ? QChar('J') : QChar(c); + QRect baseBounds = _metrics.boundingRect(ch); + if (baseBounds.isEmpty()) { + glyph = Glyph(0, QPoint(), QRect(), _metrics.width(ch)); + return glyph; + } + // grow the bounds to account for effect, if any + if (_effectType == SHADOW_EFFECT) { + baseBounds.adjust(-_effectThickness, 0, 0, _effectThickness); + + } else if (_effectType == OUTLINE_EFFECT) { + baseBounds.adjust(-_effectThickness, -_effectThickness, _effectThickness, _effectThickness); + } + + // grow the bounds to account for antialiasing + baseBounds.adjust(-1, -1, 1, 1); + + // adjust bounds for device pixel scaling + float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio(); + QRect bounds(baseBounds.x() * ratio, baseBounds.y() * ratio, baseBounds.width() * ratio, baseBounds.height() * ratio); + + if (_x + bounds.width() > IMAGE_SIZE) { + // we can't fit it on the current row; move to next + _y += _rowHeight; + _x = _rowHeight = 0; + } + if (_y + bounds.height() > IMAGE_SIZE) { + // can't fit it on current texture; make a new one + glGenTextures(1, &_currentTextureID); + _x = _y = _rowHeight = 0; + + glBindTexture(GL_TEXTURE_2D, _currentTextureID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IMAGE_SIZE, IMAGE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + _allTextureIDs.append(_currentTextureID); + + } else { + glBindTexture(GL_TEXTURE_2D, _currentTextureID); + } + // render the glyph into an image and copy it into the texture + QImage image(bounds.width(), bounds.height(), QImage::Format_ARGB32); + if (c == SOLID_BLOCK_CHAR) { + image.fill(_color); + + } else { + image.fill(0); + QPainter painter(&image); + QFont font = _font; + if (ratio == 1.0f) { + painter.setFont(_font); + } else { + QFont enlargedFont = _font; + enlargedFont.setPointSize(_font.pointSize() * ratio); + painter.setFont(enlargedFont); + } + if (_effectType == SHADOW_EFFECT) { + for (int i = 0; i < _effectThickness * ratio; i++) { + painter.drawText(-bounds.x() - 1 - i, -bounds.y() + 1 + i, ch); + } + } else if (_effectType == OUTLINE_EFFECT) { + QPainterPath path; + QFont font = _font; + font.setStyleStrategy(QFont::ForceOutline); + path.addText(-bounds.x() - 0.5, -bounds.y() + 0.5, font, ch); + QPen pen; + pen.setWidth(_effectThickness * ratio); + pen.setJoinStyle(Qt::RoundJoin); + pen.setCapStyle(Qt::RoundCap); + painter.setPen(pen); + painter.setRenderHint(QPainter::Antialiasing); + painter.drawPath(path); + } + painter.setPen(_color); + painter.drawText(-bounds.x(), -bounds.y(), ch); + } + glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, bounds.width(), bounds.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.constBits()); + + glyph = Glyph(_currentTextureID, QPoint(_x / ratio, _y / ratio), baseBounds, _metrics.width(ch)); + _x += bounds.width(); + _rowHeight = qMax(_rowHeight, bounds.height()); + + glBindTexture(GL_TEXTURE_2D, 0); + return glyph; +} + +void TextRenderer::drawBatch() { + if (_numGlyphsBatched<=0) { + return; + } + + // TODO: Right now the drawBatch is called while calling the draw() function but in the future we'll need to apply the correct transform stack + /* + GLint matrixMode; + glGetIntegerv(GL_MATRIX_MODE, &matrixMode); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + */ + + glEnable(GL_TEXTURE_2D); + // TODO: Apply the correct font atlas texture, for now only one texture per TextRenderer so it should be good + glBindTexture(GL_TEXTURE_2D, _currentTextureID); + + GLuint vbo = _glyphsBuffer.getGLBufferObject(); + GLuint colorvbo = _glyphsColorBuffer.getGLBufferObject(); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + const int NUM_POS_COORDS = 2; + const int NUM_TEX_COORDS = 2; + const int VERTEX_STRIDE = (NUM_POS_COORDS + NUM_TEX_COORDS) * sizeof(float); + const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glVertexPointer(2, GL_FLOAT, VERTEX_STRIDE, 0); + glTexCoordPointer(2, GL_FLOAT, VERTEX_STRIDE, (GLvoid*) VERTEX_TEXCOORD_OFFSET ); + + glBindBuffer(GL_ARRAY_BUFFER, colorvbo); + glColorPointer(4, GL_UNSIGNED_BYTE, 0, (GLvoid*) 0 ); + + glDrawArrays(GL_QUADS, 0, _numGlyphsBatched * 4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + // TODO: Right now the drawBatch is called while calling the draw() function but in the future we'll need to apply the correct transform stack + /* + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(matrixMode); + */ +} + +void TextRenderer::clearBatch() { + _numGlyphsBatched = 0; +} + +QHash TextRenderer::_instances; + +Glyph::Glyph(int textureID, const QPoint& location, const QRect& bounds, int width) : + _textureID(textureID), _location(location), _bounds(bounds), _width(width) { +} From b0c02311c09ae29931d8bb68a9dc3780cccd92b0 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 9 Oct 2014 10:56:41 -0700 Subject: [PATCH 10/33] Fixing include filename case --- interface/src/gpu/Resource.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/gpu/Resource.h b/interface/src/gpu/Resource.h index e74f2d7697..56defeb554 100644 --- a/interface/src/gpu/Resource.h +++ b/interface/src/gpu/Resource.h @@ -12,7 +12,7 @@ #define hifi_gpu_Resource_h #include -#include "interfaceconfig.h" +#include "InterfaceConfig.h" namespace gpu { From d080d6347b86942cb8bd5085e054e89a9d776f68 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 9 Oct 2014 11:10:04 -0700 Subject: [PATCH 11/33] Fixing const usage on a template function --- interface/src/gpu/Resource.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/gpu/Resource.h b/interface/src/gpu/Resource.h index 56defeb554..a7b87881c5 100644 --- a/interface/src/gpu/Resource.h +++ b/interface/src/gpu/Resource.h @@ -94,7 +94,7 @@ protected: template< typename T > const T* read() const { return reinterpret_cast< T* > ( _data ); } template< typename T > - T* edit() const { _stamp++; return reinterpret_cast< T* > ( _data ); } + T* edit() { _stamp++; return reinterpret_cast< T* > ( _data ); } // Access the current version of the sysmem, used to compare if copies are in sync inline Stamp getStamp() const { return _stamp; } From 86c20b26ebb8f3b1ab81581dfb15e84259b83be3 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 9 Oct 2014 12:20:46 -0700 Subject: [PATCH 12/33] Adding proper TODO and comments --- interface/src/gpu/Resource.cpp | 3 ++- interface/src/gpu/Resource.h | 6 ++++-- interface/src/ui/TextRenderer.cpp | 4 ++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/interface/src/gpu/Resource.cpp b/interface/src/gpu/Resource.cpp index 5e5f8d8ae4..23ea12e6e8 100644 --- a/interface/src/gpu/Resource.cpp +++ b/interface/src/gpu/Resource.cpp @@ -206,7 +206,8 @@ void syncGPUObject(const Buffer& buffer) { } // Now let's update the content of the bo with the sysmem version - //if (object->_size < buffer.getSize()) { + // TODO: in the future, be smarter about when to actually upload the glBO version based on the data that did change + //if () { glBindBuffer(GL_ARRAY_BUFFER, object->_buffer); glBufferData(GL_ARRAY_BUFFER, buffer.getSysmem().getSize(), buffer.getSysmem().readData(), GL_DYNAMIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); diff --git a/interface/src/gpu/Resource.h b/interface/src/gpu/Resource.h index a7b87881c5..5c5b7f6367 100644 --- a/interface/src/gpu/Resource.h +++ b/interface/src/gpu/Resource.h @@ -129,9 +129,11 @@ public: Size resize(Size pSize); // Assign data bytes and size (allocate for size, then copy bytes if exists) + // \return the size of the buffer Size setData(Size size, const Byte* data); // Assign data bytes and size (allocate for size, then copy bytes if exists) + // \return the number of bytes copied Size setSubData(Size offset, Size size, const Byte* data); // Append new data at the end of the current buffer @@ -145,10 +147,10 @@ public: protected: - Sysmem* _sysmem; + Sysmem* _sysmem; typedef backend::BufferObject GPUObject; - mutable backend::BufferObject* _gpuObject; + mutable backend::BufferObject* _gpuObject; inline const Sysmem& getSysmem() const { assert(_sysmem); return (*_sysmem); } inline Sysmem& editSysmem() { assert(_sysmem); return (*_sysmem); } diff --git a/interface/src/ui/TextRenderer.cpp b/interface/src/ui/TextRenderer.cpp index 9cb88f1f03..138c10b26c 100644 --- a/interface/src/ui/TextRenderer.cpp +++ b/interface/src/ui/TextRenderer.cpp @@ -75,6 +75,7 @@ int TextRenderer::draw(int x, int y, const char* str) { ((int( currentColor[2] * 255.f) & 0xFF) << 16) | ((int( currentColor[3] * 255.f) & 0xFF) << 24); +// TODO: Remove that code once we test for performance improvments //glEnable(GL_TEXTURE_2D); int maxHeight = 0; @@ -103,6 +104,8 @@ int TextRenderer::draw(int x, int y, const char* str) { float rs = (glyph.location().x() + glyph.bounds().width()) * scale; float bt = glyph.location().y() * scale; float tt = (glyph.location().y() + glyph.bounds().height()) * scale; + +// TODO: Remove that code once we test for performance improvments /* glBegin(GL_QUADS); glTexCoord2f(ls, bt); @@ -143,6 +146,7 @@ int TextRenderer::draw(int x, int y, const char* str) { drawBatch(); clearBatch(); +// TODO: Remove that code once we test for performance improvments // glBindTexture(GL_TEXTURE_2D, 0); // glDisable(GL_TEXTURE_2D); From 2da4345b217ed3d8707dd2e0a398c6c0528001e8 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 9 Oct 2014 13:32:35 -0700 Subject: [PATCH 13/33] First person and mirror HMD view almost fixed --- interface/src/Application.cpp | 7 +++++-- interface/src/Camera.cpp | 25 ++++--------------------- interface/src/Camera.h | 22 +++++++++++++++------- interface/src/avatar/MyAvatar.cpp | 2 +- interface/src/devices/OculusManager.cpp | 17 ++++++++++++----- 5 files changed, 37 insertions(+), 36 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 718360864a..55fe438cfe 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -605,10 +605,14 @@ void Application::paintGL() { if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { if (!OculusManager::isConnected()) { + // If there isn't an HMD, match exactly to avatar's head _myCamera.setPosition(_myAvatar->getHead()->getEyePosition()); _myCamera.setRotation(_myAvatar->getHead()->getCameraOrientation()); + } else { + // For an HMD, set the base position and orientation to that of the avatar body + _myCamera.setPosition(_myAvatar->getDefaultEyePosition()); + _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation()); } - // OculusManager::display() updates camera position and rotation a bit further on. } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { static const float THIRD_PERSON_CAMERA_DISTANCE = 1.5f; @@ -664,7 +668,6 @@ void Application::paintGL() { _viewFrustumOffsetCamera.setRotation(_myCamera.getRotation() * frustumRotation); - _viewFrustumOffsetCamera.initialize(); // force immediate snap to ideal position and orientation _viewFrustumOffsetCamera.update(1.f/_fps); whichCamera = &_viewFrustumOffsetCamera; } diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index 79d66568bf..a8138363fa 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -22,13 +22,16 @@ Camera::Camera() : - _needsToInitialize(true), _mode(CAMERA_MODE_THIRD_PERSON), _position(0.0f, 0.0f, 0.0f), _fieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES), _aspectRatio(16.0f/9.0f), _nearClip(DEFAULT_NEAR_CLIP), // default _farClip(DEFAULT_FAR_CLIP), // default + _hmdPosition(), + _hmdRotation(), + _targetPosition(), + _targetRotation(), _scale(1.0f) { } @@ -64,26 +67,6 @@ void Camera::setFarClip(float f) { _farClip = f; } -void Camera::setEyeOffsetPosition(const glm::vec3& p) { - _eyeOffsetPosition = p; -} - -void Camera::setEyeOffsetOrientation(const glm::quat& o) { - _eyeOffsetOrientation = o; - -} - -void Camera::setScale(float s) { - _scale = s; - _needsToInitialize = true; - -} - -void Camera::initialize() { - _needsToInitialize = true; -} - - CameraScriptableObject::CameraScriptableObject(Camera* camera, ViewFrustum* viewFrustum) : _camera(camera), _viewFrustum(viewFrustum) { diff --git a/interface/src/Camera.h b/interface/src/Camera.h index ee4930272d..80454a969e 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -38,17 +38,23 @@ public: void setPosition(const glm::vec3& p) { _position = p; } void setRotation(const glm::quat& rotation) { _rotation = rotation; }; + void setHmdPosition(const glm::vec3& hmdPosition) { _hmdPosition = hmdPosition; } + void setHmdRotation(const glm::quat& hmdRotation) { _hmdRotation = hmdRotation; }; + void setMode(CameraMode m); void setFieldOfView(float f); void setAspectRatio(float a); void setNearClip(float n); void setFarClip(float f); - void setEyeOffsetPosition(const glm::vec3& p); - void setEyeOffsetOrientation(const glm::quat& o); - void setScale(const float s); + void setEyeOffsetPosition(const glm::vec3& p) { _eyeOffsetPosition = p; } + void setEyeOffsetOrientation(const glm::quat& o) { _eyeOffsetOrientation = o; } + void setScale(const float s) { _scale = s; } + + glm::vec3 getPosition() const { return _position + _hmdPosition; } + glm::quat getRotation() const { return _rotation * _hmdRotation; } + const glm::vec3& getHmdPosition() const { return _hmdPosition; } + const glm::quat& getHmdRotation() const { return _hmdRotation; } - const glm::vec3& getPosition() const { return _position; } - const glm::quat& getRotation() const { return _rotation; } CameraMode getMode() const { return _mode; } float getFieldOfView() const { return _fieldOfView; } float getAspectRatio() const { return _aspectRatio; } @@ -60,7 +66,6 @@ public: private: - bool _needsToInitialize; CameraMode _mode; glm::vec3 _position; float _fieldOfView; // degrees @@ -70,7 +75,10 @@ private: glm::vec3 _eyeOffsetPosition; glm::quat _eyeOffsetOrientation; glm::quat _rotation; - + glm::vec3 _hmdPosition; + glm::quat _hmdRotation; + glm::vec3 _targetPosition; + glm::quat _targetRotation; float _scale; }; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c0ce474d16..36035880fd 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1151,7 +1151,7 @@ const float RENDER_HEAD_CUTOFF_DISTANCE = 0.50f; bool MyAvatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderMode renderMode) const { const Head* head = getHead(); - return (renderMode != NORMAL_RENDER_MODE) || + return (renderMode != NORMAL_RENDER_MODE) || (Application::getInstance()->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON) || (glm::length(cameraPosition - head->getEyePosition()) > RENDER_HEAD_CUTOFF_DISTANCE * _scale); } diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index eb48e3d463..c5aa73582e 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -337,15 +337,22 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p #else ovrEyeType eye = _ovrHmdDesc.EyeRenderOrder[eyeIndex]; #endif - //Set the camera rotation for this eye + // Set the camera rotation for this eye eyeRenderPose[eye] = ovrHmd_GetEyePose(_ovrHmd, eye); orientation.x = eyeRenderPose[eye].Orientation.x; orientation.y = eyeRenderPose[eye].Orientation.y; orientation.z = eyeRenderPose[eye].Orientation.z; orientation.w = eyeRenderPose[eye].Orientation.w; - _camera->setRotation(bodyOrientation * orientation); - _camera->setPosition(position + trackerPosition); + // Update the application camera with the latest HMD position + whichCamera.setHmdPosition(trackerPosition); + whichCamera.setHmdRotation(orientation); + + //_camera->setRotation(bodyOrientation * orientation); + //_camera->setPosition(position + trackerPosition); + // Update our camera to what the application camera is doing + _camera->setRotation(whichCamera.getRotation()); + _camera->setPosition(whichCamera.getPosition()); // Store the latest left and right eye render locations for things that need to know glm::vec3 thisEyePosition = position + trackerPosition + @@ -409,8 +416,8 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p glBindTexture(GL_TEXTURE_2D, 0); // Update camera for use by rest of Interface. - whichCamera.setPosition((_leftEyePosition + _rightEyePosition) / 2.f); - whichCamera.setRotation(_camera->getRotation()); + //whichCamera.setPosition((_leftEyePosition + _rightEyePosition) / 2.f); + //whichCamera.setRotation(_camera->getRotation()); #endif } From 0fce89a62796724021a1e5bc51ad32baef4c0ee9 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 9 Oct 2014 16:22:16 -0700 Subject: [PATCH 14/33] Add gamepad.js --- examples/gamepad.js | 266 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 examples/gamepad.js diff --git a/examples/gamepad.js b/examples/gamepad.js new file mode 100644 index 0000000000..1911e71a25 --- /dev/null +++ b/examples/gamepad.js @@ -0,0 +1,266 @@ +// +// controller.js +// examples +// +// Created by Ryan Huffman on 10/9/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +gamepad = Joysticks.joystickWithName("Wireless 360 Controller"); + +if (!gamepad) { + print("No gamepad found."); +} + +// Controller axis/button mappings +var GAMEPAD = { + AXES: { + LEFT_JOYSTICK_X: 0, + LEFT_JOYSTICK_Y: 1, + + RIGHT_JOYSTICK_X: 2, + RIGHT_JOYSTICK_Y: 3, + + LEFT_TRIGGER: 4, + RIGHT_TRIGGER: 5, + }, + BUTTONS: { + DPAD_UP: 0, + DPAD_DOWN: 1, + DPAD_LEFT: 2, + DPAD_RIGHT: 3, + + LEFT_JOYSTICK: 6, + RIGHT_JOYSTICK: 7, + + LEFT_BUMPER: 8, + RIGHT_BUMPER: 9, + + // Face buttons, ABXY on an XBOX controller + FACE_BOTTOM: 11, + FACE_RIGHT: 12, + FACE_LEFT: 13, + FACE_TOP: 14, + } +} + +// Button/axis mappings +var AXIS_STRAFE = GAMEPAD.AXES.LEFT_JOYSTICK_X; +var AXIS_FORWARD = GAMEPAD.AXES.LEFT_JOYSTICK_Y; +var AXIS_ROTATE = GAMEPAD.AXES.RIGHT_JOYSTICK_X; + +var BUTTON_TURN_AROUND = GAMEPAD.BUTTONS.RIGHT_JOYSTICK; + +var BUTTON_FLY_UP = GAMEPAD.BUTTONS.RIGHT_BUMPER; +var BUTTON_FLY_DOWN = GAMEPAD.BUTTONS.LEFT_BUMPER +var BUTTON_WARP = GAMEPAD.BUTTONS.FACE_BOTTOM; + +var BUTTON_WARP_FORWARD = GAMEPAD.BUTTONS.DPAD_UP; +var BUTTON_WARP_BACKWARD = GAMEPAD.BUTTONS.DPAD_DOWN; +var BUTTON_WARP_LEFT = GAMEPAD.BUTTONS.DPAD_LEFT; +var BUTTON_WARP_RIGHT = GAMEPAD.BUTTONS.DPAD_RIGHT; + +// Distance in meters to warp via BUTTON_WARP_* +var WARP_DISTANCE = 1; + +// Walk speed in m/s +var MOVE_SPEED = 2; + +// Amount to rotate in radians +var ROTATE_INCREMENT = Math.PI / 8; + +// Pick from above where we want to warp +var WARP_PICK_OFFSET = { x: 0, y: 10, z: 0 }; + +// When warping, the warp position will snap to a target below the current warp position. +// This is the max distance it will snap to. +var WARP_PICK_MAX_DISTANCE = 100; + +var flyDownButtonState = false; +var flyUpButtonState = false; + +// Current move direction, axis aligned - that is, looking down and moving forward +// will not move you into the ground, but instead will keep you on the horizontal plane. +var moveDirection = { x: 0, y: 0, z: 0 }; + +var warpActive = false; +var warpPosition = { x: 0, y: 0, z: 0 }; + +var WARP_SPHERE_SIZE = 1; +var warpSphere = Overlays.addOverlay("sphere", { + position: { x: 0, y: 0, z: 0 }, + size: WARP_SPHERE_SIZE, + color: { red: 0, green: 255, blue: 0 }, + alpha: 1.0, + solid: true, + visible: false, +}); + +var WARP_LINE_HEIGHT = 10; +var warpLine = Overlays.addOverlay("line3d", { + position: { x: 0, y: 0, z:0 }, + end: { x: 0, y: 0, z: 0 }, + color: { red: 0, green: 255, blue: 255}, + alpha: 1, + lineWidth: 5, + visible: false, +}); + +function copyVec3(vec) { + return { x: vec.x, y: vec.y, z: vec.z }; +} + +function activateWarp() { + if (warpActive) return; + warpActive = true; + + updateWarp(); +} + +function updateWarp() { + if (!warpActive) return; + + var look = Quat.getFront(Camera.getOrientation()); + var pitch = Math.asin(look.y); + + // Get relative to looking straight down + pitch += Math.PI / 2; + + // Scale up + pitch *= 2; + var distance = pitch * pitch * pitch; + + var warpDirection = Vec3.normalize({ x: look.x, y: 0, z: look.z }); + warpPosition = Vec3.multiply(warpDirection, distance); + warpPosition = Vec3.sum(MyAvatar.position, warpPosition); + + var pickRay = { + origin: Vec3.sum(warpPosition, WARP_PICK_OFFSET), + direction: { x: 0, y: -1, z: 0 } + }; + + var intersection = Voxels.findRayIntersection(pickRay); + + if (intersection.intersects && intersection.distance < WARP_PICK_MAX_DISTANCE) { + // Warp 1 meter above the object - this is an approximation + // TODO Get the actual offset to the Avatar's feet and plant them to + // the object. + warpPosition = Vec3.sum(intersection.intersection, { x: 0, y: 1, z:0 }); + } + + // Adjust overlays to match warp position + Overlays.editOverlay(warpSphere, { + position: warpPosition, + visible: true, + }); + Overlays.editOverlay(warpLine, { + position: warpPosition, + end: Vec3.sum(warpPosition, { x: 0, y: WARP_LINE_HEIGHT, z: 0 }), + visible: true, + }); +} + +function finishWarp() { + if (!warpActive) return; + warpActive = false; + Overlays.editOverlay(warpSphere, { + visible: false, + }); + Overlays.editOverlay(warpLine, { + visible: false, + }); + MyAvatar.position = warpPosition; +} + +function reportAxisValue(axis, newValue, oldValue) { + if (Math.abs(oldValue) < 0.2) oldValue = 0; + if (Math.abs(newValue) < 0.2) newValue = 0; + + if (axis == AXIS_FORWARD) { + moveDirection.z = newValue; + } else if (axis == AXIS_STRAFE) { + moveDirection.x = newValue; + } else if (axis == AXIS_ROTATE) { + if (oldValue == 0 && newValue != 0) { + var rotateRadians = newValue > 0 ? -ROTATE_INCREMENT : ROTATE_INCREMENT; + var orientation = MyAvatar.orientation; + orientation = Quat.multiply(Quat.fromPitchYawRollRadians(0, rotateRadians, 0), orientation) ; + MyAvatar.orientation = orientation; + } + } +} + +function reportButtonValue(button, newValue, oldValue) { + print("The value for button " + button + " has changed from " + oldValue + " to " + newValue); + if (button == BUTTON_FLY_DOWN) { + flyDownButtonState = newValue; + } else if (button == BUTTON_FLY_UP) { + flyUpButtonState = newValue; + } else if (button == BUTTON_WARP) { + if (newValue) { + activateWarp(); + } else { + finishWarp(); + } + } else if (button == BUTTON_TURN_AROUND) { + if (newValue) { + MyAvatar.orientation = Quat.multiply( + Quat.fromPitchYawRollRadians(0, Math.PI, 0), MyAvatar.orientation); + } + } else if (newValue) { + var direction = null; + + if (button == BUTTON_WARP_FORWARD) { + direction = Quat.getFront(Camera.getOrientation()); + } else if (button == BUTTON_WARP_BACKWARD) { + direction = Quat.getFront(Camera.getOrientation()); + direction = Vec3.multiply(-1, direction); + } else if (button == BUTTON_WARP_LEFT) { + direction = Quat.getRight(Camera.getOrientation()); + direction = Vec3.multiply(-1, direction); + } else if (button == BUTTON_WARP_RIGHT) { + direction = Quat.getRight(Camera.getOrientation()); + } + + if (direction) { + direction.y = 0; + direction = Vec3.multiply(Vec3.normalize(direction), WARP_DISTANCE); + MyAvatar.position = Vec3.sum(MyAvatar.position, direction); + } + } + + if (flyUpButtonState && !flyDownButtonState) { + moveDirection.y = 1; + } else if (!flyUpButtonState && flyDownButtonState) { + moveDirection.y = -1; + } else { + moveDirection.y = 0; + } +} + +function update(dt) { + var velocity = { x: 0, y: 0, z: 0 }; + var move = copyVec3(moveDirection); + move.y = 0; + if (Vec3.length(move) > 0) { + velocity = Vec3.multiplyQbyV(Camera.getOrientation(), move); + velocity.y = 0; + velocity = Vec3.multiply(Vec3.normalize(velocity), MOVE_SPEED); + } + + if (moveDirection.y != 0) { + velocity.y = moveDirection.y * MOVE_SPEED; + } + + MyAvatar.setVelocity(velocity); + + updateWarp(); +} + +gamepad.axisValueChanged.connect(reportAxisValue); +gamepad.buttonStateChanged.connect(reportButtonValue); + +Script.update.connect(update); From f18faf9e3e7505e25b6043d26f48f1ef6b7a8aef Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 9 Oct 2014 16:46:06 -0700 Subject: [PATCH 15/33] removed commented code. --- interface/src/devices/OculusManager.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index c5aa73582e..ad6a914112 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -348,8 +348,6 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p whichCamera.setHmdPosition(trackerPosition); whichCamera.setHmdRotation(orientation); - //_camera->setRotation(bodyOrientation * orientation); - //_camera->setPosition(position + trackerPosition); // Update our camera to what the application camera is doing _camera->setRotation(whichCamera.getRotation()); _camera->setPosition(whichCamera.getPosition()); @@ -414,10 +412,7 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p renderDistortionMesh(eyeRenderPose); glBindTexture(GL_TEXTURE_2D, 0); - - // Update camera for use by rest of Interface. - //whichCamera.setPosition((_leftEyePosition + _rightEyePosition) / 2.f); - //whichCamera.setRotation(_camera->getRotation()); + #endif } From 1fc78e42779889aec55d2a3b3b4332a93b0538b8 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 9 Oct 2014 17:18:51 -0700 Subject: [PATCH 16/33] Fixing Andrzej's comments --- interface/src/gpu/Resource.h | 10 +++++----- interface/src/ui/TextRenderer.cpp | 6 +++--- interface/src/ui/TextRenderer.h | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/interface/src/gpu/Resource.h b/interface/src/gpu/Resource.h index 5c5b7f6367..d4daae8690 100644 --- a/interface/src/gpu/Resource.h +++ b/interface/src/gpu/Resource.h @@ -59,7 +59,7 @@ protected: public: Sysmem(); - Sysmem(Size size , const Byte* bytes); + Sysmem(Size size, const Byte* bytes); ~Sysmem(); Size getSize() const { return _size; } @@ -74,12 +74,12 @@ protected: Size resize(Size pSize); // Assign data bytes and size (allocate for size, then copy bytes if exists) - Size setData( Size size, const Byte* bytes ); + Size setData(Size size, const Byte* bytes ); // Update Sub data, // doesn't allocate and only copy size * bytes at the offset location // only if all fits in the existing allocated buffer - Size setSubData( Size offset, Size size, const Byte* bytes); + Size setSubData(Size offset, Size size, const Byte* bytes); // Append new data at the end of the current buffer // do a resize( size + getSIze) and copy the new data @@ -117,7 +117,7 @@ class Buffer : public Resource { public: Buffer(); - Buffer(const Buffer& buf ); + Buffer(const Buffer& buf); ~Buffer(); // The size in bytes of data stored in the buffer @@ -143,7 +143,7 @@ public: // this is a temporary hack so the current rendering code can access the underneath gl Buffer Object // TODO: remove asap, when the backend is doing more of the gl features - inline GLuint getGLBufferObject() const { backend::syncGPUObject(*this); return getGPUObject()->_buffer; } + inline GLuint getGLBufferObject() const { backend::syncGPUObject(*this); return getGPUObject()->_buffer; } protected: diff --git a/interface/src/ui/TextRenderer.cpp b/interface/src/ui/TextRenderer.cpp index 138c10b26c..8c78a06d55 100644 --- a/interface/src/ui/TextRenderer.cpp +++ b/interface/src/ui/TextRenderer.cpp @@ -128,8 +128,8 @@ int TextRenderer::draw(int x, int y, const char* str) { const int NUM_COLOR_SCALARS_PER_GLYPH = 4; unsigned int colorBuffer[NUM_COLOR_SCALARS_PER_GLYPH] = { compactColor, compactColor, compactColor, compactColor }; - gpu::Buffer::Size offset = sizeof(vertexBuffer)*_numGlyphsBatched; - gpu::Buffer::Size colorOffset = sizeof(colorBuffer)*_numGlyphsBatched; + gpu::Buffer::Size offset = sizeof(vertexBuffer) * _numGlyphsBatched; + gpu::Buffer::Size colorOffset = sizeof(colorBuffer) * _numGlyphsBatched; if ((offset + sizeof(vertexBuffer)) > _glyphsBuffer.getSize()) { _glyphsBuffer.append(sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); _glyphsColorBuffer.append(sizeof(colorBuffer), (gpu::Buffer::Byte*) colorBuffer); @@ -275,7 +275,7 @@ const Glyph& TextRenderer::getGlyph(char c) { } void TextRenderer::drawBatch() { - if (_numGlyphsBatched<=0) { + if (_numGlyphsBatched <= 0) { return; } diff --git a/interface/src/ui/TextRenderer.h b/interface/src/ui/TextRenderer.h index 70283eedd8..1953dda422 100644 --- a/interface/src/ui/TextRenderer.h +++ b/interface/src/ui/TextRenderer.h @@ -107,7 +107,7 @@ private: // Graphics Buffer containing the current accumulated glyphs to render gpu::Buffer _glyphsBuffer; gpu::Buffer _glyphsColorBuffer; - int _numGlyphsBatched; + int _numGlyphsBatched; static QHash _instances; }; From 6687660d2ff5caf780fe2c12743a44cd12f6bdcf Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 9 Oct 2014 17:29:06 -0700 Subject: [PATCH 17/33] Fixing Andrzej's comments bis --- interface/src/gpu/Resource.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/gpu/Resource.h b/interface/src/gpu/Resource.h index d4daae8690..fbbbe7f7fd 100644 --- a/interface/src/gpu/Resource.h +++ b/interface/src/gpu/Resource.h @@ -104,7 +104,7 @@ protected: private: Sysmem(const Sysmem& sysmem) {} - Sysmem &operator=(const Sysmem &other) {return *this;} + Sysmem &operator=(const Sysmem& other) {return *this;} Stamp _stamp; Size _size; From b50e996ec8cc1bd66835a788b1b9cc09097280d6 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 17:35:58 -0700 Subject: [PATCH 18/33] added Vec3.orientedAngle() --- libraries/script-engine/src/Vec3.cpp | 8 +++++++- libraries/script-engine/src/Vec3.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libraries/script-engine/src/Vec3.cpp b/libraries/script-engine/src/Vec3.cpp index 689ce2c747..f88df3b7c0 100644 --- a/libraries/script-engine/src/Vec3.cpp +++ b/libraries/script-engine/src/Vec3.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include #include "Vec3.h" @@ -37,7 +39,7 @@ glm::vec3 Vec3::sum(const glm::vec3& v1, const glm::vec3& v2) { return v1 + v2; } glm::vec3 Vec3::subtract(const glm::vec3& v1, const glm::vec3& v2) { - return v1 - v2; + return v1 - v2; } float Vec3::length(const glm::vec3& v) { @@ -48,6 +50,10 @@ float Vec3::distance(const glm::vec3& v1, const glm::vec3& v2) { return glm::distance(v1, v2); } +float Vec3::orientedAngle(const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3) { + return glm::degrees(glm::orientedAngle(glm::normalize(v1), glm::normalize(v2), glm::normalize(v3))); +} + glm::vec3 Vec3::normalize(const glm::vec3& v) { return glm::normalize(v); } diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index 2af1350e4a..693fd604f7 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -34,6 +34,7 @@ public slots: glm::vec3 subtract(const glm::vec3& v1, const glm::vec3& v2); float length(const glm::vec3& v); float distance(const glm::vec3& v1, const glm::vec3& v2); + float orientedAngle(const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3); glm::vec3 normalize(const glm::vec3& v); void print(const QString& lable, const glm::vec3& v); bool equal(const glm::vec3& v1, const glm::vec3& v2); From f6c07a5ada8dfc69c35ca74225a06a99f95ae87f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 17:37:56 -0700 Subject: [PATCH 19/33] first cut at making the rotate handles actually rotate --- examples/libraries/entitySelectionTool.js | 221 +++++++++++++++------- 1 file changed, 154 insertions(+), 67 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index d88a8a52eb..281ac07261 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -37,11 +37,22 @@ SelectionDisplay = (function () { var yawCenter; var pitchCenter; var rollCenter; + var yawZero; + var pitchZero; + var rollZero; + var yawNormal; + var pitchNormal; + var rollNormal; + var rotationNormal; + + var originalPitch; + var originalYaw; + var originalRoll; + var rotateHandleColor = { red: 0, green: 0, blue: 0 }; var rotateHandleAlpha = 0.7; - var grabberSizeCorner = 0.025; var grabberSizeEdge = 0.015; var grabberSizeFace = 0.025; @@ -168,6 +179,21 @@ SelectionDisplay = (function () { var rollOverlayAngles = { x: 0, y: 180, z: 0 }; var rollOverlayRotation = Quat.fromVec3Degrees(rollOverlayAngles); + var rotateZeroOverlay = Overlays.addOverlay("line3d", { + visible: false, + lineWidth: 2.0, + start: { x: 0, y: 0, z: 0 }, + end: { x: 0, y: 0, z: 0 }, + color: { red: 255, green: 0, blue: 0 }, + }); + + var rotateCurrentOverlay = Overlays.addOverlay("line3d", { + visible: false, + lineWidth: 2.0, + start: { x: 0, y: 0, z: 0 }, + end: { x: 0, y: 0, z: 0 }, + color: { red: 0, green: 0, blue: 255 }, + }); var rotateOverlayInner = Overlays.addOverlay("circle3d", { position: { x:0, y: 0, z: 0}, @@ -290,7 +316,9 @@ SelectionDisplay = (function () { overlayNames[rotateOverlayOuter] = "rotateOverlayOuter"; overlayNames[rotateOverlayCurrent] = "rotateOverlayCurrent"; - + overlayNames[rotateZeroOverlay] = "rotateZeroOverlay"; + overlayNames[rotateCurrentOverlay] = "rotateCurrentOverlay"; + that.cleanup = function () { Overlays.deleteOverlay(highlightBox); Overlays.deleteOverlay(selectionBox); @@ -333,6 +361,10 @@ SelectionDisplay = (function () { Overlays.deleteOverlay(rotateOverlayOuter); Overlays.deleteOverlay(rotateOverlayCurrent); + Overlays.deleteOverlay(rotateZeroOverlay); + Overlays.deleteOverlay(rotateCurrentOverlay); + + }; that.highlightSelectable = function(entityID) { @@ -439,6 +471,10 @@ SelectionDisplay = (function () { pitchHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 180, z: 180 }); rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 90, z: 180 }); + yawNormal = { x: 0, y: 1, z: 0 }; + pitchNormal = { x: 0, y: 0, z: -1 }; + rollNormal = { x: 1, y: 0, z: 0 }; + yawCorner = { x: right + rotateHandleOffset, y: bottom - rotateHandleOffset, z: near - rotateHandleOffset }; @@ -451,23 +487,19 @@ SelectionDisplay = (function () { y: top + rotateHandleOffset, z: near - rotateHandleOffset}; - yawCenter = { x: center.x, - y: bottom, - z: center.z }; - - pitchCenter = { x: center.x, - y: center.y, - z: far }; - - rollCenter = { x: left, - y: center.y, - z: center.z}; - + yawCenter = { x: center.x, y: bottom, z: center.z }; + pitchCenter = { x: center.x, y: center.y, z: far }; + rollCenter = { x: left, y: center.y, z: center.z}; } else { yawHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 270, z: 0 }); pitchHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 90 }); + yawNormal = { x: 0, y: 1, z: 0 }; + pitchNormal = { x: 1, y: 0, z: 0 }; + rollNormal = { x: 0, y: 0, z: 1 }; + + yawCorner = { x: right + rotateHandleOffset, y: bottom - rotateHandleOffset, z: far + rotateHandleOffset }; @@ -481,18 +513,9 @@ SelectionDisplay = (function () { z: near - rotateHandleOffset}; - yawCenter = { x: center.x, - y: bottom, - z: center.z }; - - pitchCenter = { x: left, - y: center.y, - z: center.z }; - - rollCenter = { x: center.x, - y: center.y, - z: near}; - + yawCenter = { x: center.x, y: bottom, z: center.z }; + pitchCenter = { x: left, y: center.y, z: center.z }; + rollCenter = { x: center.x, y: center.y, z: near}; } } else { // must be BLF or BLN @@ -501,6 +524,10 @@ SelectionDisplay = (function () { pitchHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 0, z: 90 }); rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); + yawNormal = { x: 0, y: 1, z: 0 }; + pitchNormal = { x: -1, y: 0, z: 0 }; + rollNormal = { x: 0, y: 0, z: -1 }; + yawCorner = { x: left - rotateHandleOffset, y: bottom - rotateHandleOffset, z: near - rotateHandleOffset }; @@ -513,25 +540,20 @@ SelectionDisplay = (function () { y: top + rotateHandleOffset, z: far + rotateHandleOffset}; - yawCenter = { x: center.x, - y: bottom, - z: center.z }; + yawCenter = { x: center.x, y: bottom, z: center.z }; + pitchCenter = { x: right, y: center.y, z: center.z }; + rollCenter = { x: center.x, y: center.y, z: far}; - pitchCenter = { x: right, - y: center.y, - z: center.z }; - - rollCenter = { x: center.x, - y: center.y, - z: far}; - - - } else { yawHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 180, z: 0 }); pitchHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); rollHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); + // TODO: fix these + yawNormal = { x: 0, y: 1, z: 0 }; + pitchNormal = { x: 0, y: 0, z: 1 }; + rollNormal = { x: -1, y: 0, z: 0 }; + yawCorner = { x: left - rotateHandleOffset, y: bottom - rotateHandleOffset, z: far + rotateHandleOffset }; @@ -544,19 +566,9 @@ SelectionDisplay = (function () { y: top + rotateHandleOffset, z: far + rotateHandleOffset}; - yawCenter = { x: center.x, - y: bottom, - z: center.z }; - - pitchCenter = { x: center.x, - y: center.y, - z: near }; - - rollCenter = { x: right, - y: center.y, - z: center.z}; - - + yawCenter = { x: center.x, y: bottom, z: center.z }; + pitchCenter = { x: center.x, y: center.y, z: near }; + rollCenter = { x: right, y: center.y, z: center.z}; } } @@ -660,6 +672,9 @@ SelectionDisplay = (function () { endAt: 0, innerRadius: 0.9, }); + + Overlays.editOverlay(rotateZeroOverlay, { visible: false }); + Overlays.editOverlay(rotateCurrentOverlay, { visible: false }); // TODO: we have not implemented the rotating handle/controls yet... so for now, these handles are hidden Overlays.editOverlay(yawHandle, { visible: rotateHandlesVisible, position: yawCorner, rotation: yawHandleRotation}); @@ -718,6 +733,9 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayOuter, { visible: false }); Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); + Overlays.editOverlay(rotateZeroOverlay, { visible: false }); + Overlays.editOverlay(rotateCurrentOverlay, { visible: false }); + Entities.editEntity(entityID, { localRenderAlpha: 1.0 }); currentSelection = { id: -1, isKnownID: false }; @@ -1514,11 +1532,26 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); var result = Overlays.findRayIntersection(pickRay); - print("result.intersects:" + result.intersects); - print("result.overlayID:" + overlayNames[result.overlayID]); - print("result.distance:" + result.distance); - print("result.face:" + result.face); - Vec3.print("result.intersection:", result.intersection); + if (result.intersects) { + print("ROTATE_YAW"); + var properties = Entities.getEntityProperties(currentSelection); + var center = yawCenter; + var zero = yawZero; + var centerToZero = Vec3.subtract(center, zero); + var centerToIntersect = Vec3.subtract(center, result.intersection); + var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); + Overlays.editOverlay(rotateCurrentOverlay, + { + visible: true, + start: center, + end: result.intersection + }); + + + var newYaw = originalYaw + angleFromZero; + var newRotation = Quat.fromVec3Degrees({ x: originalPitch, y: newYaw, z: originalRoll }); + Entities.editEntity(currentSelection, { rotation: newRotation }); + } }; that.rotatePitch = function(event) { @@ -1532,11 +1565,27 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); var result = Overlays.findRayIntersection(pickRay); - print("result.intersects:" + result.intersects); - print("result.overlayID:" + overlayNames[result.overlayID]); - print("result.distance:" + result.distance); - print("result.face:" + result.face); - Vec3.print("result.intersection:", result.intersection); + + if (result.intersects) { + print("ROTATE_PITCH"); + + var properties = Entities.getEntityProperties(currentSelection); + var center = pitchCenter; + var zero = pitchZero; + var centerToZero = Vec3.subtract(center, zero); + var centerToIntersect = Vec3.subtract(center, result.intersection); + var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); + Overlays.editOverlay(rotateCurrentOverlay, + { + visible: true, + start: center, + end: result.intersection + }); + + var newPitch = originalPitch + angleFromZero; + var newRotation = Quat.fromVec3Degrees({ x: newPitch, y: originalYaw, z: originalRoll }); + Entities.editEntity(currentSelection, { rotation: newRotation }); + } }; that.rotateRoll = function(event) { @@ -1550,11 +1599,25 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); var result = Overlays.findRayIntersection(pickRay); - print("result.intersects:" + result.intersects); - print("result.overlayID:" + overlayNames[result.overlayID]); - print("result.distance:" + result.distance); - print("result.face:" + result.face); - Vec3.print("result.intersection:", result.intersection); + if (result.intersects) { + print("ROTATE_ROLL"); + var properties = Entities.getEntityProperties(currentSelection); + var center = rollCenter; + var zero = rollZero; + var centerToZero = Vec3.subtract(center, zero); + var centerToIntersect = Vec3.subtract(center, result.intersection); + var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); + Overlays.editOverlay(rotateCurrentOverlay, + { + visible: true, + start: center, + end: result.intersection + }); + + var newRoll = originalRoll + angleFromZero; + var newRotation = Quat.fromVec3Degrees({ x: originalPitch, y: originalYaw, z: newRoll }); + Entities.editEntity(currentSelection, { rotation: newRotation }); + } }; that.checkMove = function() { @@ -1736,6 +1799,10 @@ SelectionDisplay = (function () { var roll = angles.z; var currentRotation; + originalPitch = pitch; + originalYaw = yaw; + originalRoll = roll; + if (result.intersects) { switch(result.overlayID) { case yawHandle: @@ -1744,6 +1811,8 @@ SelectionDisplay = (function () { overlayOrientation = yawHandleRotation; overlayCenter = yawCenter; currentRotation = yaw; + yawZero = result.intersection; + rotationNormal = yawNormal; break; case pitchHandle: @@ -1752,6 +1821,8 @@ SelectionDisplay = (function () { overlayOrientation = pitchHandleRotation; overlayCenter = pitchCenter; currentRotation = pitch; + pitchZero = result.intersection; + rotationNormal = pitchNormal; break; case rollHandle: @@ -1760,6 +1831,8 @@ SelectionDisplay = (function () { overlayOrientation = rollHandleRotation; overlayCenter = rollCenter; currentRotation = roll; + rollZero = result.intersection; + rotationNormal = rollNormal; break; default: @@ -1807,6 +1880,20 @@ SelectionDisplay = (function () { startAt: 0, endAt: currentRotation }); + + Overlays.editOverlay(rotateZeroOverlay, + { + visible: true, + start: overlayCenter, + end: result.intersection + }); + + Overlays.editOverlay(rotateCurrentOverlay, + { + visible: true, + start: overlayCenter, + end: result.intersection + }); Overlays.editOverlay(yawHandle, { visible: false }); Overlays.editOverlay(pitchHandle, { visible: false }); From f1bc6d729cdbf57a956a620864ee48b67f5a3a1d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 18:01:48 -0700 Subject: [PATCH 20/33] make rotate handles work as expected --- examples/libraries/entitySelectionTool.js | 77 ++++++++--------------- 1 file changed, 27 insertions(+), 50 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 281ac07261..b5df2df9b8 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -45,6 +45,7 @@ SelectionDisplay = (function () { var rollNormal; var rotationNormal; + var originalRotation; var originalPitch; var originalYaw; var originalRoll; @@ -576,10 +577,12 @@ SelectionDisplay = (function () { var rotateHandlesVisible = true; var translateHandlesVisible = true; var stretchHandlesVisible = true; + var selectionBoxVisible = true; if (mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL" || mode == "TRANSLATE_XZ") { rotateHandlesVisible = false; translateHandlesVisible = false; stretchHandlesVisible = false; + selectionBoxVisible = false; } else if (mode == "TRANSLATE_UP_DOWN") { rotateHandlesVisible = false; stretchHandlesVisible = false; @@ -591,9 +594,10 @@ SelectionDisplay = (function () { Overlays.editOverlay(highlightBox, { visible: false }); + print("selectionBoxVisible:" + selectionBoxVisible); Overlays.editOverlay(selectionBox, { - visible: true, + visible: selectionBoxVisible, position: center, dimensions: properties.dimensions, rotation: properties.rotation, @@ -1525,8 +1529,8 @@ SelectionDisplay = (function () { } var pickRay = Camera.computePickRay(event.x, event.y); - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true }); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true }); + Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: false }); Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); @@ -1546,10 +1550,10 @@ SelectionDisplay = (function () { start: center, end: result.intersection }); - - - var newYaw = originalYaw + angleFromZero; - var newRotation = Quat.fromVec3Degrees({ x: originalPitch, y: newYaw, z: originalRoll }); + + var yawChange = Quat.fromVec3Degrees({ x: 0, y: angleFromZero, z: 0 }); + var newRotation = Quat.multiply(yawChange, originalRotation); + Entities.editEntity(currentSelection, { rotation: newRotation }); } }; @@ -1559,8 +1563,8 @@ SelectionDisplay = (function () { return; // not allowed } var pickRay = Camera.computePickRay(event.x, event.y); - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true }); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true }); + Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: false }); Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); @@ -1582,8 +1586,9 @@ SelectionDisplay = (function () { end: result.intersection }); - var newPitch = originalPitch + angleFromZero; - var newRotation = Quat.fromVec3Degrees({ x: newPitch, y: originalYaw, z: originalRoll }); + var pitchChange = Quat.fromVec3Degrees({ x: angleFromZero, y: 0, z: 0 }); + var newRotation = Quat.multiply(pitchChange, originalRotation); + Entities.editEntity(currentSelection, { rotation: newRotation }); } }; @@ -1593,8 +1598,8 @@ SelectionDisplay = (function () { return; // not allowed } var pickRay = Camera.computePickRay(event.x, event.y); - Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true }); - Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true }); + Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); + Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: false }); Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); @@ -1614,8 +1619,9 @@ SelectionDisplay = (function () { end: result.intersection }); - var newRoll = originalRoll + angleFromZero; - var newRotation = Quat.fromVec3Degrees({ x: originalPitch, y: originalYaw, z: newRoll }); + var rollChange = Quat.fromVec3Degrees({ x: 0, y: 0, z: angleFromZero }); + var newRotation = Quat.multiply(rollChange, originalRotation); + Entities.editEntity(currentSelection, { rotation: newRotation }); } }; @@ -1799,6 +1805,7 @@ SelectionDisplay = (function () { var roll = angles.z; var currentRotation; + originalRotation = properties.rotation; originalPitch = pitch; originalYaw = yaw; originalRoll = roll; @@ -2053,45 +2060,15 @@ SelectionDisplay = (function () { showHandles = true; } - if (showHandles) { - Overlays.editOverlay(yawHandle, { visible: true }); - Overlays.editOverlay(pitchHandle, { visible: true }); - Overlays.editOverlay(rollHandle, { visible: true }); - Overlays.editOverlay(grabberMoveUp, { visible: true }); - Overlays.editOverlay(grabberLBN, { visible: true }); - Overlays.editOverlay(grabberLBF, { visible: true }); - Overlays.editOverlay(grabberRBN, { visible: true }); - Overlays.editOverlay(grabberRBF, { visible: true }); - Overlays.editOverlay(grabberLTN, { visible: true }); - Overlays.editOverlay(grabberLTF, { visible: true }); - Overlays.editOverlay(grabberRTN, { visible: true }); - Overlays.editOverlay(grabberRTF, { visible: true }); - - Overlays.editOverlay(grabberTOP, { visible: true }); - Overlays.editOverlay(grabberBOTTOM, { visible: true }); - Overlays.editOverlay(grabberLEFT, { visible: true }); - Overlays.editOverlay(grabberRIGHT, { visible: true }); - Overlays.editOverlay(grabberNEAR, { visible: true }); - Overlays.editOverlay(grabberFAR, { visible: true }); - - Overlays.editOverlay(grabberEdgeTR, { visible: true }); - Overlays.editOverlay(grabberEdgeTL, { visible: true }); - Overlays.editOverlay(grabberEdgeTF, { visible: true }); - Overlays.editOverlay(grabberEdgeTN, { visible: true }); - Overlays.editOverlay(grabberEdgeBR, { visible: true }); - Overlays.editOverlay(grabberEdgeBL, { visible: true }); - Overlays.editOverlay(grabberEdgeBF, { visible: true }); - Overlays.editOverlay(grabberEdgeBN, { visible: true }); - Overlays.editOverlay(grabberEdgeNR, { visible: true }); - Overlays.editOverlay(grabberEdgeNL, { visible: true }); - Overlays.editOverlay(grabberEdgeFR, { visible: true }); - Overlays.editOverlay(grabberEdgeFL, { visible: true }); - } - mode = "UNKNOWN"; // if something is selected, then reset the "original" properties for any potential next click+move operation if (entitySelected) { + + if (showHandles) { + that.select(currentSelection, event); + } + selectedEntityProperties = Entities.getEntityProperties(currentSelection); selectedEntityPropertiesOriginalPosition = properties.position; selectedEntityPropertiesOriginalDimensions = properties.dimensions; From e5572e9deb69a27567537bf4e85d796f1d6fa6d3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 19:20:33 -0700 Subject: [PATCH 21/33] don't render if alpha is 0 --- interface/src/ui/overlays/Circle3DOverlay.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 7cd385f963..62ddee4cf2 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -40,13 +40,18 @@ void Circle3DOverlay::render() { if (!_visible) { return; // do nothing if we're not visible } + + float alpha = getAlpha(); + + if (alpha == 0.0) { + return; // do nothing if our alpha is 0, we're not visible + } const float FULL_CIRCLE = 360.0f; const float SLICES = 180.0f; // The amount of segment to create the circle const float SLICE_ANGLE = FULL_CIRCLE / SLICES; //const int slices = 15; - float alpha = getAlpha(); xColor color = getColor(); const float MAX_COLOR = 255.0f; glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); From 89f423255ef887255e7cfbb7dc21bfbc03462823 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 19:21:15 -0700 Subject: [PATCH 22/33] use large target for rotate overlays so we are guarenteed to hit the plane --- examples/libraries/entitySelectionTool.js | 47 ++++++++++++++++++----- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index b5df2df9b8..111e7aabee 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -31,6 +31,7 @@ SelectionDisplay = (function () { var handleHoverColor = { red: 224, green: 67, blue: 36 }; var handleHoverAlpha = 1.0; + var rotateOverlayTargetSize = 10000; // really big target var yawHandleRotation; var pitchHandleRotation; var rollHandleRotation; @@ -196,6 +197,17 @@ SelectionDisplay = (function () { color: { red: 0, green: 0, blue: 255 }, }); + + var rotateOverlayTarget = Overlays.addOverlay("circle3d", { + position: { x:0, y: 0, z: 0}, + size: rotateOverlayTargetSize, + color: { red: 0, green: 0, blue: 0 }, + alpha: 0.0, + solid: true, + visible: false, + rotation: yawOverlayRotation, + }); + var rotateOverlayInner = Overlays.addOverlay("circle3d", { position: { x:0, y: 0, z: 0}, size: 1, @@ -313,6 +325,7 @@ SelectionDisplay = (function () { overlayNames[pitchHandle] = "pitchHandle"; overlayNames[rollHandle] = "rollHandle"; + overlayNames[rotateOverlayTarget] = "rotateOverlayTarget"; overlayNames[rotateOverlayInner] = "rotateOverlayInner"; overlayNames[rotateOverlayOuter] = "rotateOverlayOuter"; overlayNames[rotateOverlayCurrent] = "rotateOverlayCurrent"; @@ -358,6 +371,7 @@ SelectionDisplay = (function () { Overlays.deleteOverlay(pitchHandle); Overlays.deleteOverlay(rollHandle); + Overlays.deleteOverlay(rotateOverlayTarget); Overlays.deleteOverlay(rotateOverlayInner); Overlays.deleteOverlay(rotateOverlayOuter); Overlays.deleteOverlay(rotateOverlayCurrent); @@ -649,7 +663,10 @@ SelectionDisplay = (function () { dimensions: { x: properties.dimensions.x, y: properties.dimensions.z }, rotation: properties.rotation, }); + + Overlays.editOverlay(rotateOverlayTarget, { visible: false }); + Overlays.editOverlay(rotateOverlayInner, { visible: false, @@ -733,6 +750,7 @@ SelectionDisplay = (function () { Overlays.editOverlay(pitchHandle, { visible: false }); Overlays.editOverlay(rollHandle, { visible: false }); + Overlays.editOverlay(rotateOverlayTarget, { visible: false }); Overlays.editOverlay(rotateOverlayInner, { visible: false }); Overlays.editOverlay(rotateOverlayOuter, { visible: false }); Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); @@ -1531,9 +1549,10 @@ SelectionDisplay = (function () { var pickRay = Camera.computePickRay(event.x, event.y); Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: false }); - Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); - Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true }); var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { @@ -1565,9 +1584,10 @@ SelectionDisplay = (function () { var pickRay = Camera.computePickRay(event.x, event.y); Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: false }); - Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); - Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true }); var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { @@ -1600,9 +1620,10 @@ SelectionDisplay = (function () { var pickRay = Camera.computePickRay(event.x, event.y); Overlays.editOverlay(selectionBox, { ignoreRayIntersection: true, visible: false}); Overlays.editOverlay(baseOfEntityProjectionOverlay, { ignoreRayIntersection: true, visible: false }); - Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: false }); - Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: false }); - Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayTarget, { ignoreRayIntersection: false }); + Overlays.editOverlay(rotateOverlayInner, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayOuter, { ignoreRayIntersection: true }); + Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true }); var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { print("ROTATE_ROLL"); @@ -1862,6 +1883,13 @@ SelectionDisplay = (function () { // TODO: need to place correctly.... print(" attempting to show overlays:" + somethingClicked); print(" currentRotation:" + currentRotation); + + Overlays.editOverlay(rotateOverlayTarget, + { + visible: true, + rotation: overlayOrientation, + position: overlayCenter + }); Overlays.editOverlay(rotateOverlayInner, { @@ -2050,6 +2078,7 @@ SelectionDisplay = (function () { var showHandles = false; // hide our rotation overlays..., and show our handles if (mode == "ROTATE_YAW" || mode == "ROTATE_PITCH" || mode == "ROTATE_ROLL") { + Overlays.editOverlay(rotateOverlayTarget, { visible: false }); Overlays.editOverlay(rotateOverlayInner, { visible: false }); Overlays.editOverlay(rotateOverlayOuter, { visible: false }); Overlays.editOverlay(rotateOverlayCurrent, { visible: false }); From b1da04f0f0e78b71a4e219676eac9719b6ce5386 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 19:41:51 -0700 Subject: [PATCH 23/33] support for snap to rotation --- examples/libraries/entitySelectionTool.js | 24 +++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index 111e7aabee..ab176da559 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -32,6 +32,8 @@ SelectionDisplay = (function () { var handleHoverAlpha = 1.0; var rotateOverlayTargetSize = 10000; // really big target + var innerSnapAngle = 22.5; // the angle which we snap to on the inner rotation tool + var innerRadius; var yawHandleRotation; var pitchHandleRotation; var rollHandleRotation; @@ -217,7 +219,7 @@ SelectionDisplay = (function () { visible: false, rotation: yawOverlayRotation, hasTickMarks: true, - majorTickMarksAngle: 12.5, + majorTickMarksAngle: innerSnapAngle, minorTickMarksAngle: 0, majorTickMarksLength: -0.25, minorTickMarksLength: 0, @@ -431,7 +433,7 @@ SelectionDisplay = (function () { var diagonal = (Vec3.length(properties.dimensions) / 2) * 1.1; var halfDimensions = Vec3.multiply(properties.dimensions, 0.5); - var innerRadius = diagonal; + innerRadius = diagonal; var outerRadius = diagonal * 1.15; var innerActive = false; var innerAlpha = 0.2; @@ -1563,6 +1565,12 @@ SelectionDisplay = (function () { var centerToZero = Vec3.subtract(center, zero); var centerToIntersect = Vec3.subtract(center, result.intersection); var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); + + var distanceFromCenter = Vec3.distance(center, result.intersection); + if (distanceFromCenter < innerRadius) { + angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; + } + Overlays.editOverlay(rotateCurrentOverlay, { visible: true, @@ -1599,6 +1607,12 @@ SelectionDisplay = (function () { var centerToZero = Vec3.subtract(center, zero); var centerToIntersect = Vec3.subtract(center, result.intersection); var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); + + var distanceFromCenter = Vec3.distance(center, result.intersection); + if (distanceFromCenter < innerRadius) { + angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; + } + Overlays.editOverlay(rotateCurrentOverlay, { visible: true, @@ -1633,6 +1647,12 @@ SelectionDisplay = (function () { var centerToZero = Vec3.subtract(center, zero); var centerToIntersect = Vec3.subtract(center, result.intersection); var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); + + var distanceFromCenter = Vec3.distance(center, result.intersection); + if (distanceFromCenter < innerRadius) { + angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; + } + Overlays.editOverlay(rotateCurrentOverlay, { visible: true, From 8105ded4ebe07e8a46b3be4f9e788d8b788324cd Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 21:12:05 -0700 Subject: [PATCH 24/33] handle reloading a new url in billboard overlays --- .../src/ui/overlays/BillboardOverlay.cpp | 25 ++++++++++++++----- interface/src/ui/overlays/BillboardOverlay.h | 5 ++-- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 4f9e7c84f6..c7511ca466 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -18,7 +18,8 @@ BillboardOverlay::BillboardOverlay() : _fromImage(-1,-1,-1,-1), _scale(1.0f), - _isFacingAvatar(true) { + _isFacingAvatar(true), + _newTextureNeeded(true) { _isLoaded = false; } @@ -28,6 +29,9 @@ void BillboardOverlay::render() { } if (!_billboard.isEmpty()) { + if (_newTextureNeeded && _billboardTexture) { + _billboardTexture.reset(); + } if (!_billboardTexture) { QImage image = QImage::fromData(_billboard); if (image.format() != QImage::Format_ARGB32) { @@ -38,6 +42,7 @@ void BillboardOverlay::render() { _fromImage.setRect(0, 0, _size.width(), _size.height()); } _billboardTexture.reset(new Texture()); + _newTextureNeeded = false; glBindTexture(GL_TEXTURE_2D, _billboardTexture->getID()); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _size.width(), _size.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, image.constBits()); @@ -107,9 +112,10 @@ void BillboardOverlay::setProperties(const QScriptValue &properties) { QScriptValue urlValue = properties.property("url"); if (urlValue.isValid()) { - _url = urlValue.toVariant().toString(); - - setBillboardURL(_url); + QString newURL = urlValue.toVariant().toString(); + if (newURL != _url) { + setBillboardURL(newURL); + } } QScriptValue subImageBounds = properties.property("subImage"); @@ -150,9 +156,16 @@ void BillboardOverlay::setProperties(const QScriptValue &properties) { } } -void BillboardOverlay::setBillboardURL(const QUrl url) { +void BillboardOverlay::setBillboardURL(const QString& url) { + _url = url; + QUrl actualURL = url; _isLoaded = false; - QNetworkReply* reply = NetworkAccessManager::getInstance().get(QNetworkRequest(url)); + + // clear the billboard if previously set + _billboard.clear(); + _newTextureNeeded = true; + + QNetworkReply* reply = NetworkAccessManager::getInstance().get(QNetworkRequest(actualURL)); connect(reply, &QNetworkReply::finished, this, &BillboardOverlay::replyFinished); } diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index a0b76869b3..fd594abe67 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -33,12 +33,13 @@ private slots: void replyFinished(); private: - void setBillboardURL(const QUrl url); + void setBillboardURL(const QString& url); - QUrl _url; + QString _url; QByteArray _billboard; QSize _size; QScopedPointer _billboardTexture; + bool _newTextureNeeded; QRect _fromImage; // where from in the image to sample From 3b01b652f3cb1fa2ca7efa1b86352a53d97b393a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 21:12:23 -0700 Subject: [PATCH 25/33] clean up of rotation from all directions --- examples/libraries/entitySelectionTool.js | 234 +++++++++++++--------- 1 file changed, 137 insertions(+), 97 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index ab176da559..c4ca72ad76 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -34,6 +34,7 @@ SelectionDisplay = (function () { var rotateOverlayTargetSize = 10000; // really big target var innerSnapAngle = 22.5; // the angle which we snap to on the inner rotation tool var innerRadius; + var outerRadius; var yawHandleRotation; var pitchHandleRotation; var rollHandleRotation; @@ -173,7 +174,8 @@ SelectionDisplay = (function () { alpha: 0.5, solid: true, visible: false, - rotation: baseOverlayRotation + rotation: baseOverlayRotation, + ignoreRayIntersection: true, // always ignore this }); var yawOverlayAngles = { x: 90, y: 0, z: 0 }; @@ -189,6 +191,7 @@ SelectionDisplay = (function () { start: { x: 0, y: 0, z: 0 }, end: { x: 0, y: 0, z: 0 }, color: { red: 255, green: 0, blue: 0 }, + ignoreRayIntersection: true, // always ignore this }); var rotateCurrentOverlay = Overlays.addOverlay("line3d", { @@ -197,6 +200,7 @@ SelectionDisplay = (function () { start: { x: 0, y: 0, z: 0 }, end: { x: 0, y: 0, z: 0 }, color: { red: 0, green: 0, blue: 255 }, + ignoreRayIntersection: true, // always ignore this }); @@ -225,6 +229,7 @@ SelectionDisplay = (function () { minorTickMarksLength: 0, majorTickMarksColor: { red: 0, green: 0, blue: 0 }, minorTickMarksColor: { red: 0, green: 0, blue: 0 }, + ignoreRayIntersection: true, // always ignore this }); var rotateOverlayOuter = Overlays.addOverlay("circle3d", { @@ -243,6 +248,7 @@ SelectionDisplay = (function () { minorTickMarksLength: 0.1, majorTickMarksColor: { red: 0, green: 0, blue: 0 }, minorTickMarksColor: { red: 0, green: 0, blue: 0 }, + ignoreRayIntersection: true, // always ignore this }); var rotateOverlayCurrent = Overlays.addOverlay("circle3d", { @@ -253,10 +259,11 @@ SelectionDisplay = (function () { solid: true, visible: false, rotation: yawOverlayRotation, + ignoreRayIntersection: true, // always ignore this }); var yawHandle = Overlays.addOverlay("billboard", { - url: "https://s3.amazonaws.com/uploads.hipchat.com/33953/231323/HRRhkMk8ueLk8ku/rotate-arrow.png", + url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png", position: { x:0, y: 0, z: 0}, color: rotateHandleColor, alpha: rotateHandleAlpha, @@ -268,7 +275,7 @@ SelectionDisplay = (function () { var pitchHandle = Overlays.addOverlay("billboard", { - url: "https://s3.amazonaws.com/uploads.hipchat.com/33953/231323/HRRhkMk8ueLk8ku/rotate-arrow.png", + url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png", position: { x:0, y: 0, z: 0}, color: rotateHandleColor, alpha: rotateHandleAlpha, @@ -280,7 +287,7 @@ SelectionDisplay = (function () { var rollHandle = Overlays.addOverlay("billboard", { - url: "https://s3.amazonaws.com/uploads.hipchat.com/33953/231323/HRRhkMk8ueLk8ku/rotate-arrow.png", + url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png", position: { x:0, y: 0, z: 0}, color: rotateHandleColor, alpha: rotateHandleAlpha, @@ -434,7 +441,7 @@ SelectionDisplay = (function () { var diagonal = (Vec3.length(properties.dimensions) / 2) * 1.1; var halfDimensions = Vec3.multiply(properties.dimensions, 0.5); innerRadius = diagonal; - var outerRadius = diagonal * 1.15; + outerRadius = diagonal * 1.15; var innerActive = false; var innerAlpha = 0.2; var outerAlpha = 0.2; @@ -484,31 +491,37 @@ SelectionDisplay = (function () { if (MyAvatar.position.x > center.x) { // must be BRF or BRN if (MyAvatar.position.z < center.z) { - yawHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 0, z: 0 }); - pitchHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 180, z: 180 }); - rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 90, z: 180 }); + yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 90, z: 0 }); + pitchHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 90, z: 0 }); // 0, 90, 0?? + rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 0 }); yawNormal = { x: 0, y: 1, z: 0 }; - pitchNormal = { x: 0, y: 0, z: -1 }; - rollNormal = { x: 1, y: 0, z: 0 }; + pitchNormal = { x: 1, y: 0, z: 0 }; + rollNormal = { x: 0, y: 0, z: 1 }; yawCorner = { x: right + rotateHandleOffset, y: bottom - rotateHandleOffset, z: near - rotateHandleOffset }; - pitchCorner = { x: right + rotateHandleOffset, - y: top + rotateHandleOffset, - z: far + rotateHandleOffset }; - - rollCorner = { x: left - rotateHandleOffset, + pitchCorner = { x: left - rotateHandleOffset, y: top + rotateHandleOffset, z: near - rotateHandleOffset}; + rollCorner = { x: right + rotateHandleOffset, + y: top + rotateHandleOffset, + z: far + rotateHandleOffset }; + yawCenter = { x: center.x, y: bottom, z: center.z }; - pitchCenter = { x: center.x, y: center.y, z: far }; - rollCenter = { x: left, y: center.y, z: center.z}; + pitchCenter = { x: left, y: center.y, z: center.z}; + rollCenter = { x: center.x, y: center.y, z: far }; + + + Overlays.editOverlay(pitchHandle, { url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-south.png" }); + Overlays.editOverlay(rollHandle, { url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-south.png" }); + + } else { - yawHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 270, z: 0 }); + yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 0, z: 0 }); pitchHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 90 }); @@ -533,17 +546,20 @@ SelectionDisplay = (function () { yawCenter = { x: center.x, y: bottom, z: center.z }; pitchCenter = { x: left, y: center.y, z: center.z }; rollCenter = { x: center.x, y: center.y, z: near}; + + Overlays.editOverlay(pitchHandle, { url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png" }); + Overlays.editOverlay(rollHandle, { url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png" }); } } else { // must be BLF or BLN if (MyAvatar.position.z < center.z) { - yawHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 90, z: 0 }); + yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 180, z: 0 }); pitchHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 0, z: 90 }); rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); yawNormal = { x: 0, y: 1, z: 0 }; - pitchNormal = { x: -1, y: 0, z: 0 }; - rollNormal = { x: 0, y: 0, z: -1 }; + pitchNormal = { x: 1, y: 0, z: 0 }; + rollNormal = { x: 0, y: 0, z: 1 }; yawCorner = { x: left - rotateHandleOffset, y: bottom - rotateHandleOffset, @@ -561,31 +577,38 @@ SelectionDisplay = (function () { pitchCenter = { x: right, y: center.y, z: center.z }; rollCenter = { x: center.x, y: center.y, z: far}; + Overlays.editOverlay(pitchHandle, { url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png" }); + Overlays.editOverlay(rollHandle, { url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png" }); + } else { - yawHandleRotation = Quat.fromVec3Degrees({ x: 90, y: 180, z: 0 }); - pitchHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); - rollHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); + yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 270, z: 0 }); + rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); + pitchHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); // TODO: fix these yawNormal = { x: 0, y: 1, z: 0 }; - pitchNormal = { x: 0, y: 0, z: 1 }; - rollNormal = { x: -1, y: 0, z: 0 }; + rollNormal = { x: 0, y: 0, z: 1 }; + pitchNormal = { x: 1, y: 0, z: 0 }; yawCorner = { x: left - rotateHandleOffset, y: bottom - rotateHandleOffset, z: far + rotateHandleOffset }; - pitchCorner = { x: left - rotateHandleOffset, + rollCorner = { x: left - rotateHandleOffset, y: top + rotateHandleOffset, z: near - rotateHandleOffset }; - rollCorner = { x: right + rotateHandleOffset, + pitchCorner = { x: right + rotateHandleOffset, y: top + rotateHandleOffset, z: far + rotateHandleOffset}; yawCenter = { x: center.x, y: bottom, z: center.z }; - pitchCenter = { x: center.x, y: center.y, z: near }; - rollCenter = { x: right, y: center.y, z: center.z}; + rollCenter = { x: center.x, y: center.y, z: near }; + pitchCenter = { x: right, y: center.y, z: center.z}; + + Overlays.editOverlay(pitchHandle, { url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png" }); + Overlays.editOverlay(rollHandle, { url: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/rotate-arrow-west-north.png" }); + } } @@ -1558,7 +1581,6 @@ SelectionDisplay = (function () { var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { - print("ROTATE_YAW"); var properties = Entities.getEntityProperties(currentSelection); var center = yawCenter; var zero = yawZero; @@ -1567,21 +1589,41 @@ SelectionDisplay = (function () { var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); var distanceFromCenter = Vec3.distance(center, result.intersection); + var snapToInner = false; if (distanceFromCenter < innerRadius) { angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; + snapToInner = true; } - Overlays.editOverlay(rotateCurrentOverlay, - { - visible: true, - start: center, - end: result.intersection - }); + // for debugging + //Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection }); var yawChange = Quat.fromVec3Degrees({ x: 0, y: angleFromZero, z: 0 }); var newRotation = Quat.multiply(yawChange, originalRotation); Entities.editEntity(currentSelection, { rotation: newRotation }); + + // update the rotation display accordingly... + var startAtCurrent = 0; + var endAtCurrent = angleFromZero; + var startAtRemainder = angleFromZero; + var endAtRemainder = 360; + if (angleFromZero < 0) { + startAtCurrent = 360 + angleFromZero; + endAtCurrent = 360; + startAtRemainder = 0; + endAtRemainder = startAtCurrent; + } + if (snapToInner) { + Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); + Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius }); + } else { + Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); + Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius }); + } + } }; @@ -1599,8 +1641,6 @@ SelectionDisplay = (function () { var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { - print("ROTATE_PITCH"); - var properties = Entities.getEntityProperties(currentSelection); var center = pitchCenter; var zero = pitchZero; @@ -1609,21 +1649,40 @@ SelectionDisplay = (function () { var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); var distanceFromCenter = Vec3.distance(center, result.intersection); + var snapToInner = false; if (distanceFromCenter < innerRadius) { angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; + snapToInner = true; } - Overlays.editOverlay(rotateCurrentOverlay, - { - visible: true, - start: center, - end: result.intersection - }); + // for debugging + //Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection }); var pitchChange = Quat.fromVec3Degrees({ x: angleFromZero, y: 0, z: 0 }); var newRotation = Quat.multiply(pitchChange, originalRotation); Entities.editEntity(currentSelection, { rotation: newRotation }); + + // update the rotation display accordingly... + var startAtCurrent = 0; + var endAtCurrent = angleFromZero; + var startAtRemainder = angleFromZero; + var endAtRemainder = 360; + if (angleFromZero < 0) { + startAtCurrent = 360 + angleFromZero; + endAtCurrent = 360; + startAtRemainder = 0; + endAtRemainder = startAtCurrent; + } + if (snapToInner) { + Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); + Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius }); + } else { + Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); + Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius }); + } } }; @@ -1640,7 +1699,6 @@ SelectionDisplay = (function () { Overlays.editOverlay(rotateOverlayCurrent, { ignoreRayIntersection: true }); var result = Overlays.findRayIntersection(pickRay); if (result.intersects) { - print("ROTATE_ROLL"); var properties = Entities.getEntityProperties(currentSelection); var center = rollCenter; var zero = rollZero; @@ -1649,21 +1707,40 @@ SelectionDisplay = (function () { var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal); var distanceFromCenter = Vec3.distance(center, result.intersection); + var snapToInner = false; if (distanceFromCenter < innerRadius) { angleFromZero = Math.floor(angleFromZero/innerSnapAngle) * innerSnapAngle; + snapToInner = true; } - Overlays.editOverlay(rotateCurrentOverlay, - { - visible: true, - start: center, - end: result.intersection - }); + // for debugging + //Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: center, end: result.intersection }); var rollChange = Quat.fromVec3Degrees({ x: 0, y: 0, z: angleFromZero }); var newRotation = Quat.multiply(rollChange, originalRotation); Entities.editEntity(currentSelection, { rotation: newRotation }); + + // update the rotation display accordingly... + var startAtCurrent = 0; + var endAtCurrent = angleFromZero; + var startAtRemainder = angleFromZero; + var endAtRemainder = 360; + if (angleFromZero < 0) { + startAtCurrent = 360 + angleFromZero; + endAtCurrent = 360; + startAtRemainder = 0; + endAtRemainder = startAtCurrent; + } + if (snapToInner) { + Overlays.editOverlay(rotateOverlayOuter, { startAt: 0, endAt: 360 }); + Overlays.editOverlay(rotateOverlayInner, { startAt: startAtRemainder, endAt: endAtRemainder }); + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: innerRadius }); + } else { + Overlays.editOverlay(rotateOverlayInner, { startAt: 0, endAt: 360 }); + Overlays.editOverlay(rotateOverlayOuter, { startAt: startAtRemainder, endAt: endAtRemainder }); + Overlays.editOverlay(rotateOverlayCurrent, { startAt: startAtCurrent, endAt: endAtCurrent, size: outerRadius }); + } } }; @@ -1904,51 +1981,14 @@ SelectionDisplay = (function () { print(" attempting to show overlays:" + somethingClicked); print(" currentRotation:" + currentRotation); - Overlays.editOverlay(rotateOverlayTarget, - { - visible: true, - rotation: overlayOrientation, - position: overlayCenter - }); - - Overlays.editOverlay(rotateOverlayInner, - { - visible: true, - rotation: overlayOrientation, - position: overlayCenter - }); - - Overlays.editOverlay(rotateOverlayOuter, - { - visible: true, - rotation: overlayOrientation, - position: overlayCenter, - startAt: currentRotation, - endAt: 360 - }); - - Overlays.editOverlay(rotateOverlayCurrent, - { - visible: true, - rotation: overlayOrientation, - position: overlayCenter, - startAt: 0, - endAt: currentRotation - }); - - Overlays.editOverlay(rotateZeroOverlay, - { - visible: true, - start: overlayCenter, - end: result.intersection - }); - - Overlays.editOverlay(rotateCurrentOverlay, - { - visible: true, - start: overlayCenter, - end: result.intersection - }); + Overlays.editOverlay(rotateOverlayTarget, { visible: true, rotation: overlayOrientation, position: overlayCenter }); + Overlays.editOverlay(rotateOverlayInner, { visible: true, rotation: overlayOrientation, position: overlayCenter }); + Overlays.editOverlay(rotateOverlayOuter, { visible: true, rotation: overlayOrientation, position: overlayCenter, startAt: 0, endAt: 360 }); + Overlays.editOverlay(rotateOverlayCurrent, { visible: true, rotation: overlayOrientation, position: overlayCenter, startAt: 0, endAt: 0 }); + + // for debugging + //Overlays.editOverlay(rotateZeroOverlay, { visible: true, start: overlayCenter, end: result.intersection }); + //Overlays.editOverlay(rotateCurrentOverlay, { visible: true, start: overlayCenter, end: result.intersection }); Overlays.editOverlay(yawHandle, { visible: false }); Overlays.editOverlay(pitchHandle, { visible: false }); From df16bd2b3c77424ba377abd1a45dce68504f592f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 21:24:09 -0700 Subject: [PATCH 26/33] cleanup comment --- examples/libraries/entitySelectionTool.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index c4ca72ad76..d39234a90f 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -492,7 +492,7 @@ SelectionDisplay = (function () { // must be BRF or BRN if (MyAvatar.position.z < center.z) { yawHandleRotation = Quat.fromVec3Degrees({ x: 270, y: 90, z: 0 }); - pitchHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 90, z: 0 }); // 0, 90, 0?? + pitchHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 90, z: 0 }); rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 0 }); yawNormal = { x: 0, y: 1, z: 0 }; From ec5ba59f0e2962a8c893fe42b46d1b2071a28c78 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 21:24:34 -0700 Subject: [PATCH 27/33] cleanup comment --- examples/libraries/entitySelectionTool.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index d39234a90f..bd02182af3 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -585,7 +585,6 @@ SelectionDisplay = (function () { rollHandleRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 180 }); pitchHandleRotation = Quat.fromVec3Degrees({ x: 180, y: 270, z: 0 }); - // TODO: fix these yawNormal = { x: 0, y: 1, z: 0 }; rollNormal = { x: 0, y: 0, z: 1 }; pitchNormal = { x: 1, y: 0, z: 0 }; From eae1e45adaecb3852de458b1d808c76c95410de2 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 9 Oct 2014 21:29:02 -0700 Subject: [PATCH 28/33] cleanup comments and dead code --- examples/libraries/entitySelectionTool.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/examples/libraries/entitySelectionTool.js b/examples/libraries/entitySelectionTool.js index bd02182af3..0f68ef4442 100644 --- a/examples/libraries/entitySelectionTool.js +++ b/examples/libraries/entitySelectionTool.js @@ -1920,7 +1920,6 @@ SelectionDisplay = (function () { var pitch = angles.x; var yaw = angles.y; var roll = angles.z; - var currentRotation; originalRotation = properties.rotation; originalPitch = pitch; @@ -1934,7 +1933,6 @@ SelectionDisplay = (function () { somethingClicked = true; overlayOrientation = yawHandleRotation; overlayCenter = yawCenter; - currentRotation = yaw; yawZero = result.intersection; rotationNormal = yawNormal; break; @@ -1944,7 +1942,6 @@ SelectionDisplay = (function () { somethingClicked = true; overlayOrientation = pitchHandleRotation; overlayCenter = pitchCenter; - currentRotation = pitch; pitchZero = result.intersection; rotationNormal = pitchNormal; break; @@ -1954,7 +1951,6 @@ SelectionDisplay = (function () { somethingClicked = true; overlayOrientation = rollHandleRotation; overlayCenter = rollCenter; - currentRotation = roll; rollZero = result.intersection; rotationNormal = rollNormal; break; @@ -1972,14 +1968,6 @@ SelectionDisplay = (function () { if (somethingClicked) { - if (currentRotation < 0) { - currentRotation = currentRotation + 360; - } - - // TODO: need to place correctly.... - print(" attempting to show overlays:" + somethingClicked); - print(" currentRotation:" + currentRotation); - Overlays.editOverlay(rotateOverlayTarget, { visible: true, rotation: overlayOrientation, position: overlayCenter }); Overlays.editOverlay(rotateOverlayInner, { visible: true, rotation: overlayOrientation, position: overlayCenter }); Overlays.editOverlay(rotateOverlayOuter, { visible: true, rotation: overlayOrientation, position: overlayCenter, startAt: 0, endAt: 360 }); From d2c7bf4334a552319e6c0cabfc6b235c6d67c6c1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Oct 2014 15:58:26 -0700 Subject: [PATCH 29/33] fix for delete of object table row --- domain-server/resources/web/js/settings.js | 27 ++++++++++++---------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 923a01a8a9..38baf223b3 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -474,21 +474,24 @@ function deleteTableRow(delete_glyphicon) { if (!isArray) { // this is a hash row, so we empty it but leave the hidden input blank so it is cleared when we save row.empty() - row.html(""); - } else if (table.find('.' + Settings.DATA_ROW_CLASS).length > 1) { - updateDataChangedForSiblingRows(row) - - // this isn't the last row - we can just remove it - row.remove() + row.html(""); } else { - // this is the last row, we can't remove it completely since we need to post an empty array - row.empty() + if (table.find('.' + Settings.DATA_ROW_CLASS).length) { + updateDataChangedForSiblingRows(row) - row.removeClass(Settings.DATA_ROW_CLASS).removeClass(Settings.NEW_ROW_CLASS) - row.addClass('empty-array-row') + // this isn't the last row - we can just remove it + row.remove() + } else { + // this is the last row, we can't remove it completely since we need to post an empty array + row.empty() - row.html(""); + row.removeClass(Settings.DATA_ROW_CLASS).removeClass(Settings.NEW_ROW_CLASS) + row.addClass('empty-array-row') + + row.html(""); + } } // we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated From a9eaf3b740537d9994b1ef11c980743ea86a1a1b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 10 Oct 2014 09:27:07 -0700 Subject: [PATCH 30/33] Update gamepad.js to work with wired 360 controller --- examples/gamepad.js | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/examples/gamepad.js b/examples/gamepad.js index 1911e71a25..e4ca03451a 100644 --- a/examples/gamepad.js +++ b/examples/gamepad.js @@ -9,7 +9,18 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -gamepad = Joysticks.joystickWithName("Wireless 360 Controller"); +var CONTROLLER_NAMES = [ + "Wireless 360 Controller", + "Controller", // Wired 360 controller +] + +for (var i = 0; i < CONTROLLER_NAMES.length; i++) { + gamepad = Joysticks.joystickWithName(CONTROLLER_NAMES[i]); + if (gamepad) { + print("Found controller: " + CONTROLLER_NAMES[i]); + break; + } +} if (!gamepad) { print("No gamepad found."); @@ -194,7 +205,6 @@ function reportAxisValue(axis, newValue, oldValue) { } function reportButtonValue(button, newValue, oldValue) { - print("The value for button " + button + " has changed from " + oldValue + " to " + newValue); if (button == BUTTON_FLY_DOWN) { flyDownButtonState = newValue; } else if (button == BUTTON_FLY_UP) { @@ -260,7 +270,9 @@ function update(dt) { updateWarp(); } -gamepad.axisValueChanged.connect(reportAxisValue); -gamepad.buttonStateChanged.connect(reportButtonValue); +if (gamepad) { + gamepad.axisValueChanged.connect(reportAxisValue); + gamepad.buttonStateChanged.connect(reportButtonValue); -Script.update.connect(update); + Script.update.connect(update); +} From 3aca910e17a9e6694d3e4beed9bc98c3be2715f7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 10 Oct 2014 09:27:15 -0700 Subject: [PATCH 31/33] remove some extra console logs --- domain-server/resources/web/js/settings.js | 1 - 1 file changed, 1 deletion(-) diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 38baf223b3..9d586c8436 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -152,7 +152,6 @@ $(document).ready(function(){ }) $('#settings-form').on('change', 'select', function(){ - console.log("Changed" + $(this)) $("input[name='" + $(this).attr('data-hidden-input') + "']").val($(this).val()).change() }) From c69ad78d3b67d1ee736e04920962fb5556a6d303 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 10 Oct 2014 09:30:51 -0700 Subject: [PATCH 32/33] Add wired 360 controller on Windows support --- examples/gamepad.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/gamepad.js b/examples/gamepad.js index e4ca03451a..4ec0309511 100644 --- a/examples/gamepad.js +++ b/examples/gamepad.js @@ -9,8 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// TODO Update to work with any controller that is plugged in. var CONTROLLER_NAMES = [ "Wireless 360 Controller", + "Controller (XBOX 360 For Windows)", "Controller", // Wired 360 controller ] From f5ee55bef4052c0f63bf9b971a735d4d9836fcb8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 10 Oct 2014 09:44:38 -0700 Subject: [PATCH 33/33] remove xbox.js now that we have gamepad.js --- examples/xbox.js | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 examples/xbox.js diff --git a/examples/xbox.js b/examples/xbox.js deleted file mode 100644 index 603e0dbf56..0000000000 --- a/examples/xbox.js +++ /dev/null @@ -1,19 +0,0 @@ -// -// xbox.js -// examples -// -// Created by Stephen Birarda on September 23, 2014 -// -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -gamepad = Joysticks.joystickWithName("Wireless 360 Controller"); - -function reportAxisValue(axis, newValue, oldValue) { - print("The value for axis " + axis + " has changed to " + newValue + ". It was " + oldValue); -} - -gamepad.axisValueChanged.connect(reportAxisValue); \ No newline at end of file