From 611779b58b0f92461fc97a1ed78c9228238a0ecb Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Fri, 8 Jul 2016 16:25:50 -0700
Subject: [PATCH 01/78] Improve gamma correction implementation in cube map
 processing

---
 libraries/gpu/src/gpu/Texture.cpp | 9 +++------
 libraries/shared/src/ColorUtils.h | 7 +++++++
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp
index f9a8f3b26b..bd0ad0ce7b 100755
--- a/libraries/gpu/src/gpu/Texture.cpp
+++ b/libraries/gpu/src/gpu/Texture.cpp
@@ -788,12 +788,9 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
                 uint pixOffsetIndex = (x + y * width) * numComponents;
 
                 // get color from texture and map to range [0, 1]
-                glm::vec3 clr(float(data[pixOffsetIndex]) * UCHAR_TO_FLOAT,
-                            float(data[pixOffsetIndex+1]) * UCHAR_TO_FLOAT,
-                            float(data[pixOffsetIndex+2]) * UCHAR_TO_FLOAT);
-
-                // Gamma correct
-                clr = ColorUtils::sRGBToLinearVec3(clr);
+                glm::vec3 clr(ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex]) * UCHAR_TO_FLOAT,
+                              ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex+1]) * UCHAR_TO_FLOAT,
+                              ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex+2]) * UCHAR_TO_FLOAT);
 
                 // scale color and add to previously accumulated coefficients
                 sphericalHarmonicsScale(shBuffB.data(), order,
diff --git a/libraries/shared/src/ColorUtils.h b/libraries/shared/src/ColorUtils.h
index 5ee9254bc9..42d36ebd4b 100644
--- a/libraries/shared/src/ColorUtils.h
+++ b/libraries/shared/src/ColorUtils.h
@@ -17,6 +17,9 @@
 
 #include "DependencyManager.h"
 
+static const float srgbToLinearLookupTable[256] =
+    { 0.0f, 0.000303526983549f, 0.000607053967098f, 0.000910580950647f, 0.0012141079342f, 0.00151763491774f, 0.00182116190129f, 0.00212468888484f, 0.00242821586839f, 0.00273174285194f, 0.00303526983549f, 0.00251584218443f, 0.00279619194822f, 0.00309396642819f, 0.00340946205345f, 0.00374296799396f, 0.00409476661624f, 0.00446513389425f, 0.00485433978143f, 0.00526264854875f, 0.00569031909303f, 0.00613760521883f, 0.00660475589722f, 0.00709201550367f, 0.00759962403765f, 0.00812781732551f, 0.00867682720861f, 0.00924688171802f, 0.00983820523704f, 0.0104510186528f, 0.0110855394981f, 0.0117419820834f, 0.0124205576216f, 0.0131214743443f, 0.0138449376117f, 0.0145911500156f, 0.0153603114768f, 0.0161526193372f, 0.0169682684465f, 0.0178074512441f, 0.0186703578377f, 0.0195571760767f, 0.0204680916222f, 0.0214032880141f, 0.0223629467344f, 0.0233472472675f, 0.0243563671578f, 0.0253904820647f, 0.026449765815f, 0.0275343904531f, 0.0286445262888f, 0.0297803419432f, 0.0309420043928f, 0.0321296790111f, 0.0333435296099f, 0.0345837184768f, 0.0358504064137f, 0.0371437527716f, 0.0384639154854f, 0.0398110511069f, 0.0411853148367f, 0.0425868605546f, 0.0440158408496f, 0.045472407048f, 0.046956709241f, 0.0484688963113f, 0.0500091159586f, 0.0515775147244f, 0.0531742380159f, 0.0547994301291f, 0.0564532342711f, 0.058135792582f, 0.0598472461555f, 0.0615877350593f, 0.063357398355f, 0.0651563741167f, 0.0669847994499f, 0.0688428105093f, 0.0707305425158f, 0.0726481297741f, 0.0745957056885f, 0.0765734027789f, 0.0785813526965f, 0.0806196862387f, 0.0826885333636f, 0.0847880232044f, 0.086918284083f, 0.0890794435236f, 0.0912716282659f, 0.0934949642776f, 0.0957495767668f, 0.0980355901944f, 0.100353128286f, 0.102702314041f, 0.10508326975f, 0.107496116997f, 0.109940976678f, 0.112417969007f, 0.114927213525f, 0.117468829116f, 0.120042934009f, 0.122649645793f, 0.125289081424f, 0.127961357236f, 0.130666588944f, 0.13340489166f, 0.136176379898f, 0.138981167581f, 0.141819368051f, 0.144691094076f, 0.147596457859f, 0.150535571041f, 0.153508544716f, 0.156515489432f, 0.1595565152f, 0.1626317315f, 0.16574124729f, 0.168885171012f, 0.172063610595f, 0.175276673468f, 0.178524466557f, 0.181807096302f, 0.185124668654f, 0.188477289086f, 0.191865062595f, 0.195288093712f, 0.198746486503f, 0.202240344578f, 0.205769771096f, 0.209334868766f, 0.212935739858f, 0.216572486205f, 0.220245209207f, 0.223954009837f, 0.227698988648f, 0.231480245773f, 0.235297880934f, 0.239151993444f, 0.243042682212f, 0.246970045747f, 0.250934182163f, 0.254935189183f, 0.258973164144f, 0.263048203998f, 0.267160405319f, 0.271309864307f, 0.27549667679f, 0.279720938228f, 0.283982743718f, 0.288282187998f, 0.292619365448f, 0.296994370096f, 0.30140729562f, 0.305858235354f, 0.310347282289f, 0.314874529074f, 0.319440068025f, 0.324043991126f, 0.32868639003f, 0.333367356062f, 0.338086980228f, 0.34284535321f, 0.347642565374f, 0.352478706774f, 0.357353867148f, 0.36226813593f, 0.367221602246f, 0.372214354918f, 0.37724648247f, 0.382318073128f, 0.387429214822f, 0.392579995191f, 0.397770501584f, 0.403000821062f, 0.408271040402f, 0.413581246099f, 0.418931524369f, 0.424321961148f, 0.4297526421f, 0.435223652615f, 0.440735077813f, 0.446287002544f, 0.451879511396f, 0.45751268869f, 0.463186618488f, 0.46890138459f, 0.474657070542f, 0.480453759632f, 0.486291534897f, 0.492170479122f, 0.498090674843f, 0.50405220435f, 0.510055149687f, 0.516099592656f, 0.522185614816f, 0.528313297489f, 0.534482721758f, 0.54069396847f, 0.546947118241f, 0.553242251452f, 0.559579448254f, 0.565958788573f, 0.572380352104f, 0.578844218319f, 0.585350466467f, 0.591899175574f, 0.598490424448f, 0.605124291677f, 0.611800855632f, 0.61852019447f, 0.625282386134f, 0.632087508355f, 0.638935638652f, 0.645826854338f, 0.652761232515f, 0.659738850081f, 0.66675978373f, 0.673824109951f, 0.680931905032f, 0.688083245062f, 0.695278205929f, 0.702516863324f, 0.709799292744f, 0.717125569488f, 0.724495768663f, 0.731909965185f, 0.739368233777f, 0.746870648974f, 0.754417285121f, 0.762008216379f, 0.76964351672f, 0.777323259932f, 0.785047519623f, 0.792816369214f, 0.800629881949f, 0.80848813089f, 0.816391188922f, 0.824339128751f, 0.832332022907f, 0.840369943747f, 0.848452963452f, 0.856581154031f, 0.864754587319f, 0.872973334984f, 0.881237468522f, 0.889547059261f, 0.897902178361f, 0.906302896816f, 0.914749285456f, 0.923241414944f, 0.931779355781f, 0.940363178305f, 0.948992952695f, 0.957668748966f, 0.966390636975f, 0.975158686423f };
+
 class ColorUtils {
 public:
     inline static glm::vec3 toVec3(const xColor& color);
@@ -33,6 +36,7 @@ public:
     inline static glm::vec4 tosRGBVec4(const glm::vec4& srgb);
     
     inline static float sRGBToLinearFloat(const float& srgb);
+    inline static float sRGB8ToLinearFloat(const uint8_t srgb);
     inline static float tosRGBFloat(const float& linear);
 };
 
@@ -82,6 +86,9 @@ inline float ColorUtils::sRGBToLinearFloat(const float &srgb) {
     
     return linearValue;
 }
+inline float ColorUtils::sRGB8ToLinearFloat(const uint8_t srgb) {
+    return srgbToLinearLookupTable[srgb];
+}
 
 // This is based upon the conversions found in section 17.3.9 of the OpenGL 4.4 specification.
 // glm::pow(color, 1.0f/2.2f) is approximate, and will cause subtle differences when used with sRGB framebuffers.

From 676f4bdfcfdbbe0e2b1d11559d458dc98d11e86a Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Fri, 29 Jul 2016 15:36:32 -0700
Subject: [PATCH 02/78] misc fixes from particles branch

---
 cmake/macros/AutoScribeShader.cmake           | 16 +++++++++-------
 cmake/macros/SetupHifiLibrary.cmake           |  3 ++-
 interface/src/ui/overlays/Overlays.h          |  2 +-
 .../src/RenderableModelEntityItem.cpp         | 19 -------------------
 .../src/RenderableModelEntityItem.h           |  1 -
 .../src/textured_particle.slv                 |  2 +-
 libraries/entities/src/EntityTreeElement.cpp  |  4 ++--
 libraries/gpu-gl/src/gpu/gl/GLShared.cpp      |  2 +-
 libraries/gpu/src/gpu/Shader.h                | 15 +++++++++------
 libraries/render-utils/CMakeLists.txt         |  4 ++--
 libraries/render-utils/src/AnimDebugDraw.cpp  |  2 ++
 libraries/render-utils/src/GeometryCache.cpp  |  2 +-
 libraries/render/CMakeLists.txt               |  2 +-
 13 files changed, 31 insertions(+), 43 deletions(-)

diff --git a/cmake/macros/AutoScribeShader.cmake b/cmake/macros/AutoScribeShader.cmake
index dfa59943d6..e586304503 100755
--- a/cmake/macros/AutoScribeShader.cmake
+++ b/cmake/macros/AutoScribeShader.cmake
@@ -23,13 +23,13 @@ function(AUTOSCRIBE_SHADER SHADER_FILE)
 
     #Extract the unique include shader paths
     set(INCLUDES ${HIFI_LIBRARIES_SHADER_INCLUDE_FILES})
-	#message(Hifi for includes ${INCLUDES})
-	foreach(EXTRA_SHADER_INCLUDE ${INCLUDES})
+    #message(${TARGET_NAME} Hifi for includes ${INCLUDES})
+    foreach(EXTRA_SHADER_INCLUDE ${INCLUDES})
       list(APPEND SHADER_INCLUDES_PATHS ${EXTRA_SHADER_INCLUDE})
     endforeach()
 
     list(REMOVE_DUPLICATES SHADER_INCLUDES_PATHS)
-	#message(ready for includes ${SHADER_INCLUDES_PATHS})
+    #message(ready for includes ${SHADER_INCLUDES_PATHS})
 
     # make the scribe include arguments
     set(SCRIBE_INCLUDES)
@@ -77,6 +77,7 @@ endfunction()
 
 
 macro(AUTOSCRIBE_SHADER_LIB)
+  set(HIFI_LIBRARIES_SHADER_INCLUDE_FILES "")
   file(RELATIVE_PATH RELATIVE_LIBRARY_DIR_PATH ${CMAKE_CURRENT_SOURCE_DIR} "${HIFI_LIBRARY_DIR}")
   foreach(HIFI_LIBRARY ${ARGN})    
     #if (NOT TARGET ${HIFI_LIBRARY})
@@ -86,7 +87,7 @@ macro(AUTOSCRIBE_SHADER_LIB)
     #file(GLOB_RECURSE HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src/*.slh)
     list(APPEND HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src)
   endforeach()
-  #message(${HIFI_LIBRARIES_SHADER_INCLUDE_FILES})
+  #message("${TARGET_NAME} ${HIFI_LIBRARIES_SHADER_INCLUDE_FILES}")
 
   file(GLOB_RECURSE SHADER_INCLUDE_FILES src/*.slh)
   file(GLOB_RECURSE SHADER_SOURCE_FILES src/*.slv src/*.slf src/*.slg)
@@ -95,13 +96,14 @@ macro(AUTOSCRIBE_SHADER_LIB)
   set(SHADERS_DIR "${CMAKE_CURRENT_BINARY_DIR}/shaders/${TARGET_NAME}")
   file(MAKE_DIRECTORY ${SHADERS_DIR})
 
-  #message(${SHADER_INCLUDE_FILES})
+  #message("${TARGET_NAME} ${SHADER_INCLUDE_FILES}")
+  set(AUTOSCRIBE_SHADER_SRC "")
   foreach(SHADER_FILE ${SHADER_SOURCE_FILES})
       AUTOSCRIBE_SHADER(${SHADER_FILE} ${SHADER_INCLUDE_FILES})
       file(TO_CMAKE_PATH "${AUTOSCRIBE_SHADER_RETURN}" AUTOSCRIBE_GENERATED_FILE)
       list(APPEND AUTOSCRIBE_SHADER_SRC ${AUTOSCRIBE_GENERATED_FILE})
   endforeach()
-  #message(${AUTOSCRIBE_SHADER_SRC})
+  #message(${TARGET_NAME} ${AUTOSCRIBE_SHADER_SRC})
 
   if (WIN32)
     source_group("Shaders" FILES ${SHADER_INCLUDE_FILES})
@@ -116,4 +118,4 @@ macro(AUTOSCRIBE_SHADER_LIB)
   # Link library shaders, if they exist
   include_directories("${SHADERS_DIR}")
 
-endmacro()
+endmacro()
\ No newline at end of file
diff --git a/cmake/macros/SetupHifiLibrary.cmake b/cmake/macros/SetupHifiLibrary.cmake
index 26c769c6e6..a10c7c11e6 100644
--- a/cmake/macros/SetupHifiLibrary.cmake
+++ b/cmake/macros/SetupHifiLibrary.cmake
@@ -54,8 +54,9 @@ macro(SETUP_HIFI_LIBRARY)
     target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE})
   endforeach()
 
-  # Don't make scribed shaders cumulative
+  # Don't make scribed shaders or QT resource files cumulative
   set(AUTOSCRIBE_SHADER_LIB_SRC "")
+  set(QT_RESOURCES_FILE "")
 
   target_glm()
     
diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h
index 99f74fa0f9..e19a6b36a9 100644
--- a/interface/src/ui/overlays/Overlays.h
+++ b/interface/src/ui/overlays/Overlays.h
@@ -93,7 +93,7 @@ public slots:
     /// successful edit, if the input id is for an unknown overlay this function will have no effect
     bool editOverlays(const QVariant& propertiesById);
 
-    /// deletes a particle
+    /// deletes an overlay
     void deleteOverlay(unsigned int id);
 
     /// get the string type of the overlay used in addOverlay
diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
index 4e8ecf3054..b0207358d2 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
@@ -176,25 +176,6 @@ void RenderableModelEntityItem::doInitialModelSimulation() {
     _needsInitialSimulation = false;
 }
 
-
-// TODO: we need a solution for changes to the postion/rotation/etc of a model...
-// this current code path only addresses that in this setup case... not the changing/moving case
-bool RenderableModelEntityItem::readyToAddToScene(RenderArgs* renderArgs) {
-    if (!_model && renderArgs) {
-        // TODO: this getModel() appears to be about 3% of model render time. We should optimize
-        PerformanceTimer perfTimer("getModel");
-        EntityTreeRenderer* renderer = static_cast<EntityTreeRenderer*>(renderArgs->_renderer);
-        getModel(renderer);
-    }
-    if (renderArgs && _model && _needsInitialSimulation && _model->isActive() && _model->isLoaded()) {
-        // make sure to simulate so everything gets set up correctly for rendering
-        doInitialModelSimulation();
-        _model->renderSetup(renderArgs);
-    }
-    bool ready = !_needsInitialSimulation && _model && _model->readyToAddToScene(renderArgs);
-    return ready;
-}
-
 class RenderableModelEntityItemMeta {
 public:
     RenderableModelEntityItemMeta(EntityItemPointer entity) : entity(entity){ }
diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h
index 339c907532..cced8df6ab 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h
@@ -40,7 +40,6 @@ public:
 
     void doInitialModelSimulation();
 
-    virtual bool readyToAddToScene(RenderArgs* renderArgs = nullptr);
     virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) override;
     virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) override;
 
diff --git a/libraries/entities-renderer/src/textured_particle.slv b/libraries/entities-renderer/src/textured_particle.slv
index 79f75187c5..cab76227c4 100644
--- a/libraries/entities-renderer/src/textured_particle.slv
+++ b/libraries/entities-renderer/src/textured_particle.slv
@@ -44,7 +44,7 @@ out vec4 varColor;
 out vec2 varTexcoord;
 
 const int NUM_VERTICES_PER_PARTICLE = 4;
-// This ordering ensures that un-rotated particles render upright in the wiewer.
+// This ordering ensures that un-rotated particles render upright in the viewer.
 const vec4 UNIT_QUAD[NUM_VERTICES_PER_PARTICLE] = vec4[NUM_VERTICES_PER_PARTICLE](
     vec4(-1.0, 1.0, 0.0, 0.0),
     vec4(-1.0, -1.0, 0.0, 0.0),
diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp
index 0523933ee6..657e0b286b 100644
--- a/libraries/entities/src/EntityTreeElement.cpp
+++ b/libraries/entities/src/EntityTreeElement.cpp
@@ -634,8 +634,8 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con
                     }
                 } else {
                     // if the entity type doesn't support a detailed intersection, then just return the non-AABox results
-                    // Never intersect with particle effect entities
-                    if (localDistance < distance && EntityTypes::getEntityTypeName(entity->getType()) != "ParticleEffect") {
+                    // Never intersect with particle entities
+                    if (localDistance < distance && entity->getType() != EntityTypes::ParticleEffect) {
                         distance = localDistance;
                         face = localFace;
                         surfaceNormal = localSurfaceNormal;
diff --git a/libraries/gpu-gl/src/gpu/gl/GLShared.cpp b/libraries/gpu-gl/src/gpu/gl/GLShared.cpp
index 8f234ca6b4..35cf9b83ba 100644
--- a/libraries/gpu-gl/src/gpu/gl/GLShared.cpp
+++ b/libraries/gpu-gl/src/gpu/gl/GLShared.cpp
@@ -596,7 +596,7 @@ int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindin
         }
 
         Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER);
-        buffers.insert(Shader::Slot(name, binding, element, Resource::BUFFER));
+        buffers.insert(Shader::Slot(name, binding, element, Resource::BUFFER, size));
     }
     return buffersCount;
 }
diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h
index 9072bf23a9..a741eafd40 100755
--- a/libraries/gpu/src/gpu/Shader.h
+++ b/libraries/gpu/src/gpu/Shader.h
@@ -53,19 +53,22 @@ public:
         int32 _location{INVALID_LOCATION};
         Element _element;
         uint16 _resourceType{Resource::BUFFER};
+        uint32 _size { 0 };
  
-        Slot(const Slot& s) : _name(s._name), _location(s._location), _element(s._element), _resourceType(s._resourceType) {}
-        Slot(Slot&& s) : _name(s._name), _location(s._location), _element(s._element), _resourceType(s._resourceType) {}
-        Slot(const std::string& name, int32 location, const Element& element, uint16 resourceType = Resource::BUFFER) :
-             _name(name), _location(location), _element(element), _resourceType(resourceType) {}
+        Slot(const Slot& s) : _name(s._name), _location(s._location), _element(s._element), _resourceType(s._resourceType), _size(s._size) {}
+        Slot(Slot&& s) : _name(s._name), _location(s._location), _element(s._element), _resourceType(s._resourceType), _size(s._size) {}
+        Slot(const std::string& name, int32 location, const Element& element, uint16 resourceType = Resource::BUFFER, uint32 size = 0) :
+        _name(name), _location(location), _element(element), _resourceType(resourceType), _size(size) {}
         Slot(const std::string& name) : _name(name) {}
-        
+
         Slot& operator= (const Slot& s) {
             _name = s._name;
             _location = s._location;
             _element = s._element;
             _resourceType = s._resourceType;
-            return (*this); }
+            _size = s._size;
+            return (*this);
+        }
     };
 
     class Binding {
diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt
index 2a7d33e33a..7b272f7b7d 100644
--- a/libraries/render-utils/CMakeLists.txt
+++ b/libraries/render-utils/CMakeLists.txt
@@ -1,9 +1,9 @@
 set(TARGET_NAME render-utils)
-AUTOSCRIBE_SHADER_LIB(gpu model render procedural)
+AUTOSCRIBE_SHADER_LIB(gpu model render)
 # pull in the resources.qrc file
 qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc")
 setup_hifi_library(Widgets OpenGL Network Qml Quick Script)
-link_hifi_libraries(shared gpu procedural model model-networking render animation fbx)
+link_hifi_libraries(shared gpu model model-networking render animation fbx)
 
 target_nsight()
 target_oglplus()
diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp
index 11c43eaee4..f1443f7e4d 100644
--- a/libraries/render-utils/src/AnimDebugDraw.cpp
+++ b/libraries/render-utils/src/AnimDebugDraw.cpp
@@ -15,6 +15,8 @@
 #include "GLMHelpers.h"
 #include "DebugDraw.h"
 
+#include <qmath.h>
+
 #include "AnimDebugDraw.h"
 
 struct Vertex {
diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp
index cebd8ad37f..bead7549db 100644
--- a/libraries/render-utils/src/GeometryCache.cpp
+++ b/libraries/render-utils/src/GeometryCache.cpp
@@ -11,7 +11,7 @@
 
 #include "GeometryCache.h"
 
-
+#include <qmath.h>
 #include <cmath>
 
 #include <QtCore/QThreadPool>
diff --git a/libraries/render/CMakeLists.txt b/libraries/render/CMakeLists.txt
index c5cfdf3668..76fc8303ce 100644
--- a/libraries/render/CMakeLists.txt
+++ b/libraries/render/CMakeLists.txt
@@ -1,5 +1,5 @@
 set(TARGET_NAME render)
-AUTOSCRIBE_SHADER_LIB(gpu model procedural)
+AUTOSCRIBE_SHADER_LIB(gpu model)
 setup_hifi_library()
 link_hifi_libraries(shared gpu model)
 

From 217a102926c39e732437559a60a87f1a8938864a Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Mon, 1 Aug 2016 13:15:55 -0700
Subject: [PATCH 03/78] working on loading fade

---
 libraries/render-utils/src/MeshPartPayload.cpp | 16 +++++++++++++++-
 libraries/render-utils/src/MeshPartPayload.h   |  7 +++++++
 2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp
index cb6c73f414..da9ba50271 100644
--- a/libraries/render-utils/src/MeshPartPayload.cpp
+++ b/libraries/render-utils/src/MeshPartPayload.cpp
@@ -329,6 +329,8 @@ ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int par
 
     updateTransform(transform, offsetTransform);
     initCache();
+
+    _fadeStartTime = usecTimestampNow();
 }
 
 void ModelMeshPartPayload::initCache() {
@@ -352,6 +354,11 @@ void ModelMeshPartPayload::initCache() {
 
 }
 
+float ModelMeshPartPayload::calcFadeRatio() const {
+    const float FADE_TIME = 0.5f;
+    float t =  std::min(((float)(usecTimestampNow() - _fadeStartTime)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f);
+    return -0.5f * (cosf(M_PI*t) - 1.0f);
+}
 
 void ModelMeshPartPayload::notifyLocationChanged() {
 
@@ -392,6 +399,10 @@ ItemKey ModelMeshPartPayload::getKey() const {
         }
     }
 
+    if (calcFadeRatio() < 1.0f) {
+        builder.withTransparent();
+    }
+
     return builder.build();
 }
 
@@ -429,7 +440,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const {
         drawMaterialKey = _drawMaterial->getKey();
     }
 
-    bool isTranslucent = drawMaterialKey.isTranslucent();
+    bool isTranslucent = drawMaterialKey.isTranslucent() || calcFadeRatio() < 1.0f;
     bool hasTangents = drawMaterialKey.isNormalMap() && !mesh.tangents.isEmpty();
     bool hasSpecular = drawMaterialKey.isMetallicMap();
     bool hasLightmap = drawMaterialKey.isLightmapMap();
@@ -541,6 +552,9 @@ void ModelMeshPartPayload::render(RenderArgs* args) const {
     // apply material properties
     bindMaterial(batch, locations);
 
+    // model fading
+    batch._glColor4f(1.0f, 1.0f, 1.0f, calcFadeRatio());
+
     if (args) {
         args->_details._materialSwitches++;
     }
diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h
index 41869ec7e3..d5c59a7967 100644
--- a/libraries/render-utils/src/MeshPartPayload.h
+++ b/libraries/render-utils/src/MeshPartPayload.h
@@ -81,6 +81,9 @@ public:
     void notifyLocationChanged() override;
     void updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector<glm::mat4>& clusterMatrices);
 
+    // Entity fade in
+    float calcFadeRatio() const;
+
     // Render Item interface
     render::ItemKey getKey() const override;
     render::ShapeKey getShapeKey() const override; // shape interface
@@ -99,6 +102,10 @@ public:
 
     bool _isSkinned{ false };
     bool _isBlendShaped{ false };
+
+private:
+    quint64 _fadeStartTime { usecTimestampNow() };
+    bool _hasFadeStarted { false };
 };
 
 namespace render {

From 313ba87fce2788ed441c3d3d377f828564e7b91c Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Mon, 1 Aug 2016 14:47:20 -0700
Subject: [PATCH 04/78] fade on texture load

---
 libraries/render-utils/src/MeshPartPayload.cpp | 15 +++++----------
 libraries/render-utils/src/MeshPartPayload.h   |  7 +++++--
 libraries/render-utils/src/Model.cpp           |  4 ++++
 3 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp
index da9ba50271..99fedefc99 100644
--- a/libraries/render-utils/src/MeshPartPayload.cpp
+++ b/libraries/render-utils/src/MeshPartPayload.cpp
@@ -329,8 +329,6 @@ ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int par
 
     updateTransform(transform, offsetTransform);
     initCache();
-
-    _fadeStartTime = usecTimestampNow();
 }
 
 void ModelMeshPartPayload::initCache() {
@@ -357,7 +355,7 @@ void ModelMeshPartPayload::initCache() {
 float ModelMeshPartPayload::calcFadeRatio() const {
     const float FADE_TIME = 0.5f;
     float t =  std::min(((float)(usecTimestampNow() - _fadeStartTime)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f);
-    return -0.5f * (cosf(M_PI*t) - 1.0f);
+    return -(cosf(M_PI_2*t) - 1.0f);
 }
 
 void ModelMeshPartPayload::notifyLocationChanged() {
@@ -495,9 +493,9 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const {
         batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2));
     }
 
-    // TODO: Get rid of that extra call
-    if (!_hasColorAttrib) {
-        batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+    float fadeRatio = calcFadeRatio();
+    if (!_hasColorAttrib || fadeRatio < 1.0f) {
+        batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio);
     }
 }
 
@@ -528,7 +526,7 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline:
 void ModelMeshPartPayload::render(RenderArgs* args) const {
     PerformanceTimer perfTimer("ModelMeshPartPayload::render");
 
-    if (!_model->_readyWhenAdded || !_model->_isVisible) {
+    if (!_model->_readyWhenAdded || !_model->_isVisible || !_hasStartedFade) {
         return; // bail asap
     }
 
@@ -552,9 +550,6 @@ void ModelMeshPartPayload::render(RenderArgs* args) const {
     // apply material properties
     bindMaterial(batch, locations);
 
-    // model fading
-    batch._glColor4f(1.0f, 1.0f, 1.0f, calcFadeRatio());
-
     if (args) {
         args->_details._materialSwitches++;
     }
diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h
index d5c59a7967..4be6132122 100644
--- a/libraries/render-utils/src/MeshPartPayload.h
+++ b/libraries/render-utils/src/MeshPartPayload.h
@@ -83,6 +83,9 @@ public:
 
     // Entity fade in
     float calcFadeRatio() const;
+    void startFade() { _fadeStartTime = usecTimestampNow(); }
+    bool hasStartedFade() { return _hasStartedFade; }
+    void setHasStartedFade(bool hasStartedFade) { _hasStartedFade = hasStartedFade; }
 
     // Render Item interface
     render::ItemKey getKey() const override;
@@ -104,8 +107,8 @@ public:
     bool _isBlendShaped{ false };
 
 private:
-    quint64 _fadeStartTime { usecTimestampNow() };
-    bool _hasFadeStarted { false };
+    quint64 _fadeStartTime { 0 };
+    bool _hasStartedFade { false };
 };
 
 namespace render {
diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index b04a1d8023..cc7178587f 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -207,6 +207,10 @@ void Model::updateRenderItems() {
         render::PendingChanges pendingChanges;
         foreach (auto itemID, self->_modelMeshRenderItems.keys()) {
             pendingChanges.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, modelMeshOffset, deleteGeometryCounter](ModelMeshPartPayload& data) {
+                if (!data.hasStartedFade() && data._model && data._model->isLoaded() && data._model->getGeometry()->areTexturesLoaded()) {
+                    data.startFade();
+                    data.setHasStartedFade(true);
+                }
                 // Ensure the model geometry was not reset between frames
                 if (data._model && data._model->isLoaded() && deleteGeometryCounter == data._model->_deleteGeometryCounter) {
                     // lazy update of cluster matrices used for rendering.  We need to update them here, so we can correctly update the bounding box.

From ae0b9ea9a32ea85b4a73df33d37ec8e0404128cc Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Mon, 1 Aug 2016 15:26:04 -0700
Subject: [PATCH 05/78] fade wireframes

---
 libraries/render-utils/src/MeshPartPayload.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp
index 99fedefc99..030b5699aa 100644
--- a/libraries/render-utils/src/MeshPartPayload.cpp
+++ b/libraries/render-utils/src/MeshPartPayload.cpp
@@ -438,7 +438,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const {
         drawMaterialKey = _drawMaterial->getKey();
     }
 
-    bool isTranslucent = drawMaterialKey.isTranslucent() || calcFadeRatio() < 1.0f;
+    bool isTranslucent = drawMaterialKey.isTranslucent();
     bool hasTangents = drawMaterialKey.isNormalMap() && !mesh.tangents.isEmpty();
     bool hasSpecular = drawMaterialKey.isMetallicMap();
     bool hasLightmap = drawMaterialKey.isLightmapMap();
@@ -452,7 +452,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const {
     }
 
     ShapeKey::Builder builder;
-    if (isTranslucent) {
+    if (isTranslucent || calcFadeRatio() < 0.9f) {
         builder.withTranslucent();
     }
     if (hasTangents) {

From 6154aaddda9c911a202e4a8a2b058fe3005cfa77 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Mon, 1 Aug 2016 16:19:03 -0700
Subject: [PATCH 06/78] try to fix linux build

---
 libraries/render-utils/src/MeshPartPayload.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp
index 030b5699aa..48c1c88757 100644
--- a/libraries/render-utils/src/MeshPartPayload.cpp
+++ b/libraries/render-utils/src/MeshPartPayload.cpp
@@ -355,7 +355,7 @@ void ModelMeshPartPayload::initCache() {
 float ModelMeshPartPayload::calcFadeRatio() const {
     const float FADE_TIME = 0.5f;
     float t =  std::min(((float)(usecTimestampNow() - _fadeStartTime)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f);
-    return -(cosf(M_PI_2*t) - 1.0f);
+    return -(cosf((float)M_PI_2 * t) - 1.0f);
 }
 
 void ModelMeshPartPayload::notifyLocationChanged() {

From 0b5c7909b829f48dc61954b62fef61d1ad8e9a38 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Mon, 1 Aug 2016 18:45:25 -0700
Subject: [PATCH 07/78] a calculated change

---
 libraries/render-utils/src/MeshPartPayload.cpp | 8 ++++----
 libraries/render-utils/src/MeshPartPayload.h   | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp
index 48c1c88757..9f107bd3e1 100644
--- a/libraries/render-utils/src/MeshPartPayload.cpp
+++ b/libraries/render-utils/src/MeshPartPayload.cpp
@@ -352,7 +352,7 @@ void ModelMeshPartPayload::initCache() {
 
 }
 
-float ModelMeshPartPayload::calcFadeRatio() const {
+float ModelMeshPartPayload::calculateFadeRatio() const {
     const float FADE_TIME = 0.5f;
     float t =  std::min(((float)(usecTimestampNow() - _fadeStartTime)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f);
     return -(cosf((float)M_PI_2 * t) - 1.0f);
@@ -397,7 +397,7 @@ ItemKey ModelMeshPartPayload::getKey() const {
         }
     }
 
-    if (calcFadeRatio() < 1.0f) {
+    if (calculateFadeRatio() < 1.0f) {
         builder.withTransparent();
     }
 
@@ -452,7 +452,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const {
     }
 
     ShapeKey::Builder builder;
-    if (isTranslucent || calcFadeRatio() < 0.9f) {
+    if (isTranslucent || calculateFadeRatio() < 0.9f) {
         builder.withTranslucent();
     }
     if (hasTangents) {
@@ -493,7 +493,7 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const {
         batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2));
     }
 
-    float fadeRatio = calcFadeRatio();
+    float fadeRatio = calculateFadeRatio();
     if (!_hasColorAttrib || fadeRatio < 1.0f) {
         batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio);
     }
diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h
index 4be6132122..e0181a366d 100644
--- a/libraries/render-utils/src/MeshPartPayload.h
+++ b/libraries/render-utils/src/MeshPartPayload.h
@@ -82,7 +82,7 @@ public:
     void updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector<glm::mat4>& clusterMatrices);
 
     // Entity fade in
-    float calcFadeRatio() const;
+    float calculateFadeRatio() const;
     void startFade() { _fadeStartTime = usecTimestampNow(); }
     bool hasStartedFade() { return _hasStartedFade; }
     void setHasStartedFade(bool hasStartedFade) { _hasStartedFade = hasStartedFade; }

From 74f11eb70b5692eb1b0bca523cf709ab39250747 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Tue, 2 Aug 2016 10:50:31 -0700
Subject: [PATCH 08/78] try a different easing function

---
 libraries/render-utils/src/MeshPartPayload.cpp | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp
index 9f107bd3e1..63068f7796 100644
--- a/libraries/render-utils/src/MeshPartPayload.cpp
+++ b/libraries/render-utils/src/MeshPartPayload.cpp
@@ -353,9 +353,13 @@ void ModelMeshPartPayload::initCache() {
 }
 
 float ModelMeshPartPayload::calculateFadeRatio() const {
-    const float FADE_TIME = 0.5f;
-    float t =  std::min(((float)(usecTimestampNow() - _fadeStartTime)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f);
-    return -(cosf((float)M_PI_2 * t) - 1.0f);
+    const float FADE_TIME = 1.0f;
+    float t =  2.0f * std::min(((float)(usecTimestampNow() - _fadeStartTime)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f);
+    float fadeRatio = (t < 1.0f) ? 0.5f * powf(2.0f, 10.0f * (t - 1.0f)) : 0.5f * (-pow(2.0f, -10.0f * (t - 1.0f)) + 2.0f);
+
+    // The easing function isn't exactly 1 at t = 2, so we need to scale the whole function up slightly
+    const float EASING_SCALE = 1.001f;
+    return std::min(EASING_SCALE * fadeRatio, 1.0f);
 }
 
 void ModelMeshPartPayload::notifyLocationChanged() {

From 3d08502080f40477a42d9b885216432a29fe0983 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Tue, 2 Aug 2016 10:50:57 -0700
Subject: [PATCH 09/78] space

---
 libraries/render-utils/src/MeshPartPayload.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp
index 63068f7796..0616ae20f7 100644
--- a/libraries/render-utils/src/MeshPartPayload.cpp
+++ b/libraries/render-utils/src/MeshPartPayload.cpp
@@ -354,7 +354,7 @@ void ModelMeshPartPayload::initCache() {
 
 float ModelMeshPartPayload::calculateFadeRatio() const {
     const float FADE_TIME = 1.0f;
-    float t =  2.0f * std::min(((float)(usecTimestampNow() - _fadeStartTime)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f);
+    float t = 2.0f * std::min(((float)(usecTimestampNow() - _fadeStartTime)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f);
     float fadeRatio = (t < 1.0f) ? 0.5f * powf(2.0f, 10.0f * (t - 1.0f)) : 0.5f * (-pow(2.0f, -10.0f * (t - 1.0f)) + 2.0f);
 
     // The easing function isn't exactly 1 at t = 2, so we need to scale the whole function up slightly

From ad5dec829c22695afd40a69406900d4e3e968129 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Tue, 2 Aug 2016 10:52:51 -0700
Subject: [PATCH 10/78] why did I change that

---
 libraries/render-utils/src/MeshPartPayload.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp
index 0616ae20f7..83b25a49e3 100644
--- a/libraries/render-utils/src/MeshPartPayload.cpp
+++ b/libraries/render-utils/src/MeshPartPayload.cpp
@@ -456,7 +456,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const {
     }
 
     ShapeKey::Builder builder;
-    if (isTranslucent || calculateFadeRatio() < 0.9f) {
+    if (isTranslucent || calculateFadeRatio() < 1.0f) {
         builder.withTranslucent();
     }
     if (hasTangents) {

From 5de21982be4b1e587a6bd6ece6c9c1bc96137ffd Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Tue, 2 Aug 2016 11:44:17 -0700
Subject: [PATCH 11/78] fix linux build

---
 libraries/render-utils/src/MeshPartPayload.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp
index 83b25a49e3..f599e9df67 100644
--- a/libraries/render-utils/src/MeshPartPayload.cpp
+++ b/libraries/render-utils/src/MeshPartPayload.cpp
@@ -355,7 +355,7 @@ void ModelMeshPartPayload::initCache() {
 float ModelMeshPartPayload::calculateFadeRatio() const {
     const float FADE_TIME = 1.0f;
     float t = 2.0f * std::min(((float)(usecTimestampNow() - _fadeStartTime)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f);
-    float fadeRatio = (t < 1.0f) ? 0.5f * powf(2.0f, 10.0f * (t - 1.0f)) : 0.5f * (-pow(2.0f, -10.0f * (t - 1.0f)) + 2.0f);
+    float fadeRatio = (t < 1.0f) ? 0.5f * powf(2.0f, 10.0f * (t - 1.0f)) : 0.5f * (-powf(2.0f, -10.0f * (t - 1.0f)) + 2.0f);
 
     // The easing function isn't exactly 1 at t = 2, so we need to scale the whole function up slightly
     const float EASING_SCALE = 1.001f;

From d63a0ef08f36be368dc61448a4c8507b5c9b3942 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Tue, 2 Aug 2016 18:09:42 -0700
Subject: [PATCH 12/78] working on making other entities transparent

---
 .../src/RenderableEntityItem.cpp               |  4 ++--
 .../src/RenderableShapeEntityItem.cpp          |  9 +++++++--
 .../src/RenderableShapeEntityItem.h            |  6 +++++-
 libraries/entities/src/EntityItem.h            |  1 +
 libraries/render-utils/src/MeshPartPayload.cpp | 18 +++++-------------
 libraries/render-utils/src/MeshPartPayload.h   |  1 -
 libraries/shared/src/Interpolate.cpp           | 12 ++++++++++++
 libraries/shared/src/Interpolate.h             |  4 ++++
 8 files changed, 36 insertions(+), 19 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp
index 011675fc82..d49eacdf7b 100644
--- a/libraries/entities-renderer/src/RenderableEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp
@@ -1,4 +1,4 @@
-//
+ //
 //  RenderableEntityItem.cpp
 //  interface/src
 //
@@ -19,7 +19,7 @@ namespace render {
             if (payload->entity->getType() == EntityTypes::Light) {
                 return ItemKey::Builder::light();
             }
-            if (payload && payload->entity->getType() == EntityTypes::PolyLine) {
+            if (payload && (payload->entity->getType() == EntityTypes::PolyLine || payload->entity->isTransparent())) {
                 return ItemKey::Builder::transparentShape();
             }
         }
diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
index 48ad05a714..61e152f5ac 100644
--- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
@@ -40,7 +40,6 @@ static std::array<GeometryCache::Shape, entity::NUM_SHAPES> MAPPING { {
     GeometryCache::Cylinder,
 } };
 
-
 RenderableShapeEntityItem::Pointer RenderableShapeEntityItem::baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {
     Pointer entity = std::make_shared<RenderableShapeEntityItem>(entityID);
     entity->setProperties(properties);
@@ -106,7 +105,13 @@ void RenderableShapeEntityItem::render(RenderArgs* args) {
         DependencyManager::get<GeometryCache>()->renderShape(batch, MAPPING[_shape]);
     } else {
         // FIXME, support instanced multi-shape rendering using multidraw indirect
-        DependencyManager::get<GeometryCache>()->renderSolidShapeInstance(batch, MAPPING[_shape], color);
+        float fadeRatio = Interpolate::calculateFadeRatio(_fadeStartTime);
+        if (fadeRatio < 1.0f) {
+            color = glm::vec4(0.0f, 0.0f, 1.0f, 0.5f);
+            DependencyManager::get<GeometryCache>()->renderSolidShapeInstance(batch, MAPPING[_shape], color);
+        } else {
+            DependencyManager::get<GeometryCache>()->renderSolidShapeInstance(batch, MAPPING[_shape], color);
+        }
     }
 
 
diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h
index b18370b13c..7bfb411874 100644
--- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h
@@ -11,6 +11,7 @@
 
 #include <ShapeEntityItem.h>
 #include <procedural/Procedural.h>
+#include <Interpolate.h>
 
 #include "RenderableEntityItem.h"
 
@@ -21,15 +22,18 @@ public:
     static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
     static EntityItemPointer boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
     static EntityItemPointer sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
-    RenderableShapeEntityItem(const EntityItemID& entityItemID) : ShapeEntityItem(entityItemID) {}
+    RenderableShapeEntityItem(const EntityItemID& entityItemID) : ShapeEntityItem(entityItemID), _fadeStartTime(usecTimestampNow()) {}
 
     void render(RenderArgs* args) override;
     void setUserData(const QString& value) override;
 
+    bool isTransparent() override { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; }
+
     SIMPLE_RENDERABLE();
 
 private:
     QSharedPointer<Procedural> _procedural;
+    quint64 _fadeStartTime { 0 };
 };
 
 
diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h
index 9fa13690f1..f1715a2525 100644
--- a/libraries/entities/src/EntityItem.h
+++ b/libraries/entities/src/EntityItem.h
@@ -435,6 +435,7 @@ public:
     QUuid getOwningAvatarID() const { return _owningAvatarID; }
     void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; }
 
+    virtual bool isTransparent() { return false; }
 
 protected:
 
diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp
index f599e9df67..fe914f4d1a 100644
--- a/libraries/render-utils/src/MeshPartPayload.cpp
+++ b/libraries/render-utils/src/MeshPartPayload.cpp
@@ -13,6 +13,8 @@
 
 #include <PerfStat.h>
 
+#include <Interpolate.h>
+
 #include "DeferredLightingEffect.h"
 #include "Model.h"
 
@@ -352,16 +354,6 @@ void ModelMeshPartPayload::initCache() {
 
 }
 
-float ModelMeshPartPayload::calculateFadeRatio() const {
-    const float FADE_TIME = 1.0f;
-    float t = 2.0f * std::min(((float)(usecTimestampNow() - _fadeStartTime)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f);
-    float fadeRatio = (t < 1.0f) ? 0.5f * powf(2.0f, 10.0f * (t - 1.0f)) : 0.5f * (-powf(2.0f, -10.0f * (t - 1.0f)) + 2.0f);
-
-    // The easing function isn't exactly 1 at t = 2, so we need to scale the whole function up slightly
-    const float EASING_SCALE = 1.001f;
-    return std::min(EASING_SCALE * fadeRatio, 1.0f);
-}
-
 void ModelMeshPartPayload::notifyLocationChanged() {
 
 }
@@ -401,7 +393,7 @@ ItemKey ModelMeshPartPayload::getKey() const {
         }
     }
 
-    if (calculateFadeRatio() < 1.0f) {
+    if (Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f) {
         builder.withTransparent();
     }
 
@@ -456,7 +448,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const {
     }
 
     ShapeKey::Builder builder;
-    if (isTranslucent || calculateFadeRatio() < 1.0f) {
+    if (isTranslucent || Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f) {
         builder.withTranslucent();
     }
     if (hasTangents) {
@@ -497,7 +489,7 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const {
         batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2));
     }
 
-    float fadeRatio = calculateFadeRatio();
+    float fadeRatio = Interpolate::calculateFadeRatio(_fadeStartTime);
     if (!_hasColorAttrib || fadeRatio < 1.0f) {
         batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio);
     }
diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h
index e0181a366d..54878f3352 100644
--- a/libraries/render-utils/src/MeshPartPayload.h
+++ b/libraries/render-utils/src/MeshPartPayload.h
@@ -82,7 +82,6 @@ public:
     void updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector<glm::mat4>& clusterMatrices);
 
     // Entity fade in
-    float calculateFadeRatio() const;
     void startFade() { _fadeStartTime = usecTimestampNow(); }
     bool hasStartedFade() { return _hasStartedFade; }
     void setHasStartedFade(bool hasStartedFade) { _hasStartedFade = hasStartedFade; }
diff --git a/libraries/shared/src/Interpolate.cpp b/libraries/shared/src/Interpolate.cpp
index bef69c9a33..b20c7b8fac 100644
--- a/libraries/shared/src/Interpolate.cpp
+++ b/libraries/shared/src/Interpolate.cpp
@@ -14,6 +14,8 @@
 #include <assert.h>
 #include <math.h>
 
+#include "NumericalConstants.h"
+
 float Interpolate::bezierInterpolate(float y1, float y2, float y3, float u) {
     // https://en.wikipedia.org/wiki/Bezier_curve
     assert(0.0f <= u && u <= 1.0f);
@@ -58,3 +60,13 @@ float Interpolate::interpolate3Points(float y1, float y2, float y3, float u) {
         }
     }
 }
+
+float Interpolate::calculateFadeRatio(quint64 start) {
+    const float FADE_TIME = 1.0f;
+    float t = 2.0f * std::min(((float)(usecTimestampNow() - start)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f);
+    float fadeRatio = (t < 1.0f) ? 0.5f * powf(2.0f, 10.0f * (t - 1.0f)) : 0.5f * (-powf(2.0f, -10.0f * (t - 1.0f)) + 2.0f);
+
+    // The easing function isn't exactly 1 at t = 2, so we need to scale the whole function up slightly
+    const float EASING_SCALE = 1.001f;
+    return std::min(EASING_SCALE * fadeRatio, 1.0f);
+}
\ No newline at end of file
diff --git a/libraries/shared/src/Interpolate.h b/libraries/shared/src/Interpolate.h
index 316bee1339..a9fa5baad2 100644
--- a/libraries/shared/src/Interpolate.h
+++ b/libraries/shared/src/Interpolate.h
@@ -12,6 +12,8 @@
 #ifndef hifi_Interpolate_h
 #define hifi_Interpolate_h
 
+#include "SharedUtil.h"
+
 class Interpolate {
 
 public:
@@ -22,6 +24,8 @@ public:
     // Interpolate at position u [0.0 - 1.0] between y values equally spaced along the x-axis such that the interpolated values
     // pass through all three y values. Return value lies wholly within the range of y values passed in.
     static float interpolate3Points(float y1, float y2, float y3, float u);
+
+    static float calculateFadeRatio(quint64 start);
 };
 
 #endif // hifi_Interpolate_h

From 2eb0c7735f7a52bd1c6acb12fbd42ff4398028b9 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Wed, 3 Aug 2016 13:30:05 -0700
Subject: [PATCH 13/78] working on fading shape entities

---
 interface/src/avatar/Avatar.cpp               |  2 +-
 interface/src/ui/overlays/Cube3DOverlay.cpp   |  4 +--
 interface/src/ui/overlays/Line3DOverlay.cpp   |  4 +--
 .../src/ui/overlays/Rectangle3DOverlay.cpp    |  2 +-
 interface/src/ui/overlays/Shape3DOverlay.cpp  |  2 +-
 interface/src/ui/overlays/Sphere3DOverlay.cpp |  2 +-
 interface/src/ui/overlays/Web3DOverlay.cpp    |  2 +-
 .../src/RenderableShapeEntityItem.cpp         | 12 +++----
 .../src/RenderableShapeEntityItem.h           |  4 +--
 .../src/RenderableTextEntityItem.cpp          |  2 +-
 .../src/RenderableWebEntityItem.cpp           |  5 +--
 .../render-utils/src/DeferredBufferWrite.slh  |  2 ++
 libraries/render-utils/src/GeometryCache.cpp  | 34 +++++++++++++------
 libraries/render-utils/src/GeometryCache.h    | 30 ++++++++--------
 .../render-utils/src/simple_textured.slf      | 28 ++++++++++-----
 tests/gpu-test/src/TestFloorTexture.cpp       |  2 +-
 16 files changed, 81 insertions(+), 56 deletions(-)

diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp
index 4d9481f002..018a9bb2fe 100644
--- a/interface/src/avatar/Avatar.cpp
+++ b/interface/src/avatar/Avatar.cpp
@@ -776,7 +776,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const
 
         {
             PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderBevelCornersRect");
-            DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, true, true, true);
+            DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, false, true, true, true);
             DependencyManager::get<GeometryCache>()->renderBevelCornersRect(batch, left, bottom, width, height,
                 bevelDistance, backgroundColor);
         }
diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp
index f72fb8d920..a61e442436 100644
--- a/interface/src/ui/overlays/Cube3DOverlay.cpp
+++ b/interface/src/ui/overlays/Cube3DOverlay.cpp
@@ -47,7 +47,7 @@ void Cube3DOverlay::render(RenderArgs* args) {
         auto geometryCache = DependencyManager::get<GeometryCache>();
         auto pipeline = args->_pipeline;
         if (!pipeline) {
-            pipeline = _isSolid ? geometryCache->getShapePipeline() : geometryCache->getWireShapePipeline();
+            pipeline = _isSolid ? geometryCache->getOpaqueShapePipeline() : geometryCache->getWireShapePipeline();
         }
 
         if (_isSolid) {
@@ -55,7 +55,7 @@ void Cube3DOverlay::render(RenderArgs* args) {
             batch->setModelTransform(transform);
             geometryCache->renderSolidCubeInstance(*batch, cubeColor, pipeline);
         } else {
-            geometryCache->bindSimpleProgram(*batch, false, false, true, true);
+            geometryCache->bindSimpleProgram(*batch, false, false, false, true, true);
             if (getIsDashedLine()) {
                 transform.setScale(1.0f);
                 batch->setModelTransform(transform);
diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp
index c9a8b19f6a..a05783fea2 100644
--- a/interface/src/ui/overlays/Line3DOverlay.cpp
+++ b/interface/src/ui/overlays/Line3DOverlay.cpp
@@ -57,12 +57,12 @@ void Line3DOverlay::render(RenderArgs* args) {
         auto geometryCache = DependencyManager::get<GeometryCache>();
         if (getIsDashedLine()) {
             // TODO: add support for color to renderDashedLine()
-            geometryCache->bindSimpleProgram(*batch, false, false, true, true);
+            geometryCache->bindSimpleProgram(*batch, false, false, false, true, true);
             geometryCache->renderDashedLine(*batch, _start, _end, colorv4, _geometryCacheID);
         } else if (_glow > 0.0f) {
             geometryCache->renderGlowLine(*batch, _start, _end, colorv4, _glow, _glowWidth, _geometryCacheID);
         } else {
-            geometryCache->bindSimpleProgram(*batch, false, false, true, true);
+            geometryCache->bindSimpleProgram(*batch, false, false, false, true, true);
             geometryCache->renderLine(*batch, _start, _end, colorv4, _geometryCacheID);
         }
     }
diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp
index 75d7ec565c..35c479dce6 100644
--- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp
+++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp
@@ -61,7 +61,7 @@ void Rectangle3DOverlay::render(RenderArgs* args) {
             geometryCache->bindSimpleProgram(*batch);
             geometryCache->renderQuad(*batch, topLeft, bottomRight, rectangleColor);
         } else {
-            geometryCache->bindSimpleProgram(*batch, false, false, true, true);
+            geometryCache->bindSimpleProgram(*batch, false, false, false, true, true);
             if (getIsDashedLine()) {
                 glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f);
                 glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f);
diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp
index cd07385aab..2556bc84aa 100644
--- a/interface/src/ui/overlays/Shape3DOverlay.cpp
+++ b/interface/src/ui/overlays/Shape3DOverlay.cpp
@@ -47,7 +47,7 @@ void Shape3DOverlay::render(RenderArgs* args) {
         auto geometryCache = DependencyManager::get<GeometryCache>();
         auto pipeline = args->_pipeline;
         if (!pipeline) {
-            pipeline = _isSolid ? geometryCache->getShapePipeline() : geometryCache->getWireShapePipeline();
+            pipeline = _isSolid ? geometryCache->getOpaqueShapePipeline() : geometryCache->getWireShapePipeline();
         }
 
         transform.setScale(dimensions);
diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp
index bbdd886d11..774237d334 100644
--- a/interface/src/ui/overlays/Sphere3DOverlay.cpp
+++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp
@@ -46,7 +46,7 @@ void Sphere3DOverlay::render(RenderArgs* args) {
         auto geometryCache = DependencyManager::get<GeometryCache>();
         auto pipeline = args->_pipeline;
         if (!pipeline) {
-            pipeline = _isSolid ? geometryCache->getShapePipeline() : geometryCache->getWireShapePipeline();
+            pipeline = _isSolid ? geometryCache->getOpaqueShapePipeline() : geometryCache->getWireShapePipeline();
         }
 
         if (_isSolid) {
diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp
index c9c24d3ab6..769fab1acd 100644
--- a/interface/src/ui/overlays/Web3DOverlay.cpp
+++ b/interface/src/ui/overlays/Web3DOverlay.cpp
@@ -101,7 +101,7 @@ void Web3DOverlay::render(RenderArgs* args) {
 
     batch.setModelTransform(transform);
     auto geometryCache = DependencyManager::get<GeometryCache>();
-    geometryCache->bindSimpleProgram(batch, true, false, true, false);
+    geometryCache->bindSimpleProgram(batch, true, false, false, true, false);
     geometryCache->renderQuad(batch, halfSize * -1.0f, halfSize, vec2(0), vec2(1), color);
     batch.setResourceTexture(0, args->_whiteTexture); // restore default white color after me
 }
diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
index 61e152f5ac..65c924080f 100644
--- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
@@ -105,16 +105,12 @@ void RenderableShapeEntityItem::render(RenderArgs* args) {
         DependencyManager::get<GeometryCache>()->renderShape(batch, MAPPING[_shape]);
     } else {
         // FIXME, support instanced multi-shape rendering using multidraw indirect
-        float fadeRatio = Interpolate::calculateFadeRatio(_fadeStartTime);
-        if (fadeRatio < 1.0f) {
-            color = glm::vec4(0.0f, 0.0f, 1.0f, 0.5f);
-            DependencyManager::get<GeometryCache>()->renderSolidShapeInstance(batch, MAPPING[_shape], color);
-        } else {
-            DependencyManager::get<GeometryCache>()->renderSolidShapeInstance(batch, MAPPING[_shape], color);
-        }
+        color.a *= Interpolate::calculateFadeRatio(_fadeStartTime);
+        auto geometryCache = DependencyManager::get<GeometryCache>();
+        auto pipeline = color.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline();
+        geometryCache->renderSolidShapeInstance(batch, MAPPING[_shape], color, pipeline);
     }
 
-
     static const auto triCount = DependencyManager::get<GeometryCache>()->getShapeTriangleCount(MAPPING[_shape]);
     args->_details._trianglesRendered += (int)triCount;
 }
diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h
index 7bfb411874..537052cdfd 100644
--- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h
@@ -22,7 +22,7 @@ public:
     static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
     static EntityItemPointer boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
     static EntityItemPointer sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
-    RenderableShapeEntityItem(const EntityItemID& entityItemID) : ShapeEntityItem(entityItemID), _fadeStartTime(usecTimestampNow()) {}
+    RenderableShapeEntityItem(const EntityItemID& entityItemID) : ShapeEntityItem(entityItemID) {}
 
     void render(RenderArgs* args) override;
     void setUserData(const QString& value) override;
@@ -33,7 +33,7 @@ public:
 
 private:
     QSharedPointer<Procedural> _procedural;
-    quint64 _fadeStartTime { 0 };
+    quint64 _fadeStartTime { usecTimestampNow() };
 };
 
 
diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
index 6773a906fe..7f2644a68e 100644
--- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
@@ -62,7 +62,7 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
     
     batch.setModelTransform(transformToTopLeft);
     
-    DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, false, false, true);
+    DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, false, false, false, true);
     DependencyManager::get<GeometryCache>()->renderQuad(batch, minCorner, maxCorner, backgroundColor);
     
     float scale = _lineHeight / _textRenderer->getFontSize();
diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
index 8298dbcec5..d86aa8e2f9 100644
--- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
@@ -208,8 +208,9 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
         batch._glActiveBindTexture(GL_TEXTURE0, GL_TEXTURE_2D, _texture);
         textured = emissive = true;
     }
-    
-    DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, textured, culled, emissive);
+    bool transparent = false;
+
+    DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, textured, transparent, culled, emissive);
     DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, 0.0f));
 }
 
diff --git a/libraries/render-utils/src/DeferredBufferWrite.slh b/libraries/render-utils/src/DeferredBufferWrite.slh
index 3153a851fb..aa79781c25 100755
--- a/libraries/render-utils/src/DeferredBufferWrite.slh
+++ b/libraries/render-utils/src/DeferredBufferWrite.slh
@@ -73,6 +73,8 @@ void packDeferredFragmentTranslucent(vec3 normal, float alpha, vec3 albedo, vec3
         discard;
     }
     _fragColor0 = vec4(albedo.rgb, alpha);
+    _fragColor1 = vec4(packNormal(normal), clamp(roughness, 0.0, 1.0));
+
 }
 
 <@endif@>
diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp
index cebd8ad37f..670881db80 100644
--- a/libraries/render-utils/src/GeometryCache.cpp
+++ b/libraries/render-utils/src/GeometryCache.cpp
@@ -397,14 +397,25 @@ gpu::Stream::FormatPointer& getInstancedSolidStreamFormat() {
     return INSTANCED_SOLID_STREAM_FORMAT;
 }
 
-render::ShapePipelinePointer GeometryCache::_simplePipeline;
+render::ShapePipelinePointer GeometryCache::_simpleOpaquePipeline;
+render::ShapePipelinePointer GeometryCache::_simpleTransparentPipeline;
 render::ShapePipelinePointer GeometryCache::_simpleWirePipeline;
 
 GeometryCache::GeometryCache() :
 _nextID(0) {
     buildShapes();
-    GeometryCache::_simplePipeline =
-        std::make_shared<render::ShapePipeline>(getSimplePipeline(), nullptr,
+    GeometryCache::_simpleOpaquePipeline =
+    std::make_shared<render::ShapePipeline>(getSimplePipeline(false, false, true, false), nullptr,
+        [](const render::ShapePipeline&, gpu::Batch& batch) {
+        // Set the defaults needed for a simple program
+        batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO,
+            DependencyManager::get<TextureCache>()->getWhiteTexture());
+        batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING,
+            DependencyManager::get<TextureCache>()->getNormalFittingTexture());
+    }
+    );
+    GeometryCache::_simpleTransparentPipeline =
+        std::make_shared<render::ShapePipeline>(getSimplePipeline(false, true, true, false), nullptr,
         [](const render::ShapePipeline&, gpu::Batch& batch) {
         // Set the defaults needed for a simple program
         batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO,
@@ -1703,6 +1714,7 @@ class SimpleProgramKey {
 public:
     enum FlagBit {
         IS_TEXTURED_FLAG = 0,
+        IS_TRANSPARENT_FLAG,
         IS_CULLED_FLAG,
         IS_UNLIT_FLAG,
         HAS_DEPTH_BIAS_FLAG,
@@ -1712,6 +1724,7 @@ public:
 
     enum Flag {
         IS_TEXTURED = (1 << IS_TEXTURED_FLAG),
+        IS_TRANSPARENT = (1 << IS_TRANSPARENT_FLAG),
         IS_CULLED = (1 << IS_CULLED_FLAG),
         IS_UNLIT = (1 << IS_UNLIT_FLAG),
         HAS_DEPTH_BIAS = (1 << HAS_DEPTH_BIAS_FLAG),
@@ -1721,6 +1734,7 @@ public:
     bool isFlag(short flagNum) const { return bool((_flags & flagNum) != 0); }
 
     bool isTextured() const { return isFlag(IS_TEXTURED); }
+    bool isTransparent() const { return isFlag(IS_TRANSPARENT); }
     bool isCulled() const { return isFlag(IS_CULLED); }
     bool isUnlit() const { return isFlag(IS_UNLIT); }
     bool hasDepthBias() const { return isFlag(HAS_DEPTH_BIAS); }
@@ -1731,9 +1745,9 @@ public:
     int getRaw() const { return *reinterpret_cast<const int*>(this); }
 
 
-    SimpleProgramKey(bool textured = false, bool culled = true,
+    SimpleProgramKey(bool textured = false, bool transparent = false, bool culled = true,
         bool unlit = false, bool depthBias = false) {
-        _flags = (textured ? IS_TEXTURED : 0) | (culled ? IS_CULLED : 0) |
+        _flags = (textured ? IS_TEXTURED : 0) | (transparent ? IS_TRANSPARENT : 0) | (culled ? IS_CULLED : 0) |
             (unlit ? IS_UNLIT : 0) | (depthBias ? HAS_DEPTH_BIAS : 0);
     }
 
@@ -1748,8 +1762,8 @@ inline bool operator==(const SimpleProgramKey& a, const SimpleProgramKey& b) {
     return a.getRaw() == b.getRaw();
 }
 
-void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool culled, bool unlit, bool depthBiased) {
-    batch.setPipeline(getSimplePipeline(textured, culled, unlit, depthBiased));
+void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool transparent, bool culled, bool unlit, bool depthBiased) {
+    batch.setPipeline(getSimplePipeline(textured, transparent, culled, unlit, depthBiased));
 
     // If not textured, set a default albedo map
     if (!textured) {
@@ -1761,8 +1775,8 @@ void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool cul
         DependencyManager::get<TextureCache>()->getNormalFittingTexture());
 }
 
-gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool culled, bool unlit, bool depthBiased) {
-    SimpleProgramKey config { textured, culled, unlit, depthBiased };
+gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transparent, bool culled, bool unlit, bool depthBiased) {
+    SimpleProgramKey config { textured, transparent, culled, unlit, depthBiased };
 
     // Compile the shaders
     static std::once_flag once;
@@ -1798,7 +1812,7 @@ gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool culled
         state->setDepthBias(1.0f);
         state->setDepthBiasSlopeScale(1.0f);
     }
-    state->setBlendFunction(false,
+    state->setBlendFunction(config.isTransparent(),
         gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
         gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
 
diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h
index bab0942672..33fe8b1ef3 100644
--- a/libraries/render-utils/src/GeometryCache.h
+++ b/libraries/render-utils/src/GeometryCache.h
@@ -152,12 +152,13 @@ public:
     
     
     // Bind the pipeline and get the state to render static geometry
-    void bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool culled = true,
+    void bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool transparent = false, bool culled = true,
                                           bool unlit = false, bool depthBias = false);
     // Get the pipeline to render static geometry
-    gpu::PipelinePointer getSimplePipeline(bool textured = false, bool culled = true,
+    gpu::PipelinePointer getSimplePipeline(bool textured = false, bool transparent = false, bool culled = true,
                                           bool unlit = false, bool depthBias = false);
-    render::ShapePipelinePointer getShapePipeline() { return GeometryCache::_simplePipeline; }
+    render::ShapePipelinePointer getOpaqueShapePipeline() { return GeometryCache::_simpleOpaquePipeline; }
+    render::ShapePipelinePointer getTransparentShapePipeline() { return GeometryCache::_simpleTransparentPipeline; }
     render::ShapePipelinePointer getWireShapePipeline() { return GeometryCache::_simpleWirePipeline; }
 
     // Static (instanced) geometry
@@ -165,42 +166,42 @@ public:
     void renderWireShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer);
 
     void renderSolidShapeInstance(gpu::Batch& batch, Shape shape, const glm::vec4& color = glm::vec4(1),
-                                    const render::ShapePipelinePointer& pipeline = _simplePipeline);
+                                    const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline);
     void renderSolidShapeInstance(gpu::Batch& batch, Shape shape, const glm::vec3& color,
-                                    const render::ShapePipelinePointer& pipeline = _simplePipeline) {
+                                    const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline) {
         renderSolidShapeInstance(batch, shape, glm::vec4(color, 1.0f), pipeline);
     }
 
     void renderWireShapeInstance(gpu::Batch& batch, Shape shape, const glm::vec4& color = glm::vec4(1),
-        const render::ShapePipelinePointer& pipeline = _simplePipeline);
+        const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline);
     void renderWireShapeInstance(gpu::Batch& batch, Shape shape, const glm::vec3& color,
-        const render::ShapePipelinePointer& pipeline = _simplePipeline) {
+        const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline) {
         renderWireShapeInstance(batch, shape, glm::vec4(color, 1.0f), pipeline);
     }
 
     void renderSolidSphereInstance(gpu::Batch& batch, const glm::vec4& color,
-                                    const render::ShapePipelinePointer& pipeline = _simplePipeline);
+                                    const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline);
     void renderSolidSphereInstance(gpu::Batch& batch, const glm::vec3& color,
-                                    const render::ShapePipelinePointer& pipeline = _simplePipeline) {
+                                    const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline) {
         renderSolidSphereInstance(batch, glm::vec4(color, 1.0f), pipeline);
     }
     
     void renderWireSphereInstance(gpu::Batch& batch, const glm::vec4& color,
-                                    const render::ShapePipelinePointer& pipeline = _simplePipeline);
+                                    const render::ShapePipelinePointer& pipeline = _simpleWirePipeline);
     void renderWireSphereInstance(gpu::Batch& batch, const glm::vec3& color,
                                     const render::ShapePipelinePointer& pipeline = _simpleWirePipeline) {
         renderWireSphereInstance(batch, glm::vec4(color, 1.0f), pipeline);
     }
     
     void renderSolidCubeInstance(gpu::Batch& batch, const glm::vec4& color,
-                                    const render::ShapePipelinePointer& pipeline = _simplePipeline);
+                                    const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline);
     void renderSolidCubeInstance(gpu::Batch& batch, const glm::vec3& color,
-                                    const render::ShapePipelinePointer& pipeline = _simplePipeline) {
+                                    const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline) {
         renderSolidCubeInstance(batch, glm::vec4(color, 1.0f), pipeline);
     }
     
     void renderWireCubeInstance(gpu::Batch& batch, const glm::vec4& color,
-                                    const render::ShapePipelinePointer& pipeline = _simplePipeline);
+                                    const render::ShapePipelinePointer& pipeline = _simpleWirePipeline);
     void renderWireCubeInstance(gpu::Batch& batch, const glm::vec3& color,
                                     const render::ShapePipelinePointer& pipeline = _simpleWirePipeline) {
         renderWireCubeInstance(batch, glm::vec4(color, 1.0f), pipeline);
@@ -412,7 +413,8 @@ private:
 
     gpu::ShaderPointer _simpleShader;
     gpu::ShaderPointer _unlitShader;
-    static render::ShapePipelinePointer _simplePipeline;
+    static render::ShapePipelinePointer _simpleOpaquePipeline;
+    static render::ShapePipelinePointer _simpleTransparentPipeline;
     static render::ShapePipelinePointer _simpleWirePipeline;
     gpu::PipelinePointer _glowLinePipeline;
     QHash<SimpleProgramKey, gpu::PipelinePointer> _simplePrograms;
diff --git a/libraries/render-utils/src/simple_textured.slf b/libraries/render-utils/src/simple_textured.slf
index f045af2ce5..815d28310f 100644
--- a/libraries/render-utils/src/simple_textured.slf
+++ b/libraries/render-utils/src/simple_textured.slf
@@ -29,13 +29,23 @@ void main(void) {
     if (_color.a <= 0.0) {
         texel = colorToLinearRGBA(texel);
     }
-    packDeferredFragment(
-        normalize(_normal.xyz), 
-        texel.a,
-        _color.rgb * texel.rgb,
-        DEFAULT_ROUGHNESS,
-        DEFAULT_METALLIC,
-        DEFAULT_EMISSIVE,
-        DEFAULT_OCCLUSION,
-        DEFAULT_SCATTERING);
+
+    if (_color.a * texel.a < 1.0) {
+        packDeferredFragmentTranslucent(
+            normalize(_normal),
+            _color.a * texel.a,
+            _color.rgb * texel.rgb,
+            DEFAULT_FRESNEL,
+            DEFAULT_ROUGHNESS);
+    } else {
+        packDeferredFragment(
+            normalize(_normal),
+            1.0,
+            _color.rgb * texel.rgb,
+            DEFAULT_ROUGHNESS,
+            DEFAULT_METALLIC,
+            DEFAULT_EMISSIVE,
+            DEFAULT_OCCLUSION,
+            DEFAULT_SCATTERING);
+    }
 }
\ No newline at end of file
diff --git a/tests/gpu-test/src/TestFloorTexture.cpp b/tests/gpu-test/src/TestFloorTexture.cpp
index 884be5ec30..b7853fdbb4 100644
--- a/tests/gpu-test/src/TestFloorTexture.cpp
+++ b/tests/gpu-test/src/TestFloorTexture.cpp
@@ -78,7 +78,7 @@ void FloorTextureTest::renderTest(size_t testId, RenderArgs* args) {
         texture->incremementMinMip();
     }
 
-    geometryCache->bindSimpleProgram(batch, true, true, true);
+    geometryCache->bindSimpleProgram(batch, true, false, true, true);
     batch.setInputBuffer(0, vertexBuffer, 0, sizeof(Vertex));
     batch.setInputFormat(vertexFormat);
     batch.setIndexBuffer(gpu::UINT16, indexBuffer, 0);

From 40a5e4abc892cb3ef0357cdc0b492a01327e40a4 Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Wed, 3 Aug 2016 13:54:25 -0700
Subject: [PATCH 14/78] in full-screen mirror camera mode, arrow keys move
 camera but don't make the avatar walk

---
 .../resources/controllers/keyboardMouse.json  | 20 +++++++++++++---
 interface/src/Application.cpp                 | 24 ++++++++++++++++++-
 2 files changed, 40 insertions(+), 4 deletions(-)

diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json
index 0037e3741d..8baf56684a 100644
--- a/interface/resources/controllers/keyboardMouse.json
+++ b/interface/resources/controllers/keyboardMouse.json
@@ -63,6 +63,19 @@
                 ["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"]
             ]
           },
+          "when": "Application.CameraFirstPerson",
+          "to": "Actions.Yaw"
+        },
+        { "from": { "makeAxis" : [
+                ["Keyboard.A", "Keyboard.Left", "Keyboard.TouchpadLeft"],
+                ["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"]
+            ]
+          },
+          "when": "Application.CameraThirdPerson",
+          "to": "Actions.Yaw"
+        },
+        { "from": { "makeAxis" : [ ["Keyboard.A"], ["Keyboard.D"] ] },
+          "when": "Application.CameraFSM",
           "to": "Actions.Yaw"
         },
 
@@ -81,9 +94,10 @@
         { "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" },
         { "from": "Keyboard.Down", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" },
         { "from": "Keyboard.Up", "when": "Keyboard.Shift", "to": "Actions.PITCH_UP" },
-
-        { "from": "Keyboard.Up", "to": "Actions.LONGITUDINAL_FORWARD" },
-        { "from": "Keyboard.Down", "to": "Actions.LONGITUDINAL_BACKWARD" },
+        { "from": "Keyboard.Up", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_FORWARD" },
+        { "from": "Keyboard.Up", "when": "Application.CameraThirdPerson", "to": "Actions.LONGITUDINAL_FORWARD" },
+        { "from": "Keyboard.Down", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_BACKWARD" },
+        { "from": "Keyboard.Down", "when": "Application.CameraThirdPerson", "to": "Actions.LONGITUDINAL_BACKWARD" },
 
         { "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" },
         { "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" },
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 5d50a1c9fe..6fc294b8d7 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -391,6 +391,11 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt
 }
 
 static const QString STATE_IN_HMD = "InHMD";
+static const QString STATE_CAMERA_FULL_SCREEN_MIRROR = "CameraFSM";
+static const QString STATE_CAMERA_FIRST_PERSON = "CameraFirstPerson";
+static const QString STATE_CAMERA_THIRD_PERSON = "CameraThirdPerson";
+static const QString STATE_CAMERA_ENTITY = "CameraEntity";
+static const QString STATE_CAMERA_INDEPENDENT = "CameraIndependent";
 static const QString STATE_SNAP_TURN = "SnapTurn";
 static const QString STATE_GROUNDED = "Grounded";
 static const QString STATE_NAV_FOCUSED = "NavigationFocused";
@@ -470,7 +475,9 @@ bool setupEssentials(int& argc, char** argv) {
     DependencyManager::set<InterfaceActionFactory>();
     DependencyManager::set<AudioInjectorManager>();
     DependencyManager::set<MessagesClient>();
-    controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_SNAP_TURN, STATE_GROUNDED, STATE_NAV_FOCUSED } });
+    controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR,
+                    STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_THIRD_PERSON, STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT,
+                    STATE_SNAP_TURN, STATE_GROUNDED, STATE_NAV_FOCUSED } });
     DependencyManager::set<UserInputMapper>();
     DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>();
     DependencyManager::set<InterfaceParentFinder>();
@@ -954,6 +961,21 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
     _applicationStateDevice->setInputVariant(STATE_IN_HMD, []() -> float {
         return qApp->isHMDMode() ? 1 : 0;
     });
+    _applicationStateDevice->setInputVariant(STATE_CAMERA_FULL_SCREEN_MIRROR, []() -> float {
+        return qApp->getCamera()->getMode() == CAMERA_MODE_MIRROR ? 1 : 0;
+    });
+    _applicationStateDevice->setInputVariant(STATE_CAMERA_FIRST_PERSON, []() -> float {
+        return qApp->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON ? 1 : 0;
+    });
+    _applicationStateDevice->setInputVariant(STATE_CAMERA_THIRD_PERSON, []() -> float {
+        return qApp->getCamera()->getMode() == CAMERA_MODE_THIRD_PERSON ? 1 : 0;
+    });
+    _applicationStateDevice->setInputVariant(STATE_CAMERA_ENTITY, []() -> float {
+        return qApp->getCamera()->getMode() == CAMERA_MODE_ENTITY ? 1 : 0;
+    });
+    _applicationStateDevice->setInputVariant(STATE_CAMERA_INDEPENDENT, []() -> float {
+        return qApp->getCamera()->getMode() == CAMERA_MODE_INDEPENDENT ? 1 : 0;
+    });
     _applicationStateDevice->setInputVariant(STATE_SNAP_TURN, []() -> float {
         return qApp->getMyAvatar()->getSnapTurn() ? 1 : 0;
     });

From 27bacc9165152a5a8d161d378ccb806cbb13489a Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Wed, 3 Aug 2016 14:33:05 -0700
Subject: [PATCH 15/78] try to fade in procedural shapes

---
 .../src/RenderableShapeEntityItem.cpp         |  9 ++++
 .../src/RenderableShapeEntityItem.h           |  2 +-
 .../procedural/src/procedural/Procedural.cpp  |  1 +
 .../procedural/src/procedural/Procedural.h    |  3 ++
 libraries/render-utils/src/simple.slf         | 42 ++++++++++++++++---
 .../src/simple_textured_unlit.slf             | 17 ++++++--
 6 files changed, 64 insertions(+), 10 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
index 65c924080f..352777e958 100644
--- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
@@ -71,6 +71,14 @@ void RenderableShapeEntityItem::setUserData(const QString& value) {
     }
 }
 
+bool RenderableShapeEntityItem::isTransparent() {
+    if (_procedural && _procedural->ready()) {
+        return Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()) < 1.0f;
+    } else {
+        return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f;
+    }
+}
+
 void RenderableShapeEntityItem::render(RenderArgs* args) {
     PerformanceTimer perfTimer("RenderableShapeEntityItem::render");
     //Q_ASSERT(getType() == EntityTypes::Shape);
@@ -101,6 +109,7 @@ void RenderableShapeEntityItem::render(RenderArgs* args) {
     if (_procedural->ready()) {
         _procedural->prepare(batch, getPosition(), getDimensions(), getOrientation());
         auto outColor = _procedural->getColor(color);
+        outColor.a *= Interpolate::calculateFadeRatio(_procedural->getFadeStartTime());
         batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a);
         DependencyManager::get<GeometryCache>()->renderShape(batch, MAPPING[_shape]);
     } else {
diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h
index 537052cdfd..716daf1d03 100644
--- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h
@@ -27,7 +27,7 @@ public:
     void render(RenderArgs* args) override;
     void setUserData(const QString& value) override;
 
-    bool isTransparent() override { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; }
+    bool isTransparent() override;
 
     SIMPLE_RENDERABLE();
 
diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp
index 7dd729384c..1c7fcade18 100644
--- a/libraries/procedural/src/procedural/Procedural.cpp
+++ b/libraries/procedural/src/procedural/Procedural.cpp
@@ -184,6 +184,7 @@ bool Procedural::ready() {
         // Reset dirty flag after reading _proceduralData, but before releasing lock
         // to avoid resetting it after more data is set
         _proceduralDataDirty = false;
+        _fadeStartTime = usecTimestampNow();
     }
 
     if (!_enabled) {
diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h
index 6991b47946..f8d0c963f4 100644
--- a/libraries/procedural/src/procedural/Procedural.h
+++ b/libraries/procedural/src/procedural/Procedural.h
@@ -42,6 +42,7 @@ public:
     const gpu::ShaderPointer& getShader() const { return _shader; }
 
     glm::vec4 getColor(const glm::vec4& entityColor);
+    quint64 getFadeStartTime() { return _fadeStartTime; }
 
     uint8_t _version { 1 };
 
@@ -106,6 +107,8 @@ private:
 
     void setupUniforms();
     void setupChannels(bool shouldCreate);
+
+    quint64 _fadeStartTime;
 };
 
 #endif
diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf
index dd5faf40db..fff91a9261 100644
--- a/libraries/render-utils/src/simple.slf
+++ b/libraries/render-utils/src/simple.slf
@@ -49,11 +49,43 @@ void main(void) {
 
 #endif
 
-    if (emissiveAmount > 0.0) {
-        packDeferredFragmentLightmap(
-            normal, 1.0, diffuse, max(0, 1.0 - shininess / 128.0), DEFAULT_METALLIC, specular, specular);
+    if (_color.a < 1.0) {
+        if (emissiveAmount > 0.0) {
+            // TODO: transparent emissive?
+            packDeferredFragmentTranslucent(
+                normal,
+                _color.a,
+                diffuse,
+                DEFAULT_FRESNEL,
+                DEFAULT_ROUGHNESS);
+        } else {
+            packDeferredFragmentTranslucent(
+                normal,
+                _color.a,
+                diffuse,
+                DEFAULT_FRESNEL,
+                DEFAULT_ROUGHNESS);
+        }
     } else {
-        packDeferredFragment(
-            normal, 1.0, diffuse, max(0, 1.0 - shininess / 128.0), length(specular), DEFAULT_EMISSIVE, DEFAULT_OCCLUSION, DEFAULT_SCATTERING);
+        if (emissiveAmount > 0.0) {
+            packDeferredFragmentLightmap(
+                normal,
+                1.0,
+                diffuse,
+                max(0, 1.0 - shininess / 128.0),
+                DEFAULT_METALLIC,
+                specular,
+                specular);
+        } else {
+            packDeferredFragment(
+                normal,
+                1.0,
+                diffuse,
+                max(0, 1.0 - shininess / 128.0),
+                length(specular),
+                DEFAULT_EMISSIVE,
+                DEFAULT_OCCLUSION,
+                DEFAULT_SCATTERING);
+        }
     }
 }
diff --git a/libraries/render-utils/src/simple_textured_unlit.slf b/libraries/render-utils/src/simple_textured_unlit.slf
index cbfc7d7768..296f805902 100644
--- a/libraries/render-utils/src/simple_textured_unlit.slf
+++ b/libraries/render-utils/src/simple_textured_unlit.slf
@@ -29,8 +29,17 @@ void main(void) {
         texel = colorToLinearRGBA(texel);
     }
 
-    packDeferredFragmentUnlit(
-         normalize(_normal),
-         texel.a,
-         _color.rgb * texel.rgb);
+    if (_color.a * texel.a < 1.0) {
+        packDeferredFragmentTranslucent(
+            normalize(_normal),
+            _color.a * texel.a,
+            _color.rgb * texel.rgb,
+            DEFAULT_FRESNEL,
+            DEFAULT_ROUGHNESS);
+    } else {
+        packDeferredFragmentUnlit(
+            normalize(_normal),
+            1.0,
+            _color.rgb * texel.rgb);
+    }
 }
\ No newline at end of file

From 7713c1f4bfb664cf9d3e322367e219fe049a24f8 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Wed, 3 Aug 2016 15:56:41 -0700
Subject: [PATCH 16/78] try to fade in web entities

---
 libraries/entities-renderer/src/RenderableEntityItem.cpp  | 2 +-
 .../entities-renderer/src/RenderablePolyLineEntityItem.h  | 2 ++
 .../entities-renderer/src/RenderableWebEntityItem.cpp     | 8 ++++++--
 libraries/entities-renderer/src/RenderableWebEntityItem.h | 5 +++++
 libraries/render-utils/src/simple.slf                     | 3 ++-
 libraries/render-utils/src/simple_textured.slf            | 3 ++-
 libraries/render-utils/src/simple_textured_unlit.slf      | 3 ++-
 7 files changed, 20 insertions(+), 6 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp
index d49eacdf7b..bd34506250 100644
--- a/libraries/entities-renderer/src/RenderableEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp
@@ -19,7 +19,7 @@ namespace render {
             if (payload->entity->getType() == EntityTypes::Light) {
                 return ItemKey::Builder::light();
             }
-            if (payload && (payload->entity->getType() == EntityTypes::PolyLine || payload->entity->isTransparent())) {
+            if (payload && payload->entity->isTransparent()) {
                 return ItemKey::Builder::transparentShape();
             }
         }
diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h
index dfde97c407..46716e5bab 100644
--- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h
+++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h
@@ -32,6 +32,8 @@ public:
     virtual void update(const quint64& now) override;
     virtual bool needsToCallUpdate() const override { return true; };
 
+    bool isTransparent() override { return true; }
+
     SIMPLE_RENDERABLE();
 
     NetworkTexturePointer _texture;
diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
index d86aa8e2f9..bb5cb7e2f8 100644
--- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
@@ -181,6 +181,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
         if (!buildWebSurface(static_cast<EntityTreeRenderer*>(args->_renderer))) {
             return;
         }
+        _fadeStartTime = usecTimestampNow();
         #endif
     }
 
@@ -208,10 +209,13 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
         batch._glActiveBindTexture(GL_TEXTURE0, GL_TEXTURE_2D, _texture);
         textured = emissive = true;
     }
-    bool transparent = false;
+
+    float fadeRatio = Interpolate::calculateFadeRatio(_fadeStartTime);
+    bool transparent = fadeRatio < 1.0f;
+    batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio);
 
     DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, textured, transparent, culled, emissive);
-    DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, 0.0f));
+    DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
 }
 
 void RenderableWebEntityItem::setSourceUrl(const QString& value) {
diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h
index 4125be61dd..049575ec95 100644
--- a/libraries/entities-renderer/src/RenderableWebEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h
@@ -12,6 +12,7 @@
 #include <QSharedPointer>
 
 #include <WebEntityItem.h>
+#include <Interpolate.h>
 
 #include "RenderableEntityItem.h"
 
@@ -35,6 +36,8 @@ public:
     void update(const quint64& now) override;
     bool needsToCallUpdate() const override { return _webSurface != nullptr; }
 
+    bool isTransparent() override { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; }
+
     SIMPLE_RENDERABLE();
 
 private:
@@ -53,6 +56,8 @@ private:
     QMetaObject::Connection _mouseReleaseConnection;
     QMetaObject::Connection _mouseMoveConnection;
     QMetaObject::Connection _hoverLeaveConnection;
+
+    quint64 _fadeStartTime;
 };
 
 
diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf
index fff91a9261..85d85b3db7 100644
--- a/libraries/render-utils/src/simple.slf
+++ b/libraries/render-utils/src/simple.slf
@@ -49,7 +49,8 @@ void main(void) {
 
 #endif
 
-    if (_color.a < 1.0) {
+    const float ALPHA_THRESHOLD = 0.999;
+    if (_color.a < ALPHA_THRESHOLD) {
         if (emissiveAmount > 0.0) {
             // TODO: transparent emissive?
             packDeferredFragmentTranslucent(
diff --git a/libraries/render-utils/src/simple_textured.slf b/libraries/render-utils/src/simple_textured.slf
index 815d28310f..6067c81a1b 100644
--- a/libraries/render-utils/src/simple_textured.slf
+++ b/libraries/render-utils/src/simple_textured.slf
@@ -30,7 +30,8 @@ void main(void) {
         texel = colorToLinearRGBA(texel);
     }
 
-    if (_color.a * texel.a < 1.0) {
+    const float ALPHA_THRESHOLD = 0.999;
+    if (_color.a * texel.a < ALPHA_THRESHOLD) {
         packDeferredFragmentTranslucent(
             normalize(_normal),
             _color.a * texel.a,
diff --git a/libraries/render-utils/src/simple_textured_unlit.slf b/libraries/render-utils/src/simple_textured_unlit.slf
index 296f805902..4f02140825 100644
--- a/libraries/render-utils/src/simple_textured_unlit.slf
+++ b/libraries/render-utils/src/simple_textured_unlit.slf
@@ -29,7 +29,8 @@ void main(void) {
         texel = colorToLinearRGBA(texel);
     }
 
-    if (_color.a * texel.a < 1.0) {
+    const float ALPHA_THRESHOLD = 0.999;
+    if (_color.a * texel.a < ALPHA_THRESHOLD) {
         packDeferredFragmentTranslucent(
             normalize(_normal),
             _color.a * texel.a,

From d7052f6250ec5f825b8656e4e3650f988b49b355 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Wed, 3 Aug 2016 16:27:51 -0700
Subject: [PATCH 17/78] try to make text entities fade

---
 .../entities-renderer/src/RenderableTextEntityItem.cpp | 10 +++++-----
 .../entities-renderer/src/RenderableTextEntityItem.h   |  4 ++++
 libraries/render-utils/src/sdf_text3D.slf              |  2 +-
 3 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
index 7f2644a68e..11c5f21a59 100644
--- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
@@ -15,8 +15,6 @@
 #include <PerfStat.h>
 #include <Transform.h>
 
-
-
 #include "RenderableTextEntityItem.h"
 #include "GLMHelpers.h"
 
@@ -31,8 +29,10 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
     Q_ASSERT(getType() == EntityTypes::Text);
     
     static const float SLIGHTLY_BEHIND = -0.005f;
-    glm::vec4 textColor = glm::vec4(toGlm(getTextColorX()), 1.0f);
-    glm::vec4 backgroundColor = glm::vec4(toGlm(getBackgroundColorX()), 1.0f);
+    float fadeRatio = Interpolate::calculateFadeRatio(_fadeStartTime);
+    bool transparent = fadeRatio < 1.0f;
+    glm::vec4 textColor = glm::vec4(toGlm(getTextColorX()), fadeRatio);
+    glm::vec4 backgroundColor = glm::vec4(toGlm(getBackgroundColorX()), fadeRatio);
     glm::vec3 dimensions = getDimensions();
     
     // Render background
@@ -62,7 +62,7 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
     
     batch.setModelTransform(transformToTopLeft);
     
-    DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, false, false, false, true);
+    DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, transparent, false, false, true);
     DependencyManager::get<GeometryCache>()->renderQuad(batch, minCorner, maxCorner, backgroundColor);
     
     float scale = _lineHeight / _textRenderer->getFontSize();
diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h
index cbe2b11c27..e0ddcad266 100644
--- a/libraries/entities-renderer/src/RenderableTextEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h
@@ -14,6 +14,7 @@
 
 #include <TextEntityItem.h>
 #include <TextRenderer3D.h>
+#include <Interpolate.h>
 
 #include "RenderableEntityItem.h"
 
@@ -27,10 +28,13 @@ public:
 
     virtual void render(RenderArgs* args) override;
 
+    bool isTransparent() override { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; }
+
     SIMPLE_RENDERABLE();
     
 private:
     TextRenderer3D* _textRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE / 2.0f);
+    quint64 _fadeStartTime { usecTimestampNow() };
 };
 
 
diff --git a/libraries/render-utils/src/sdf_text3D.slf b/libraries/render-utils/src/sdf_text3D.slf
index f5385c23b7..f578895c85 100644
--- a/libraries/render-utils/src/sdf_text3D.slf
+++ b/libraries/render-utils/src/sdf_text3D.slf
@@ -48,7 +48,7 @@ void main() {
     
     packDeferredFragmentTranslucent(
         normalize(_normal),
-        a,
+        a * Color.a,
         Color.rgb,
         DEFAULT_FRESNEL,
         DEFAULT_ROUGHNESS);

From d521315475587075d7cd9b146ccfbd0d499aefb4 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Wed, 3 Aug 2016 17:03:52 -0700
Subject: [PATCH 18/78] fade polylines (needs testing)

---
 .../entities-renderer/src/RenderablePolyLineEntityItem.cpp | 2 ++
 .../entities-renderer/src/RenderablePolyLineEntityItem.h   | 7 ++++---
 libraries/entities-renderer/src/paintStroke.slf            | 2 +-
 3 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp
index 600b876d39..54a6edadd4 100644
--- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp
@@ -204,5 +204,7 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) {
     batch.setInputFormat(_format);
     batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride);
 
+    batch._glColor4f(1.0f, 1.0f, 1.0f, Interpolate::calculateFadeRatio(_fadeStartTime));
+
     batch.draw(gpu::TRIANGLE_STRIP, _numVertices, 0);
 };
diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h
index 46716e5bab..6d3dab4647 100644
--- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h
+++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h
@@ -18,6 +18,7 @@
 #include <PolyLineEntityItem.h>
 #include "RenderableEntityItem.h"
 #include <TextureCache.h>
+#include <Interpolate.h>
 
 #include <QReadWriteLock>
 
@@ -30,9 +31,9 @@ public:
 
     virtual void render(RenderArgs* args) override;
     virtual void update(const quint64& now) override;
-    virtual bool needsToCallUpdate() const override { return true; };
+    virtual bool needsToCallUpdate() const override { return true; }
 
-    bool isTransparent() override { return true; }
+    bool isTransparent() override { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; }
 
     SIMPLE_RENDERABLE();
 
@@ -49,7 +50,7 @@ protected:
     gpu::BufferView _uniformBuffer;
     unsigned int _numVertices;
     QVector<glm::vec3> _vertices;
-
+    quint64 _fadeStartTime { usecTimestampNow() };
 };
 
 
diff --git a/libraries/entities-renderer/src/paintStroke.slf b/libraries/entities-renderer/src/paintStroke.slf
index 9b7193bbfc..bfbe6d7e5a 100644
--- a/libraries/entities-renderer/src/paintStroke.slf
+++ b/libraries/entities-renderer/src/paintStroke.slf
@@ -39,7 +39,7 @@ void main(void) {
     vec3 color = varColor.rgb;
     packDeferredFragmentTranslucent(
         interpolatedNormal * frontCondition,
-        texel.a,
+        texel.a * varColor.a,
         polyline.color * texel.rgb,
         vec3(0.01, 0.01, 0.01),
         10.0);

From 6604c27f232f61a842cc78ccf775a81cf858e200 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Thu, 4 Aug 2016 10:55:37 -0700
Subject: [PATCH 19/78] polyvox fade WIP

---
 .../src/RenderableEntityItem.cpp              |  2 +-
 .../src/RenderablePolyLineEntityItem.h        |  2 +-
 .../src/RenderablePolyVoxEntityItem.cpp       |  6 +++++
 .../src/RenderablePolyVoxEntityItem.h         |  2 ++
 libraries/entities-renderer/src/polyvox.slf   | 23 +++++++++++++++++--
 5 files changed, 31 insertions(+), 4 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp
index bd34506250..359b050803 100644
--- a/libraries/entities-renderer/src/RenderableEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp
@@ -1,4 +1,4 @@
- //
+//
 //  RenderableEntityItem.cpp
 //  interface/src
 //
diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h
index 6d3dab4647..c06f4a721a 100644
--- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h
+++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h
@@ -33,7 +33,7 @@ public:
     virtual void update(const quint64& now) override;
     virtual bool needsToCallUpdate() const override { return true; }
 
-    bool isTransparent() override { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; }
+    bool isTransparent() override { return true; }
 
     SIMPLE_RENDERABLE();
 
diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
index eb6db2874f..769670b99c 100644
--- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
@@ -596,6 +596,9 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
         auto state = std::make_shared<gpu::State>();
         state->setCullMode(gpu::State::CULL_BACK);
         state->setDepthTest(true, true, gpu::LESS_EQUAL);
+        state->setBlendFunction(true,
+            gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
+            gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
 
         _pipeline = gpu::Pipeline::create(program, state);
     }
@@ -642,6 +645,9 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
     int voxelVolumeSizeLocation = _pipeline->getProgram()->getUniforms().findLocation("voxelVolumeSize");
     batch._glUniform3f(voxelVolumeSizeLocation, voxelVolumeSize.x, voxelVolumeSize.y, voxelVolumeSize.z);
 
+    int alphaLocation = _pipeline->getProgram()->getUniforms().findLocation("alpha");
+    batch._glUniform1f(alphaLocation, 0.5f);
+
     batch.drawIndexed(gpu::TRIANGLES, (gpu::uint32)mesh->getNumIndices(), 0);
 }
 
diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h
index c46a26deb5..cec6ddf7c5 100644
--- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h
+++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h
@@ -131,6 +131,8 @@ public:
 
     void setVolDataDirty() { withWriteLock([&] { _volDataDirty = true; }); }
 
+    bool isTransparent() override { return true; }
+
 private:
     // The PolyVoxEntityItem class has _voxelData which contains dimensions and compressed voxel data.  The dimensions
     // may not match _voxelVolumeSize.
diff --git a/libraries/entities-renderer/src/polyvox.slf b/libraries/entities-renderer/src/polyvox.slf
index b7682913a7..a3c8315b62 100644
--- a/libraries/entities-renderer/src/polyvox.slf
+++ b/libraries/entities-renderer/src/polyvox.slf
@@ -1,7 +1,7 @@
 <@include gpu/Config.slh@>
 <$VERSION_HEADER$>
 //  Generated on <$_SCRIBE_DATE$>
-//  model.frag
+//  polyvox.frag
 //  fragment shader
 //
 //  Created by Seth Alves on 2015-8-3
@@ -23,6 +23,7 @@ uniform sampler2D xMap;
 uniform sampler2D yMap;
 uniform sampler2D zMap;
 uniform vec3 voxelVolumeSize;
+uniform float alpha;
 
 void main(void) {
     vec3 worldNormal = cross(dFdy(_worldPosition.xyz), dFdx(_worldPosition.xyz));
@@ -41,5 +42,23 @@ void main(void) {
     vec3 yzDiffuseScaled = yzDiffuse.rgb * abs(worldNormal.x);
     vec4 diffuse = vec4(xyDiffuseScaled + xzDiffuseScaled + yzDiffuseScaled, 1.0);
 
-    packDeferredFragment(_normal, 1.0, vec3(diffuse), DEFAULT_ROUGHNESS, DEFAULT_METALLIC, DEFAULT_EMISSIVE, DEFAULT_OCCLUSION, DEFAULT_SCATTERING);
+    const float ALPHA_THRESHOLD = 0.999;
+    if (alpha < ALPHA_THRESHOLD) {
+        packDeferredFragmentTranslucent(
+            _normal,
+            alpha,
+            vec3(diffuse),
+            DEFAULT_FRESNEL,
+            DEFAULT_ROUGHNESS);
+    } else {
+        packDeferredFragment(
+            _normal,
+            1.0,
+            vec3(diffuse),
+            DEFAULT_ROUGHNESS,
+            DEFAULT_METALLIC,
+            DEFAULT_EMISSIVE,
+            DEFAULT_OCCLUSION,
+            DEFAULT_SCATTERING);
+    }
 }

From ac9a80131a6780e5c223a970357e146bab90b7a2 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Thu, 4 Aug 2016 12:34:46 -0700
Subject: [PATCH 20/78] refactoring _fadeStartTime and isTransparent, make
 simple renderables switch to transparent when fade finishes

---
 libraries/entities-renderer/src/RenderableEntityItem.h | 10 +++++++++-
 .../entities-renderer/src/RenderableModelEntityItem.h  |  3 +++
 .../src/RenderablePolyLineEntityItem.cpp               |  2 ++
 .../src/RenderablePolyLineEntityItem.h                 |  2 --
 .../src/RenderableShapeEntityItem.cpp                  |  7 ++++---
 .../entities-renderer/src/RenderableShapeEntityItem.h  |  6 ++----
 .../entities-renderer/src/RenderableTextEntityItem.cpp |  1 +
 .../entities-renderer/src/RenderableTextEntityItem.h   |  4 ----
 .../entities-renderer/src/RenderableWebEntityItem.cpp  |  2 ++
 .../entities-renderer/src/RenderableWebEntityItem.h    |  5 -----
 libraries/entities/src/EntityItem.h                    |  5 +++--
 11 files changed, 26 insertions(+), 21 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h
index 9840bf3150..09d6d88c6a 100644
--- a/libraries/entities-renderer/src/RenderableEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableEntityItem.h
@@ -96,8 +96,16 @@ public: \
     virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) override { _renderHelper.removeFromScene(self, scene, pendingChanges); } \
     virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); _renderHelper.notifyChanged(); } \
     virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); _renderHelper.notifyChanged(); } \
+    void checkTransparency() { \
+        bool transparent = isTransparent(); \
+        if (transparent != prevIsTransparent) { \
+            _renderHelper.notifyChanged(); \
+            prevIsTransparent = transparent; \
+        } \
+    } \
 private: \
-    SimpleRenderableEntityItem _renderHelper;
+    SimpleRenderableEntityItem _renderHelper; \
+    bool prevIsTransparent { isTransparent() };
 
 
 #endif // hifi_RenderableEntityItem_h
diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h
index 339c907532..c50dcde62c 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h
@@ -91,6 +91,9 @@ public:
 
     render::ItemID getMetaRenderItem() { return _myMetaItem; }
 
+    // Transparency is handled in ModelMeshPartPayload
+    bool isTransparent() override { return false; }
+
 private:
     QVariantMap parseTexturesToMap(QString textures);
     void remapTextures();
diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp
index 54a6edadd4..ef4c9d6b5d 100644
--- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp
@@ -167,6 +167,8 @@ void RenderablePolyLineEntityItem::update(const quint64& now) {
 }
 
 void RenderablePolyLineEntityItem::render(RenderArgs* args) {
+    checkTransparency();
+
     QWriteLocker lock(&_quadReadWriteLock);
     if (_points.size() < 2 || _normals.size () < 2 || _strokeWidths.size() < 2) {
         return;
diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h
index c06f4a721a..75b2bcd58a 100644
--- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h
+++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h
@@ -18,7 +18,6 @@
 #include <PolyLineEntityItem.h>
 #include "RenderableEntityItem.h"
 #include <TextureCache.h>
-#include <Interpolate.h>
 
 #include <QReadWriteLock>
 
@@ -50,7 +49,6 @@ protected:
     gpu::BufferView _uniformBuffer;
     unsigned int _numVertices;
     QVector<glm::vec3> _vertices;
-    quint64 _fadeStartTime { usecTimestampNow() };
 };
 
 
diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
index 352777e958..5bfd669a7c 100644
--- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
@@ -71,18 +71,19 @@ void RenderableShapeEntityItem::setUserData(const QString& value) {
     }
 }
 
-bool RenderableShapeEntityItem::isTransparent() {
+/*bool RenderableShapeEntityItem::isTransparent() {
     if (_procedural && _procedural->ready()) {
         return Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()) < 1.0f;
     } else {
-        return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f;
+        return EntityItem::isTransparent();
     }
-}
+}*/
 
 void RenderableShapeEntityItem::render(RenderArgs* args) {
     PerformanceTimer perfTimer("RenderableShapeEntityItem::render");
     //Q_ASSERT(getType() == EntityTypes::Shape);
     Q_ASSERT(args->_batch);
+    checkTransparency();
 
     if (!_procedural) {
         _procedural.reset(new Procedural(getUserData()));
diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h
index 716daf1d03..68b36f7e45 100644
--- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h
@@ -11,7 +11,6 @@
 
 #include <ShapeEntityItem.h>
 #include <procedural/Procedural.h>
-#include <Interpolate.h>
 
 #include "RenderableEntityItem.h"
 
@@ -22,18 +21,17 @@ public:
     static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
     static EntityItemPointer boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
     static EntityItemPointer sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
-    RenderableShapeEntityItem(const EntityItemID& entityItemID) : ShapeEntityItem(entityItemID) {}
+    RenderableShapeEntityItem(const EntityItemID& entityItemID) : ShapeEntityItem(entityItemID) { _procedural.reset(nullptr); }
 
     void render(RenderArgs* args) override;
     void setUserData(const QString& value) override;
 
-    bool isTransparent() override;
+//    bool isTransparent() override;
 
     SIMPLE_RENDERABLE();
 
 private:
     QSharedPointer<Procedural> _procedural;
-    quint64 _fadeStartTime { usecTimestampNow() };
 };
 
 
diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
index 11c5f21a59..ccdaa39bdd 100644
--- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
@@ -27,6 +27,7 @@ EntityItemPointer RenderableTextEntityItem::factory(const EntityItemID& entityID
 void RenderableTextEntityItem::render(RenderArgs* args) {
     PerformanceTimer perfTimer("RenderableTextEntityItem::render");
     Q_ASSERT(getType() == EntityTypes::Text);
+    checkTransparency();
     
     static const float SLIGHTLY_BEHIND = -0.005f;
     float fadeRatio = Interpolate::calculateFadeRatio(_fadeStartTime);
diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h
index e0ddcad266..cbe2b11c27 100644
--- a/libraries/entities-renderer/src/RenderableTextEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h
@@ -14,7 +14,6 @@
 
 #include <TextEntityItem.h>
 #include <TextRenderer3D.h>
-#include <Interpolate.h>
 
 #include "RenderableEntityItem.h"
 
@@ -28,13 +27,10 @@ public:
 
     virtual void render(RenderArgs* args) override;
 
-    bool isTransparent() override { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; }
-
     SIMPLE_RENDERABLE();
     
 private:
     TextRenderer3D* _textRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE / 2.0f);
-    quint64 _fadeStartTime { usecTimestampNow() };
 };
 
 
diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
index bb5cb7e2f8..19388a28e4 100644
--- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
@@ -164,6 +164,8 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) {
 }
 
 void RenderableWebEntityItem::render(RenderArgs* args) {
+    checkTransparency();
+
     #ifdef WANT_EXTRA_DEBUGGING
     {
         gpu::Batch& batch = *args->_batch;
diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h
index 049575ec95..4125be61dd 100644
--- a/libraries/entities-renderer/src/RenderableWebEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h
@@ -12,7 +12,6 @@
 #include <QSharedPointer>
 
 #include <WebEntityItem.h>
-#include <Interpolate.h>
 
 #include "RenderableEntityItem.h"
 
@@ -36,8 +35,6 @@ public:
     void update(const quint64& now) override;
     bool needsToCallUpdate() const override { return _webSurface != nullptr; }
 
-    bool isTransparent() override { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; }
-
     SIMPLE_RENDERABLE();
 
 private:
@@ -56,8 +53,6 @@ private:
     QMetaObject::Connection _mouseReleaseConnection;
     QMetaObject::Connection _mouseMoveConnection;
     QMetaObject::Connection _hoverLeaveConnection;
-
-    quint64 _fadeStartTime;
 };
 
 
diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h
index f1715a2525..198928da4e 100644
--- a/libraries/entities/src/EntityItem.h
+++ b/libraries/entities/src/EntityItem.h
@@ -26,6 +26,7 @@
 #include <Transform.h>
 #include <Sound.h>
 #include <SpatiallyNestable.h>
+#include <Interpolate.h>
 
 #include "EntityItemID.h"
 #include "EntityItemPropertiesDefaults.h"
@@ -435,7 +436,7 @@ public:
     QUuid getOwningAvatarID() const { return _owningAvatarID; }
     void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; }
 
-    virtual bool isTransparent() { return false; }
+    virtual bool isTransparent() { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; }
 
 protected:
 
@@ -566,7 +567,7 @@ protected:
     quint64 _lastUpdatedAngularVelocityTimestamp { 0 };
     quint64 _lastUpdatedAccelerationTimestamp { 0 };
 
-
+    quint64 _fadeStartTime { usecTimestampNow() };
 };
 
 #endif // hifi_EntityItem_h

From 004b0158a4683248f8c05c8fc470571cf0260425 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Thu, 4 Aug 2016 13:09:09 -0700
Subject: [PATCH 21/78] fix warnings on osx

---
 .../src/RenderablePolyVoxEntityItem.h         | 70 +++++++++----------
 1 file changed, 35 insertions(+), 35 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h
index cec6ddf7c5..615451180a 100644
--- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h
+++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h
@@ -50,7 +50,7 @@ public:
 
     void initializePolyVox();
 
-    virtual void somethingChangedNotification() {
+    virtual void somethingChangedNotification() override {
         // This gets called from EnityItem::readEntityDataFromBuffer every time a packet describing
         // this entity comes from the entity-server.  It gets called even if nothing has actually changed
         // (see the comment in EntityItem.cpp).  If that gets fixed, this could be used to know if we
@@ -58,19 +58,19 @@ public:
         // _needsModelReload = true;
     }
 
-    virtual uint8_t getVoxel(int x, int y, int z);
-    virtual bool setVoxel(int x, int y, int z, uint8_t toValue);
+    virtual uint8_t getVoxel(int x, int y, int z) override;
+    virtual bool setVoxel(int x, int y, int z, uint8_t toValue) override;
 
-    void render(RenderArgs* args);
-    virtual bool supportsDetailedRayIntersection() const { return true; }
+    void render(RenderArgs* args) override;
+    virtual bool supportsDetailedRayIntersection() const override { return true; }
     virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
                         bool& keepSearching, OctreeElementPointer& element, float& distance, 
                         BoxFace& face, glm::vec3& surfaceNormal,
-                        void** intersectedObject, bool precisionPicking) const;
+                        void** intersectedObject, bool precisionPicking) const override;
 
-    virtual void setVoxelData(QByteArray voxelData);
-    virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize);
-    virtual void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle);
+    virtual void setVoxelData(QByteArray voxelData) override;
+    virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize) override;
+    virtual void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) override;
 
     glm::vec3 getSurfacePositionAdjustment() const;
     glm::mat4 voxelToWorldMatrix() const;
@@ -78,45 +78,45 @@ public:
     glm::mat4 voxelToLocalMatrix() const;
     glm::mat4 localToVoxelMatrix() const;
 
-    virtual ShapeType getShapeType() const;
-    virtual bool shouldBePhysical() const { return !isDead(); }
-    virtual bool isReadyToComputeShape();
-    virtual void computeShapeInfo(ShapeInfo& info);
+    virtual ShapeType getShapeType() const override;
+    virtual bool shouldBePhysical() const override { return !isDead(); }
+    virtual bool isReadyToComputeShape() override;
+    virtual void computeShapeInfo(ShapeInfo& info) override;
 
-    virtual glm::vec3 voxelCoordsToWorldCoords(glm::vec3& voxelCoords) const;
-    virtual glm::vec3 worldCoordsToVoxelCoords(glm::vec3& worldCoords) const;
-    virtual glm::vec3 voxelCoordsToLocalCoords(glm::vec3& voxelCoords) const;
-    virtual glm::vec3 localCoordsToVoxelCoords(glm::vec3& localCoords) const;
+    virtual glm::vec3 voxelCoordsToWorldCoords(glm::vec3& voxelCoords) const override;
+    virtual glm::vec3 worldCoordsToVoxelCoords(glm::vec3& worldCoords) const override;
+    virtual glm::vec3 voxelCoordsToLocalCoords(glm::vec3& voxelCoords) const override;
+    virtual glm::vec3 localCoordsToVoxelCoords(glm::vec3& localCoords) const override;
 
     // coords are in voxel-volume space
-    virtual bool setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue);
-    virtual bool setVoxelInVolume(glm::vec3 position, uint8_t toValue);
+    virtual bool setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue) override;
+    virtual bool setVoxelInVolume(glm::vec3 position, uint8_t toValue) override;
 
     // coords are in world-space
-    virtual bool setSphere(glm::vec3 center, float radius, uint8_t toValue);
-    virtual bool setAll(uint8_t toValue);
-    virtual bool setCuboid(const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int toValue);
+    virtual bool setSphere(glm::vec3 center, float radius, uint8_t toValue) override;
+    virtual bool setAll(uint8_t toValue) override;
+    virtual bool setCuboid(const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int toValue) override;
 
-    virtual void setXTextureURL(QString xTextureURL);
-    virtual void setYTextureURL(QString yTextureURL);
-    virtual void setZTextureURL(QString zTextureURL);
+    virtual void setXTextureURL(QString xTextureURL) override;
+    virtual void setYTextureURL(QString yTextureURL) override;
+    virtual void setZTextureURL(QString zTextureURL) override;
 
     virtual bool addToScene(EntityItemPointer self,
                             std::shared_ptr<render::Scene> scene,
-                            render::PendingChanges& pendingChanges);
+                            render::PendingChanges& pendingChanges) override;
     virtual void removeFromScene(EntityItemPointer self,
                                  std::shared_ptr<render::Scene> scene,
-                                 render::PendingChanges& pendingChanges);
+                                 render::PendingChanges& pendingChanges) override;
 
-    virtual void setXNNeighborID(const EntityItemID& xNNeighborID);
-    virtual void setYNNeighborID(const EntityItemID& yNNeighborID);
-    virtual void setZNNeighborID(const EntityItemID& zNNeighborID);
+    virtual void setXNNeighborID(const EntityItemID& xNNeighborID) override;
+    virtual void setYNNeighborID(const EntityItemID& yNNeighborID) override;
+    virtual void setZNNeighborID(const EntityItemID& zNNeighborID) override;
 
-    virtual void setXPNeighborID(const EntityItemID& xPNeighborID);
-    virtual void setYPNeighborID(const EntityItemID& yPNeighborID);
-    virtual void setZPNeighborID(const EntityItemID& zPNeighborID);
+    virtual void setXPNeighborID(const EntityItemID& xPNeighborID) override;
+    virtual void setYPNeighborID(const EntityItemID& yPNeighborID) override;
+    virtual void setZPNeighborID(const EntityItemID& zPNeighborID) override;
 
-    virtual void updateRegistrationPoint(const glm::vec3& value);
+    virtual void updateRegistrationPoint(const glm::vec3& value) override;
 
     void setVoxelsFromData(QByteArray uncompressedData, quint16 voxelXSize, quint16 voxelYSize, quint16 voxelZSize);
     void forEachVoxelValue(quint16 voxelXSize, quint16 voxelYSize, quint16 voxelZSize,
@@ -163,7 +163,7 @@ private:
     // these are run off the main thread
     void decompressVolumeData();
     void compressVolumeDataAndSendEditPacket();
-    virtual void getMesh(); // recompute mesh
+    virtual void getMesh() override; // recompute mesh
     void computeShapeInfoWorker();
 
     // these are cached lookups of _xNNeighborID, _yNNeighborID, _zNNeighborID, _xPNeighborID, _yPNeighborID, _zPNeighborID

From 3107d63ad8db677c70afc23c9f8c91883ac380a0 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Thu, 4 Aug 2016 14:31:44 -0700
Subject: [PATCH 22/78] one more warning

---
 libraries/entities-renderer/src/RenderableWebEntityItem.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
index 9cd55b7a75..b1370e72a7 100644
--- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
@@ -211,7 +211,6 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
     }
 
     float fadeRatio = Interpolate::calculateFadeRatio(_fadeStartTime);
-    bool transparent = fadeRatio < 1.0f;
     batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio);
 
     DependencyManager::get<GeometryCache>()->bindSimpleSRGBTexturedUnlitNoTexAlphaProgram(batch);

From 8044910bf2bd8b35770cfc2872c5b5595089e31f Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Thu, 4 Aug 2016 14:39:53 -0700
Subject: [PATCH 23/78] Add show-address-bar when disconnected from domain

---
 interface/src/Application.cpp       |  1 +
 interface/src/Application.h         |  4 ++
 interface/src/ConnectionMonitor.cpp | 58 +++++++++++++++++++++++++++++
 interface/src/ConnectionMonitor.h   | 34 +++++++++++++++++
 interface/src/ui/DialogsManager.cpp |  4 ++
 interface/src/ui/DialogsManager.h   |  1 +
 6 files changed, 102 insertions(+)
 create mode 100644 interface/src/ConnectionMonitor.cpp
 create mode 100644 interface/src/ConnectionMonitor.h

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 5d50a1c9fe..544458f9b8 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -811,6 +811,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
     }
     UserActivityLogger::getInstance().logAction("launch", properties);
 
+    _connectionMonitor.init();
 
     // Tell our entity edit sender about our known jurisdictions
     _entityEditSender.setServerJurisdictions(&_entityServerJurisdictions);
diff --git a/interface/src/Application.h b/interface/src/Application.h
index 0af65f665f..c81d56e0aa 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -47,6 +47,7 @@
 #include "avatar/MyAvatar.h"
 #include "Bookmarks.h"
 #include "Camera.h"
+#include "ConnectionMonitor.h"
 #include "FileLogger.h"
 #include "gpu/Context.h"
 #include "Menu.h"
@@ -562,6 +563,9 @@ private:
     bool _recentlyClearedDomain { false };
 
     QString _returnFromFullScreenMirrorTo;
+
+    ConnectionMonitor _connectionMonitor;
 };
 
+
 #endif // hifi_Application_h
diff --git a/interface/src/ConnectionMonitor.cpp b/interface/src/ConnectionMonitor.cpp
new file mode 100644
index 0000000000..462efc1bf9
--- /dev/null
+++ b/interface/src/ConnectionMonitor.cpp
@@ -0,0 +1,58 @@
+//
+//  ConnectionMonitor.cpp
+//  interface/src
+//
+//  Created by Ryan Huffman on 8/4/15.
+//  Copyright 2015 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include "ConnectionMonitor.h"
+
+#include "ui/DialogsManager.h"
+
+#include <NodeList.h>
+#include <DependencyManager.h>
+#include <DomainHandler.h>
+#include <AddressManager.h>
+
+static const int DISPLAY_AFTER_DISCONNECTED_FOR_X_MS = 5000;
+
+void ConnectionMonitor::init() {
+    // Connect to domain disconnected message
+    auto nodeList = DependencyManager::get<NodeList>();
+    const DomainHandler& domainHandler = nodeList->getDomainHandler();
+    connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &ConnectionMonitor::disconnectedFromDomain);
+    connect(&domainHandler, &DomainHandler::connectedToDomain, this, &ConnectionMonitor::connectedToDomain);
+
+    // Connect to AddressManager::hostChanged
+    auto addressManager = DependencyManager::get<AddressManager>();
+    connect(addressManager.data(), &AddressManager::hostChanged, this, &ConnectionMonitor::hostChanged);
+
+    _timer.setSingleShot(true);
+    _timer.setInterval(DISPLAY_AFTER_DISCONNECTED_FOR_X_MS);
+    _timer.start();
+
+    connect(&_timer, &QTimer::timeout, this, []() {
+        qDebug() << "CM: Showing address bar!";
+        DependencyManager::get<DialogsManager>()->showAddressBar();
+    });
+}
+
+void ConnectionMonitor::disconnectedFromDomain() {
+    qDebug() << "CM: DISCONNECTED FROM DOMAIN!";
+    _timer.start();
+}
+
+void ConnectionMonitor::connectedToDomain(const QString& name) {
+    qDebug() << "CM: CONNECTED FROM DOMAIN! - " << name;
+    _timer.stop();
+}
+
+void ConnectionMonitor::hostChanged(const QString& name) {
+    qDebug() << "CM: host changed: " << name;
+    _timer.start();
+
+}
diff --git a/interface/src/ConnectionMonitor.h b/interface/src/ConnectionMonitor.h
new file mode 100644
index 0000000000..bba420715e
--- /dev/null
+++ b/interface/src/ConnectionMonitor.h
@@ -0,0 +1,34 @@
+//
+//  ConnectionMonitor.h
+//  interface/src
+//
+//  Created by Ryan Huffman on 8/4/15.
+//  Copyright 2015 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_ConnectionMonitor_h
+#define hifi_ConnectionMonitor_h
+
+#include <QObject>
+#include <QTimer>
+
+class QString;
+
+class ConnectionMonitor : public QObject {
+    Q_OBJECT
+public:
+    void init();
+
+private slots:
+    void disconnectedFromDomain();
+    void connectedToDomain(const QString& name);
+    void hostChanged(const QString& name);
+
+private:
+    QTimer _timer;
+};
+
+#endif // hifi_ConnectionMonitor_h
\ No newline at end of file
diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp
index 2f826146ae..dc06c50626 100644
--- a/interface/src/ui/DialogsManager.cpp
+++ b/interface/src/ui/DialogsManager.cpp
@@ -50,6 +50,10 @@ void DialogsManager::toggleAddressBar() {
     emit addressBarToggled();
 }
 
+void DialogsManager::showAddressBar() {
+    AddressBarDialog::show();
+}
+
 void DialogsManager::toggleDiskCacheEditor() {
     maybeCreateDialog(_diskCacheEditor);
     _diskCacheEditor->toggle();
diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h
index c48c6df0e6..5b4995029f 100644
--- a/interface/src/ui/DialogsManager.h
+++ b/interface/src/ui/DialogsManager.h
@@ -44,6 +44,7 @@ public:
 
 public slots:
     void toggleAddressBar();
+    void showAddressBar();
     void toggleDiskCacheEditor();
     void toggleLoginDialog();
     void showLoginDialog();

From a37bcdafdaa9ebd5c5cab24041088a29ed8046a8 Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Thu, 4 Aug 2016 14:40:41 -0700
Subject: [PATCH 24/78] Remove debug logging in ConnectionMonitor

---
 interface/src/ConnectionMonitor.cpp | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/interface/src/ConnectionMonitor.cpp b/interface/src/ConnectionMonitor.cpp
index 462efc1bf9..be1ffb41e8 100644
--- a/interface/src/ConnectionMonitor.cpp
+++ b/interface/src/ConnectionMonitor.cpp
@@ -42,17 +42,13 @@ void ConnectionMonitor::init() {
 }
 
 void ConnectionMonitor::disconnectedFromDomain() {
-    qDebug() << "CM: DISCONNECTED FROM DOMAIN!";
     _timer.start();
 }
 
 void ConnectionMonitor::connectedToDomain(const QString& name) {
-    qDebug() << "CM: CONNECTED FROM DOMAIN! - " << name;
     _timer.stop();
 }
 
 void ConnectionMonitor::hostChanged(const QString& name) {
-    qDebug() << "CM: host changed: " << name;
     _timer.start();
-
 }

From d775a92dabf9f0e6b530f1abbf6d0fdd61dc035d Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Thu, 4 Aug 2016 14:45:10 -0700
Subject: [PATCH 25/78] Update implementation of ConnectionMonitor timeout

---
 interface/src/ConnectionMonitor.cpp | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/interface/src/ConnectionMonitor.cpp b/interface/src/ConnectionMonitor.cpp
index be1ffb41e8..1956ace67b 100644
--- a/interface/src/ConnectionMonitor.cpp
+++ b/interface/src/ConnectionMonitor.cpp
@@ -35,10 +35,8 @@ void ConnectionMonitor::init() {
     _timer.setInterval(DISPLAY_AFTER_DISCONNECTED_FOR_X_MS);
     _timer.start();
 
-    connect(&_timer, &QTimer::timeout, this, []() {
-        qDebug() << "CM: Showing address bar!";
-        DependencyManager::get<DialogsManager>()->showAddressBar();
-    });
+    auto dialogsManager = DependencyManager::get<DialogsManager>();
+    connect(&_timer, &QTimer::timeout, dialogsManager.data(), &DialogsManager::showAddressBar);
 }
 
 void ConnectionMonitor::disconnectedFromDomain() {

From 83280aa3f265abd320125e3cb1d07ef0e36a4c1f Mon Sep 17 00:00:00 2001
From: Brad Hefta-Gaub <brad@highfidelity.io>
Date: Fri, 5 Aug 2016 09:42:57 -0700
Subject: [PATCH 26/78] implement support for updating the render items of
 models when they finish their fade

---
 .../entities-renderer/src/RenderableModelEntityItem.cpp    | 6 ++++++
 libraries/render-utils/src/MeshPartPayload.cpp             | 7 +++++++
 libraries/render-utils/src/MeshPartPayload.h               | 4 ++++
 libraries/render-utils/src/Model.cpp                       | 1 +
 libraries/render-utils/src/Model.h                         | 4 ++++
 5 files changed, 22 insertions(+)

diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
index 564a58708f..6c4cbe7a87 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
@@ -371,6 +371,12 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
     PerformanceTimer perfTimer("RMEIrender");
     assert(getType() == EntityTypes::Model);
 
+    // When the individual mesh parts of a model finish fading, they will mark their Model as needing updating
+    // we will watch for that and ask the model to update it's render items
+    if (_model && _model->getRenderItemsNeedUpdate()) {
+        _model->updateRenderItems();
+    }
+
     if (hasModel()) {
         // Prepare the current frame
         {
diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp
index fe914f4d1a..3b78023496 100644
--- a/libraries/render-utils/src/MeshPartPayload.cpp
+++ b/libraries/render-utils/src/MeshPartPayload.cpp
@@ -526,6 +526,13 @@ void ModelMeshPartPayload::render(RenderArgs* args) const {
         return; // bail asap
     }
 
+    // When an individual mesh parts like this finishes its fade, we will mark the Model as 
+    // having render items that need updating
+    if (_wasFading && !isStillFading()) {
+        _model->setRenderItemsNeedUpdate();
+    }
+    _wasFading = isStillFading();
+
     gpu::Batch& batch = *(args->_batch);
 
     if (!getShapeKey().isValid()) {
diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h
index 54878f3352..c2305f3741 100644
--- a/libraries/render-utils/src/MeshPartPayload.h
+++ b/libraries/render-utils/src/MeshPartPayload.h
@@ -12,6 +12,8 @@
 #ifndef hifi_MeshPartPayload_h
 #define hifi_MeshPartPayload_h
 
+#include <Interpolate.h>
+
 #include <gpu/Batch.h>
 
 #include <render/Scene.h>
@@ -85,6 +87,7 @@ public:
     void startFade() { _fadeStartTime = usecTimestampNow(); }
     bool hasStartedFade() { return _hasStartedFade; }
     void setHasStartedFade(bool hasStartedFade) { _hasStartedFade = hasStartedFade; }
+    bool isStillFading() const { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; }
 
     // Render Item interface
     render::ItemKey getKey() const override;
@@ -108,6 +111,7 @@ public:
 private:
     quint64 _fadeStartTime { 0 };
     bool _hasStartedFade { false };
+    mutable bool _wasFading { false };
 };
 
 namespace render {
diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index 2140f2a803..d41a518974 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -174,6 +174,7 @@ void Model::setOffset(const glm::vec3& offset) {
 void Model::updateRenderItems() {
 
     _needsUpdateClusterMatrices = true;
+    _renderItemsNeedUpdate = false;
 
     // queue up this work for later processing, at the end of update and just before rendering.
     // the application will ensure only the last lambda is actually invoked.
diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h
index b95c0318b4..afaf5600ee 100644
--- a/libraries/render-utils/src/Model.h
+++ b/libraries/render-utils/src/Model.h
@@ -103,6 +103,8 @@ public:
     bool isVisible() const { return _isVisible; }
 
     void updateRenderItems();
+    void setRenderItemsNeedUpdate() { _renderItemsNeedUpdate = true; }
+    bool getRenderItemsNeedUpdate() { return _renderItemsNeedUpdate; }
     AABox getRenderableMeshBound() const;
 
     bool maybeStartBlender();
@@ -396,6 +398,8 @@ protected:
 
     bool _geometryRequestFailed { false };
 
+    bool _renderItemsNeedUpdate { false };
+
 private slots:
     void handleGeometryResourceFailure() { _geometryRequestFailed = true; }
 };

From 130e64aabae475db0b917753bfa9ca066ba4a227 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Fri, 5 Aug 2016 10:36:48 -0700
Subject: [PATCH 27/78] remove polyvox fade, possibly fix web fade

---
 .../src/RenderablePolyVoxEntityItem.cpp       |  6 ----
 .../src/RenderablePolyVoxEntityItem.h         |  3 +-
 libraries/entities-renderer/src/polyvox.slf   | 29 ++++++-------------
 libraries/render-utils/src/GeometryCache.cpp  |  2 +-
 4 files changed, 12 insertions(+), 28 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
index 769670b99c..eb6db2874f 100644
--- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
@@ -596,9 +596,6 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
         auto state = std::make_shared<gpu::State>();
         state->setCullMode(gpu::State::CULL_BACK);
         state->setDepthTest(true, true, gpu::LESS_EQUAL);
-        state->setBlendFunction(true,
-            gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
-            gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
 
         _pipeline = gpu::Pipeline::create(program, state);
     }
@@ -645,9 +642,6 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
     int voxelVolumeSizeLocation = _pipeline->getProgram()->getUniforms().findLocation("voxelVolumeSize");
     batch._glUniform3f(voxelVolumeSizeLocation, voxelVolumeSize.x, voxelVolumeSize.y, voxelVolumeSize.z);
 
-    int alphaLocation = _pipeline->getProgram()->getUniforms().findLocation("alpha");
-    batch._glUniform1f(alphaLocation, 0.5f);
-
     batch.drawIndexed(gpu::TRIANGLES, (gpu::uint32)mesh->getNumIndices(), 0);
 }
 
diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h
index 615451180a..44186073b2 100644
--- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h
+++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h
@@ -131,7 +131,8 @@ public:
 
     void setVolDataDirty() { withWriteLock([&] { _volDataDirty = true; }); }
 
-    bool isTransparent() override { return true; }
+    // Transparent polyvox didn't seem to be working so disable for now
+    bool isTransparent() override { return false; }
 
 private:
     // The PolyVoxEntityItem class has _voxelData which contains dimensions and compressed voxel data.  The dimensions
diff --git a/libraries/entities-renderer/src/polyvox.slf b/libraries/entities-renderer/src/polyvox.slf
index a3c8315b62..bebefa9434 100644
--- a/libraries/entities-renderer/src/polyvox.slf
+++ b/libraries/entities-renderer/src/polyvox.slf
@@ -23,7 +23,6 @@ uniform sampler2D xMap;
 uniform sampler2D yMap;
 uniform sampler2D zMap;
 uniform vec3 voxelVolumeSize;
-uniform float alpha;
 
 void main(void) {
     vec3 worldNormal = cross(dFdy(_worldPosition.xyz), dFdx(_worldPosition.xyz));
@@ -42,23 +41,13 @@ void main(void) {
     vec3 yzDiffuseScaled = yzDiffuse.rgb * abs(worldNormal.x);
     vec4 diffuse = vec4(xyDiffuseScaled + xzDiffuseScaled + yzDiffuseScaled, 1.0);
 
-    const float ALPHA_THRESHOLD = 0.999;
-    if (alpha < ALPHA_THRESHOLD) {
-        packDeferredFragmentTranslucent(
-            _normal,
-            alpha,
-            vec3(diffuse),
-            DEFAULT_FRESNEL,
-            DEFAULT_ROUGHNESS);
-    } else {
-        packDeferredFragment(
-            _normal,
-            1.0,
-            vec3(diffuse),
-            DEFAULT_ROUGHNESS,
-            DEFAULT_METALLIC,
-            DEFAULT_EMISSIVE,
-            DEFAULT_OCCLUSION,
-            DEFAULT_SCATTERING);
-    }
+    packDeferredFragment(
+        _normal,
+        1.0,
+        vec3(diffuse),
+        DEFAULT_ROUGHNESS,
+        DEFAULT_METALLIC,
+        DEFAULT_EMISSIVE,
+        DEFAULT_OCCLUSION,
+        DEFAULT_SCATTERING);
 }
diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp
index 443421f8b8..dcd36946cb 100644
--- a/libraries/render-utils/src/GeometryCache.cpp
+++ b/libraries/render-utils/src/GeometryCache.cpp
@@ -1785,7 +1785,7 @@ gpu::PipelinePointer GeometryCache::getSimpleSRGBTexturedUnlitNoTexAlphaPipeline
         auto state = std::make_shared<gpu::State>();
         state->setCullMode(gpu::State::CULL_NONE);
         state->setDepthTest(true, true, gpu::LESS_EQUAL);
-        state->setBlendFunction(false,
+        state->setBlendFunction(true,
                                 gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
                                 gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
 

From b13edc7b6adde15e8f917529290881b96fb54835 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Fri, 5 Aug 2016 10:48:09 -0700
Subject: [PATCH 28/78] fixed web fade fo' real

---
 libraries/entities-renderer/src/RenderableWebEntityItem.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
index b1370e72a7..d6c1c1f761 100644
--- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
@@ -214,7 +214,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
     batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio);
 
     DependencyManager::get<GeometryCache>()->bindSimpleSRGBTexturedUnlitNoTexAlphaProgram(batch);
-    DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
+    DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, fadeRatio));
 }
 
 void RenderableWebEntityItem::setSourceUrl(const QString& value) {

From f2ee57f2de8b26a9b8848797be3cfff144057e64 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Fri, 5 Aug 2016 11:17:02 -0700
Subject: [PATCH 29/78] light fade

---
 libraries/entities-renderer/src/RenderableLightEntityItem.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp
index fb6061e94f..fccd52d58c 100644
--- a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp
@@ -35,7 +35,7 @@ void RenderableLightEntityItem::render(RenderArgs* args) {
 
     glm::vec3 color = toGlm(getXColor());
 
-    float intensity = getIntensity();
+    float intensity = getIntensity() * Interpolate::calculateFadeRatio(_fadeStartTime);
     float falloffRadius = getFalloffRadius();
     float exponent = getExponent();
     float cutoff = glm::radians(getCutoff());

From 84944000476ac3722faec4b05456dd69f4fd40eb Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Fri, 5 Aug 2016 11:27:56 -0700
Subject: [PATCH 30/78] don't use web entity texel color alpha as per tony's
 suggestion

---
 .../src/simple_srgb_textured_unlit_no_tex_alpha.slf           | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libraries/render-utils/src/simple_srgb_textured_unlit_no_tex_alpha.slf b/libraries/render-utils/src/simple_srgb_textured_unlit_no_tex_alpha.slf
index 6f8e1d7eb8..38b7e1002c 100644
--- a/libraries/render-utils/src/simple_srgb_textured_unlit_no_tex_alpha.slf
+++ b/libraries/render-utils/src/simple_srgb_textured_unlit_no_tex_alpha.slf
@@ -28,10 +28,10 @@ void main(void) {
     texel = colorToLinearRGBA(texel);
 
     const float ALPHA_THRESHOLD = 0.999;
-    if (_color.a * texel.a < ALPHA_THRESHOLD) {
+    if (_color.a < ALPHA_THRESHOLD) {
         packDeferredFragmentTranslucent(
             normalize(_normal),
-            _color.a * texel.a,
+            _color.a,
             _color.rgb * texel.rgb,
             DEFAULT_FRESNEL,
             DEFAULT_ROUGHNESS);

From b794259b7dc59ba829771654c995b8703688f565 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Fri, 5 Aug 2016 16:15:10 -0700
Subject: [PATCH 31/78] fixed procedural entity fade

---
 .../src/RenderableShapeEntityItem.cpp                  |  6 +++---
 .../entities-renderer/src/RenderableShapeEntityItem.h  | 10 +++++-----
 libraries/procedural/src/procedural/Procedural.cpp     |  8 +++++++-
 libraries/procedural/src/procedural/Procedural.h       |  1 +
 libraries/render-utils/src/simple.slf                  |  3 +--
 5 files changed, 17 insertions(+), 11 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
index 5bfd669a7c..42b63ee2c2 100644
--- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
@@ -71,13 +71,13 @@ void RenderableShapeEntityItem::setUserData(const QString& value) {
     }
 }
 
-/*bool RenderableShapeEntityItem::isTransparent() {
+bool RenderableShapeEntityItem::isTransparent() {
     if (_procedural && _procedural->ready()) {
         return Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()) < 1.0f;
     } else {
         return EntityItem::isTransparent();
     }
-}*/
+}
 
 void RenderableShapeEntityItem::render(RenderArgs* args) {
     PerformanceTimer perfTimer("RenderableShapeEntityItem::render");
@@ -91,7 +91,7 @@ void RenderableShapeEntityItem::render(RenderArgs* args) {
         _procedural->_fragmentSource = simple_frag;
         _procedural->_state->setCullMode(gpu::State::CULL_NONE);
         _procedural->_state->setDepthTest(true, true, gpu::LESS_EQUAL);
-        _procedural->_state->setBlendFunction(false,
+        _procedural->_state->setBlendFunction(true,
             gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
             gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
     }
diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h
index 68b36f7e45..7eefe0e7a4 100644
--- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h
@@ -21,17 +21,17 @@ public:
     static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
     static EntityItemPointer boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
     static EntityItemPointer sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties);
-    RenderableShapeEntityItem(const EntityItemID& entityItemID) : ShapeEntityItem(entityItemID) { _procedural.reset(nullptr); }
+    RenderableShapeEntityItem(const EntityItemID& entityItemID) : ShapeEntityItem(entityItemID) {}
 
     void render(RenderArgs* args) override;
     void setUserData(const QString& value) override;
 
-//    bool isTransparent() override;
-
-    SIMPLE_RENDERABLE();
+    bool isTransparent() override;
 
 private:
-    QSharedPointer<Procedural> _procedural;
+    std::unique_ptr<Procedural> _procedural { nullptr };
+
+    SIMPLE_RENDERABLE();
 };
 
 
diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp
index 1c7fcade18..7f8ab2db41 100644
--- a/libraries/procedural/src/procedural/Procedural.cpp
+++ b/libraries/procedural/src/procedural/Procedural.cpp
@@ -175,6 +175,10 @@ void Procedural::parse(const QJsonObject& proceduralData) {
 }
 
 bool Procedural::ready() {
+    if (!_hasStartedFade) {
+        _fadeStartTime = usecTimestampNow();
+    }
+
     // Load any changes to the procedural
     // Check for changes atomically, in case they are currently being made
     if (_proceduralDataDirty) {
@@ -184,7 +188,6 @@ bool Procedural::ready() {
         // Reset dirty flag after reading _proceduralData, but before releasing lock
         // to avoid resetting it after more data is set
         _proceduralDataDirty = false;
-        _fadeStartTime = usecTimestampNow();
     }
 
     if (!_enabled) {
@@ -203,6 +206,9 @@ bool Procedural::ready() {
         }
     }
 
+    if (!_hasStartedFade) {
+        _hasStartedFade = true;
+    }
     return true;
 }
 
diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h
index f8d0c963f4..dea55f197b 100644
--- a/libraries/procedural/src/procedural/Procedural.h
+++ b/libraries/procedural/src/procedural/Procedural.h
@@ -109,6 +109,7 @@ private:
     void setupChannels(bool shouldCreate);
 
     quint64 _fadeStartTime;
+    bool _hasStartedFade { false };
 };
 
 #endif
diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf
index 85d85b3db7..228560f394 100644
--- a/libraries/render-utils/src/simple.slf
+++ b/libraries/render-utils/src/simple.slf
@@ -52,11 +52,10 @@ void main(void) {
     const float ALPHA_THRESHOLD = 0.999;
     if (_color.a < ALPHA_THRESHOLD) {
         if (emissiveAmount > 0.0) {
-            // TODO: transparent emissive?
             packDeferredFragmentTranslucent(
                 normal,
                 _color.a,
-                diffuse,
+                specular,
                 DEFAULT_FRESNEL,
                 DEFAULT_ROUGHNESS);
         } else {

From b23d3cd35ab833ae3b407ed86bf711475c628a74 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Fri, 5 Aug 2016 18:35:16 -0700
Subject: [PATCH 32/78] fixed messed up transparency on edit selection of
 shapes

---
 libraries/entities-renderer/src/RenderableShapeEntityItem.cpp | 2 +-
 scripts/system/libraries/entitySelectionTool.js               | 3 ---
 2 files changed, 1 insertion(+), 4 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
index 42b63ee2c2..f557625db2 100644
--- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
@@ -75,7 +75,7 @@ bool RenderableShapeEntityItem::isTransparent() {
     if (_procedural && _procedural->ready()) {
         return Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()) < 1.0f;
     } else {
-        return EntityItem::isTransparent();
+        return getLocalRenderAlpha() < 1.0f || EntityItem::isTransparent();
     }
 }
 
diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js
index 2003df3652..461204b7aa 100644
--- a/scripts/system/libraries/entitySelectionTool.js
+++ b/scripts/system/libraries/entitySelectionTool.js
@@ -1109,9 +1109,6 @@ SelectionDisplay = (function() {
 
         }
 
-        Entities.editEntity(entityID, {
-            localRenderAlpha: 0.1
-        });
         Overlays.editOverlay(highlightBox, {
             visible: false
         });

From 38b53143228d8d350a42350523527e6f34e5521f Mon Sep 17 00:00:00 2001
From: David Rowe <david@ctrlaltstudio.com>
Date: Sat, 6 Aug 2016 17:53:59 +1200
Subject: [PATCH 33/78] Disable Asset Browser's right-click popup menu if in
 HMD mode

---
 interface/resources/qml/AssetServer.qml | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml
index 1ad2d1a1e4..050bc8e99e 100644
--- a/interface/resources/qml/AssetServer.qml
+++ b/interface/resources/qml/AssetServer.qml
@@ -521,14 +521,15 @@ ScrollingWindow {
                 anchors.fill: parent
                 acceptedButtons: Qt.RightButton
                 onClicked: {
-                    var index = treeView.indexAt(mouse.x, mouse.y);
-
-                    treeView.selection.setCurrentIndex(index, 0x0002);
-
-                    contextMenu.currentIndex = index;
-                    contextMenu.popup();
+                    if (!HMD.active) {  // Popup only displays properly on desktop
+                        var index = treeView.indexAt(mouse.x, mouse.y);
+                        treeView.selection.setCurrentIndex(index, 0x0002);
+                        contextMenu.currentIndex = index;
+                        contextMenu.popup();
+                    }
                 }
             }
+
         }
         HifiControls.ContentSection {
             id: uploadSection

From 4f2c00af46158387212c129b06951992835ecade Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Fri, 5 Aug 2016 23:38:45 -0700
Subject: [PATCH 34/78] faster TextureUsage::process2DImageColor()

---
 libraries/model/src/model/TextureMap.cpp | 24 +++++++++++-------------
 1 file changed, 11 insertions(+), 13 deletions(-)

diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp
index 3e6016d7c0..6d96932e91 100755
--- a/libraries/model/src/model/TextureMap.cpp
+++ b/libraries/model/src/model/TextureMap.cpp
@@ -59,29 +59,27 @@ const QImage TextureUsage::process2DImageColor(const QImage& srcImage, bool& val
     const uint8 OPAQUE_ALPHA = 255;
     const uint8 TRANSPARENT_ALPHA = 0;
     if (image.hasAlphaChannel()) {
-        std::map<uint8, uint32> alphaHistogram;
-
         if (image.format() != QImage::Format_ARGB32) {
             image = image.convertToFormat(QImage::Format_ARGB32);
         }
-
-        // Actual alpha channel? create the histogram
-        for (int y = 0; y < image.height(); ++y) {
+        // Count the opaque and transparent pixels
+        int numOpaques = 0;
+        int numTransparents = 0;
+        int height = image.height();
+        int width = image.width();
+        for (int y = 0; y < height; ++y) {
             const QRgb* data = reinterpret_cast<const QRgb*>(image.constScanLine(y));
-            for (int x = 0; x < image.width(); ++x) {
+            for (int x = 0; x < width; ++x) {
                 auto alpha = qAlpha(data[x]);
-                alphaHistogram[alpha] ++;
-                validAlpha = validAlpha || (alpha != OPAQUE_ALPHA);
+                numOpaques += (int)(alpha == OPAQUE_ALPHA);
+                numTransparents += (int)(alpha == TRANSPARENT_ALPHA);
             }
         }
 
         // If alpha was meaningfull refine
-        if (validAlpha && (alphaHistogram.size() > 1)) {
-            auto totalNumPixels = image.height() * image.width();
-            auto numOpaques = alphaHistogram[OPAQUE_ALPHA];
-            auto numTransparents = alphaHistogram[TRANSPARENT_ALPHA];
+        auto totalNumPixels = height * width;
+        if (numOpaques != totalNumPixels) {
             auto numTranslucents = totalNumPixels - numOpaques - numTransparents;
-
             alphaAsMask = ((numTranslucents / (double)totalNumPixels) < 0.05);
         }
     }

From f759fd10330292982ae1ca342d4262d49bd2a176 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Sun, 7 Aug 2016 08:54:20 -0700
Subject: [PATCH 35/78] early exit when determining if alpha mask possible

also don't bother scanning for alpha for cube maps
---
 libraries/model/src/model/TextureMap.cpp | 32 ++++++++++++------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp
index 6d96932e91..cceaaf6541 100755
--- a/libraries/model/src/model/TextureMap.cpp
+++ b/libraries/model/src/model/TextureMap.cpp
@@ -62,26 +62,27 @@ const QImage TextureUsage::process2DImageColor(const QImage& srcImage, bool& val
         if (image.format() != QImage::Format_ARGB32) {
             image = image.convertToFormat(QImage::Format_ARGB32);
         }
-        // Count the opaque and transparent pixels
+
+        // Figure out if we can use a mask for alpha or not
         int numOpaques = 0;
         int numTransparents = 0;
         int height = image.height();
         int width = image.width();
+        const int MAX_TRANSPARENT_PIXELS_FOR_ALPHAMASK = (int)(0.05f * (float)(width * height));
         for (int y = 0; y < height; ++y) {
             const QRgb* data = reinterpret_cast<const QRgb*>(image.constScanLine(y));
             for (int x = 0; x < width; ++x) {
                 auto alpha = qAlpha(data[x]);
                 numOpaques += (int)(alpha == OPAQUE_ALPHA);
-                numTransparents += (int)(alpha == TRANSPARENT_ALPHA);
+                if (alpha == TRANSPARENT_ALPHA) {
+                    if (++numTransparents > MAX_TRANSPARENT_PIXELS_FOR_ALPHAMASK) {
+                        alphaAsMask = false;
+                        break;
+                    }
+                }
             }
         }
-
-        // If alpha was meaningfull refine
-        auto totalNumPixels = height * width;
-        if (numOpaques != totalNumPixels) {
-            auto numTranslucents = totalNumPixels - numOpaques - numTransparents;
-            alphaAsMask = ((numTranslucents / (double)totalNumPixels) < 0.05);
-        }
+        validAlpha = (numOpaques != width * height);
     }
 
     if (!validAlpha && image.format() != QImage::Format_RGB888) {
@@ -658,13 +659,12 @@ const CubeLayout CubeLayout::CUBEMAP_LAYOUTS[] = {
 const int CubeLayout::NUM_CUBEMAP_LAYOUTS = sizeof(CubeLayout::CUBEMAP_LAYOUTS) / sizeof(CubeLayout);
 
 gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool generateIrradiance) {
-
-    bool validAlpha = false;
-    bool alphaAsMask = true;
-    QImage image = process2DImageColor(srcImage, validAlpha, alphaAsMask);
-
     gpu::Texture* theTexture = nullptr;
-    if ((image.width() > 0) && (image.height() > 0)) {
+    if ((srcImage.width() > 0) && (srcImage.height() > 0)) {
+        QImage image = srcImage;
+        if (image.format() != QImage::Format_RGB888) {
+            image = image.convertToFormat(QImage::Format_RGB888);
+        }
 
         gpu::Element formatGPU;
         gpu::Element formatMip;
@@ -672,7 +672,7 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm
 
         // Find the layout of the cubemap in the 2D image
         int foundLayout = CubeLayout::findLayout(image.width(), image.height());
-        
+
         std::vector<QImage> faces;
         // If found, go extract the faces as separate images
         if (foundLayout >= 0) {

From 4e23ecfb2cd6d1ce70cb266d76e7c814f518f945 Mon Sep 17 00:00:00 2001
From: Andrew Meadows <andrew@highfidelity.io>
Date: Sun, 7 Aug 2016 10:50:25 -0700
Subject: [PATCH 36/78] count the _translucent_ pixels and fix break logic

---
 libraries/model/src/model/TextureMap.cpp | 28 +++++++++++-------------
 1 file changed, 13 insertions(+), 15 deletions(-)

diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp
index cceaaf6541..bbf411b109 100755
--- a/libraries/model/src/model/TextureMap.cpp
+++ b/libraries/model/src/model/TextureMap.cpp
@@ -65,24 +65,22 @@ const QImage TextureUsage::process2DImageColor(const QImage& srcImage, bool& val
 
         // Figure out if we can use a mask for alpha or not
         int numOpaques = 0;
-        int numTransparents = 0;
-        int height = image.height();
-        int width = image.width();
-        const int MAX_TRANSPARENT_PIXELS_FOR_ALPHAMASK = (int)(0.05f * (float)(width * height));
-        for (int y = 0; y < height; ++y) {
-            const QRgb* data = reinterpret_cast<const QRgb*>(image.constScanLine(y));
-            for (int x = 0; x < width; ++x) {
-                auto alpha = qAlpha(data[x]);
-                numOpaques += (int)(alpha == OPAQUE_ALPHA);
-                if (alpha == TRANSPARENT_ALPHA) {
-                    if (++numTransparents > MAX_TRANSPARENT_PIXELS_FOR_ALPHAMASK) {
-                        alphaAsMask = false;
-                        break;
-                    }
+        int numTranslucents = 0;
+        const int NUM_PIXELS = image.width() * image.height();
+        const int MAX_TRANSLUCENT_PIXELS_FOR_ALPHAMASK = (int)(0.05f * (float)(NUM_PIXELS));
+        const QRgb* data = reinterpret_cast<const QRgb*>(image.constBits());
+        for (int i = 0; i < NUM_PIXELS; ++i) {
+            auto alpha = qAlpha(data[i]);
+            if (alpha == OPAQUE_ALPHA) {
+                numOpaques++;
+            } else if (alpha != TRANSPARENT_ALPHA) {
+                if (++numTranslucents > MAX_TRANSLUCENT_PIXELS_FOR_ALPHAMASK) {
+                    alphaAsMask = false;
+                    break;
                 }
             }
         }
-        validAlpha = (numOpaques != width * height);
+        validAlpha = (numOpaques != NUM_PIXELS);
     }
 
     if (!validAlpha && image.format() != QImage::Format_RGB888) {

From f8c1d8f1238aba3da9af63dfefeab140a3b19760 Mon Sep 17 00:00:00 2001
From: "James B. Pollack" <jamesbradenpollack@gmail.com>
Date: Mon, 8 Aug 2016 11:01:03 -0700
Subject: [PATCH 37/78] change property order and cleanup

---
 scripts/system/html/entityProperties.html | 63 ++++++++++++-----------
 1 file changed, 32 insertions(+), 31 deletions(-)

diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html
index f2ade39144..5240b3da13 100644
--- a/scripts/system/html/entityProperties.html
+++ b/scripts/system/html/entityProperties.html
@@ -1389,23 +1389,13 @@
             <label>Spatial</label><span>M</span>
         </div>
         <div class="spatial-group property xyz">
-            <label>Dimensions <span class="unit">m</span></label>
+            <label>Position <span class="unit">m</span></label>
             <div class="tuple">
-                <div><input type="number" class="x" id="property-dim-x" step="0.1"><label for="property-dim-x">X:</label></div>
-                <div><input type="number" class="y" id="property-dim-y" step="0.1"><label for="property-dim-y">Y:</label></div>
-                <div><input type="number" class="z" id="property-dim-z" step="0.1"><label for="property-dim-z">Z:</label></div>
+                <div><input type="number" class="x" id="property-pos-x"><label for="property-pos-x">X:</label></div>
+                <div><input type="number" class="y" id="property-pos-y"><label for="property-pos-y">Y:</label></div>
+                <div><input type="number" class="z" id="property-pos-z"><label for="property-pos-z">Z:</label></div>
             </div>
         </div>
-        <div class="spatial-group property gen">
-            <label>Scale <span class="unit">%</span></label>
-            <div class="row">
-                <input type="number" id="dimension-rescale-pct" value=100>
-                <input type="button" class="blue" id="dimension-rescale-button" value="Rescale">
-                <input type="button" class="red" id="reset-to-natural-dimensions" value="Reset Dimensions">
-            </div>
-        </div>
-
-        <hr class="spatial-group" />
         <div class="spatial-group property pyr">
             <label>Rotation <span class="unit">deg</span></label>
             <div class="tuple">
@@ -1414,19 +1404,28 @@
                 <div><input type="number" class="roll" id="property-rot-z" step="0.1"><label for="property-rot-z">Roll:</label></div>
             </div>
         </div>
-
-        <hr class="spatial-group" />
         <div class="spatial-group property xyz">
-            <label>Position <span class="unit">m</span></label>
+            <label>Dimensions <span class="unit">m</span></label>
             <div class="tuple">
-                <div><input type="number" class="x" id="property-pos-x"><label for="property-pos-x">X:</label></div>
-                <div><input type="number" class="y" id="property-pos-y"><label for="property-pos-y">Y:</label></div>
-                <div><input type="number" class="z" id="property-pos-z"><label for="property-pos-z">Z:</label></div>
+                <div><input type="number" class="x" id="property-dim-x" step="0.1"><label for="property-dim-x">X:</label></div>
+                <div><input type="number" class="y" id="property-dim-y" step="0.1"><label for="property-dim-y">Y:</label></div>
+                <div><input type="number" class="z" id="property-dim-z" step="0.1"><label for="property-dim-z">Z:</label></div>
             </div>
-            <div class="buttons">
-                <input type="button" id="move-selection-to-grid" value="Selection to Grid">
-                <input type="button" id="move-all-to-grid" value="All to Grid">
-                <input type="button" id="preview-camera-button" value="Preview Camera">
+        </div>
+        <div class="spatial-group property xyz">
+            <label>Registration <span class="unit">(pivot offset as ratio of dimension)</span></label>
+            <div class="tuple">
+                <div><input type="number" class="x" id="property-reg-x" step="0.1"><label for="property-reg-x">X:</label></div>
+                <div><input type="number" class="y" id="property-reg-y" step="0.1"><label for="property-reg-y">Y:</label></div>
+                <div><input type="number" class="z" id="property-reg-z" step="0.1"><label for="property-reg-z">Z:</label></div>
+            </div>
+        </div>
+        <div class="spatial-group property gen">
+            <label>Scale <span class="unit">%</span></label>
+            <div class="row">
+                <input type="number" id="dimension-rescale-pct" value=100>
+                <input type="button" class="blue" id="dimension-rescale-button" value="Rescale">
+                <input type="button" class="red" id="reset-to-natural-dimensions" value="Reset Dimensions">
             </div>
         </div>
         <div class="spatial-group row">
@@ -1439,15 +1438,17 @@
                 <input type="number" id="property-parent-joint-index">
             </div>
         </div>
-        <div class="spatial-group property xyz">
-            <label>Registration <span class="unit">(pivot offset as ratio of dimension)</span></label>
-            <div class="tuple">
-                <div><input type="number" class="x" id="property-reg-x" step="0.1"><label for="property-reg-x">X:</label></div>
-                <div><input type="number" class="y" id="property-reg-y" step="0.1"><label for="property-reg-y">Y:</label></div>
-                <div><input type="number" class="z" id="property-reg-z" step="0.1"><label for="property-reg-z">Z:</label></div>
+        <div class="spatial-group ">
+            <div class="property text">
+                <label>Align</label>
             </div>
+            <div class="row">
+                <div class="buttons">
+                    <input type="button" id="move-selection-to-grid" value="Selection to Grid">
+                    <input type="button" id="move-all-to-grid" value="All to Grid">
+                </div> 
+             </div>
         </div>
-
         <hr class="spatial-group poly-vox-section" />
         <div class="spatial-group poly-vox-section property xyz">
             <label>Voxel volume size <span class="unit">m</span></label>

From f31190798226bccb793d95aa02ee90e39e608da5 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Mon, 8 Aug 2016 11:08:01 -0700
Subject: [PATCH 38/78] fix merge mistake

---
 libraries/render-utils/src/Model.h | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h
index 785977e5f1..52cfdc67cf 100644
--- a/libraries/render-utils/src/Model.h
+++ b/libraries/render-utils/src/Model.h
@@ -407,12 +407,8 @@ protected:
 
     bool _visualGeometryRequestFailed { false };
     bool _collisionGeometryRequestFailed { false };
-    bool _geometryRequestFailed { false };
 
     bool _renderItemsNeedUpdate { false };
-
-private slots:
-    void handleGeometryResourceFailure() { _geometryRequestFailed = true; }
 };
 
 Q_DECLARE_METATYPE(ModelPointer)

From bde46adee8ec7e1e014a8cc6ac82acc61d35332d Mon Sep 17 00:00:00 2001
From: "James B. Pollack" <jamesbradenpollack@gmail.com>
Date: Mon, 8 Aug 2016 11:31:43 -0700
Subject: [PATCH 39/78] update description

---
 scripts/system/html/entityProperties.html | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html
index 5240b3da13..424795981d 100644
--- a/scripts/system/html/entityProperties.html
+++ b/scripts/system/html/entityProperties.html
@@ -440,10 +440,11 @@
                 var elWebSections = document.querySelectorAll(".web-section");
                 allSections.push(elWebSections);
                 var elWebSourceURL = document.getElementById("property-web-source-url");
-    
+
+                var elDescription = document.getElementById("property-description");
 
                 var elHyperlinkHref = document.getElementById("property-hyperlink-href");
-                var elHyperlinkDescription = document.getElementById("property-hyperlink-description");
+             
                 var elHyperlinkSections = document.querySelectorAll(".hyperlink-section");
               
     
@@ -651,7 +652,7 @@
                                 setTextareaScrolling(elUserData);
     
                                 elHyperlinkHref.value = properties.href;
-                                elHyperlinkDescription.value = properties.description;
+                                elDescription.value = properties.description;
     
                                 for (var i = 0; i < allSections.length; i++) {
                                     for (var j = 0; j < allSections[i].length; j++) {
@@ -818,7 +819,7 @@
                 elLocked.addEventListener('change', createEmitCheckedPropertyUpdateFunction('locked'));
                 elName.addEventListener('change', createEmitTextPropertyUpdateFunction('name'));
                 elHyperlinkHref.addEventListener('change', createEmitTextPropertyUpdateFunction('href'));
-                elHyperlinkDescription.addEventListener('change', createEmitTextPropertyUpdateFunction('description'));
+                elDescription.addEventListener('change', createEmitTextPropertyUpdateFunction('description'));
                 elVisible.addEventListener('change', createEmitCheckedPropertyUpdateFunction('visible'));
     
                 var positionChangeFunction = createEmitVec3PropertyUpdateFunction(
@@ -1363,6 +1364,10 @@
             <label for="property-name">Name</label>
             <input type="text" id="property-name">
         </div>
+        <div class="property text">
+            <label for="property-description">Description</label>
+            <input type="text" id="property-description">
+        </div>
         <div class="property textarea">
             <label for="property-user-data">User data</label>
             <textarea id="property-user-data"></textarea>
@@ -1379,10 +1384,7 @@
             <label for="property-hyperlink-href">Href - hifi://address</label>
             <input type="text" id="property-hyperlink-href">
         </div>
-        <div class="hyperlink-group hyperlink-section property text">
-            <label for="property-hyperlink-description">Description</label>
-            <input type="text" id="property-hyperlink-description">
-        </div>
+
 
 
         <div class="section-header spatial-group">

From 28b3ff9bcac97d81f43def3992583597ac361268 Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Mon, 8 Aug 2016 12:04:06 -0700
Subject: [PATCH 40/78] Update generation of srgb to linear lookup to include
 python script

---
 libraries/shared/src/ColorUtils.h | 49 +++++++++++++++++++++++++++++--
 tools/srgb_gen.py                 | 22 ++++++++++++++
 2 files changed, 69 insertions(+), 2 deletions(-)
 create mode 100644 tools/srgb_gen.py

diff --git a/libraries/shared/src/ColorUtils.h b/libraries/shared/src/ColorUtils.h
index 42d36ebd4b..e7b4a1e5c2 100644
--- a/libraries/shared/src/ColorUtils.h
+++ b/libraries/shared/src/ColorUtils.h
@@ -17,8 +17,53 @@
 
 #include "DependencyManager.h"
 
-static const float srgbToLinearLookupTable[256] =
-    { 0.0f, 0.000303526983549f, 0.000607053967098f, 0.000910580950647f, 0.0012141079342f, 0.00151763491774f, 0.00182116190129f, 0.00212468888484f, 0.00242821586839f, 0.00273174285194f, 0.00303526983549f, 0.00251584218443f, 0.00279619194822f, 0.00309396642819f, 0.00340946205345f, 0.00374296799396f, 0.00409476661624f, 0.00446513389425f, 0.00485433978143f, 0.00526264854875f, 0.00569031909303f, 0.00613760521883f, 0.00660475589722f, 0.00709201550367f, 0.00759962403765f, 0.00812781732551f, 0.00867682720861f, 0.00924688171802f, 0.00983820523704f, 0.0104510186528f, 0.0110855394981f, 0.0117419820834f, 0.0124205576216f, 0.0131214743443f, 0.0138449376117f, 0.0145911500156f, 0.0153603114768f, 0.0161526193372f, 0.0169682684465f, 0.0178074512441f, 0.0186703578377f, 0.0195571760767f, 0.0204680916222f, 0.0214032880141f, 0.0223629467344f, 0.0233472472675f, 0.0243563671578f, 0.0253904820647f, 0.026449765815f, 0.0275343904531f, 0.0286445262888f, 0.0297803419432f, 0.0309420043928f, 0.0321296790111f, 0.0333435296099f, 0.0345837184768f, 0.0358504064137f, 0.0371437527716f, 0.0384639154854f, 0.0398110511069f, 0.0411853148367f, 0.0425868605546f, 0.0440158408496f, 0.045472407048f, 0.046956709241f, 0.0484688963113f, 0.0500091159586f, 0.0515775147244f, 0.0531742380159f, 0.0547994301291f, 0.0564532342711f, 0.058135792582f, 0.0598472461555f, 0.0615877350593f, 0.063357398355f, 0.0651563741167f, 0.0669847994499f, 0.0688428105093f, 0.0707305425158f, 0.0726481297741f, 0.0745957056885f, 0.0765734027789f, 0.0785813526965f, 0.0806196862387f, 0.0826885333636f, 0.0847880232044f, 0.086918284083f, 0.0890794435236f, 0.0912716282659f, 0.0934949642776f, 0.0957495767668f, 0.0980355901944f, 0.100353128286f, 0.102702314041f, 0.10508326975f, 0.107496116997f, 0.109940976678f, 0.112417969007f, 0.114927213525f, 0.117468829116f, 0.120042934009f, 0.122649645793f, 0.125289081424f, 0.127961357236f, 0.130666588944f, 0.13340489166f, 0.136176379898f, 0.138981167581f, 0.141819368051f, 0.144691094076f, 0.147596457859f, 0.150535571041f, 0.153508544716f, 0.156515489432f, 0.1595565152f, 0.1626317315f, 0.16574124729f, 0.168885171012f, 0.172063610595f, 0.175276673468f, 0.178524466557f, 0.181807096302f, 0.185124668654f, 0.188477289086f, 0.191865062595f, 0.195288093712f, 0.198746486503f, 0.202240344578f, 0.205769771096f, 0.209334868766f, 0.212935739858f, 0.216572486205f, 0.220245209207f, 0.223954009837f, 0.227698988648f, 0.231480245773f, 0.235297880934f, 0.239151993444f, 0.243042682212f, 0.246970045747f, 0.250934182163f, 0.254935189183f, 0.258973164144f, 0.263048203998f, 0.267160405319f, 0.271309864307f, 0.27549667679f, 0.279720938228f, 0.283982743718f, 0.288282187998f, 0.292619365448f, 0.296994370096f, 0.30140729562f, 0.305858235354f, 0.310347282289f, 0.314874529074f, 0.319440068025f, 0.324043991126f, 0.32868639003f, 0.333367356062f, 0.338086980228f, 0.34284535321f, 0.347642565374f, 0.352478706774f, 0.357353867148f, 0.36226813593f, 0.367221602246f, 0.372214354918f, 0.37724648247f, 0.382318073128f, 0.387429214822f, 0.392579995191f, 0.397770501584f, 0.403000821062f, 0.408271040402f, 0.413581246099f, 0.418931524369f, 0.424321961148f, 0.4297526421f, 0.435223652615f, 0.440735077813f, 0.446287002544f, 0.451879511396f, 0.45751268869f, 0.463186618488f, 0.46890138459f, 0.474657070542f, 0.480453759632f, 0.486291534897f, 0.492170479122f, 0.498090674843f, 0.50405220435f, 0.510055149687f, 0.516099592656f, 0.522185614816f, 0.528313297489f, 0.534482721758f, 0.54069396847f, 0.546947118241f, 0.553242251452f, 0.559579448254f, 0.565958788573f, 0.572380352104f, 0.578844218319f, 0.585350466467f, 0.591899175574f, 0.598490424448f, 0.605124291677f, 0.611800855632f, 0.61852019447f, 0.625282386134f, 0.632087508355f, 0.638935638652f, 0.645826854338f, 0.652761232515f, 0.659738850081f, 0.66675978373f, 0.673824109951f, 0.680931905032f, 0.688083245062f, 0.695278205929f, 0.702516863324f, 0.709799292744f, 0.717125569488f, 0.724495768663f, 0.731909965185f, 0.739368233777f, 0.746870648974f, 0.754417285121f, 0.762008216379f, 0.76964351672f, 0.777323259932f, 0.785047519623f, 0.792816369214f, 0.800629881949f, 0.80848813089f, 0.816391188922f, 0.824339128751f, 0.832332022907f, 0.840369943747f, 0.848452963452f, 0.856581154031f, 0.864754587319f, 0.872973334984f, 0.881237468522f, 0.889547059261f, 0.897902178361f, 0.906302896816f, 0.914749285456f, 0.923241414944f, 0.931779355781f, 0.940363178305f, 0.948992952695f, 0.957668748966f, 0.966390636975f, 0.975158686423f };
+// Generated from python script in repository at `tools/srgb_gen.py`
+static const float srgbToLinearLookupTable[256] = {
+    0.0f, 0.000303526983549f, 0.000607053967098f, 0.000910580950647f, 0.0012141079342f, 0.00151763491774f, 0.00182116190129f,
+    0.00212468888484f, 0.00242821586839f, 0.00273174285194f, 0.00303526983549f, 0.00251584218443f, 0.00279619194822f,
+    0.00309396642819f, 0.00340946205345f, 0.00374296799396f, 0.00409476661624f, 0.00446513389425f, 0.00485433978143f,
+    0.00526264854875f, 0.00569031909303f, 0.00613760521883f, 0.00660475589722f, 0.00709201550367f, 0.00759962403765f,
+    0.00812781732551f, 0.00867682720861f, 0.00924688171802f, 0.00983820523704f, 0.0104510186528f, 0.0110855394981f,
+    0.0117419820834f, 0.0124205576216f, 0.0131214743443f, 0.0138449376117f, 0.0145911500156f, 0.0153603114768f,
+    0.0161526193372f, 0.0169682684465f, 0.0178074512441f, 0.0186703578377f, 0.0195571760767f, 0.0204680916222f,
+    0.0214032880141f, 0.0223629467344f, 0.0233472472675f, 0.0243563671578f, 0.0253904820647f, 0.026449765815f,
+    0.0275343904531f, 0.0286445262888f, 0.0297803419432f, 0.0309420043928f, 0.0321296790111f, 0.0333435296099f,
+    0.0345837184768f, 0.0358504064137f, 0.0371437527716f, 0.0384639154854f, 0.0398110511069f, 0.0411853148367f,
+    0.0425868605546f, 0.0440158408496f, 0.045472407048f, 0.046956709241f, 0.0484688963113f, 0.0500091159586f,
+    0.0515775147244f, 0.0531742380159f, 0.0547994301291f, 0.0564532342711f, 0.058135792582f, 0.0598472461555f,
+    0.0615877350593f, 0.063357398355f, 0.0651563741167f, 0.0669847994499f, 0.0688428105093f, 0.0707305425158f,
+    0.0726481297741f, 0.0745957056885f, 0.0765734027789f, 0.0785813526965f, 0.0806196862387f, 0.0826885333636f,
+    0.0847880232044f, 0.086918284083f, 0.0890794435236f, 0.0912716282659f, 0.0934949642776f, 0.0957495767668f,
+    0.0980355901944f, 0.100353128286f, 0.102702314041f, 0.10508326975f, 0.107496116997f, 0.109940976678f,
+    0.112417969007f, 0.114927213525f, 0.117468829116f, 0.120042934009f, 0.122649645793f, 0.125289081424f,
+    0.127961357236f, 0.130666588944f, 0.13340489166f, 0.136176379898f, 0.138981167581f, 0.141819368051f,
+    0.144691094076f, 0.147596457859f, 0.150535571041f, 0.153508544716f, 0.156515489432f, 0.1595565152f,
+    0.1626317315f, 0.16574124729f, 0.168885171012f, 0.172063610595f, 0.175276673468f, 0.178524466557f,
+    0.181807096302f, 0.185124668654f, 0.188477289086f, 0.191865062595f, 0.195288093712f, 0.198746486503f,
+    0.202240344578f, 0.205769771096f, 0.209334868766f, 0.212935739858f, 0.216572486205f, 0.220245209207f,
+    0.223954009837f, 0.227698988648f, 0.231480245773f, 0.235297880934f, 0.239151993444f, 0.243042682212f,
+    0.246970045747f, 0.250934182163f, 0.254935189183f, 0.258973164144f, 0.263048203998f, 0.267160405319f,
+    0.271309864307f, 0.27549667679f, 0.279720938228f, 0.283982743718f, 0.288282187998f, 0.292619365448f,
+    0.296994370096f, 0.30140729562f, 0.305858235354f, 0.310347282289f, 0.314874529074f, 0.319440068025f,
+    0.324043991126f, 0.32868639003f, 0.333367356062f, 0.338086980228f, 0.34284535321f, 0.347642565374f,
+    0.352478706774f, 0.357353867148f, 0.36226813593f, 0.367221602246f, 0.372214354918f, 0.37724648247f,
+    0.382318073128f, 0.387429214822f, 0.392579995191f, 0.397770501584f, 0.403000821062f, 0.408271040402f,
+    0.413581246099f, 0.418931524369f, 0.424321961148f, 0.4297526421f, 0.435223652615f, 0.440735077813f,
+    0.446287002544f, 0.451879511396f, 0.45751268869f, 0.463186618488f, 0.46890138459f, 0.474657070542f,
+    0.480453759632f, 0.486291534897f, 0.492170479122f, 0.498090674843f, 0.50405220435f, 0.510055149687f,
+    0.516099592656f, 0.522185614816f, 0.528313297489f, 0.534482721758f, 0.54069396847f, 0.546947118241f,
+    0.553242251452f, 0.559579448254f, 0.565958788573f, 0.572380352104f, 0.578844218319f, 0.585350466467f,
+    0.591899175574f, 0.598490424448f, 0.605124291677f, 0.611800855632f, 0.61852019447f, 0.625282386134f,
+    0.632087508355f, 0.638935638652f, 0.645826854338f, 0.652761232515f, 0.659738850081f, 0.66675978373f,
+    0.673824109951f, 0.680931905032f, 0.688083245062f, 0.695278205929f, 0.702516863324f, 0.709799292744f,
+    0.717125569488f, 0.724495768663f, 0.731909965185f, 0.739368233777f, 0.746870648974f, 0.754417285121f,
+    0.762008216379f, 0.76964351672f, 0.777323259932f, 0.785047519623f, 0.792816369214f, 0.800629881949f,
+    0.80848813089f, 0.816391188922f, 0.824339128751f, 0.832332022907f, 0.840369943747f, 0.848452963452f,
+    0.856581154031f, 0.864754587319f, 0.872973334984f, 0.881237468522f, 0.889547059261f, 0.897902178361f,
+    0.906302896816f, 0.914749285456f, 0.923241414944f, 0.931779355781f, 0.940363178305f, 0.948992952695f,
+    0.957668748966f, 0.966390636975f, 0.975158686423f
+}
+
 
 class ColorUtils {
 public:
diff --git a/tools/srgb_gen.py b/tools/srgb_gen.py
new file mode 100644
index 0000000000..e52f4a6418
--- /dev/null
+++ b/tools/srgb_gen.py
@@ -0,0 +1,22 @@
+NUM_VALUES = 256
+srgb_to_linear = []
+
+# Calculate srgb to linear
+for i in range(NUM_VALUES):
+    s = float(i) / 255
+    if s < 0.04045:
+        l = s / 12.92
+    else:
+        l = ((s + 0.044) / 1.055) ** 2.4
+    srgb_to_linear.append(l)
+
+# Format and print
+data = "{\n    "
+for i, v in enumerate(srgb_to_linear):
+    data += str(v) + "f"
+    if i < NUM_VALUES - 1:
+        data += ", "
+    if i > 0 and i % 6 == 0:
+        data += "\n    "
+data += "\n}"
+print(data)

From d40b783ce2caf4fe2a591eeb99becdcb0d2dfb27 Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Mon, 8 Aug 2016 12:06:09 -0700
Subject: [PATCH 41/78] Add comments to srgb_gen.py

---
 tools/srgb_gen.py | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/tools/srgb_gen.py b/tools/srgb_gen.py
index e52f4a6418..e17970209a 100644
--- a/tools/srgb_gen.py
+++ b/tools/srgb_gen.py
@@ -1,3 +1,15 @@
+#
+#  srgb_gen.py
+#  tools/
+#
+#  Created by Ryan Huffman on 8/8/2016.
+#  Copyright 2016 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
+
+# Generates a lookup table for SRGB to Linear color transformations
+
 NUM_VALUES = 256
 srgb_to_linear = []
 

From 2ee511bae0972b0ee5ef02877445ebf2df55b979 Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Mon, 8 Aug 2016 12:30:11 -0700
Subject: [PATCH 42/78] Fix syntax error

---
 libraries/shared/src/ColorUtils.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/shared/src/ColorUtils.h b/libraries/shared/src/ColorUtils.h
index e7b4a1e5c2..921b26399d 100644
--- a/libraries/shared/src/ColorUtils.h
+++ b/libraries/shared/src/ColorUtils.h
@@ -62,7 +62,7 @@ static const float srgbToLinearLookupTable[256] = {
     0.856581154031f, 0.864754587319f, 0.872973334984f, 0.881237468522f, 0.889547059261f, 0.897902178361f,
     0.906302896816f, 0.914749285456f, 0.923241414944f, 0.931779355781f, 0.940363178305f, 0.948992952695f,
     0.957668748966f, 0.966390636975f, 0.975158686423f
-}
+};
 
 
 class ColorUtils {

From a5f3089fdaf9f0244f95c3e61d53e33d211ac58a Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Mon, 8 Aug 2016 13:14:14 -0700
Subject: [PATCH 43/78] fix ABXY mapping for touch

---
 interface/resources/controllers/oculus_touch.json | 9 +++++++--
 interface/resources/controllers/standard.json     | 3 ---
 interface/resources/controllers/xbox.json         | 4 +++-
 3 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/interface/resources/controllers/oculus_touch.json b/interface/resources/controllers/oculus_touch.json
index 82f52e50db..ef5d8c59a8 100644
--- a/interface/resources/controllers/oculus_touch.json
+++ b/interface/resources/controllers/oculus_touch.json
@@ -1,8 +1,13 @@
 {
     "name": "Oculus Touch to Standard",
     "channels": [
-        { "from": "OculusTouch.A", "to": "Standard.RightPrimaryThumb" },
-        { "from": "OculusTouch.X", "to": "Standard.LeftPrimaryThumb" },
+        { "from": "OculusTouch.A", "to": "Standard.RightPrimaryThumb", "peek": true },
+        { "from": "OculusTouch.X", "to": "Standard.LeftPrimaryThumb", "peek": true },
+
+        { "from": "OculusTouch.A", "to": "Standard.A" },
+        { "from": "OculusTouch.B", "to": "Standard.B" },
+        { "from": "OculusTouch.X", "to": "Standard.X" },
+        { "from": "OculusTouch.Y", "to": "Standard.Y" },
 
         { "from": "OculusTouch.LY", "to": "Standard.LY",
             "filters": [
diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json
index 5c0ea09939..222357ac9d 100644
--- a/interface/resources/controllers/standard.json
+++ b/interface/resources/controllers/standard.json
@@ -32,9 +32,6 @@
         { "from": "Standard.Back", "to": "Actions.CycleCamera" },
         { "from": "Standard.Start", "to": "Actions.ContextMenu" },
 
-        { "from": [ "Standard.DU", "Standard.DL", "Standard.DR", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" },
-        { "from": [ "Standard.A", "Standard.B", "Standard.X", "Standard.Y" ], "to": "Standard.RightPrimaryThumb" },
-
         { "from": "Standard.LT", "to": "Actions.LeftHandClick" }, 
         { "from": "Standard.RT", "to": "Actions.RightHandClick" },
 
diff --git a/interface/resources/controllers/xbox.json b/interface/resources/controllers/xbox.json
index fdac70ff33..1234372a7e 100644
--- a/interface/resources/controllers/xbox.json
+++ b/interface/resources/controllers/xbox.json
@@ -15,12 +15,14 @@
 
         { "from": "GamePad.Back", "to": "Standard.Back" }, 
         { "from": "GamePad.Start", "to": "Standard.Start" }, 
-        
+
+        { "from": [ "GamePad.DU", "GamePad.DL", "GamePad.DR", "GamePad.DD" ], "to": "Standard.LeftPrimaryThumb", "peek": true },
         { "from": "GamePad.DU", "to": "Standard.DU" },
         { "from": "GamePad.DD", "to": "Standard.DD" }, 
         { "from": "GamePad.DL", "to": "Standard.DL" },
         { "from": "GamePad.DR", "to": "Standard.DR" }, 
 
+        { "from": [ "GamePad.A", "GamePad.B", "GamePad.X", "GamePad.Y" ], "to": "Standard.RightPrimaryThumb", "peek": true },
         { "from": "GamePad.A", "to": "Standard.A" }, 
         { "from": "GamePad.B", "to": "Standard.B" }, 
         { "from": "GamePad.X", "to": "Standard.X" },

From de900e85c059a951129f143e8cca0175cad327b1 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Mon, 8 Aug 2016 14:10:47 -0700
Subject: [PATCH 44/78] remove duplicate include

---
 libraries/render-utils/src/MeshPartPayload.cpp | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp
index 3b78023496..c2c27fb298 100644
--- a/libraries/render-utils/src/MeshPartPayload.cpp
+++ b/libraries/render-utils/src/MeshPartPayload.cpp
@@ -13,8 +13,6 @@
 
 #include <PerfStat.h>
 
-#include <Interpolate.h>
-
 #include "DeferredLightingEffect.h"
 #include "Model.h"
 

From 43bbe790d6389396d37306c985b5c80b098e20a4 Mon Sep 17 00:00:00 2001
From: Zander Otavka <zotavka@gmail.com>
Date: Fri, 5 Aug 2016 17:01:33 -0700
Subject: [PATCH 45/78] Add atp support to qml

---
 libraries/gl/src/gl/OffscreenQmlSurface.cpp   |  3 +
 .../networking/src/AssetResourceRequest.cpp   |  5 --
 libraries/networking/src/QmlAtpReply.cpp      | 88 +++++++++++++++++++
 libraries/networking/src/QmlAtpReply.h        | 40 +++++++++
 .../src/QmlNetworkAccessManager.cpp           | 29 ++++++
 .../networking/src/QmlNetworkAccessManager.h  | 33 +++++++
 libraries/ui/src/QmlWindowClass.cpp           |  7 +-
 7 files changed, 197 insertions(+), 8 deletions(-)
 create mode 100644 libraries/networking/src/QmlAtpReply.cpp
 create mode 100644 libraries/networking/src/QmlAtpReply.h
 create mode 100644 libraries/networking/src/QmlNetworkAccessManager.cpp
 create mode 100644 libraries/networking/src/QmlNetworkAccessManager.h

diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp
index 8c167fafdc..ebccc8a1fc 100644
--- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp
+++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp
@@ -25,6 +25,7 @@
 #include <NumericalConstants.h>
 #include <Finally.h>
 #include <PathUtils.h>
+#include <QmlNetworkAccessManager.h>
 
 #include "OffscreenGLCanvas.h"
 #include "GLEscrow.h"
@@ -402,6 +403,8 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
     // Create a QML engine.
     _qmlEngine = new QQmlEngine;
 
+    _qmlEngine->setNetworkAccessManagerFactory(new QmlNetworkAccessManagerFactory);
+
     auto importList = _qmlEngine->importPathList();
     importList.insert(importList.begin(), PathUtils::resourcesPath());
     _qmlEngine->setImportPathList(importList);
diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp
index 6b94ee152a..a8311c6146 100644
--- a/libraries/networking/src/AssetResourceRequest.cpp
+++ b/libraries/networking/src/AssetResourceRequest.cpp
@@ -33,12 +33,7 @@ bool AssetResourceRequest::urlIsAssetHash() const {
 }
 
 void AssetResourceRequest::doSend() {
-    auto parts = _url.path().split(".", QString::SkipEmptyParts);
-    auto hash = parts.length() > 0 ? parts[0] : "";
-    auto extension = parts.length() > 1 ? parts[1] : "";
-
     // We'll either have a hash or an ATP path to a file (that maps to a hash)
-
     if (urlIsAssetHash()) {
         // We've detected that this is a hash - simply use AssetClient to request that asset
         auto parts = _url.path().split(".", QString::SkipEmptyParts);
diff --git a/libraries/networking/src/QmlAtpReply.cpp b/libraries/networking/src/QmlAtpReply.cpp
new file mode 100644
index 0000000000..fd1e1e5023
--- /dev/null
+++ b/libraries/networking/src/QmlAtpReply.cpp
@@ -0,0 +1,88 @@
+//
+//  QmlAtpReply.cpp
+//  libraries/networking/src
+//
+//  Created by Zander Otavka on 8/4/16.
+//  Copyright 2016 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 "ResourceManager.h"
+#include "QmlAtpReply.h"
+
+QmlAtpReply::QmlAtpReply(const QUrl& url, QObject* parent) :
+        _resourceRequest(ResourceManager::createResourceRequest(parent, url)) {
+    setOperation(QNetworkAccessManager::GetOperation);
+
+    connect(_resourceRequest, &AssetResourceRequest::progress, this, &QmlAtpReply::downloadProgress);
+    connect(_resourceRequest, &AssetResourceRequest::finished, this, &QmlAtpReply::handleRequestFinish);
+
+    _resourceRequest->send();
+}
+
+QmlAtpReply::~QmlAtpReply() {
+    if (_resourceRequest) {
+        _resourceRequest->deleteLater();
+        _resourceRequest = nullptr;
+    }
+}
+
+qint64 QmlAtpReply::bytesAvailable() const {
+    return _content.size() - _readOffset + QIODevice::bytesAvailable();
+}
+
+qint64 QmlAtpReply::readData(char* data, qint64 maxSize) {
+    if (_readOffset < _content.size()) {
+        qint64 readSize = qMin(maxSize, _content.size() - _readOffset);
+        memcpy(data, _content.constData() + _readOffset, readSize);
+        _readOffset += readSize;
+        return readSize;
+    } else {
+        return -1;
+    }
+}
+
+void QmlAtpReply::handleRequestFinish() {
+    Q_ASSERT(_resourceRequest->getState() == ResourceRequest::State::Finished);
+
+    switch (_resourceRequest->getResult()) {
+        case ResourceRequest::Result::Success:
+            setError(NoError, "Success");
+            _content = _resourceRequest->getData();
+            break;
+        case ResourceRequest::Result::InvalidURL:
+            setError(ContentNotFoundError, "Invalid URL");
+            break;
+        case ResourceRequest::Result::NotFound:
+            setError(ContentNotFoundError, "Not found");
+            break;
+        case ResourceRequest::Result::ServerUnavailable:
+            setError(ServiceUnavailableError, "Service unavailable");
+            break;
+        case ResourceRequest::Result::AccessDenied:
+            setError(ContentAccessDenied, "Access denied");
+            break;
+        case ResourceRequest::Result::Timeout:
+            setError(TimeoutError, "Timeout");
+            break;
+        default:
+            setError(UnknownNetworkError, "Unknown error");
+            break;
+    }
+
+    open(ReadOnly | Unbuffered);
+    setHeader(QNetworkRequest::ContentLengthHeader, QVariant(_content.size()));
+
+    if (error() != NoError) {
+        emit error(error());
+    }
+
+    setFinished(true);
+    emit readyRead();
+    emit finished();
+
+    _resourceRequest->deleteLater();
+    _resourceRequest = nullptr;
+}
\ No newline at end of file
diff --git a/libraries/networking/src/QmlAtpReply.h b/libraries/networking/src/QmlAtpReply.h
new file mode 100644
index 0000000000..7b0fa5686e
--- /dev/null
+++ b/libraries/networking/src/QmlAtpReply.h
@@ -0,0 +1,40 @@
+//
+//  QmlAtpReply.h
+//  libraries/networking/src
+//
+//  Created by Zander Otavka on 8/4/16.
+//  Copyright 2016 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_QmlAtpReply_h
+#define hifi_QmlAtpReply_h
+
+#include <QtNetwork/QNetworkReply>
+#include <QUrl>
+
+#include "AssetResourceRequest.h"
+
+class QmlAtpReply : public QNetworkReply {
+    Q_OBJECT
+public:
+    QmlAtpReply(const QUrl& url, QObject* parent = Q_NULLPTR);
+    ~QmlAtpReply();
+    qint64 bytesAvailable() const override;
+    void abort() override { }
+    bool isSequential() const override { return true; }
+
+protected:
+    qint64 readData(char* data, qint64 maxSize) override;
+
+private:
+    void handleRequestFinish();
+
+    ResourceRequest* _resourceRequest { nullptr };
+    QByteArray _content;
+    qint64 _readOffset { 0 };
+};
+
+#endif // hifi_QmlAtpReply_h
\ No newline at end of file
diff --git a/libraries/networking/src/QmlNetworkAccessManager.cpp b/libraries/networking/src/QmlNetworkAccessManager.cpp
new file mode 100644
index 0000000000..9e86d3b85f
--- /dev/null
+++ b/libraries/networking/src/QmlNetworkAccessManager.cpp
@@ -0,0 +1,29 @@
+//
+//  QmlNetworkAccessManager.cpp
+//
+//
+//  Created by Clement on 7/1/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
+//
+
+#include <QThreadStorage>
+
+#include "QmlAtpReply.h"
+#include "QmlNetworkAccessManager.h"
+
+QNetworkAccessManager* QmlNetworkAccessManagerFactory::create(QObject* parent) {
+    return new QmlNetworkAccessManager(parent);
+}
+
+QNetworkReply* QmlNetworkAccessManager::createRequest(Operation operation, const QNetworkRequest& request, QIODevice* device) {
+    if (request.url().scheme() == "atp" && operation == GetOperation) {
+        return new QmlAtpReply(request.url());
+        //auto url = request.url().toString();
+        //return QNetworkAccessManager::createRequest(operation, request, device);
+    } else {
+        return QNetworkAccessManager::createRequest(operation, request, device);
+    }
+}
\ No newline at end of file
diff --git a/libraries/networking/src/QmlNetworkAccessManager.h b/libraries/networking/src/QmlNetworkAccessManager.h
new file mode 100644
index 0000000000..72ca0a4cb4
--- /dev/null
+++ b/libraries/networking/src/QmlNetworkAccessManager.h
@@ -0,0 +1,33 @@
+//
+//  QmlNetworkAccessManager.h
+//
+//
+//  Created by Clement on 7/1/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
+//
+
+#ifndef hifi_QmlNetworkAccessManager_h
+#define hifi_QmlNetworkAccessManager_h
+
+#include <QtNetwork/QNetworkAccessManager>
+#include <QtNetwork/QNetworkRequest>
+#include <QtQml/QQmlNetworkAccessManagerFactory>
+
+class QmlNetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory {
+public:
+    QNetworkAccessManager* create(QObject* parent);
+};
+
+
+class QmlNetworkAccessManager : public QNetworkAccessManager {
+    Q_OBJECT
+public:
+    QmlNetworkAccessManager(QObject* parent = Q_NULLPTR) : QNetworkAccessManager(parent) { }
+protected:
+    QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice* device = Q_NULLPTR);
+};
+
+#endif // hifi_QmlNetworkAccessManager_h
\ No newline at end of file
diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp
index c3ca5f54d9..554ae7d8c2 100644
--- a/libraries/ui/src/QmlWindowClass.cpp
+++ b/libraries/ui/src/QmlWindowClass.cpp
@@ -59,9 +59,10 @@ QVariantMap QmlWindowClass::parseArguments(QScriptContext* context) {
         properties = context->argument(0).toVariant().toMap();
     }
 
-    QString url = properties[SOURCE_PROPERTY].toString();
-    if (!url.startsWith("http") && !url.startsWith("file://") && !url.startsWith("about:")) {
-        properties[SOURCE_PROPERTY] = QUrl::fromLocalFile(url).toString();
+    QUrl url { properties[SOURCE_PROPERTY].toString() };
+    if (url.scheme() != "http" && url.scheme() != "https" && url.scheme() != "file" && url.scheme() != "about" &&
+            url.scheme() != "atp") {
+        properties[SOURCE_PROPERTY] = QUrl::fromLocalFile(url.toString()).toString();
     }
 
     return properties;

From 246a8457e176f8b8e95e098c9a10dc9c59917af3 Mon Sep 17 00:00:00 2001
From: Zander Otavka <zotavka@gmail.com>
Date: Mon, 8 Aug 2016 11:52:04 -0700
Subject: [PATCH 46/78] Add some asserts

---
 libraries/networking/src/ResourceCache.cpp  | 1 +
 libraries/script-engine/src/ScriptCache.cpp | 1 +
 2 files changed, 2 insertions(+)

diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp
index 36828b3992..5fb686ca6f 100644
--- a/libraries/networking/src/ResourceCache.cpp
+++ b/libraries/networking/src/ResourceCache.cpp
@@ -462,6 +462,7 @@ int ResourceCache::getPendingRequestCount() {
 }
 
 bool ResourceCache::attemptRequest(QSharedPointer<Resource> resource) {
+    Q_ASSERT(!resource.isNull());
     auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>();
 
     if (_requestsActive >= _requestLimit) {
diff --git a/libraries/script-engine/src/ScriptCache.cpp b/libraries/script-engine/src/ScriptCache.cpp
index 40234e8134..91d7f36102 100644
--- a/libraries/script-engine/src/ScriptCache.cpp
+++ b/libraries/script-engine/src/ScriptCache.cpp
@@ -167,6 +167,7 @@ void ScriptCache::scriptContentAvailable() {
         Lock lock(_containerLock);
         allCallbacks = _contentCallbacks.values(url);
         _contentCallbacks.remove(url);
+        Q_ASSERT(req->getState() == ResourceRequest::Finished);
         success = req->getResult() == ResourceRequest::Success;
 
         if (success) {

From aebd18db7f50e207489e7a7da76f3ccadbd28baf Mon Sep 17 00:00:00 2001
From: Zander Otavka <zotavka@gmail.com>
Date: Mon, 8 Aug 2016 13:38:14 -0700
Subject: [PATCH 47/78] Add EOF newlines

---
 libraries/networking/src/QmlAtpReply.cpp             | 2 +-
 libraries/networking/src/QmlAtpReply.h               | 2 +-
 libraries/networking/src/QmlNetworkAccessManager.cpp | 2 +-
 libraries/networking/src/QmlNetworkAccessManager.h   | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/libraries/networking/src/QmlAtpReply.cpp b/libraries/networking/src/QmlAtpReply.cpp
index fd1e1e5023..a2e537ba1f 100644
--- a/libraries/networking/src/QmlAtpReply.cpp
+++ b/libraries/networking/src/QmlAtpReply.cpp
@@ -85,4 +85,4 @@ void QmlAtpReply::handleRequestFinish() {
 
     _resourceRequest->deleteLater();
     _resourceRequest = nullptr;
-}
\ No newline at end of file
+}
diff --git a/libraries/networking/src/QmlAtpReply.h b/libraries/networking/src/QmlAtpReply.h
index 7b0fa5686e..a8f6dfde14 100644
--- a/libraries/networking/src/QmlAtpReply.h
+++ b/libraries/networking/src/QmlAtpReply.h
@@ -37,4 +37,4 @@ private:
     qint64 _readOffset { 0 };
 };
 
-#endif // hifi_QmlAtpReply_h
\ No newline at end of file
+#endif // hifi_QmlAtpReply_h
diff --git a/libraries/networking/src/QmlNetworkAccessManager.cpp b/libraries/networking/src/QmlNetworkAccessManager.cpp
index 9e86d3b85f..ef44eb8526 100644
--- a/libraries/networking/src/QmlNetworkAccessManager.cpp
+++ b/libraries/networking/src/QmlNetworkAccessManager.cpp
@@ -26,4 +26,4 @@ QNetworkReply* QmlNetworkAccessManager::createRequest(Operation operation, const
     } else {
         return QNetworkAccessManager::createRequest(operation, request, device);
     }
-}
\ No newline at end of file
+}
diff --git a/libraries/networking/src/QmlNetworkAccessManager.h b/libraries/networking/src/QmlNetworkAccessManager.h
index 72ca0a4cb4..9b817d612e 100644
--- a/libraries/networking/src/QmlNetworkAccessManager.h
+++ b/libraries/networking/src/QmlNetworkAccessManager.h
@@ -30,4 +30,4 @@ protected:
     QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice* device = Q_NULLPTR);
 };
 
-#endif // hifi_QmlNetworkAccessManager_h
\ No newline at end of file
+#endif // hifi_QmlNetworkAccessManager_h

From cb153f2a29f59297fa8566918997a07335f741fb Mon Sep 17 00:00:00 2001
From: Zander Otavka <zotavka@gmail.com>
Date: Mon, 8 Aug 2016 15:17:17 -0700
Subject: [PATCH 48/78] Fix file header comments

---
 libraries/networking/src/QmlNetworkAccessManager.cpp | 4 ++--
 libraries/networking/src/QmlNetworkAccessManager.h   | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/libraries/networking/src/QmlNetworkAccessManager.cpp b/libraries/networking/src/QmlNetworkAccessManager.cpp
index ef44eb8526..575bc02f8c 100644
--- a/libraries/networking/src/QmlNetworkAccessManager.cpp
+++ b/libraries/networking/src/QmlNetworkAccessManager.cpp
@@ -1,8 +1,8 @@
 //
 //  QmlNetworkAccessManager.cpp
+//  libraries/networking/src
 //
-//
-//  Created by Clement on 7/1/14.
+//  Created by Zander Otavka on 8/4/16.
 //  Copyright 2014 High Fidelity, Inc.
 //
 //  Distributed under the Apache License, Version 2.0.
diff --git a/libraries/networking/src/QmlNetworkAccessManager.h b/libraries/networking/src/QmlNetworkAccessManager.h
index 9b817d612e..059d0ebba0 100644
--- a/libraries/networking/src/QmlNetworkAccessManager.h
+++ b/libraries/networking/src/QmlNetworkAccessManager.h
@@ -1,8 +1,8 @@
 //
 //  QmlNetworkAccessManager.h
+//  libraries/networking/src
 //
-//
-//  Created by Clement on 7/1/14.
+//  Created by Zander Otavka on 8/4/16.
 //  Copyright 2014 High Fidelity, Inc.
 //
 //  Distributed under the Apache License, Version 2.0.

From d36e60bbaa84e1b0bd6530a552bc27796025b7a0 Mon Sep 17 00:00:00 2001
From: "Anthony J. Thibault" <tony@highfidelity.io>
Date: Mon, 8 Aug 2016 15:20:58 -0700
Subject: [PATCH 49/78] eslintrc: accept spaces after function keyword in
 anonymous functions

Anonymous functions `function () {}` and `function() {}` will both be accepted by eslint.
---
 .eslintrc.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.eslintrc.js b/.eslintrc.js
index 82dfe9e9bd..6183fa8aec 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -72,6 +72,6 @@ module.exports = {
         "spaced-comment": ["error", "always", {
             "line": { "markers": ["/"] }
         }],
-        "space-before-function-paren": ["error", {"anonymous": "always", "named": "never"}]
+        "space-before-function-paren": ["error", {"anonymous": "ignore", "named": "never"}]
     }
 };

From 9b137570cfb2fefe613c96d7f888d9deaef0c7fe Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Mon, 8 Aug 2016 15:21:35 -0700
Subject: [PATCH 50/78] Fix srgb_gen lookup table generation

---
 tools/srgb_gen.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/srgb_gen.py b/tools/srgb_gen.py
index e17970209a..6db2c1da03 100644
--- a/tools/srgb_gen.py
+++ b/tools/srgb_gen.py
@@ -19,7 +19,7 @@ for i in range(NUM_VALUES):
     if s < 0.04045:
         l = s / 12.92
     else:
-        l = ((s + 0.044) / 1.055) ** 2.4
+        l = ((s + 0.055) / 1.055) ** 2.4
     srgb_to_linear.append(l)
 
 # Format and print

From b800aa793da8753f13279a06e32f6c2d8c24a1bd Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Mon, 8 Aug 2016 15:45:05 -0700
Subject: [PATCH 51/78] Fix gamma correction adjusting uchar to float when
 unnecessary

---
 libraries/gpu/src/gpu/Texture.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp
index bd0ad0ce7b..25e4fa549c 100755
--- a/libraries/gpu/src/gpu/Texture.cpp
+++ b/libraries/gpu/src/gpu/Texture.cpp
@@ -788,9 +788,9 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
                 uint pixOffsetIndex = (x + y * width) * numComponents;
 
                 // get color from texture and map to range [0, 1]
-                glm::vec3 clr(ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex]) * UCHAR_TO_FLOAT,
-                              ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex+1]) * UCHAR_TO_FLOAT,
-                              ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex+2]) * UCHAR_TO_FLOAT);
+                glm::vec3 clr(ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex]),
+                              ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex + 1]),
+                              ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex + 2]));
 
                 // scale color and add to previously accumulated coefficients
                 sphericalHarmonicsScale(shBuffB.data(), order,

From 43c1472b36681d4463d674c972c182fb9f0a518a Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Mon, 8 Aug 2016 15:47:53 -0700
Subject: [PATCH 52/78] Move srgbToLinear lookup table to external cpp

---
 libraries/shared/src/ColorUtils.cpp | 59 +++++++++++++++++++++++++++++
 libraries/shared/src/ColorUtils.h   | 51 ++-----------------------
 2 files changed, 62 insertions(+), 48 deletions(-)
 create mode 100644 libraries/shared/src/ColorUtils.cpp

diff --git a/libraries/shared/src/ColorUtils.cpp b/libraries/shared/src/ColorUtils.cpp
new file mode 100644
index 0000000000..f0dfc89367
--- /dev/null
+++ b/libraries/shared/src/ColorUtils.cpp
@@ -0,0 +1,59 @@
+//
+//  ColorUtils.cpp
+//  libraries/shared/src
+//
+//  Created by Ryan Huffman on 8/8/16.
+//  Copyright 2016 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 "ColorUtils.h"
+
+// Generated from python script in repository at `tools/srgb_gen.py`
+const float srgbToLinearLookupTable[256] = {
+    0.0f, 0.000303526983549f, 0.000607053967098f, 0.000910580950647f, 0.0012141079342f, 0.00151763491774f, 0.00182116190129f,
+    0.00212468888484f, 0.00242821586839f, 0.00273174285194f, 0.00303526983549f, 0.0033465357639f, 0.00367650732405f,
+    0.0040247170185f, 0.00439144203741f, 0.00477695348069f, 0.00518151670234f, 0.0056053916242f, 0.00604883302286f,
+    0.00651209079259f, 0.00699541018727f, 0.00749903204323f, 0.00802319298538f, 0.00856812561807f, 0.00913405870222f,
+    0.00972121732024f, 0.0103298230296f, 0.0109600940065f, 0.0116122451797f, 0.0122864883569f, 0.0129830323422f,
+    0.0137020830473f, 0.0144438435961f, 0.0152085144229f, 0.0159962933655f, 0.0168073757529f, 0.0176419544884f,
+    0.0185002201284f, 0.0193823609569f, 0.0202885630567f, 0.021219010376f, 0.0221738847934f, 0.0231533661781f,
+    0.0241576324485f, 0.0251868596274f, 0.0262412218948f, 0.0273208916391f, 0.0284260395044f, 0.0295568344378f,
+    0.030713443733f, 0.031896033073f, 0.0331047665709f, 0.0343398068087f, 0.035601314875f, 0.0368894504011f,
+    0.0382043715953f, 0.0395462352767f, 0.0409151969069f, 0.0423114106208f, 0.043735029257f, 0.0451862043857f,
+    0.0466650863369f, 0.0481718242269f, 0.0497065659841f, 0.051269458374f, 0.0528606470232f, 0.0544802764424f,
+    0.0561284900496f, 0.0578054301911f, 0.059511238163f, 0.0612460542316f, 0.0630100176532f, 0.0648032666929f,
+    0.0666259386438f, 0.0684781698444f, 0.0703600956966f, 0.0722718506823f, 0.0742135683801f, 0.0761853814813f,
+    0.0781874218052f, 0.0802198203145f, 0.0822827071298f, 0.0843762115441f, 0.0865004620365f, 0.0886555862858f,
+    0.0908417111834f, 0.0930589628467f, 0.095307466631f, 0.0975873471419f, 0.0998987282471f, 0.102241733088f,
+    0.104616484091f, 0.107023102978f, 0.109461710778f, 0.111932427837f, 0.114435373827f, 0.116970667759f,
+    0.119538427988f, 0.12213877223f, 0.124771817561f, 0.127437680436f, 0.13013647669f, 0.132868321554f,
+    0.135633329655f, 0.138431615032f, 0.14126329114f, 0.144128470858f, 0.147027266498f, 0.149959789811f,
+    0.152926151996f, 0.155926463708f, 0.158960835061f, 0.162029375639f, 0.165132194502f, 0.16826940019f,
+    0.171441100733f, 0.174647403656f, 0.177888415984f, 0.18116424425f, 0.1844749945f, 0.187820772301f,
+    0.191201682741f, 0.194617830442f, 0.19806931956f, 0.201556253794f, 0.20507873639f, 0.208636870145f,
+    0.212230757414f, 0.215860500114f, 0.219526199729f, 0.223227957317f, 0.22696587351f, 0.230740048524f,
+    0.234550582161f, 0.238397573812f, 0.242281122466f, 0.246201326708f, 0.25015828473f, 0.254152094331f,
+    0.258182852922f, 0.26225065753f, 0.266355604803f, 0.270497791013f, 0.27467731206f, 0.278894263477f,
+    0.28314874043f, 0.287440837727f, 0.291770649818f, 0.296138270798f, 0.300543794416f, 0.30498731407f,
+    0.309468922818f, 0.313988713376f, 0.318546778125f, 0.323143209113f, 0.327778098057f, 0.332451536346f,
+    0.337163615048f, 0.341914424909f, 0.346704056355f, 0.3515325995f, 0.356400144146f, 0.361306779784f,
+    0.366252595599f, 0.371237680474f, 0.376262122991f, 0.381326011433f, 0.386429433787f, 0.39157247775f,
+    0.396755230726f, 0.401977779832f, 0.407240211902f, 0.412542613484f, 0.417885070848f, 0.423267669986f,
+    0.428690496614f, 0.434153636175f, 0.439657173841f, 0.445201194516f, 0.450785782838f, 0.45641102318f,
+    0.462076999654f, 0.467783796112f, 0.473531496148f, 0.479320183101f, 0.485149940056f, 0.491020849848f,
+    0.496932995061f, 0.502886458033f, 0.508881320855f, 0.514917665377f, 0.520995573204f, 0.527115125706f,
+    0.533276404011f, 0.539479489012f, 0.54572446137f, 0.552011401512f, 0.558340389634f, 0.564711505705f,
+    0.571124829465f, 0.57758044043f, 0.584078417891f, 0.590618840919f, 0.597201788364f, 0.603827338855f,
+    0.610495570808f, 0.61720656242f, 0.623960391675f, 0.630757136346f, 0.637596873994f, 0.644479681971f,
+    0.65140563742f, 0.658374817279f, 0.665387298282f, 0.672443156958f, 0.679542469633f, 0.686685312435f,
+    0.693871761292f, 0.701101891933f, 0.708375779892f, 0.715693500506f, 0.723055128922f, 0.73046074009f,
+    0.737910408773f, 0.74540420954f, 0.752942216776f, 0.760524504675f, 0.768151147248f, 0.775822218317f,
+    0.783537791526f, 0.791297940333f, 0.799102738014f, 0.806952257669f, 0.814846572216f, 0.822785754396f,
+    0.830769876775f, 0.838799011741f, 0.84687323151f, 0.854992608124f, 0.863157213454f, 0.871367119199f,
+    0.879622396888f, 0.887923117882f, 0.896269353374f, 0.904661174391f, 0.913098651793f, 0.921581856277f,
+    0.930110858375f, 0.938685728458f, 0.947306536733f, 0.955973353249f, 0.964686247894f, 0.973445290398f,
+    0.982250550333f, 0.991102097114f, 1.0f
+};
\ No newline at end of file
diff --git a/libraries/shared/src/ColorUtils.h b/libraries/shared/src/ColorUtils.h
index 921b26399d..fd0bbdd8ab 100644
--- a/libraries/shared/src/ColorUtils.h
+++ b/libraries/shared/src/ColorUtils.h
@@ -13,57 +13,12 @@
 #define hifi_ColorUtils_h
 
 #include <glm/glm.hpp>
-#include <SharedUtil.h>
+
+#include "SharedUtil.h"
 
 #include "DependencyManager.h"
 
-// Generated from python script in repository at `tools/srgb_gen.py`
-static const float srgbToLinearLookupTable[256] = {
-    0.0f, 0.000303526983549f, 0.000607053967098f, 0.000910580950647f, 0.0012141079342f, 0.00151763491774f, 0.00182116190129f,
-    0.00212468888484f, 0.00242821586839f, 0.00273174285194f, 0.00303526983549f, 0.00251584218443f, 0.00279619194822f,
-    0.00309396642819f, 0.00340946205345f, 0.00374296799396f, 0.00409476661624f, 0.00446513389425f, 0.00485433978143f,
-    0.00526264854875f, 0.00569031909303f, 0.00613760521883f, 0.00660475589722f, 0.00709201550367f, 0.00759962403765f,
-    0.00812781732551f, 0.00867682720861f, 0.00924688171802f, 0.00983820523704f, 0.0104510186528f, 0.0110855394981f,
-    0.0117419820834f, 0.0124205576216f, 0.0131214743443f, 0.0138449376117f, 0.0145911500156f, 0.0153603114768f,
-    0.0161526193372f, 0.0169682684465f, 0.0178074512441f, 0.0186703578377f, 0.0195571760767f, 0.0204680916222f,
-    0.0214032880141f, 0.0223629467344f, 0.0233472472675f, 0.0243563671578f, 0.0253904820647f, 0.026449765815f,
-    0.0275343904531f, 0.0286445262888f, 0.0297803419432f, 0.0309420043928f, 0.0321296790111f, 0.0333435296099f,
-    0.0345837184768f, 0.0358504064137f, 0.0371437527716f, 0.0384639154854f, 0.0398110511069f, 0.0411853148367f,
-    0.0425868605546f, 0.0440158408496f, 0.045472407048f, 0.046956709241f, 0.0484688963113f, 0.0500091159586f,
-    0.0515775147244f, 0.0531742380159f, 0.0547994301291f, 0.0564532342711f, 0.058135792582f, 0.0598472461555f,
-    0.0615877350593f, 0.063357398355f, 0.0651563741167f, 0.0669847994499f, 0.0688428105093f, 0.0707305425158f,
-    0.0726481297741f, 0.0745957056885f, 0.0765734027789f, 0.0785813526965f, 0.0806196862387f, 0.0826885333636f,
-    0.0847880232044f, 0.086918284083f, 0.0890794435236f, 0.0912716282659f, 0.0934949642776f, 0.0957495767668f,
-    0.0980355901944f, 0.100353128286f, 0.102702314041f, 0.10508326975f, 0.107496116997f, 0.109940976678f,
-    0.112417969007f, 0.114927213525f, 0.117468829116f, 0.120042934009f, 0.122649645793f, 0.125289081424f,
-    0.127961357236f, 0.130666588944f, 0.13340489166f, 0.136176379898f, 0.138981167581f, 0.141819368051f,
-    0.144691094076f, 0.147596457859f, 0.150535571041f, 0.153508544716f, 0.156515489432f, 0.1595565152f,
-    0.1626317315f, 0.16574124729f, 0.168885171012f, 0.172063610595f, 0.175276673468f, 0.178524466557f,
-    0.181807096302f, 0.185124668654f, 0.188477289086f, 0.191865062595f, 0.195288093712f, 0.198746486503f,
-    0.202240344578f, 0.205769771096f, 0.209334868766f, 0.212935739858f, 0.216572486205f, 0.220245209207f,
-    0.223954009837f, 0.227698988648f, 0.231480245773f, 0.235297880934f, 0.239151993444f, 0.243042682212f,
-    0.246970045747f, 0.250934182163f, 0.254935189183f, 0.258973164144f, 0.263048203998f, 0.267160405319f,
-    0.271309864307f, 0.27549667679f, 0.279720938228f, 0.283982743718f, 0.288282187998f, 0.292619365448f,
-    0.296994370096f, 0.30140729562f, 0.305858235354f, 0.310347282289f, 0.314874529074f, 0.319440068025f,
-    0.324043991126f, 0.32868639003f, 0.333367356062f, 0.338086980228f, 0.34284535321f, 0.347642565374f,
-    0.352478706774f, 0.357353867148f, 0.36226813593f, 0.367221602246f, 0.372214354918f, 0.37724648247f,
-    0.382318073128f, 0.387429214822f, 0.392579995191f, 0.397770501584f, 0.403000821062f, 0.408271040402f,
-    0.413581246099f, 0.418931524369f, 0.424321961148f, 0.4297526421f, 0.435223652615f, 0.440735077813f,
-    0.446287002544f, 0.451879511396f, 0.45751268869f, 0.463186618488f, 0.46890138459f, 0.474657070542f,
-    0.480453759632f, 0.486291534897f, 0.492170479122f, 0.498090674843f, 0.50405220435f, 0.510055149687f,
-    0.516099592656f, 0.522185614816f, 0.528313297489f, 0.534482721758f, 0.54069396847f, 0.546947118241f,
-    0.553242251452f, 0.559579448254f, 0.565958788573f, 0.572380352104f, 0.578844218319f, 0.585350466467f,
-    0.591899175574f, 0.598490424448f, 0.605124291677f, 0.611800855632f, 0.61852019447f, 0.625282386134f,
-    0.632087508355f, 0.638935638652f, 0.645826854338f, 0.652761232515f, 0.659738850081f, 0.66675978373f,
-    0.673824109951f, 0.680931905032f, 0.688083245062f, 0.695278205929f, 0.702516863324f, 0.709799292744f,
-    0.717125569488f, 0.724495768663f, 0.731909965185f, 0.739368233777f, 0.746870648974f, 0.754417285121f,
-    0.762008216379f, 0.76964351672f, 0.777323259932f, 0.785047519623f, 0.792816369214f, 0.800629881949f,
-    0.80848813089f, 0.816391188922f, 0.824339128751f, 0.832332022907f, 0.840369943747f, 0.848452963452f,
-    0.856581154031f, 0.864754587319f, 0.872973334984f, 0.881237468522f, 0.889547059261f, 0.897902178361f,
-    0.906302896816f, 0.914749285456f, 0.923241414944f, 0.931779355781f, 0.940363178305f, 0.948992952695f,
-    0.957668748966f, 0.966390636975f, 0.975158686423f
-};
-
+extern const float srgbToLinearLookupTable[256];
 
 class ColorUtils {
 public:

From 35ea9908cde7aa384fefcb0de36b4dd4ae6dc433 Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Thu, 28 Jul 2016 22:37:06 -0700
Subject: [PATCH 53/78] Add default skybox

---
 interface/src/Application.cpp                 | 32 ++++++++++++++++++-
 .../src/EntityTreeRenderer.cpp                |  1 -
 2 files changed, 31 insertions(+), 2 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index bd55f32629..5c3daaa9c2 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -164,6 +164,13 @@ extern "C" {
 }
 #endif
 
+#include <procedural/ProceduralSkybox.h>
+#include <model/Skybox.h>
+
+static model::Skybox* skybox{ new ProceduralSkybox() } ;
+static NetworkTexturePointer skyboxTexture;
+static bool skyboxTextureLoaded = false;
+
 using namespace std;
 
 static QTimer locationUpdateTimer;
@@ -1212,6 +1219,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
     connect(this, &Application::applicationStateChanged, this, &Application::activeChanged);
     qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0);
 
+	auto textureCache = DependencyManager::get<TextureCache>();
+	skyboxTexture = textureCache->getTexture(QUrl("https://hifi-public.s3.amazonaws.com/images/SkyboxTextures/FullMoon1024Compressed.jpg"), NetworkTexture::CUBE_TEXTURE);
+
     // After all of the constructor is completed, then set firstRun to false.
     Setting::Handle<bool> firstRun{ Settings::firstRun, true };
     firstRun.set(false);
@@ -4241,6 +4251,7 @@ namespace render {
 
             // Fall through: if no skybox is available, render the SKY_DOME
             case model::SunSkyStage::SKY_DOME:  {
+				/*
                 if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
                     PerformanceTimer perfTimer("stars");
                     PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
@@ -4250,6 +4261,25 @@ namespace render {
                     static const float alpha = 1.0f;
                     background->_stars.render(args, alpha);
                 }
+				*/
+
+				if (!skyboxTextureLoaded && skyboxTexture && skyboxTexture->isLoaded()) {
+					skybox->setColor({ 1.0, 1.0, 1.0 });
+					skyboxTextureLoaded = true;
+					auto texture = skyboxTexture->getGPUTexture();
+					if (texture) {
+						skybox->setCubemap(texture);
+						auto scene = DependencyManager::get<SceneScriptingInterface>()->getStage();
+						auto sceneKeyLight = scene->getKeyLight();
+						sceneKeyLight->setAmbientSphere(texture->getIrradiance());
+						sceneKeyLight->setAmbientMap(texture);
+					} else {
+						skybox->setCubemap(nullptr);
+					}
+				}
+				if (skyboxTextureLoaded) {
+					skybox->render(batch, args->getViewFrustum());
+				}
             }
                 break;
 
@@ -4443,7 +4473,6 @@ void Application::updateWindowTitle() const {
 #endif
     _window->setWindowTitle(title);
 }
-
 void Application::clearDomainOctreeDetails() {
 
     // if we're about to quit, we really don't need to do any of these things...
@@ -4469,6 +4498,7 @@ void Application::clearDomainOctreeDetails() {
     getEntities()->clear();
 
     auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
+
     skyStage->setBackgroundMode(model::SunSkyStage::SKY_DOME);
 
     _recentlyClearedDomain = true;
diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
index 24827ea111..9eadd6d0b9 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
@@ -375,7 +375,6 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityIt
         }
 
         skyStage->setBackgroundMode(model::SunSkyStage::SKY_DOME); // let the application background through
-
         return; // Early exit
     }
 

From 5b69ca03f04b6e26fdc3e04aeadc8e49142ccbc4 Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Thu, 4 Aug 2016 10:26:57 -0700
Subject: [PATCH 54/78] Move default skybox out of global and make irradiance
 gen optional

---
 interface/src/Application.cpp                 | 49 +++++++++----------
 interface/src/Application.h                   | 11 +++++
 .../src/model-networking/TextureCache.cpp     | 12 +++--
 .../src/model-networking/TextureCache.h       |  2 +-
 libraries/model/src/model/TextureMap.cpp      |  4 ++
 libraries/model/src/model/TextureMap.h        |  1 +
 6 files changed, 48 insertions(+), 31 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 5c3daaa9c2..4739fedc26 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -164,13 +164,6 @@ extern "C" {
 }
 #endif
 
-#include <procedural/ProceduralSkybox.h>
-#include <model/Skybox.h>
-
-static model::Skybox* skybox{ new ProceduralSkybox() } ;
-static NetworkTexturePointer skyboxTexture;
-static bool skyboxTextureLoaded = false;
-
 using namespace std;
 
 static QTimer locationUpdateTimer;
@@ -1219,8 +1212,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
     connect(this, &Application::applicationStateChanged, this, &Application::activeChanged);
     qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0);
 
-	auto textureCache = DependencyManager::get<TextureCache>();
-	skyboxTexture = textureCache->getTexture(QUrl("https://hifi-public.s3.amazonaws.com/images/SkyboxTextures/FullMoon1024Compressed.jpg"), NetworkTexture::CUBE_TEXTURE);
+    auto textureCache = DependencyManager::get<TextureCache>();
+
+    QString skyboxUrl { PathUtils::resourcesPath() + "images/Default-Sky-9-cubemap.jpg" };
+    QString skyboxAmbientUrl { PathUtils::resourcesPath() + "images/Default-Sky-9-ambient.jpg" };
+
+    _defaultSkyboxTexture = textureCache->getImageTexture(skyboxUrl, NetworkTexture::CUBE_TEXTURE, { { "generateIrradiance", false } });
+    _defaultSkyboxAmbientTexture = textureCache->getImageTexture(skyboxAmbientUrl, NetworkTexture::CUBE_TEXTURE, { { "generateIrradiance", true } });
+
+    _defaultSkybox->setCubemap(_defaultSkyboxTexture);
+    _defaultSkybox->setColor({ 1.0, 1.0, 1.0 });
 
     // After all of the constructor is completed, then set firstRun to false.
     Setting::Handle<bool> firstRun{ Settings::firstRun, true };
@@ -4263,23 +4264,19 @@ namespace render {
                 }
 				*/
 
-				if (!skyboxTextureLoaded && skyboxTexture && skyboxTexture->isLoaded()) {
-					skybox->setColor({ 1.0, 1.0, 1.0 });
-					skyboxTextureLoaded = true;
-					auto texture = skyboxTexture->getGPUTexture();
-					if (texture) {
-						skybox->setCubemap(texture);
-						auto scene = DependencyManager::get<SceneScriptingInterface>()->getStage();
-						auto sceneKeyLight = scene->getKeyLight();
-						sceneKeyLight->setAmbientSphere(texture->getIrradiance());
-						sceneKeyLight->setAmbientMap(texture);
-					} else {
-						skybox->setCubemap(nullptr);
-					}
-				}
-				if (skyboxTextureLoaded) {
-					skybox->render(batch, args->getViewFrustum());
-				}
+                auto scene = DependencyManager::get<SceneScriptingInterface>()->getStage();
+                auto sceneKeyLight = scene->getKeyLight();
+                scene->setSunModelEnable(false);
+                sceneKeyLight->setColor(glm::vec3(255.0f / 255.0f, 220.0f / 255.0f, 194.0f / 255.0f) * 0.2f);
+                sceneKeyLight->setIntensity(0.2f);
+                sceneKeyLight->setAmbientIntensity(3.5f);
+                sceneKeyLight->setDirection({ 0.0f, 0.0f, -1.0f });
+
+                auto defaultSkyboxAmbientTexture = qApp->getDefaultSkyboxAmbientTexture();
+                sceneKeyLight->setAmbientSphere(defaultSkyboxAmbientTexture->getIrradiance());
+                sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture);
+
+                qApp->getDefaultSkybox()->render(batch, args->getViewFrustum());
             }
                 break;
 
diff --git a/interface/src/Application.h b/interface/src/Application.h
index c81d56e0aa..8936206790 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -65,6 +65,9 @@
 #include "ui/overlays/Overlays.h"
 #include "UndoStackScriptingInterface.h"
 
+#include <procedural/ProceduralSkybox.h>
+#include <model/Skybox.h>
+
 class OffscreenGLCanvas;
 class GLCanvas;
 class FaceTracker;
@@ -249,6 +252,10 @@ public:
     float getAvatarSimrate() const { return _avatarSimCounter.rate(); }
     float getAverageSimsPerSecond() const { return _simCounter.rate(); }
 
+    model::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; }
+    gpu::TexturePointer getDefaultSkyboxTexture() const { return _defaultSkyboxTexture;  }
+    gpu::TexturePointer getDefaultSkyboxAmbientTexture() const { return _defaultSkyboxAmbientTexture; }
+
 signals:
     void svoImportRequested(const QString& url);
 
@@ -565,6 +572,10 @@ private:
     QString _returnFromFullScreenMirrorTo;
 
     ConnectionMonitor _connectionMonitor;
+
+    model::SkyboxPointer _defaultSkybox { new ProceduralSkybox() } ;
+    gpu::TexturePointer _defaultSkyboxTexture;
+    gpu::TexturePointer _defaultSkyboxAmbientTexture;
 };
 
 
diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp
index 000ca67989..c373da34ba 100644
--- a/libraries/model-networking/src/model-networking/TextureCache.cpp
+++ b/libraries/model-networking/src/model-networking/TextureCache.cpp
@@ -171,7 +171,7 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, Type type, const
 }
 
 
-NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type type) {
+NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type type, const QVariantMap& options = {}) {
     using Type = NetworkTexture;
 
     switch (type) {
@@ -188,7 +188,11 @@ NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type t
             break;
         }
         case Type::CUBE_TEXTURE: {
-            return model::TextureUsage::createCubeTextureFromImage;
+            if (options.value("generateIrradiance", true).toBool()) {
+                return model::TextureUsage::createCubeTextureFromImage;
+            } else {
+                return model::TextureUsage::createCubeTextureFromImageWithoutIrradiance;
+            }
             break;
         }
         case Type::BUMP_TEXTURE: {
@@ -225,9 +229,9 @@ NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type t
 }
 
 /// Returns a texture version of an image file
-gpu::TexturePointer TextureCache::getImageTexture(const QString& path, Type type) {
+gpu::TexturePointer TextureCache::getImageTexture(const QString& path, Type type, QVariantMap options) {
     QImage image = QImage(path);
-    auto loader = getTextureLoaderForType(type);
+    auto loader = getTextureLoaderForType(type, options);
     return gpu::TexturePointer(loader(image, QUrl::fromLocalFile(path).fileName().toStdString()));
 }
 
diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h
index 0108a3dd6c..66634b6ac0 100644
--- a/libraries/model-networking/src/model-networking/TextureCache.h
+++ b/libraries/model-networking/src/model-networking/TextureCache.h
@@ -122,7 +122,7 @@ public:
     const gpu::TexturePointer& getNormalFittingTexture();
 
     /// Returns a texture version of an image file
-    static gpu::TexturePointer getImageTexture(const QString& path, Type type = Type::DEFAULT_TEXTURE);
+    static gpu::TexturePointer getImageTexture(const QString& path, Type type = Type::DEFAULT_TEXTURE, QVariantMap options = {});
 
     /// Loads a texture from the specified URL.
     NetworkTexturePointer getTexture(const QUrl& url, Type type = Type::DEFAULT_TEXTURE,
diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp
index 3e6016d7c0..587aa6e0fa 100755
--- a/libraries/model/src/model/TextureMap.cpp
+++ b/libraries/model/src/model/TextureMap.cpp
@@ -729,3 +729,7 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm
 gpu::Texture* TextureUsage::createCubeTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
     return processCubeTextureColorFromImage(srcImage, srcImageName, false, true, true, true);
 }
+
+gpu::Texture* TextureUsage::createCubeTextureFromImageWithoutIrradiance(const QImage& srcImage, const std::string& srcImageName) {
+    return processCubeTextureColorFromImage(srcImage, srcImageName, false, true, true, false);
+}
diff --git a/libraries/model/src/model/TextureMap.h b/libraries/model/src/model/TextureMap.h
index daa4b0d7bb..795b685f27 100755
--- a/libraries/model/src/model/TextureMap.h
+++ b/libraries/model/src/model/TextureMap.h
@@ -40,6 +40,7 @@ public:
     static gpu::Texture* createRoughnessTextureFromGlossImage(const QImage& image, const std::string& srcImageName);
     static gpu::Texture* createMetallicTextureFromImage(const QImage& image, const std::string& srcImageName);
     static gpu::Texture* createCubeTextureFromImage(const QImage& image, const std::string& srcImageName);
+    static gpu::Texture* createCubeTextureFromImageWithoutIrradiance(const QImage& image, const std::string& srcImageName);
     static gpu::Texture* createLightmapTextureFromImage(const QImage& image, const std::string& srcImageName);
 
 

From c47a5c5aad8e0239f7ff3bd20609d6cb79b24619 Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Thu, 4 Aug 2016 10:28:06 -0700
Subject: [PATCH 55/78] Add default skybox images

---
 .../resources/images/Default-Sky-9-ambient.jpg | Bin 0 -> 6223 bytes
 .../resources/images/Default-Sky-9-cubemap.jpg | Bin 0 -> 403009 bytes
 2 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 interface/resources/images/Default-Sky-9-ambient.jpg
 create mode 100644 interface/resources/images/Default-Sky-9-cubemap.jpg

diff --git a/interface/resources/images/Default-Sky-9-ambient.jpg b/interface/resources/images/Default-Sky-9-ambient.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..8fb383c5e8d31fdb9aafeadddaa921aa6676c578
GIT binary patch
literal 6223
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
Y)zpC)UdYm|2N^x`r}6&-AhO&41GA7+L;wH)

literal 0
HcmV?d00001

diff --git a/interface/resources/images/Default-Sky-9-cubemap.jpg b/interface/resources/images/Default-Sky-9-cubemap.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..697fd9aeeaeb7f3b9688c75af5cdee4e581ac889
GIT binary patch
literal 403009
zcmeFY3sh6rx;DH*fT$6Ig2VzQpsj#Xf-NGbNkl{{R%5_hyD21qTvAYO)vY0bB1EME
ztyriRqR<FvzZwWZZ8w4h3st;uu}wl-tAdv9q_&8wl{EN`cZFWgKI8xXbI$lLCpk02
z!Me}2*7MH!KF{;c<#WyR?=Yvx)oWH`BoYY|U;+$#et-JbHDO_K8=`ot*SsE%kw_Re
z{k7Lqx9_lV!m#x1IayH=D?PWq@unx`CT4?8#q6;~7$!`}&R7?{dLxEm*qZP#&+Mp(
zm9YNj5C0M)!B+pvuWj)3{B!?*=r<!FBP$2PNE<K=do^)eb^?Zx_G1{9wkszC_Wy)o
zm`nWIux?`u>sedhcoV~HX2W{2dEJjV$GpA*))UjWCt{clJaa~3dLpcMVi<N{=R4aH
zF^v2=hGE4!Q@8EHF!BI~VK1h=lb(uU<X_?1^lidy46~gM`*XG>q+pnB0ES`Hvo=PB
zVVLc544X1N`7i77e_7Aj_V*lk>#!XeyR%Z0Q*u0g5_~-ug@goqzP4>w+P0jWmp2F#
z-WFyhdWP*t&k%0kjbY|@CVn#kThBG&VL^*Rf`VRt#edOXApAcb{15&hw&qix@QIj_
zTzmf9_vi1=eLHF}Echvgk<0(w7yluKT`0n^*}6aXy?6@4X!|j&<<?)`#})cZN={D3
zQWk61u3i4A+Y<Z%&_90vw<r7uet)^2KiuyR>^xU(OA@}5mg5PaChSPt@lKX!c7`xv
zo9D~_+KK<m7yjk<m*?<{+Lp8}YuomOZJwJ`x9v*Zp6nU6V|!w1PU?>Bo~hgauOj?k
zyzF0o=6;P~nDa*ym`j{L=5)&iBO7`!TW2apj;Y2ljPys`UZ1)dgORs|`N-Vwi61eV
z|LxDF48t&+?9^nU|Aa+t^h|gsYp3}f7?zkFHVva;F4zkg1Dl8WU@v2@U`w!N*a|Eh
zi^O=?M(hnN7E8cVuyia7+lA#~d$D4y1S`jmV#hEU_7T>AeS$S%7qEX|SFq2q>)37V
z4mOB=hYe%@#D2nX%t#`UC?p4xGwB5qlk^g4A?X!TFo{D7C%sOJBE3NpkdjH;N$-$y
zNrj{m()*-iq|>AZQX}aS=?du!QZGqMx<?uzjgrP~$Trh$Tx{mp%(r3L1lxq#thL!_
z^OjArO{UEroBcMWHdQt?HXqxx*j%x>Zga=xJDY#nJSAgfD%q9nMShtaOb#RS$ZwL9
z$XVn(@*(mE<QnoP<WI?6<bLvZWIg$rt-Y<Yt*7nFw##f^v)yEyV4G>1XZx=0G243E
z7TZqSe%pJtKiN*$IoNsFEwo!|x5jR(U9#OSyJEW!?9SM!?5^6~u^YC-?d|Pd?Y-@n
z*uQ4~hP}vMZ2zwPN&EBm?e@L)5A2^(D3li{3n^?0k0PYxP>LxZQa+}%QTiyulwYS%
zr!c3yGG+CYH>Yf$vTw=<Q_fDgJf&~SqbbIzPE%i+x^yaUYU0%0Q_H4)H1*S|x26tH
zHBOr`&3hVq+Qw<3X?v$tP5Wfp)oEW(dqSmB=Tbwcd}=DSfLcXWP`j!3sL!U)oW5Xs
z*z}m`?@TY9UN^mcx_0^#2M34w4l5kqbjWcib!c!<JACW#tE01{zvCLmM8|x`<Blzk
zeU3lUrqW)bh0<baduSifl(btkz0*{u`A%U@LZ>{Z<4%{HzH)jx!+FM{8N3<kGv1wX
zcE%SozMn~%Ie%vO%*2`dXV%VC&wSuacAn=P=A7VM==_oMRp((Bdlzq)H7+8T5|{tw
za?|CfSu<w^&e}Zd@3W52YMu3+E7^6v>l)WI*D}{8*Dqbi>2v6z^lkKm^iSx0^j~JX
z&t5Tm+w4QL&(FRyd;A5@7s6k7`-S&jxbVU^Znka<-1u(0+^XHKy6N3r+?Tm0xF2$F
za@Tp-cr5UU@(_E}dh~caox_+DF(+frhjY|(^bA+V3WkVL!MMyAVLCH8%v5FtvyJ(K
z=Pb`q&os{}&rZ*uyga;K^LoeYwAW29<6Q5#o96DDdw%XW^QO%Uo|iPQVqV9*pI&6X
zxbDT=7tg(@dui%R!7rt}RQXcZOTWzbp1)<jbpD0;-+RyYUgN#T`<(aJJ`O%ypG=<`
zp8;Qc-(cUjeNXz{USP9e@q&~E#~0jMXtOYAVd}z@3w!<S{6hS;`_=e;_44$WLtozc
z^0}Aq`MdhR?!V9fg8z@K7g=wz-eYyKCIS`*qy>BwFt}*uqR2)27Ja&C^pyp#B)oF`
zm4R0sUS0ia{;L;X{W)-9;I_c(K+R(3#p@PJ7k4Z+1uY563i>2yWXX$51WQgV`6}2Y
zcth~J!QCNtAuB`jL;ex+Y-!NaoTW`mA1`}(+1ty`F8hAD_wsGaKU)5PJ(n$HpJso@
z@#F|Nr#Rnny|_Yd4fp<vc`FiEoLTWG)HhTV`f=!wD+5+$tyHc2C2VP!B&>ZEc~!)!
zL#u9v&kWxbek}ak)pJ)TuRgc>r-;Q7;)wRw>|R^@+Iz2kwPwzmgf$Ioeu`WYDT(Y{
zJAG~R+GA_)zrNu0tk*wXXR|JH-H~-)uYYO%_VpKd7;g>l2=AK>-WxJEe8#uuZ{Q#2
zKa5%wl^fL+?HnB!eKs23xN>9J#=%WKo8H;fv6;3xW^=>lU$(5;^8S|Zwz9TLwqAe3
z?TyqoF1|^5bMu>L-hBGE@V_1X+i=X{nBtf(-|~Lz?{9q(>lT|9+ZsoU6UHe8c7iQ}
zvx47*JYkLSY5bb_6Y)PKge6oa{4;SyVnyP}Hukpnw>?T?C%vEaeKI@wNb*PuH|2vA
zeQH?hvDBYMuZd)$U*6`uUHA6$v@L1p)2E~h(l2eFx&7_!ojVvi{=TC(V_`;d#&?-Z
zGmmEel(jbNOg1U|t?Y|AE;$)FJ@5FuQ}oWgot&M=cmBF-^DgDzXZ(Hp-+Oj1*nM#K
zqdlwl$i=qe1aU{MSMJ{2dy-Je={!<id|pTXy!`$7!vzrq^?Rr7eS7bXeF6L4-#1pc
zrSQ}JbM_bPe^3-rbgtO3IIH+8=`yM8fbD_Q12+%8dho-86NeHHT`LJF`Jm*tcN5;d
zc6ia@s>9DqlS*%v1(j8o+m~-I|METVd-d<ncz@6P500!m(o*4Baj0VKXxz~+J_!6k
zRynQmoyza3Uaz|N;fo)ZfB4(6lw)^}uRMPKgvW`4Cw@7Zc(Sj0MfE4LIkFPj_^H%W
zn$r=dTWaRld{Ap&yR-J;N1H$Dk_XEh&bXZ^Ib*8ZUiV#nRDEZ|l7@z}9%swW*_?al
z+{ni<AK(68tNz!;PhS4y^m*6wB??UOj^c+#LF1QAYnwWh!OBmXUur&~a#kH`A+_vo
z`RPK+h3_uDak2MO<fV>Jmwl@G%>T2xe|Y`lc<Zdzvdhyh@4x)KZBN_N_KbG@mE<cA
zI^sG8)mzmAo&3(5SJz(c{(SZ4>aNhPwr+O!KfYM{#ieV(*DhQSy57>WxJPwk@eS3@
zpqnkXmfX5{JLLAKy~}$q_i_8K^snmwd|=JM^*ihD^nSVNOU+j?Uwx-Z)O@c^)Bdd6
zsWT21d~Nsjp>LeN`QTfRZ)@)Q-aY?a(08r(R^98lAANuDLBfN74rLGhHoX6#!^00A
zF&{O2|H}9O7+F1X`-i{%F#OMqe;W10h8czvKl=RG{CLIVo}ae<H2ibc&(BBSee%MS
zy0O5qj;9-*ev7B$#$OKo`ogdE&w`(Ijc*x$WZd-|<+m!6uj$i?wG&@IPk;XW`5+dC
zk!@_?ADK+HC6jIKrr6op+S*N{Q0%8npEi9ubsClG;5gIC!EuHomFnc`G{f0t)~s36
zY4q8yF0*I4%yNN7NMu`EJ6pS{c6L);9H<U1|LNcJc5KF!>6jBnCe6TXW{}7;NY6Vl
zCWevhNW|}VSwpfR+uGSvrc9kiecp+woG=>_nQUWAhNs3bl7wVK#%yQU&GcFnYVXV!
zQs!p5yt4Pb(^KZHY`iins`sZCUyaY&H+7mTefA4(FU|M%@m&zOIA}?5$kMP?;j1HF
zTN4?*ant54Ti<vyA#qz$a!RTwJLjF9yZ*j=PvQQeV(Ec{hu%L@arA@Ast=FV)P5vC
zQ&-<`wn^EnYPoRn(x)Bj&a0nyb$@ZKuYcgqmtSeLx_kE@3=Kbg^!>;WKaV~cdy4<^
z>$7oqFA_#JzZRH0;`cA_H3PFD+1T2WZ7J|xB%57u!VFtGuSNDVL-`b8rt{oa_D*qG
z`QGWqD^usa8uinx_^jS(t}h1Od+BF*HR8?wV=uPvzw~B*yx5=brNgF^Ntn$H@(hfN
z{dV@0O~EwG2ea0N&7D1oc9;vcfwaO(7j|zxp;~hUt+s3ak9jfbrjc@M9)YA4v)=CU
z`=1j}>t+n=9F{L{IkUogIS|Nw)A;}e>YCQsd#yv?sA4~~S`|vX7-{oAIy$T*SZE|!
zBLv6}T4h)f>sb&4$PQXiu#nfYN(hi0w92p|a<?D|kR7z3U?Ix3MhK7{w92p|a<?D|
zkR7z3U?Ix3N(hi0w92p|a<?D|kR7z3U?Ix3MhK7{w92p|a<?D|kR7z3U?Ix3N(hi0
zw92p|a<?D|kR7z3U?Ix3MhK7{w92p|a<?D|kR7z3U?Ix3N(hi0w92p|a<?D|kR7z3
zU?Ix3MhK7{w92p|a<?D|kR7z3U?Ix3N(hi0w92p|a<?D|kR7z3U?Ix3MhK7{w92p|
za<?D|kR7z3U?Ix3N(hi0w92p|a<?D|kR7z3U?Ix3MhK7{w92p|a<?D|kR7z3U?Ix3
zN(hi0w92p|a<?D|kR7z3U?Ix3MhK7{w92p|a<?D|kR7z3U?Ix3N(hi0w92p|a<?D|
zkR7z3U?Ix3MhK7{w92p|a<?D|kR7z3U?Ix3N(hi0w92p|a<?D|kR7z3U?Ix3MhK7{
zw92p|a<?D|kR7z3U?Ix3N(hi0w92p|a<?D|kR7z3U?Ix3MhK7{w92p|a<?D|kR7z3
zU?Ix3N(hi0w92p|a<?D|kR7z3U?Ix3MhK7{w92p|a<?D|kR7z3U?Ix3N(hi0w92p|
za<?D|kR7z3U?Ix3MhK7{w92p|a<?D|kR7z3U?Ix3N(hi0w92p|a<?D|kR7z3U?Ix3
zMhK7{w92p|a<?D|kR7z3U?Ix3N(hi0w92p|a<?D|kR7z3U?Ix3MhK7{w92p|a<?D|
zkR7z3U?Ix3N(hi0w92p|a<?D|kR7z3U?Ix3MhK7{w92p|a<?D|kR7z3U?Ix3N(hi0
zw92p|a<?D|kR7z3U?Ix3MhK7{w92p|a<?D|kR7z3U?Ix3N(hi0w92p|a<?D|kR7z3
zU?Ix3MhK7{w92p|a<?D|kR7z3U?Ix3N(hi0w92p|a<?D|kR7z3U?Ix3MhK7{w92p|
za<?D|kR7z3U?Ix3N(hi0w92p|a<?D|kR7z3U?Ix3MhK7{w92p|a<?D|kR7z3U?Ix3
zN(hi0w92p|a<?D|kR7z3U?Ix3MhK7{w92p|a<?D|kR7z3U?Ix3N(hi0w92p|a<?D|
zkR7z3U?Ix3MhK7{w92p|a<?D|kR7z3U?Ix3N(hi0w92p|a<?D|kR7z3U?Ix3MhK7{
zw92p|a<?D|kR7z3U?Ix3N(hi0w92p|a<?D|kR7z3U?Ix3MhK7{w92p|a<?D|kR7z3
zU?Ix3N(hi0w92p|a<?D|kR7z3U?Ix3MhK7{w92p|a<?D|kR7z3U?Ix3N(hi0w92p|
za<?D|kR7z3U?Ix3MhK7{w92p|a<?D|kR7z3U?Ix3N(hi0w92p|a<?D|kR7z3U?Ix3
zMhK7{w92p|a<?D|kR7z3U?Ix3N(hi0w92p|a<?D|kR7z3U?Ix3MhK7{w92p|a<?D|
zkR7z3U?Ix3N(hi0w92p|a<?D|kR7z3U?Ix3MhK7{w92p|a<?D|kR7z3U?Ix3N(hi0
zw92p|a<?D|kR7z3U?Ix3MhK7{w92p|a<?D|kR7z3U?Ix3N(hi0w92p|a<?D|kR7z3
zU?Ix3MhK7{w92p|a<?D|kR7z3U?Ix3N(hi0w92p|a<?D|kR7z3VExaeZ0om6q!o6U
z3x*a50t5kq06~BtKoB4Z5CjMU1Ob8oL4Y7Y5FiK;1PB5I0fGQQfFM8+AP5iy2m%BF
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
zgn$qb0zyCt2mv7=1cZPP5CTF#2nYcoAOwVf5D)@FKnMr{As_^VfDjM@LO=)z0U;m+
zgn$qb0zyCt2mv7=1cZPP5CTF#2nYcoAOwVf5D)@FKnMr{As_^VfDjM@LO=)z0U;m+
zgn$qb0zyCt2mv7=1cZPP5CTF#2nYcoAOwVf5D)@FKnMr{As_^VfDjM@LO=)z0U;m+
zgn$qb0zyCt2mv7=1cZPP5CTF#2nYcoAOwVf5D)@FKnMr{As_^VfDjM@LO=)z0U;m+
zgn$qb0zyCt2mv7=1cZPP5CTF#2nYcoAOwVf5D)@FKnMr{As_^VfDjM@LO=)z0U;m+
zgn$qb0zyCt2mv7=1cZPP5CTF#2nYcoAOwVf5D)@FKnMr{As_^VfDjM@LO=)z0U;m+
zgn$qb0zyCt2mv7=1cZPP5CTF#2nYcoAOwVf5D)@FKnMr{As_^VfDjM@LO=)z0U;m+
zgn$qb0zyCt2mv7=1cZPP5CTF#2nYcoAOwVf5D)@FKnMr{As_^VfDjM@LO=)z0U;m+
zgn$qb0zyCt2mv7=1cZPP5CTF#2nYcoAOwVf5D)@FKnMr{As_^VfDjM@LO=)z0U;m+
zgn$qb0zyCt2mv7=1cZPP5CTF#2nYcoAOwVf5D)@FKnMr{As_^VfDjM@LO=)z0U;m+
zgn$qb0zyCt2mv7=1cZPP5CTF#2nYcoAOwVf5D)@FKnMr{As_^VfDjM@LO=)z0U;m+
zgn$qb0zyCt2mv7=1cZPP5CTF#2nYcoAOwVf5D)@FKnMr{As_^VfDjM@LO=)z0U;m+
zgn$qb0zyCt2mv7=1cZPP5CTF#2nYcoAOwVf5D)@FKnMr{As_^VfDjM@LO=)z0U;m+
zgn$qb0zyCt2mv7=1cZPP5CTF#2nYcoAOwVf5D)@FKnMr{As_^VfDjM@LO=)z0U;m+
zgn$qb0zyCt2mv7=1cZPP5CTF#2nYcoAOwVf5D)@FKnMr{As_^VfDjM@LO=)z0U;m+
zgn$qb0zyCt2mv7=1cZPP5CTF#2nYcoAOwVf5D)@FKnMr{As_^VfDjM@LO=)z0U;m+
zgn$qb0zyCt2mv7=1cZPP5CTF#2nYcoAOwVf5D)@FKnMr{As_^VfDjM@LO=)z0U;m+
mgn$qb0zyCt2mv7=1cZPP5CTF#2nYcoAO!!vMWAqh{(k`@Bs9VR

literal 0
HcmV?d00001


From 4c662b0d60b210a393d4a113c733bcb5eb23603d Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Thu, 4 Aug 2016 10:31:06 -0700
Subject: [PATCH 56/78] Add static consts for default skybox values

---
 interface/src/Application.cpp | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 4739fedc26..1a11096a7a 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -4264,13 +4264,18 @@ namespace render {
                 }
 				*/
 
+                static const glm::vec3 DEFAULT_SKYBOX_COLOR { 255.0f / 255.0f, 220.0f / 255.0f, 194.0f / 255.0f };
+                static const float DEFAULT_SKYBOX_INTENSITY { 0.2f };
+                static const float DEFAULT_SKYBOX_AMBIENT_INTENSITY { 3.5f };
+                static const glm::vec3 DEFAULT_SKYBOX_DIRECTION { 0.0f, 0.0f, -1.0f };
+
                 auto scene = DependencyManager::get<SceneScriptingInterface>()->getStage();
                 auto sceneKeyLight = scene->getKeyLight();
                 scene->setSunModelEnable(false);
-                sceneKeyLight->setColor(glm::vec3(255.0f / 255.0f, 220.0f / 255.0f, 194.0f / 255.0f) * 0.2f);
-                sceneKeyLight->setIntensity(0.2f);
-                sceneKeyLight->setAmbientIntensity(3.5f);
-                sceneKeyLight->setDirection({ 0.0f, 0.0f, -1.0f });
+                sceneKeyLight->setColor(DEFAULT_SKYBOX_COLOR);
+                sceneKeyLight->setIntensity(DEFAULT_SKYBOX_INTENSITY);
+                sceneKeyLight->setAmbientIntensity(DEFAULT_SKYBOX_AMBIENT_INTENSITY);
+                sceneKeyLight->setDirection(DEFAULT_SKYBOX_DIRECTION);
 
                 auto defaultSkyboxAmbientTexture = qApp->getDefaultSkyboxAmbientTexture();
                 sceneKeyLight->setAmbientSphere(defaultSkyboxAmbientTexture->getIrradiance());

From 9cbfe195cd465d48d2911877ab20c5624e99a1f3 Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Thu, 4 Aug 2016 10:32:05 -0700
Subject: [PATCH 57/78] Adjust commenting out of old stars rendering code

---
 interface/src/Application.cpp | 20 +++++++++-----------
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 1a11096a7a..13267f30e7 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -4252,17 +4252,15 @@ namespace render {
 
             // Fall through: if no skybox is available, render the SKY_DOME
             case model::SunSkyStage::SKY_DOME:  {
-				/*
-                if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
-                    PerformanceTimer perfTimer("stars");
-                    PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
-                        "Application::payloadRender<BackgroundRenderData>() ... My god, it's full of stars...");
-                    // should be the first rendering pass - w/o depth buffer / lighting
-
-                    static const float alpha = 1.0f;
-                    background->_stars.render(args, alpha);
-                }
-				*/
+//                if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
+//                    PerformanceTimer perfTimer("stars");
+//                    PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
+//                        "Application::payloadRender<BackgroundRenderData>() ... My god, it's full of stars...");
+//                    // should be the first rendering pass - w/o depth buffer / lighting
+//
+//                    static const float alpha = 1.0f;
+//                    background->_stars.render(args, alpha);
+//                }
 
                 static const glm::vec3 DEFAULT_SKYBOX_COLOR { 255.0f / 255.0f, 220.0f / 255.0f, 194.0f / 255.0f };
                 static const float DEFAULT_SKYBOX_INTENSITY { 0.2f };

From 36744d72d6c3d7456ded5a7a223725e8eb1e658e Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Thu, 4 Aug 2016 11:00:21 -0700
Subject: [PATCH 58/78] Fix warning using init list for QVariantMap

---
 .../model-networking/src/model-networking/TextureCache.cpp     | 3 ++-
 libraries/model-networking/src/model-networking/TextureCache.h | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp
index c373da34ba..e10be30f54 100644
--- a/libraries/model-networking/src/model-networking/TextureCache.cpp
+++ b/libraries/model-networking/src/model-networking/TextureCache.cpp
@@ -171,7 +171,8 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, Type type, const
 }
 
 
-NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type type, const QVariantMap& options = {}) {
+NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type type,
+                                                          const QVariantMap& options = QVariantMap()) {
     using Type = NetworkTexture;
 
     switch (type) {
diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h
index 66634b6ac0..9c78e7e378 100644
--- a/libraries/model-networking/src/model-networking/TextureCache.h
+++ b/libraries/model-networking/src/model-networking/TextureCache.h
@@ -122,7 +122,7 @@ public:
     const gpu::TexturePointer& getNormalFittingTexture();
 
     /// Returns a texture version of an image file
-    static gpu::TexturePointer getImageTexture(const QString& path, Type type = Type::DEFAULT_TEXTURE, QVariantMap options = {});
+    static gpu::TexturePointer getImageTexture(const QString& path, Type type = Type::DEFAULT_TEXTURE, QVariantMap options = QVariantMap());
 
     /// Loads a texture from the specified URL.
     NetworkTexturePointer getTexture(const QUrl& url, Type type = Type::DEFAULT_TEXTURE,

From eaa77edc25b251a2e0bd708b4df43c6613f37310 Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Mon, 8 Aug 2016 08:59:57 -0700
Subject: [PATCH 59/78] Replace 'Stars' menu option with 'Default Skybox'

---
 interface/src/Application.cpp | 47 ++++++++++++++---------------------
 interface/src/Menu.cpp        |  2 +-
 interface/src/Menu.h          |  2 +-
 3 files changed, 20 insertions(+), 31 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 13267f30e7..209ef8d02f 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -139,7 +139,6 @@
 #if defined(Q_OS_MAC) || defined(Q_OS_WIN)
 #include "SpeechRecognizer.h"
 #endif
-#include "Stars.h"
 #include "ui/AddressBarDialog.h"
 #include "ui/AvatarInputs.h"
 #include "ui/DialogsManager.h"
@@ -2291,7 +2290,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
             }
 
             case Qt::Key_Asterisk:
-                Menu::getInstance()->triggerOption(MenuOption::Stars);
+                Menu::getInstance()->triggerOption(MenuOption::DefaultSkybox);
                 break;
 
             case Qt::Key_S:
@@ -4216,8 +4215,6 @@ public:
     typedef render::Payload<BackgroundRenderData> Payload;
     typedef Payload::DataPointer Pointer;
 
-    Stars _stars;
-
     static render::ItemID _item; // unique WorldBoxRenderData
 };
 
@@ -4252,34 +4249,26 @@ namespace render {
 
             // Fall through: if no skybox is available, render the SKY_DOME
             case model::SunSkyStage::SKY_DOME:  {
-//                if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {
-//                    PerformanceTimer perfTimer("stars");
-//                    PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
-//                        "Application::payloadRender<BackgroundRenderData>() ... My god, it's full of stars...");
-//                    // should be the first rendering pass - w/o depth buffer / lighting
-//
-//                    static const float alpha = 1.0f;
-//                    background->_stars.render(args, alpha);
-//                }
+               if (Menu::getInstance()->isOptionChecked(MenuOption::DefaultSkybox)) {
+                   static const glm::vec3 DEFAULT_SKYBOX_COLOR { 255.0f / 255.0f, 220.0f / 255.0f, 194.0f / 255.0f };
+                   static const float DEFAULT_SKYBOX_INTENSITY { 0.2f };
+                   static const float DEFAULT_SKYBOX_AMBIENT_INTENSITY { 3.5f };
+                   static const glm::vec3 DEFAULT_SKYBOX_DIRECTION { 0.0f, 0.0f, -1.0f };
 
-                static const glm::vec3 DEFAULT_SKYBOX_COLOR { 255.0f / 255.0f, 220.0f / 255.0f, 194.0f / 255.0f };
-                static const float DEFAULT_SKYBOX_INTENSITY { 0.2f };
-                static const float DEFAULT_SKYBOX_AMBIENT_INTENSITY { 3.5f };
-                static const glm::vec3 DEFAULT_SKYBOX_DIRECTION { 0.0f, 0.0f, -1.0f };
+                   auto scene = DependencyManager::get<SceneScriptingInterface>()->getStage();
+                   auto sceneKeyLight = scene->getKeyLight();
+                   scene->setSunModelEnable(false);
+                   sceneKeyLight->setColor(DEFAULT_SKYBOX_COLOR);
+                   sceneKeyLight->setIntensity(DEFAULT_SKYBOX_INTENSITY);
+                   sceneKeyLight->setAmbientIntensity(DEFAULT_SKYBOX_AMBIENT_INTENSITY);
+                   sceneKeyLight->setDirection(DEFAULT_SKYBOX_DIRECTION);
 
-                auto scene = DependencyManager::get<SceneScriptingInterface>()->getStage();
-                auto sceneKeyLight = scene->getKeyLight();
-                scene->setSunModelEnable(false);
-                sceneKeyLight->setColor(DEFAULT_SKYBOX_COLOR);
-                sceneKeyLight->setIntensity(DEFAULT_SKYBOX_INTENSITY);
-                sceneKeyLight->setAmbientIntensity(DEFAULT_SKYBOX_AMBIENT_INTENSITY);
-                sceneKeyLight->setDirection(DEFAULT_SKYBOX_DIRECTION);
+                   auto defaultSkyboxAmbientTexture = qApp->getDefaultSkyboxAmbientTexture();
+                   sceneKeyLight->setAmbientSphere(defaultSkyboxAmbientTexture->getIrradiance());
+                   sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture);
 
-                auto defaultSkyboxAmbientTexture = qApp->getDefaultSkyboxAmbientTexture();
-                sceneKeyLight->setAmbientSphere(defaultSkyboxAmbientTexture->getIrradiance());
-                sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture);
-
-                qApp->getDefaultSkybox()->render(batch, args->getViewFrustum());
+                   qApp->getDefaultSkybox()->render(batch, args->getViewFrustum());
+               }
             }
                 break;
 
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index 6308ac6c73..a8340e8f47 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -337,7 +337,7 @@ Menu::Menu() {
     // Developer > Render >>>
     MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render");
     addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::WorldAxes);
-    addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, 0, true);
+    addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DefaultSkybox, 0, true);
 
     // Developer > Render > Throttle FPS If Not Focus
     addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ThrottleFPSIfNotFocus, 0, true);
diff --git a/interface/src/Menu.h b/interface/src/Menu.h
index 503cbf51fa..d47b6842a5 100644
--- a/interface/src/Menu.h
+++ b/interface/src/Menu.h
@@ -80,6 +80,7 @@ namespace MenuOption {
     const QString CrashNewFaultThreaded = "New Fault (threaded)";
     const QString DeadlockInterface = "Deadlock Interface";
     const QString DecreaseAvatarSize = "Decrease Avatar Size";
+    const QString DefaultSkybox = "Default Skybox";
     const QString DeleteBookmark = "Delete Bookmark...";
     const QString DisableActivityLogger = "Disable Activity Logger";
     const QString DisableEyelidAdjustment = "Disable Eyelid Adjustment";
@@ -175,7 +176,6 @@ namespace MenuOption {
     const QString StandingHMDSensorMode = "Standing HMD Sensor Mode";
     const QString SimulateEyeTracking = "Simulate";
     const QString SMIEyeTracking = "SMI Eye Tracking";
-    const QString Stars = "Stars";
     const QString Stats = "Stats";
     const QString StopAllScripts = "Stop All Scripts";
     const QString SuppressShortTimings = "Suppress Timings Less than 10ms";

From 6854f9fda4c3358ce56eeb7adaca388732b68ba1 Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Mon, 8 Aug 2016 09:02:23 -0700
Subject: [PATCH 60/78] Remove stars shaders and renderable

---
 interface/src/Stars.cpp                  | 216 -----------------------
 interface/src/Stars.h                    |  49 -----
 libraries/render-utils/src/stars.slf     |  34 ----
 libraries/render-utils/src/stars.slv     |  36 ----
 libraries/render-utils/src/starsGrid.slf |  63 -------
 tests/shaders/src/main.cpp               |   7 -
 6 files changed, 405 deletions(-)
 delete mode 100644 interface/src/Stars.cpp
 delete mode 100644 interface/src/Stars.h
 delete mode 100644 libraries/render-utils/src/stars.slf
 delete mode 100644 libraries/render-utils/src/stars.slv
 delete mode 100644 libraries/render-utils/src/starsGrid.slf

diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp
deleted file mode 100644
index 9510710eb3..0000000000
--- a/interface/src/Stars.cpp
+++ /dev/null
@@ -1,216 +0,0 @@
-//
-//  Stars.cpp
-//  interface/src
-//
-//  Created by Tobias Schwinger on 3/22/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 "Stars.h"
-
-#include <mutex>
-
-#include <QElapsedTimer>
-#include <NumericalConstants.h>
-#include <DependencyManager.h>
-#include <GeometryCache.h>
-#include <TextureCache.h>
-#include <RenderArgs.h>
-#include <ViewFrustum.h>
-
-#include <render-utils/stars_vert.h>
-#include <render-utils/stars_frag.h>
-
-#include <render-utils/standardTransformPNTC_vert.h>
-#include <render-utils/starsGrid_frag.h>
-
-//static const float TILT = 0.23f;
-static const float TILT = 0.0f;
-static const unsigned int STARFIELD_NUM_STARS = 50000;
-static const unsigned int STARFIELD_SEED = 1;
-static const float STAR_COLORIZATION = 0.1f;
-
-static const float TAU = 6.28318530717958f;
-//static const float HALF_TAU = TAU / 2.0f;
-//static const float QUARTER_TAU = TAU / 4.0f;
-//static const float MILKY_WAY_WIDTH = TAU / 30.0f; // width in radians of one half of the Milky Way
-//static const float MILKY_WAY_INCLINATION = 0.0f; // angle of Milky Way from horizontal in degrees
-//static const float MILKY_WAY_RATIO = 0.4f;
-static const char* UNIFORM_TIME_NAME = "iGlobalTime";
-
-// Produce a random float value between 0 and 1
-static float frand() {
-    return (float)rand() / (float)RAND_MAX;
-}
-
-// http://mathworld.wolfram.com/SpherePointPicking.html
-static vec2 randPolar() {
-    vec2 result(frand(), frand());
-    result.x *= TAU;
-    result.y = powf(result.y, 2.0) / 2.0f;
-    if (frand() > 0.5f) {
-        result.y = 0.5f - result.y;
-    } else {
-        result.y += 0.5f;
-    }
-    result.y = acos((2.0f * result.y) - 1.0f);
-    return result;
-}
-
-
-static vec3 fromPolar(const vec2& polar) {
-    float sinTheta = sin(polar.x);
-    float cosTheta = cos(polar.x);
-    float sinPhi = sin(polar.y);
-    float cosPhi = cos(polar.y);
-    return vec3(
-        cosTheta * sinPhi,
-        cosPhi,
-        sinTheta * sinPhi);
-}
-
-
-// computeStarColor
-// - Generate a star color.
-//
-// colorization can be a value between 0 and 1 specifying how colorful the resulting star color is.
-//
-// 0 = completely black & white
-// 1 = very colorful
-unsigned computeStarColor(float colorization) {
-    unsigned char red, green, blue;
-    if (randFloat() < 0.3f) {
-        // A few stars are colorful
-        red = 2 + (rand() % 254);
-        green = 2 + round((red * (1 - colorization)) + ((rand() % 254) * colorization));
-        blue = 2 + round((red * (1 - colorization)) + ((rand() % 254) * colorization));
-    } else {
-        // Most stars are dimmer and white
-        red = green = blue = 2 + (rand() % 128);
-    }
-    return red | (green << 8) | (blue << 16);
-}
-
-struct StarVertex {
-    vec4 position;
-    vec4 colorAndSize;
-};
-
-static const int STARS_VERTICES_SLOT{ 0 };
-static const int STARS_COLOR_SLOT{ 1 };
-
-gpu::PipelinePointer Stars::_gridPipeline{};
-gpu::PipelinePointer Stars::_starsPipeline{};
-int32_t Stars::_timeSlot{ -1 };
-
-void Stars::init() {
-    if (!_gridPipeline) {
-        auto vs = gpu::Shader::createVertex(std::string(standardTransformPNTC_vert));
-        auto ps = gpu::Shader::createPixel(std::string(starsGrid_frag));
-        auto program = gpu::Shader::createProgram(vs, ps);
-        gpu::Shader::makeProgram((*program));
-        _timeSlot = program->getBuffers().findLocation(UNIFORM_TIME_NAME);
-        if (_timeSlot == gpu::Shader::INVALID_LOCATION) {
-            _timeSlot = program->getUniforms().findLocation(UNIFORM_TIME_NAME);
-        }
-        auto state = gpu::StatePointer(new gpu::State());
-        // enable decal blend
-        state->setDepthTest(gpu::State::DepthTest(false));
-        state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
-        state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
-        _gridPipeline = gpu::Pipeline::create(program, state);
-    }
-
-    if (!_starsPipeline) {
-        auto vs = gpu::Shader::createVertex(std::string(stars_vert));
-        auto ps = gpu::Shader::createPixel(std::string(stars_frag));
-        auto program = gpu::Shader::createProgram(vs, ps);
-        gpu::Shader::makeProgram((*program));
-        auto state = gpu::StatePointer(new gpu::State());
-        // enable decal blend
-        state->setDepthTest(gpu::State::DepthTest(false));
-        state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
-        state->setAntialiasedLineEnable(true); // line smoothing also smooth points
-        state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
-        _starsPipeline = gpu::Pipeline::create(program, state);
-    }
-
-    unsigned limit = STARFIELD_NUM_STARS;
-    std::vector<StarVertex> points;
-    points.resize(limit);
-
-    { // generate stars
-        QElapsedTimer startTime;
-        startTime.start();
-
-        vertexBuffer.reset(new gpu::Buffer);
-
-        srand(STARFIELD_SEED);
-        for (size_t star = 0; star < limit; ++star) {
-            points[star].position = vec4(fromPolar(randPolar()), 1);
-            float size = frand() * 2.5f + 0.5f;
-            if (frand() < STAR_COLORIZATION) {
-                vec3 color(frand() / 2.0f + 0.5f, frand() / 2.0f + 0.5f, frand() / 2.0f + 0.5f);
-                points[star].colorAndSize = vec4(color, size);
-            } else {
-                vec3 color(frand() / 2.0f + 0.5f);
-                points[star].colorAndSize = vec4(color, size);
-            }
-        }
-
-        double timeDiff = (double)startTime.nsecsElapsed() / 1000000.0; // ns to ms
-        qDebug() << "Total time to generate stars: " << timeDiff << " msec";
-    }
-
-    gpu::Element positionElement, colorElement;
-    const size_t VERTEX_STRIDE = sizeof(StarVertex);
-
-    vertexBuffer->append(VERTEX_STRIDE * limit, (const gpu::Byte*)&points[0]);
-    streamFormat.reset(new gpu::Stream::Format()); // 1 for everyone
-    streamFormat->setAttribute(gpu::Stream::POSITION, STARS_VERTICES_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::XYZW), 0);
-    streamFormat->setAttribute(gpu::Stream::COLOR, STARS_COLOR_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA));
-    positionElement = streamFormat->getAttributes().at(gpu::Stream::POSITION)._element;
-    colorElement = streamFormat->getAttributes().at(gpu::Stream::COLOR)._element;
-
-    size_t offset = offsetof(StarVertex, position);
-    positionView = gpu::BufferView(vertexBuffer, offset, vertexBuffer->getSize(), VERTEX_STRIDE, positionElement);
-
-    offset = offsetof(StarVertex, colorAndSize);
-    colorView = gpu::BufferView(vertexBuffer, offset, vertexBuffer->getSize(), VERTEX_STRIDE, colorElement);
-}
-
-// FIXME star colors
-void Stars::render(RenderArgs* renderArgs, float alpha) {
-    std::call_once(once, [&]{ init(); });
-
-
-    auto modelCache = DependencyManager::get<ModelCache>();
-    auto textureCache = DependencyManager::get<TextureCache>();
-    auto geometryCache = DependencyManager::get<GeometryCache>();
-
-
-    gpu::Batch& batch = *renderArgs->_batch;
-    batch.setViewTransform(Transform());
-    batch.setProjectionTransform(renderArgs->getViewFrustum().getProjection());
-    batch.setModelTransform(Transform().setRotation(glm::inverse(renderArgs->getViewFrustum().getOrientation()) *
-        quat(vec3(TILT, 0, 0))));
-    batch.setResourceTexture(0, textureCache->getWhiteTexture());
-
-    // Render the world lines
-    batch.setPipeline(_gridPipeline);
-    static auto start = usecTimestampNow();
-    float msecs = (float)(usecTimestampNow() - start) / (float)USECS_PER_MSEC;
-    float secs = msecs / (float)MSECS_PER_SECOND;
-    batch._glUniform1f(_timeSlot, secs);
-    geometryCache->renderCube(batch);
-
-    // Render the stars
-    batch.setPipeline(_starsPipeline);
-    batch.setInputFormat(streamFormat);
-    batch.setInputBuffer(STARS_VERTICES_SLOT, positionView);
-    batch.setInputBuffer(STARS_COLOR_SLOT, colorView);
-    batch.draw(gpu::Primitive::POINTS, STARFIELD_NUM_STARS);
-}
diff --git a/interface/src/Stars.h b/interface/src/Stars.h
deleted file mode 100644
index f07caff770..0000000000
--- a/interface/src/Stars.h
+++ /dev/null
@@ -1,49 +0,0 @@
-//
-//  Stars.h
-//  interface/src
-//
-//  Created by Tobias Schwinger on 3/22/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
-//
-
-#ifndef hifi_Stars_h
-#define hifi_Stars_h
-
-#include <gpu/Context.h>
-
-class RenderArgs;
-
-// Starfield rendering component. 
-class Stars  {
-public:
-    Stars() = default;
-    ~Stars() = default;
-
-    Stars(Stars const&) = delete;
-    Stars& operator=(Stars const&) = delete;
-
-    // Renders the starfield from a local viewer's perspective.
-    // The parameters specifiy the field of view.
-    void render(RenderArgs* args, float alpha);
-
-private:
-    // Pipelines
-    static gpu::PipelinePointer _gridPipeline;
-    static gpu::PipelinePointer _starsPipeline;
-    static int32_t _timeSlot;
-
-    // Buffers
-    gpu::BufferPointer vertexBuffer;
-    gpu::Stream::FormatPointer streamFormat;
-    gpu::BufferView positionView;
-    gpu::BufferView colorView;
-    std::once_flag once;
-
-    void init();
-};
-
-
-#endif // hifi_Stars_h
diff --git a/libraries/render-utils/src/stars.slf b/libraries/render-utils/src/stars.slf
deleted file mode 100644
index 14191f2a6a..0000000000
--- a/libraries/render-utils/src/stars.slf
+++ /dev/null
@@ -1,34 +0,0 @@
-<@include gpu/Config.slh@>
-<$VERSION_HEADER$>
-//  Generated on <$_SCRIBE_DATE$>
-//  fragment shader
-//
-//  Created by Bradley Austin Davis on 6/10/15.
-//  Copyright 2015 High Fidelity, Inc.
-//
-//  Distributed under the Apache License, Version 2.0.
-//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-in vec4 varColor;
-in float varSize;
-
-out vec4 outFragColor;
-
-const float EDGE_SIZE = 0.25;
-const float ALPHA_BOUNDARY = 1.0 - EDGE_SIZE;
-
-void main(void) {
-    vec2 coord = gl_PointCoord * vec2(2.0) - vec2(1.0);
-    coord = coord * coord;
-
-    float l = coord.x + coord.y;
-    if (l > 1.0) {
-        discard;
-    }
-    
-    outFragColor = varColor; 
-    if (l >= ALPHA_BOUNDARY) {
-        outFragColor.a = smoothstep(1.0, ALPHA_BOUNDARY, l);
-    }
-}
diff --git a/libraries/render-utils/src/stars.slv b/libraries/render-utils/src/stars.slv
deleted file mode 100644
index d00bb8b7dd..0000000000
--- a/libraries/render-utils/src/stars.slv
+++ /dev/null
@@ -1,36 +0,0 @@
-<@include gpu/Config.slh@>
-<$VERSION_HEADER$>
-//  Generated on <$_SCRIBE_DATE$>
-//
-//  standardTransformPNTC.slv
-//  vertex shader
-//
-//  Created by Sam Gateau on 6/10/2015.
-//  Copyright 2015 High Fidelity, Inc.
-//
-//  Distributed under the Apache License, Version 2.0.
-//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-<@include gpu/Inputs.slh@>
-<@include gpu/Color.slh@>
-<@include gpu/Transform.slh@>
-<$declareStandardTransform()$>
-
-// TODO we need to get the viewport resolution and FOV passed to us so we can modify the point size
-// and effectively producing a points that take up a constant angular size regardless of the display resolution 
-// or projection matrix
-
-out vec4 varColor;
-out float varSize;
-
-void main(void) {
-    varColor = colorToLinearRGBA(inColor);
-    
-    // standard transform
-    TransformCamera cam = getTransformCamera();
-    TransformObject obj = getTransformObject();
-    <$transformModelToClipPos(cam, obj, inPosition, gl_Position)$>
-    varSize = inColor.a;
-    gl_PointSize = varSize; 
-}
\ No newline at end of file
diff --git a/libraries/render-utils/src/starsGrid.slf b/libraries/render-utils/src/starsGrid.slf
deleted file mode 100644
index ad9a45a4f5..0000000000
--- a/libraries/render-utils/src/starsGrid.slf
+++ /dev/null
@@ -1,63 +0,0 @@
-<@include gpu/Config.slh@>
-<$VERSION_HEADER$>
-//  Generated on <$_SCRIBE_DATE$>
-//  stars.frag
-//  fragment shader
-//
-//  Created by Bradley Austin Davis on 2015/06/19
-
-in vec2 varTexcoord;
-in vec3 varNomral;
-in vec3 varPosition;
-
-uniform float iGlobalTime;
-
-const float PI = 3.14159;
-const float TAU = 3.14159 * 2.0;
-const int latitudeCount = 5;
-const float latitudeDist = PI / 2.0 / float(latitudeCount);
-const int meridianCount = 4;
-const float merdianDist = PI / float(meridianCount);
-
-out vec4 outFragColor;
-
-float clampLine(float val, float target) {
-    return clamp((1.0 - abs((val - target)) - 0.998) * 500.0, 0.0, 1.0);
-}
-
-float latitude(vec2 pos, float angle) {
-    float result = clampLine(pos.y, angle);
-    if (angle != 0.0) {
-        result += clampLine(pos.y, -angle);
-    }
-    return result;
-}
-
-float meridian(vec2 pos, float angle) {
-    return clampLine(pos.x, angle) +  clampLine(pos.x + PI, angle);
-}
-
-vec2 toPolar(in vec3 dir) {
-    vec2 polar = vec2(atan(dir.z, dir.x), asin(dir.y));
-    return polar;
-}
-
-void mainVR( out vec4 fragColor, in vec2 fragCoord, in vec3 fragRayOri, in vec3 fragRayDir )
-{
-    vec2 polar = toPolar(fragRayDir);
-    //polar.x += mod(iGlobalTime / 12.0, PI / 4.0) - PI / 4.0;
-    float c = 0.0;
-    for (int i = 0; i < latitudeCount - 1; ++i) {
-        c += latitude(polar, float(i) * latitudeDist);
-    }
-    for (int i = 0; i < meridianCount; ++i) {
-        c += meridian(polar, float(i) * merdianDist);
-    }
-    const vec3 col_lines = vec3(102.0 / 255.0, 136.0 / 255.0, 221.0 / 255.0);
-    fragColor = vec4(c * col_lines, 0.2);
-}
-
-void main(void) {
-    mainVR(outFragColor, gl_FragCoord.xy, vec3(0.0), normalize(varPosition));
-}
-
diff --git a/tests/shaders/src/main.cpp b/tests/shaders/src/main.cpp
index 2ef7bbdd02..9c4a835966 100644
--- a/tests/shaders/src/main.cpp
+++ b/tests/shaders/src/main.cpp
@@ -75,10 +75,6 @@
 #include <model/skybox_vert.h>
 #include <model/skybox_frag.h>
 
-#include <render-utils/stars_vert.h>
-#include <render-utils/stars_frag.h>
-#include <render-utils/starsGrid_frag.h>
-
 #include <gpu/DrawTransformUnitQuad_vert.h>
 #include <gpu/DrawTexcoordRectTransformUnitQuad_vert.h>
 #include <gpu/DrawViewportQuadTransformTexcoord_vert.h>
@@ -168,9 +164,6 @@ void QTestWindow::draw() {
         testShaderBuild(deferred_light_limited_vert, spot_light_frag);
         testShaderBuild(standardTransformPNTC_vert, standardDrawTexture_frag);
         testShaderBuild(standardTransformPNTC_vert, DrawTextureOpaque_frag);
-        
-        testShaderBuild(standardTransformPNTC_vert, starsGrid_frag);
-        testShaderBuild(stars_vert, stars_frag);
 
         testShaderBuild(model_vert, model_frag);
         testShaderBuild(model_normal_map_vert, model_normal_map_frag);

From 8ee3f680c91b1b3e4324bc92b1a59ac18fbe7971 Mon Sep 17 00:00:00 2001
From: Ryan Huffman <ryanhuffman@gmail.com>
Date: Mon, 8 Aug 2016 14:08:49 -0700
Subject: [PATCH 61/78] Change default skybox ambient intensity from 3.5 to 2.0

---
 interface/src/Application.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 209ef8d02f..dfebb6bcb2 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -4252,7 +4252,7 @@ namespace render {
                if (Menu::getInstance()->isOptionChecked(MenuOption::DefaultSkybox)) {
                    static const glm::vec3 DEFAULT_SKYBOX_COLOR { 255.0f / 255.0f, 220.0f / 255.0f, 194.0f / 255.0f };
                    static const float DEFAULT_SKYBOX_INTENSITY { 0.2f };
-                   static const float DEFAULT_SKYBOX_AMBIENT_INTENSITY { 3.5f };
+                   static const float DEFAULT_SKYBOX_AMBIENT_INTENSITY { 2.0f };
                    static const glm::vec3 DEFAULT_SKYBOX_DIRECTION { 0.0f, 0.0f, -1.0f };
 
                    auto scene = DependencyManager::get<SceneScriptingInterface>()->getStage();

From 3df8aa0dba370f9c0f11c3ad6099e91dcc8c6956 Mon Sep 17 00:00:00 2001
From: Zander Otavka <zotavka@gmail.com>
Date: Mon, 8 Aug 2016 17:01:32 -0700
Subject: [PATCH 62/78] Suppress repeated ice server connection messages

---
 domain-server/src/DomainServer.cpp | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index 6c4b12d4c0..d5f23f7c4e 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -1233,7 +1233,10 @@ void DomainServer::sendICEServerAddressToMetaverseAPI() {
         callbackParameters.errorCallbackReceiver = this;
         callbackParameters.errorCallbackMethod = "handleFailedICEServerAddressUpdate";
 
-        qDebug() << "Updating ice-server address in High Fidelity Metaverse API to" << _iceServerSocket.getAddress().toString();
+        static QString repeatedMessage = LogHandler::getInstance().addOnlyOnceMessageRegex
+                   ("Updating ice-server address in High Fidelity Metaverse API to [^ \n]+");
+        qDebug() << "Updating ice-server address in High Fidelity Metaverse API to"
+                 << _iceServerSocket.getAddress().toString();
 
         static const QString DOMAIN_ICE_ADDRESS_UPDATE = "/api/v1/domains/%1/ice_server_address";
 

From ae9fb3768c6154f419e739136875af6245698687 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Mon, 8 Aug 2016 17:45:25 -0700
Subject: [PATCH 63/78] CR notes

---
 libraries/render-utils/src/GeometryCache.cpp  | 37 +++++++++----------
 .../render-utils/src/MeshPartPayload.cpp      |  4 ++
 libraries/render-utils/src/MeshPartPayload.h  |  3 +-
 libraries/render-utils/src/Model.cpp          |  1 -
 4 files changed, 23 insertions(+), 22 deletions(-)

diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp
index dcd36946cb..f906a871fe 100644
--- a/libraries/render-utils/src/GeometryCache.cpp
+++ b/libraries/render-utils/src/GeometryCache.cpp
@@ -406,29 +406,28 @@ GeometryCache::GeometryCache() :
 _nextID(0) {
     buildShapes();
     GeometryCache::_simpleOpaquePipeline =
-    std::make_shared<render::ShapePipeline>(getSimplePipeline(false, false, true, false), nullptr,
-        [](const render::ShapePipeline&, gpu::Batch& batch) {
-        // Set the defaults needed for a simple program
-        batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO,
-            DependencyManager::get<TextureCache>()->getWhiteTexture());
-        batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING,
-            DependencyManager::get<TextureCache>()->getNormalFittingTexture());
-    }
-    );
+        std::make_shared<render::ShapePipeline>(getSimplePipeline(false, false, true, false), nullptr,
+            [](const render::ShapePipeline&, gpu::Batch& batch) {
+                // Set the defaults needed for a simple program
+                batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO,
+                    DependencyManager::get<TextureCache>()->getWhiteTexture());
+                batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING,
+                    DependencyManager::get<TextureCache>()->getNormalFittingTexture());
+            }
+        );
     GeometryCache::_simpleTransparentPipeline =
         std::make_shared<render::ShapePipeline>(getSimplePipeline(false, true, true, false), nullptr,
-        [](const render::ShapePipeline&, gpu::Batch& batch) {
-        // Set the defaults needed for a simple program
-        batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO,
-            DependencyManager::get<TextureCache>()->getWhiteTexture());
-        batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING,
-            DependencyManager::get<TextureCache>()->getNormalFittingTexture());
-    }
-    );
+            [](const render::ShapePipeline&, gpu::Batch& batch) {
+                // Set the defaults needed for a simple program
+                batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO,
+                    DependencyManager::get<TextureCache>()->getWhiteTexture());
+                batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING,
+                    DependencyManager::get<TextureCache>()->getNormalFittingTexture());
+            }
+        );
     GeometryCache::_simpleWirePipeline =
         std::make_shared<render::ShapePipeline>(getSimplePipeline(false, false, true, true), nullptr,
-        [](const render::ShapePipeline&, gpu::Batch& batch) {}
-    );
+            [](const render::ShapePipeline&, gpu::Batch& batch) {});
 }
 
 GeometryCache::~GeometryCache() {
diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp
index c2c27fb298..2fa31022ec 100644
--- a/libraries/render-utils/src/MeshPartPayload.cpp
+++ b/libraries/render-utils/src/MeshPartPayload.cpp
@@ -516,6 +516,10 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline:
     batch.setModelTransform(transform);
 }
 
+void ModelMeshPartPayload::startFade() {
+    _fadeStartTime = usecTimestampNow();
+    _hasStartedFade = true;
+}
 
 void ModelMeshPartPayload::render(RenderArgs* args) const {
     PerformanceTimer perfTimer("ModelMeshPartPayload::render");
diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h
index c2305f3741..8fdafee9b0 100644
--- a/libraries/render-utils/src/MeshPartPayload.h
+++ b/libraries/render-utils/src/MeshPartPayload.h
@@ -84,9 +84,8 @@ public:
     void updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector<glm::mat4>& clusterMatrices);
 
     // Entity fade in
-    void startFade() { _fadeStartTime = usecTimestampNow(); }
+    void startFade();
     bool hasStartedFade() { return _hasStartedFade; }
-    void setHasStartedFade(bool hasStartedFade) { _hasStartedFade = hasStartedFade; }
     bool isStillFading() const { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; }
 
     // Render Item interface
diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index 9d158810e7..2c7e9485fb 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -214,7 +214,6 @@ void Model::updateRenderItems() {
             pendingChanges.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, modelMeshOffset, deleteGeometryCounter](ModelMeshPartPayload& data) {
                 if (!data.hasStartedFade() && data._model && data._model->isLoaded() && data._model->getGeometry()->areTexturesLoaded()) {
                     data.startFade();
-                    data.setHasStartedFade(true);
                 }
                 // Ensure the model geometry was not reset between frames
                 if (data._model && data._model->isLoaded() && deleteGeometryCounter == data._model->_deleteGeometryCounter) {

From 51fb52977e039af704dfc74b7faf653ed80a6d09 Mon Sep 17 00:00:00 2001
From: Zander Otavka <zotavka@gmail.com>
Date: Tue, 9 Aug 2016 11:46:08 -0700
Subject: [PATCH 64/78] Generalize atp handling in network access managers

---
 libraries/gl/src/gl/OffscreenQmlSurface.cpp   | 18 +++++++++-
 .../src/{QmlAtpReply.cpp => AtpReply.cpp}     | 18 +++++-----
 .../src/{QmlAtpReply.h => AtpReply.h}         | 14 ++++----
 .../networking/src/NetworkAccessManager.cpp   | 13 +++++++-
 .../networking/src/NetworkAccessManager.h     |  9 +++--
 .../src/OAuthNetworkAccessManager.h           |  5 +--
 .../src/QmlNetworkAccessManager.cpp           | 29 ----------------
 .../networking/src/QmlNetworkAccessManager.h  | 33 -------------------
 8 files changed, 55 insertions(+), 84 deletions(-)
 rename libraries/networking/src/{QmlAtpReply.cpp => AtpReply.cpp} (86%)
 rename libraries/networking/src/{QmlAtpReply.h => AtpReply.h} (76%)
 delete mode 100644 libraries/networking/src/QmlNetworkAccessManager.cpp
 delete mode 100644 libraries/networking/src/QmlNetworkAccessManager.h

diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp
index ebccc8a1fc..d813e002c7 100644
--- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp
+++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp
@@ -25,7 +25,7 @@
 #include <NumericalConstants.h>
 #include <Finally.h>
 #include <PathUtils.h>
-#include <QmlNetworkAccessManager.h>
+#include <NetworkAccessManager.h>
 
 #include "OffscreenGLCanvas.h"
 #include "GLEscrow.h"
@@ -56,6 +56,22 @@ private:
     friend class OffscreenQmlSurface;
 };
 
+class QmlNetworkAccessManager : public NetworkAccessManager {
+public:
+    friend class QmlNetworkAccessManagerFactory;
+protected:
+    QmlNetworkAccessManager(QObject* parent) : NetworkAccessManager(parent) { }
+};
+
+class QmlNetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory {
+public:
+    QNetworkAccessManager* create(QObject* parent);
+};
+
+QNetworkAccessManager* QmlNetworkAccessManagerFactory::create(QObject* parent) {
+    return new QmlNetworkAccessManager(parent);
+}
+
 Q_DECLARE_LOGGING_CATEGORY(offscreenFocus)
 Q_LOGGING_CATEGORY(offscreenFocus, "hifi.offscreen.focus")
 
diff --git a/libraries/networking/src/QmlAtpReply.cpp b/libraries/networking/src/AtpReply.cpp
similarity index 86%
rename from libraries/networking/src/QmlAtpReply.cpp
rename to libraries/networking/src/AtpReply.cpp
index a2e537ba1f..4440995ee0 100644
--- a/libraries/networking/src/QmlAtpReply.cpp
+++ b/libraries/networking/src/AtpReply.cpp
@@ -1,5 +1,5 @@
 //
-//  QmlAtpReply.cpp
+//  AtpReply.cpp
 //  libraries/networking/src
 //
 //  Created by Zander Otavka on 8/4/16.
@@ -10,30 +10,30 @@
 //
 
 #include "ResourceManager.h"
-#include "QmlAtpReply.h"
+#include "AtpReply.h"
 
-QmlAtpReply::QmlAtpReply(const QUrl& url, QObject* parent) :
+AtpReply::AtpReply(const QUrl& url, QObject* parent) :
         _resourceRequest(ResourceManager::createResourceRequest(parent, url)) {
     setOperation(QNetworkAccessManager::GetOperation);
 
-    connect(_resourceRequest, &AssetResourceRequest::progress, this, &QmlAtpReply::downloadProgress);
-    connect(_resourceRequest, &AssetResourceRequest::finished, this, &QmlAtpReply::handleRequestFinish);
+    connect(_resourceRequest, &AssetResourceRequest::progress, this, &AtpReply::downloadProgress);
+    connect(_resourceRequest, &AssetResourceRequest::finished, this, &AtpReply::handleRequestFinish);
 
     _resourceRequest->send();
 }
 
-QmlAtpReply::~QmlAtpReply() {
+AtpReply::~AtpReply() {
     if (_resourceRequest) {
         _resourceRequest->deleteLater();
         _resourceRequest = nullptr;
     }
 }
 
-qint64 QmlAtpReply::bytesAvailable() const {
+qint64 AtpReply::bytesAvailable() const {
     return _content.size() - _readOffset + QIODevice::bytesAvailable();
 }
 
-qint64 QmlAtpReply::readData(char* data, qint64 maxSize) {
+qint64 AtpReply::readData(char* data, qint64 maxSize) {
     if (_readOffset < _content.size()) {
         qint64 readSize = qMin(maxSize, _content.size() - _readOffset);
         memcpy(data, _content.constData() + _readOffset, readSize);
@@ -44,7 +44,7 @@ qint64 QmlAtpReply::readData(char* data, qint64 maxSize) {
     }
 }
 
-void QmlAtpReply::handleRequestFinish() {
+void AtpReply::handleRequestFinish() {
     Q_ASSERT(_resourceRequest->getState() == ResourceRequest::State::Finished);
 
     switch (_resourceRequest->getResult()) {
diff --git a/libraries/networking/src/QmlAtpReply.h b/libraries/networking/src/AtpReply.h
similarity index 76%
rename from libraries/networking/src/QmlAtpReply.h
rename to libraries/networking/src/AtpReply.h
index a8f6dfde14..6ed5dd8fb8 100644
--- a/libraries/networking/src/QmlAtpReply.h
+++ b/libraries/networking/src/AtpReply.h
@@ -1,5 +1,5 @@
 //
-//  QmlAtpReply.h
+//  AtpReply.h
 //  libraries/networking/src
 //
 //  Created by Zander Otavka on 8/4/16.
@@ -9,19 +9,19 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-#ifndef hifi_QmlAtpReply_h
-#define hifi_QmlAtpReply_h
+#ifndef hifi_AtpReply_h
+#define hifi_AtpReply_h
 
 #include <QtNetwork/QNetworkReply>
 #include <QUrl>
 
 #include "AssetResourceRequest.h"
 
-class QmlAtpReply : public QNetworkReply {
+class AtpReply : public QNetworkReply {
     Q_OBJECT
 public:
-    QmlAtpReply(const QUrl& url, QObject* parent = Q_NULLPTR);
-    ~QmlAtpReply();
+    AtpReply(const QUrl& url, QObject* parent = Q_NULLPTR);
+    ~AtpReply();
     qint64 bytesAvailable() const override;
     void abort() override { }
     bool isSequential() const override { return true; }
@@ -37,4 +37,4 @@ private:
     qint64 _readOffset { 0 };
 };
 
-#endif // hifi_QmlAtpReply_h
+#endif // hifi_AtpReply_h
diff --git a/libraries/networking/src/NetworkAccessManager.cpp b/libraries/networking/src/NetworkAccessManager.cpp
index 97171d5ad7..78a05677fd 100644
--- a/libraries/networking/src/NetworkAccessManager.cpp
+++ b/libraries/networking/src/NetworkAccessManager.cpp
@@ -1,6 +1,6 @@
 //
 //  NetworkAccessManager.cpp
-//
+//  libraries/networking/src
 //
 //  Created by Clement on 7/1/14.
 //  Copyright 2014 High Fidelity, Inc.
@@ -11,6 +11,7 @@
 
 #include <QThreadStorage>
 
+#include "AtpReply.h"
 #include "NetworkAccessManager.h"
 
 QThreadStorage<QNetworkAccessManager*> networkAccessManagers;
@@ -23,3 +24,13 @@ QNetworkAccessManager& NetworkAccessManager::getInstance() {
     
     return *networkAccessManagers.localData();
 }
+
+QNetworkReply* NetworkAccessManager::createRequest(Operation operation, const QNetworkRequest& request, QIODevice* device) {
+    if (request.url().scheme() == "atp" && operation == GetOperation) {
+        return new AtpReply(request.url());
+        //auto url = request.url().toString();
+        //return QNetworkAccessManager::createRequest(operation, request, device);
+    } else {
+        return QNetworkAccessManager::createRequest(operation, request, device);
+    }
+}
diff --git a/libraries/networking/src/NetworkAccessManager.h b/libraries/networking/src/NetworkAccessManager.h
index c4b435adb6..1679ed081c 100644
--- a/libraries/networking/src/NetworkAccessManager.h
+++ b/libraries/networking/src/NetworkAccessManager.h
@@ -1,6 +1,6 @@
 //
 //  NetworkAccessManager.h
-//
+//  libraries/networking/src
 //
 //  Created by Clement on 7/1/14.
 //  Copyright 2014 High Fidelity, Inc.
@@ -13,12 +13,17 @@
 #define hifi_NetworkAccessManager_h
 
 #include <QtNetwork/QNetworkAccessManager>
+#include <QtNetwork/QNetworkRequest>
+#include <QtQml/QQmlNetworkAccessManagerFactory>
 
 /// Wrapper around QNetworkAccessManager to restrict at one instance by thread
-class NetworkAccessManager : public QObject {
+class NetworkAccessManager : public QNetworkAccessManager {
     Q_OBJECT
 public:
     static QNetworkAccessManager& getInstance();
+protected:
+    NetworkAccessManager(QObject* parent = Q_NULLPTR) : QNetworkAccessManager(parent) {}
+    virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice* device = Q_NULLPTR) override;
 };
 
 #endif // hifi_NetworkAccessManager_h
\ No newline at end of file
diff --git a/libraries/networking/src/OAuthNetworkAccessManager.h b/libraries/networking/src/OAuthNetworkAccessManager.h
index acfd52e18f..434d9b7c75 100644
--- a/libraries/networking/src/OAuthNetworkAccessManager.h
+++ b/libraries/networking/src/OAuthNetworkAccessManager.h
@@ -12,12 +12,13 @@
 #ifndef hifi_OAuthNetworkAccessManager_h
 #define hifi_OAuthNetworkAccessManager_h
 
-#include <QNetworkAccessManager>
+#include "NetworkAccessManager.h"
 
-class OAuthNetworkAccessManager : public QNetworkAccessManager {
+class OAuthNetworkAccessManager : public NetworkAccessManager {
 public:
     static OAuthNetworkAccessManager* getInstance();
 protected:
+    OAuthNetworkAccessManager(QObject* parent = Q_NULLPTR) : NetworkAccessManager(parent) { }
     virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& req, QIODevice* outgoingData = 0);
 };
 
diff --git a/libraries/networking/src/QmlNetworkAccessManager.cpp b/libraries/networking/src/QmlNetworkAccessManager.cpp
deleted file mode 100644
index 575bc02f8c..0000000000
--- a/libraries/networking/src/QmlNetworkAccessManager.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-//
-//  QmlNetworkAccessManager.cpp
-//  libraries/networking/src
-//
-//  Created by Zander Otavka on 8/4/16.
-//  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 <QThreadStorage>
-
-#include "QmlAtpReply.h"
-#include "QmlNetworkAccessManager.h"
-
-QNetworkAccessManager* QmlNetworkAccessManagerFactory::create(QObject* parent) {
-    return new QmlNetworkAccessManager(parent);
-}
-
-QNetworkReply* QmlNetworkAccessManager::createRequest(Operation operation, const QNetworkRequest& request, QIODevice* device) {
-    if (request.url().scheme() == "atp" && operation == GetOperation) {
-        return new QmlAtpReply(request.url());
-        //auto url = request.url().toString();
-        //return QNetworkAccessManager::createRequest(operation, request, device);
-    } else {
-        return QNetworkAccessManager::createRequest(operation, request, device);
-    }
-}
diff --git a/libraries/networking/src/QmlNetworkAccessManager.h b/libraries/networking/src/QmlNetworkAccessManager.h
deleted file mode 100644
index 059d0ebba0..0000000000
--- a/libraries/networking/src/QmlNetworkAccessManager.h
+++ /dev/null
@@ -1,33 +0,0 @@
-//
-//  QmlNetworkAccessManager.h
-//  libraries/networking/src
-//
-//  Created by Zander Otavka on 8/4/16.
-//  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_QmlNetworkAccessManager_h
-#define hifi_QmlNetworkAccessManager_h
-
-#include <QtNetwork/QNetworkAccessManager>
-#include <QtNetwork/QNetworkRequest>
-#include <QtQml/QQmlNetworkAccessManagerFactory>
-
-class QmlNetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory {
-public:
-    QNetworkAccessManager* create(QObject* parent);
-};
-
-
-class QmlNetworkAccessManager : public QNetworkAccessManager {
-    Q_OBJECT
-public:
-    QmlNetworkAccessManager(QObject* parent = Q_NULLPTR) : QNetworkAccessManager(parent) { }
-protected:
-    QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice* device = Q_NULLPTR);
-};
-
-#endif // hifi_QmlNetworkAccessManager_h

From 36d9f921011cc5e3ea4c5d4efedae4eaadbd4524 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Tue, 9 Aug 2016 13:10:02 -0700
Subject: [PATCH 65/78] performance optimization to minimize calling
 usecTimestampNow

---
 .../entities-renderer/src/RenderableEntityItem.h  |  9 +++++----
 .../src/RenderableLightEntityItem.cpp             |  4 +++-
 .../src/RenderablePolyLineEntityItem.cpp          |  6 ++++--
 .../src/RenderableShapeEntityItem.cpp             | 12 +++++++-----
 .../src/RenderableTextEntityItem.cpp              |  4 ++--
 .../src/RenderableWebEntityItem.cpp               |  4 ++--
 libraries/entities/src/EntityItem.cpp             |  2 +-
 libraries/entities/src/EntityItem.h               |  3 ++-
 .../procedural/src/procedural/Procedural.cpp      |  1 +
 libraries/procedural/src/procedural/Procedural.h  |  3 +++
 libraries/render-utils/src/MeshPartPayload.cpp    | 15 ++++++++++-----
 libraries/render-utils/src/MeshPartPayload.h      |  4 +++-
 12 files changed, 43 insertions(+), 24 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h
index 09d6d88c6a..22b6264520 100644
--- a/libraries/entities-renderer/src/RenderableEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableEntityItem.h
@@ -96,16 +96,17 @@ public: \
     virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) override { _renderHelper.removeFromScene(self, scene, pendingChanges); } \
     virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); _renderHelper.notifyChanged(); } \
     virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); _renderHelper.notifyChanged(); } \
-    void checkTransparency() { \
+    void checkFading() { \
         bool transparent = isTransparent(); \
-        if (transparent != prevIsTransparent) { \
+        if (transparent != _prevIsTransparent) { \
             _renderHelper.notifyChanged(); \
-            prevIsTransparent = transparent; \
+            _isFading = false; \
+            _prevIsTransparent = transparent; \
         } \
     } \
 private: \
     SimpleRenderableEntityItem _renderHelper; \
-    bool prevIsTransparent { isTransparent() };
+    bool _prevIsTransparent { isTransparent() };
 
 
 #endif // hifi_RenderableEntityItem_h
diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp
index fccd52d58c..b7f32cca65 100644
--- a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp
@@ -28,6 +28,8 @@ EntityItemPointer RenderableLightEntityItem::factory(const EntityItemID& entityI
 void RenderableLightEntityItem::render(RenderArgs* args) {
     PerformanceTimer perfTimer("RenderableLightEntityItem::render");
     assert(getType() == EntityTypes::Light);
+    checkFading();
+
     glm::vec3 position = getPosition();
     glm::vec3 dimensions = getDimensions();
     glm::quat rotation = getRotation();
@@ -35,7 +37,7 @@ void RenderableLightEntityItem::render(RenderArgs* args) {
 
     glm::vec3 color = toGlm(getXColor());
 
-    float intensity = getIntensity() * Interpolate::calculateFadeRatio(_fadeStartTime);
+    float intensity = getIntensity() * (_isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f);
     float falloffRadius = getFalloffRadius();
     float exponent = getExponent();
     float cutoff = glm::radians(getCutoff());
diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp
index ef4c9d6b5d..dc2545b956 100644
--- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp
@@ -167,7 +167,7 @@ void RenderablePolyLineEntityItem::update(const quint64& now) {
 }
 
 void RenderablePolyLineEntityItem::render(RenderArgs* args) {
-    checkTransparency();
+    checkFading();
 
     QWriteLocker lock(&_quadReadWriteLock);
     if (_points.size() < 2 || _normals.size () < 2 || _strokeWidths.size() < 2) {
@@ -206,7 +206,9 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) {
     batch.setInputFormat(_format);
     batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride);
 
-    batch._glColor4f(1.0f, 1.0f, 1.0f, Interpolate::calculateFadeRatio(_fadeStartTime));
+    if (_isFading) {
+        batch._glColor4f(1.0f, 1.0f, 1.0f, Interpolate::calculateFadeRatio(_fadeStartTime));
+    }
 
     batch.draw(gpu::TRIANGLE_STRIP, _numVertices, 0);
 };
diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
index f557625db2..73c4d99b5e 100644
--- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
@@ -72,8 +72,10 @@ void RenderableShapeEntityItem::setUserData(const QString& value) {
 }
 
 bool RenderableShapeEntityItem::isTransparent() {
-    if (_procedural && _procedural->ready()) {
-        return Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()) < 1.0f;
+    if (_procedural && _procedural->isFading()) {
+        float isFading = Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()) < 1.0f;
+        _procedural->setIsFading(isFading);
+        return isFading;
     } else {
         return getLocalRenderAlpha() < 1.0f || EntityItem::isTransparent();
     }
@@ -83,7 +85,7 @@ void RenderableShapeEntityItem::render(RenderArgs* args) {
     PerformanceTimer perfTimer("RenderableShapeEntityItem::render");
     //Q_ASSERT(getType() == EntityTypes::Shape);
     Q_ASSERT(args->_batch);
-    checkTransparency();
+    checkFading();
 
     if (!_procedural) {
         _procedural.reset(new Procedural(getUserData()));
@@ -110,12 +112,12 @@ void RenderableShapeEntityItem::render(RenderArgs* args) {
     if (_procedural->ready()) {
         _procedural->prepare(batch, getPosition(), getDimensions(), getOrientation());
         auto outColor = _procedural->getColor(color);
-        outColor.a *= Interpolate::calculateFadeRatio(_procedural->getFadeStartTime());
+        outColor.a *= _procedural->isFading() ? Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()) : 1.0f;
         batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a);
         DependencyManager::get<GeometryCache>()->renderShape(batch, MAPPING[_shape]);
     } else {
         // FIXME, support instanced multi-shape rendering using multidraw indirect
-        color.a *= Interpolate::calculateFadeRatio(_fadeStartTime);
+        color.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
         auto geometryCache = DependencyManager::get<GeometryCache>();
         auto pipeline = color.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline();
         geometryCache->renderSolidShapeInstance(batch, MAPPING[_shape], color, pipeline);
diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
index ccdaa39bdd..20adff83df 100644
--- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
@@ -27,10 +27,10 @@ EntityItemPointer RenderableTextEntityItem::factory(const EntityItemID& entityID
 void RenderableTextEntityItem::render(RenderArgs* args) {
     PerformanceTimer perfTimer("RenderableTextEntityItem::render");
     Q_ASSERT(getType() == EntityTypes::Text);
-    checkTransparency();
+    checkFading();
     
     static const float SLIGHTLY_BEHIND = -0.005f;
-    float fadeRatio = Interpolate::calculateFadeRatio(_fadeStartTime);
+    float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
     bool transparent = fadeRatio < 1.0f;
     glm::vec4 textColor = glm::vec4(toGlm(getTextColorX()), fadeRatio);
     glm::vec4 backgroundColor = glm::vec4(toGlm(getBackgroundColorX()), fadeRatio);
diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
index d6c1c1f761..c712ee506e 100644
--- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
@@ -164,7 +164,7 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) {
 }
 
 void RenderableWebEntityItem::render(RenderArgs* args) {
-    checkTransparency();
+    checkFading();
 
     #ifdef WANT_EXTRA_DEBUGGING
     {
@@ -210,7 +210,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
         batch._glActiveBindTexture(GL_TEXTURE0, GL_TEXTURE_2D, _texture);
     }
 
-    float fadeRatio = Interpolate::calculateFadeRatio(_fadeStartTime);
+    float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
     batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio);
 
     DependencyManager::get<GeometryCache>()->bindSimpleSRGBTexturedUnlitNoTexAlphaProgram(batch);
diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index f774d52274..b9f384f013 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -2212,4 +2212,4 @@ void EntityItem::globalizeProperties(EntityItemProperties& properties, const QSt
     }
     QUuid empty;
     properties.setParentID(empty);
-}
+}
\ No newline at end of file
diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h
index 198928da4e..f12075d191 100644
--- a/libraries/entities/src/EntityItem.h
+++ b/libraries/entities/src/EntityItem.h
@@ -436,7 +436,7 @@ public:
     QUuid getOwningAvatarID() const { return _owningAvatarID; }
     void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; }
 
-    virtual bool isTransparent() { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; }
+    virtual bool isTransparent() { return _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f : false; }
 
 protected:
 
@@ -568,6 +568,7 @@ protected:
     quint64 _lastUpdatedAccelerationTimestamp { 0 };
 
     quint64 _fadeStartTime { usecTimestampNow() };
+    bool _isFading { true };
 };
 
 #endif // hifi_EntityItem_h
diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp
index 7f8ab2db41..cd9edb6621 100644
--- a/libraries/procedural/src/procedural/Procedural.cpp
+++ b/libraries/procedural/src/procedural/Procedural.cpp
@@ -208,6 +208,7 @@ bool Procedural::ready() {
 
     if (!_hasStartedFade) {
         _hasStartedFade = true;
+        _isFading = true;
     }
     return true;
 }
diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h
index dea55f197b..c2939e4a01 100644
--- a/libraries/procedural/src/procedural/Procedural.h
+++ b/libraries/procedural/src/procedural/Procedural.h
@@ -43,6 +43,8 @@ public:
 
     glm::vec4 getColor(const glm::vec4& entityColor);
     quint64 getFadeStartTime() { return _fadeStartTime; }
+    bool isFading() { return _isFading; }
+    void setIsFading(bool isFading) { _isFading = isFading; }
 
     uint8_t _version { 1 };
 
@@ -110,6 +112,7 @@ private:
 
     quint64 _fadeStartTime;
     bool _hasStartedFade { false };
+    bool _isFading { false };
 };
 
 #endif
diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp
index 2fa31022ec..38d181e748 100644
--- a/libraries/render-utils/src/MeshPartPayload.cpp
+++ b/libraries/render-utils/src/MeshPartPayload.cpp
@@ -391,7 +391,7 @@ ItemKey ModelMeshPartPayload::getKey() const {
         }
     }
 
-    if (Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f) {
+    if (!_hasFinishedFade) {
         builder.withTransparent();
     }
 
@@ -446,7 +446,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const {
     }
 
     ShapeKey::Builder builder;
-    if (isTranslucent || Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f) {
+    if (isTranslucent || !_hasFinishedFade) {
         builder.withTranslucent();
     }
     if (hasTangents) {
@@ -487,7 +487,7 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const {
         batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2));
     }
 
-    float fadeRatio = Interpolate::calculateFadeRatio(_fadeStartTime);
+    float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
     if (!_hasColorAttrib || fadeRatio < 1.0f) {
         batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio);
     }
@@ -519,6 +519,8 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline:
 void ModelMeshPartPayload::startFade() {
     _fadeStartTime = usecTimestampNow();
     _hasStartedFade = true;
+    _prevHasStartedFade = false;
+    _hasFinishedFade = false;
 }
 
 void ModelMeshPartPayload::render(RenderArgs* args) const {
@@ -530,10 +532,13 @@ void ModelMeshPartPayload::render(RenderArgs* args) const {
 
     // When an individual mesh parts like this finishes its fade, we will mark the Model as 
     // having render items that need updating
-    if (_wasFading && !isStillFading()) {
+    bool nextIsFading = _isFading ? isStillFading() : false;
+    if (_isFading != nextIsFading || _prevHasStartedFade != _hasStartedFade) {
+        _isFading = nextIsFading || _prevHasStartedFade != _hasStartedFade;
+        _hasFinishedFade = _prevHasStartedFade == _hasStartedFade && !_isFading;
+        _prevHasStartedFade = _hasStartedFade;
         _model->setRenderItemsNeedUpdate();
     }
-    _wasFading = isStillFading();
 
     gpu::Batch& batch = *(args->_batch);
 
diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h
index 8fdafee9b0..67fb660f8b 100644
--- a/libraries/render-utils/src/MeshPartPayload.h
+++ b/libraries/render-utils/src/MeshPartPayload.h
@@ -110,7 +110,9 @@ public:
 private:
     quint64 _fadeStartTime { 0 };
     bool _hasStartedFade { false };
-    mutable bool _wasFading { false };
+    mutable bool _prevHasStartedFade{ false };
+    mutable bool _hasFinishedFade { false };
+    mutable bool _isFading { false };
 };
 
 namespace render {

From 5a4d15dd5bf06190148635a8a8a56b499e0be9ff Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Tue, 9 Aug 2016 14:02:42 -0700
Subject: [PATCH 66/78] fix warning

---
 libraries/gpu/src/gpu/Texture.cpp | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp
index 25e4fa549c..f1a8960fa1 100755
--- a/libraries/gpu/src/gpu/Texture.cpp
+++ b/libraries/gpu/src/gpu/Texture.cpp
@@ -700,8 +700,6 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
         return false;
     }
 
-    const float UCHAR_TO_FLOAT = 1.0f / float(std::numeric_limits<unsigned char>::max());
-
     // for each face of cube texture
     for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) {
 

From 956078bc91ef652ad1b38f66a3d767f87155de35 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Tue, 9 Aug 2016 15:17:36 -0700
Subject: [PATCH 67/78] teleport works with xbox controller

---
 scripts/system/controllers/teleport.js | 23 ++++++++++-------------
 1 file changed, 10 insertions(+), 13 deletions(-)

diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js
index fae9b98b96..5ec429fae0 100644
--- a/scripts/system/controllers/teleport.js
+++ b/scripts/system/controllers/teleport.js
@@ -232,14 +232,11 @@ function Teleporter() {
     };
 
     this.rightRay = function() {
+        var pose = Controller.getPoseValue(Controller.Standard.RightHand);
+        var rightPosition = pose.valid ? Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position) : MyAvatar.getHeadPosition();
+        var rightRotation = pose.valid ? Quat.multiply(MyAvatar.orientation, pose.rotation) : MyAvatar.headOrientation;
 
-        var rightPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getPoseValue(Controller.Standard.RightHand).translation), MyAvatar.position);
-
-        var rightControllerRotation = Controller.getPoseValue(Controller.Standard.RightHand).rotation;
-
-        var rightRotation = Quat.multiply(MyAvatar.orientation, rightControllerRotation);
-
-        var rightFinal = Quat.multiply(rightRotation, Quat.angleAxis(90, {
+        var rightFinal = Quat.multiply(rightRotation, Quat.angleAxis(-90, {
             x: 1,
             y: 0,
             z: 0
@@ -247,7 +244,7 @@ function Teleporter() {
 
         var rightPickRay = {
             origin: rightPosition,
-            direction: Quat.getUp(rightRotation),
+            direction: Quat.getUp(pose.valid ? rightRotation : rightFinal),
         };
 
         this.rightPickRay = rightPickRay;
@@ -288,11 +285,11 @@ function Teleporter() {
 
 
     this.leftRay = function() {
-        var leftPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getPoseValue(Controller.Standard.LeftHand).translation), MyAvatar.position);
+        var pose = Controller.getPoseValue(Controller.Standard.LeftHand);
+        var leftPosition = pose.valid ? Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position) : MyAvatar.getHeadPosition();
+        var leftRotation = pose.valid ? Quat.multiply(MyAvatar.orientation, pose.rotation) : MyAvatar.headOrientation;
 
-        var leftRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(Controller.Standard.LeftHand).rotation)
-
-        var leftFinal = Quat.multiply(leftRotation, Quat.angleAxis(90, {
+        var leftFinal = Quat.multiply(leftRotation, Quat.angleAxis(-90, {
             x: 1,
             y: 0,
             z: 0
@@ -300,7 +297,7 @@ function Teleporter() {
 
         var leftPickRay = {
             origin: leftPosition,
-            direction: Quat.getUp(leftRotation),
+            direction: Quat.getUp(pose.valid ? leftRotation : leftFinal),
         };
 
         this.leftPickRay = leftPickRay;

From 6965fb05206dfb8463ba435446f1fe96eefec461 Mon Sep 17 00:00:00 2001
From: Zach Pomerantz <zach@highfidelity.io>
Date: Tue, 9 Aug 2016 14:15:06 -0700
Subject: [PATCH 68/78] refactor system html to split js/css

---
 scripts/system/html/{ => css}/edit-style.css  |   46 +-
 scripts/system/html/entityList.html           |  314 +---
 scripts/system/html/entityProperties.html     | 1322 +----------------
 scripts/system/html/entityProperties.js       | 1317 ++++++++++++++++
 scripts/system/html/gridControls.html         |  142 +-
 scripts/system/html/{ => js}/colpick.js       |    0
 scripts/system/html/js/entityList.js          |  310 ++++
 scripts/system/html/js/entityProperties.js    | 1317 ++++++++++++++++
 .../system/html/{ => js}/eventBridgeLoader.js |    0
 scripts/system/html/js/gridControls.js        |  138 ++
 .../system/html/{ => js}/jquery-2.1.4.min.js  |    0
 scripts/system/html/{ => js}/list.min.js      |    0
 scripts/system/html/{ => js}/spinButtons.js   |    0
 13 files changed, 3121 insertions(+), 1785 deletions(-)
 rename scripts/system/html/{ => css}/edit-style.css (94%)
 create mode 100644 scripts/system/html/entityProperties.js
 rename scripts/system/html/{ => js}/colpick.js (100%)
 create mode 100644 scripts/system/html/js/entityList.js
 create mode 100644 scripts/system/html/js/entityProperties.js
 rename scripts/system/html/{ => js}/eventBridgeLoader.js (100%)
 create mode 100644 scripts/system/html/js/gridControls.js
 rename scripts/system/html/{ => js}/jquery-2.1.4.min.js (100%)
 rename scripts/system/html/{ => js}/list.min.js (100%)
 rename scripts/system/html/{ => js}/spinButtons.js (100%)

diff --git a/scripts/system/html/edit-style.css b/scripts/system/html/css/edit-style.css
similarity index 94%
rename from scripts/system/html/edit-style.css
rename to scripts/system/html/css/edit-style.css
index 19d1cd95a9..0b58cf22ac 100644
--- a/scripts/system/html/edit-style.css
+++ b/scripts/system/html/css/edit-style.css
@@ -10,51 +10,51 @@
 
 @font-face {
     font-family: Raleway-Regular;
-    src: url(../../../resources/fonts/Raleway-Regular.ttf),  /* Windows production */
-         url(../../../fonts/Raleway-Regular.ttf),  /* OSX production */
-         url(../../../interface/resources/fonts/Raleway-Regular.ttf);  /* Development, running script in /HiFi/examples */
+    src: url(../../../../resources/fonts/Raleway-Regular.ttf),  /* Windows production */
+         url(../../../../fonts/Raleway-Regular.ttf),  /* OSX production */
+         url(../../../../interface/resources/fonts/Raleway-Regular.ttf);  /* Development, running script in /HiFi/examples */
 }
 
 @font-face {
     font-family: Raleway-Light;
-    src: url(../../../resources/fonts/Raleway-Light.ttf),
-         url(../../../fonts/Raleway-Light.ttf),
-         url(../../../interface/resources/fonts/Raleway-Light.ttf);
+    src: url(../../../../resources/fonts/Raleway-Light.ttf),
+         url(../../../../fonts/Raleway-Light.ttf),
+         url(../../../../interface/resources/fonts/Raleway-Light.ttf);
 }
 
 @font-face {
     font-family: Raleway-Bold;
-    src: url(../../../resources/fonts/Raleway-Bold.ttf),
-         url(../../../fonts/Raleway-Bold.ttf),
-         url(../../../interface/resources/fonts/Raleway-Bold.ttf);
+    src: url(../../../../resources/fonts/Raleway-Bold.ttf),
+         url(../../../../fonts/Raleway-Bold.ttf),
+         url(../../../../interface/resources/fonts/Raleway-Bold.ttf);
 }
 
 @font-face {
     font-family: Raleway-SemiBold;
-    src: url(../../../resources/fonts/Raleway-SemiBold.ttf),
-         url(../../../fonts/Raleway-SemiBold.ttf),
-         url(../../../interface/resources/fonts/Raleway-SemiBold.ttf);
+    src: url(../../../../resources/fonts/Raleway-SemiBold.ttf),
+         url(../../../../fonts/Raleway-SemiBold.ttf),
+         url(../../../../interface/resources/fonts/Raleway-SemiBold.ttf);
 }
 
 @font-face {
     font-family: FiraSans-SemiBold;
-    src: url(../../../resources/fonts/FiraSans-SemiBold.ttf),
-         url(../../../fonts/FiraSans-SemiBold.ttf),
-         url(../../../interface/resources/fonts/FiraSans-SemiBold.ttf);
+    src: url(../../../../resources/fonts/FiraSans-SemiBold.ttf),
+         url(../../../../fonts/FiraSans-SemiBold.ttf),
+         url(../../../../interface/resources/fonts/FiraSans-SemiBold.ttf);
 }
 
 @font-face {
     font-family: AnonymousPro-Regular;
-    src: url(../../../resources/fonts/AnonymousPro-Regular.ttf),
-         url(../../../fonts/AnonymousPro-Regular.ttf),
-         url(../../../interface/resources/fonts/AnonymousPro-Regular.ttf);
+    src: url(../../../../resources/fonts/AnonymousPro-Regular.ttf),
+         url(../../../../fonts/AnonymousPro-Regular.ttf),
+         url(../../../../interface/resources/fonts/AnonymousPro-Regular.ttf);
 }
 
 @font-face {
     font-family: HiFi-Glyphs;
-    src: url(../../../resources/fonts/hifi-glyphs.ttf),
-         url(../../../fonts/hifi-glyphs.ttf),
-         url(../../../interface/resources/fonts/hifi-glyphs.ttf);
+    src: url(../../../../resources/fonts/hifi-glyphs.ttf),
+         url(../../../../fonts/hifi-glyphs.ttf),
+         url(../../../../interface/resources/fonts/hifi-glyphs.ttf);
 }
 
 * {
@@ -1077,10 +1077,6 @@ input#dimension-rescale-button {
 input#reset-to-natural-dimensions {
     margin-right: 0;
 }
-input#preview-camera-button {
-    margin-left: 1px;
-    margin-right: 0;
-}
 
 #animation-fps {
     margin-top: 48px;
diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html
index dbc224e9fb..2088898613 100644
--- a/scripts/system/html/entityList.html
+++ b/scripts/system/html/entityList.html
@@ -10,314 +10,12 @@
 
 <html>
 <head>
-    <link rel="stylesheet" type="text/css" href="edit-style.css">
-    <script src="list.min.js"></script>
+    <link rel="stylesheet" type="text/css" href="css/edit-style.css">
+    <script src="js/list.min.js"></script>
     <script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
-    <script type="text/javascript" src="eventBridgeLoader.js"></script>
-    <script type="text/javascript" src="spinButtons.js"></script>
-    <script>
-        var entities = {};
-        var selectedEntities = [];
-        var currentSortColumn = 'type';
-        var currentSortOrder = 'des';
-        var entityList = null;
-        var refreshEntityListTimer = null;
-        const ASCENDING_STRING = '&#x25BE;';
-        const DESCENDING_STRING = '&#x25B4;';
-        const LOCKED_GLYPH = "&#xe006;";
-        const VISIBLE_GLYPH = "&#xe007;";
-        const DELETE = 46; // Key code for the delete key.
-        const MAX_ITEMS = Number.MAX_VALUE; // Used to set the max length of the list of discovered entities.
-
-        debugPrint = function (message) {
-            console.log(message);
-        };
-
-        function loaded() {
-          openEventBridge(function() { 
-              entityList = new List('entity-list', { valueNames: ['name', 'type', 'url', 'locked', 'visible'], page: MAX_ITEMS});
-              entityList.clear();
-              elEntityTable = document.getElementById("entity-table");
-              elEntityTableBody = document.getElementById("entity-table-body");
-              elRefresh = document.getElementById("refresh");
-              elToggleLocked = document.getElementById("locked");
-              elToggleVisible = document.getElementById("visible");
-              elDelete = document.getElementById("delete");
-              elTeleport = document.getElementById("teleport");
-              elRadius = document.getElementById("radius");
-              elFooter = document.getElementById("footer-text");
-              elNoEntitiesMessage = document.getElementById("no-entities");
-              elNoEntitiesRadius = document.getElementById("no-entities-radius");
-              elEntityTableScroll = document.getElementById("entity-table-scroll");
-              
-              document.getElementById("entity-name").onclick = function() {
-                  setSortColumn('name');
-              };
-              document.getElementById("entity-type").onclick = function() {
-                  setSortColumn('type');
-              };
-              document.getElementById("entity-url").onclick = function() {
-                  setSortColumn('url');
-              };
-              document.getElementById("entity-locked").onclick = function () {
-                  setSortColumn('locked');
-              };
-              document.getElementById("entity-visible").onclick = function () {
-                  setSortColumn('visible');
-              };
-              
-              function onRowClicked(clickEvent) {
-                  var id = this.dataset.entityId;
-                  var selection = [this.dataset.entityId];
-                  if (clickEvent.ctrlKey) {
-                      selection = selection.concat(selectedEntities);
-                  } else if (clickEvent.shiftKey && selectedEntities.length > 0) {
-                      var previousItemFound = -1;
-                      var clickedItemFound = -1; 
-                      for (var entity in entityList.visibleItems) {
-                          if (clickedItemFound === -1 && this.dataset.entityId == entityList.visibleItems[entity].values().id) {
-                              clickedItemFound = entity;
-                          } else if(previousItemFound === -1 && selectedEntities[0] == entityList.visibleItems[entity].values().id) {
-                              previousItemFound = entity;
-                          }
-                      }
-                      if (previousItemFound !== -1 && clickedItemFound !== -1) {
-                          var betweenItems = [];
-                          var toItem = Math.max(previousItemFound, clickedItemFound);
-                          // skip first and last item in this loop, we add them to selection after the loop
-                          for (var i = (Math.min(previousItemFound, clickedItemFound) + 1); i < toItem; i++) {
-                              entityList.visibleItems[i].elm.className = 'selected';
-                              betweenItems.push(entityList.visibleItems[i].values().id);
-                          }
-                          if (previousItemFound > clickedItemFound) {
-                              // always make sure that we add the items in the right order
-                              betweenItems.reverse();
-                          }
-                          selection = selection.concat(betweenItems, selectedEntities);
-                      }
-                  }
-              
-                  selectedEntities = selection;
-              
-                  this.className = 'selected';
-              
-                  EventBridge.emitWebEvent(JSON.stringify({
-                      type: "selectionUpdate",
-                      focus: false,
-                      entityIds: selection,
-                  }));
-              }
-              
-              function onRowDoubleClicked() {
-                  EventBridge.emitWebEvent(JSON.stringify({
-                      type: "selectionUpdate",
-                      focus: true,
-                      entityIds: [this.dataset.entityId],
-                  }));
-              }
-              
-              function addEntity(id, name, type, url, locked, visible) {
-                  var urlParts = url.split('/');
-                  var filename = urlParts[urlParts.length - 1];
-
-                  if (entities[id] === undefined) {
-                      entityList.add([{ id: id, name: name, type: type, url: filename, locked: locked, visible: visible }],
-                          function (items) {
-                              var currentElement = items[0].elm;
-                              var id = items[0]._values.id;
-                              entities[id] = {
-                                  id: id,
-                                  name: name,
-                                  el: currentElement,
-                                  item: items[0]
-                              };
-                              currentElement.setAttribute('id', 'entity_' + id);
-                              currentElement.setAttribute('title', url);
-                              currentElement.dataset.entityId = id;
-                              currentElement.onclick = onRowClicked;
-                              currentElement.ondblclick = onRowDoubleClicked;
-                      });
-              
-                      if (refreshEntityListTimer) {
-                          clearTimeout(refreshEntityListTimer);
-                      }
-                      refreshEntityListTimer = setTimeout(refreshEntityListObject, 50);
-                  } else {
-                      var item = entities[id].item;
-                      item.values({ name: name, url: filename, locked: locked, visible: visible });
-                  }
-              }
-              
-              function clearEntities() {
-                  entities = {};
-                  entityList.clear();
-              }
-              
-              var elSortOrder = {
-                  name: document.querySelector('#entity-name .sort-order'),
-                  type: document.querySelector('#entity-type .sort-order'),
-                  url: document.querySelector('#entity-url .sort-order'),
-                  locked: document.querySelector('#entity-locked .sort-order'),
-                  visible: document.querySelector('#entity-visible .sort-order')
-              }
-              function setSortColumn(column) {
-                  if (currentSortColumn == column) {
-                      currentSortOrder = currentSortOrder == "asc" ? "desc" : "asc";
-                  } else {
-                      elSortOrder[currentSortColumn].innerHTML = "";
-                      currentSortColumn = column;
-                      currentSortOrder = "asc";
-                  }
-                  elSortOrder[column].innerHTML = currentSortOrder == "asc" ? ASCENDING_STRING : DESCENDING_STRING;
-                  entityList.sort(currentSortColumn, { order: currentSortOrder });
-              }
-              setSortColumn('type');
-              
-              function refreshEntities() {
-                  clearEntities();
-                  EventBridge.emitWebEvent(JSON.stringify({ type: 'refresh' }));
-              }
-              
-              function refreshEntityListObject() {
-                  refreshEntityListTimer = null;
-                  entityList.sort(currentSortColumn, { order: currentSortOrder });
-                  entityList.search(document.getElementById("filter").value);
-              }
-              
-              function updateSelectedEntities(selectedEntities) {
-                  var notFound = false;
-                  for (var id in entities) {
-                      entities[id].el.className = '';
-                  }
-                  for (var i = 0; i < selectedEntities.length; i++) {
-                      var id = selectedEntities[i];
-                      if (id in entities) {
-                          var entity = entities[id];
-                          entity.el.className = 'selected';
-                      } else {
-                          notFound = true;
-                      }
-                  }
-
-                  if (selectedEntities.length > 1) {
-                      elFooter.firstChild.nodeValue = selectedEntities.length + " entities selected";
-                  } else if (selectedEntities.length === 1) {
-                      elFooter.firstChild.nodeValue = "1 entity selected";
-                  } else if (entityList.visibleItems.length === 1) {
-                      elFooter.firstChild.nodeValue = "1 entity found";
-                  } else {
-                      elFooter.firstChild.nodeValue = entityList.visibleItems.length + " entities found";
-                  }
-
-                  // HACK: Fixes the footer and header text sometimes not displaying after adding or deleting entities.
-                  // The problem appears to be a bug in the Qt HTML/CSS rendering (Qt 5.5).
-                  document.getElementById("radius").focus();
-                  document.getElementById("radius").blur();
-
-                  return notFound;
-              }
-              
-              elRefresh.onclick = function() {
-                  refreshEntities();
-              }
-              elToggleLocked.onclick = function () {
-                  EventBridge.emitWebEvent(JSON.stringify({ type: 'toggleLocked' }));
-              }
-              elToggleVisible.onclick = function () {
-                  EventBridge.emitWebEvent(JSON.stringify({ type: 'toggleVisible' }));
-              }
-              elTeleport.onclick = function () {
-                  EventBridge.emitWebEvent(JSON.stringify({ type: 'teleport' }));
-              }
-              elDelete.onclick = function() {
-                  EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' }));
-                  refreshEntities();
-              }
-              
-              document.addEventListener("keydown", function (keyDownEvent) {
-                  if (keyDownEvent.target.nodeName === "INPUT") {
-                      return;
-                  }
-                  var keyCode = keyDownEvent.keyCode;
-                  if (keyCode === DELETE) {
-                      EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' }));
-                      refreshEntities();
-                  }
-              }, false);
-              
-              elRadius.onchange = function () {
-                  elRadius.value = Math.max(elRadius.value, 0);
-                  EventBridge.emitWebEvent(JSON.stringify({ type: 'radius', radius: elRadius.value }));
-                  refreshEntities();
-                  elNoEntitiesRadius.firstChild.nodeValue = elRadius.value;
-              }
-              
-              if (window.EventBridge !== undefined) {
-                  EventBridge.scriptEventReceived.connect(function(data) {
-                      data = JSON.parse(data);
-              
-                      if (data.type === "clearEntityList") {
-                          clearEntities();
-                      } else if (data.type == "selectionUpdate") {
-                          var notFound = updateSelectedEntities(data.selectedIDs);
-                          if (notFound) {
-                              refreshEntities();
-                          }
-                      } else if (data.type == "update") {
-                          var newEntities = data.entities;
-                          if (newEntities.length == 0) {
-                              elNoEntitiesMessage.style.display = "block";
-                              elFooter.firstChild.nodeValue = "0 entities found";
-                          } else {
-                              elNoEntitiesMessage.style.display = "none";
-                              for (var i = 0; i < newEntities.length; i++) {
-                                  var id = newEntities[i].id;
-                                  addEntity(id, newEntities[i].name, newEntities[i].type, newEntities[i].url,
-                                      newEntities[i].locked ? LOCKED_GLYPH : null,
-                                      newEntities[i].visible ? VISIBLE_GLYPH : null);
-                              }
-                              updateSelectedEntities(data.selectedIDs);
-                              resize();
-                          }
-                      }
-                  });
-                  setTimeout(refreshEntities, 1000);
-              }
-
-              function resize() {
-                  // Take up available window space
-                  elEntityTableScroll.style.height = window.innerHeight - 207;
-
-                  var tds = document.querySelectorAll("#entity-table-body tr:first-child td");
-                  var ths = document.querySelectorAll("#entity-table thead th");
-                  if (tds.length >= ths.length) {
-                      // Update the widths of the header cells to match the body
-                      for (var i = 0; i < ths.length; i++) {
-                          ths[i].width = tds[i].offsetWidth;
-                      }
-                  } else {
-                      // Reasonable widths if nothing is displayed
-                      var tableWidth = document.getElementById("entity-table").offsetWidth;
-                      ths[0].width = 0.16 * tableWidth;
-                      ths[1].width = 0.34 * tableWidth;
-                      ths[2].width = 0.34 * tableWidth;
-                      ths[3].width = 0.08 * tableWidth;
-                      ths[4].width = 0.08 * tableWidth;
-                  }
-              };
-
-              window.onresize = resize;
-              resize();
-          });
-
-          augmentSpinButtons();
-
-          // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
-          document.addEventListener("contextmenu", function (event) {
-              event.preventDefault();
-          }, false);
-      }
-    </script>
+    <script type="text/javascript" src="js/eventBridgeLoader.js"></script>
+    <script type="text/javascript" src="js/spinButtons.js"></script>
+    <script type="text/javascript" src="js/entityList.js"></script>
 </head>
 <body onload='loaded();'>
     <div id="entity-list-header">
@@ -378,4 +76,4 @@
         </div>
     </div>
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html
index 424795981d..01826b8e10 100644
--- a/scripts/system/html/entityProperties.html
+++ b/scripts/system/html/entityProperties.html
@@ -11,1325 +11,15 @@
 <html>
 <head>
     <title>Properties</title>
-    <link rel="stylesheet" type="text/css" href="edit-style.css">
+    <link rel="stylesheet" type="text/css" href="css/edit-style.css">
     <link rel="stylesheet" type="text/css" href="css/colpick.css">
-    <script src="jquery-2.1.4.min.js"></script>
-    <script src="colpick.js"></script>
+    <script src="js/jquery-2.1.4.min.js"></script>
+    <script src="js/colpick.js"></script>
     <script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
-    <script type="text/javascript" src="eventBridgeLoader.js"></script>
-    <script type="text/javascript" src="spinButtons.js"></script>
-    <script>
-        var PI = 3.14159265358979;
-        var DEGREES_TO_RADIANS = PI / 180.0;
-        var RADIANS_TO_DEGREES = 180.0 / PI;
-        var ICON_FOR_TYPE = {
-            Box: "V",
-            Sphere: "n",
-            Shape: "n",
-            ParticleEffect: "&#xe004;",
-            Model: "&#xe008;",
-            Web: "q",
-            Text: "l",
-            Light: "p",
-            Zone: "o",
-            PolyVox: "&#xe005;",
-            Multiple: "&#xe000;"
-        }
-
-        var colorPickers = [];
-
-        debugPrint = function(message) {
-            EventBridge.emitWebEvent(
-                JSON.stringify({
-                    type:"print", 
-                    message: message
-                })
-            );
-        };
-        
-        function enableChildren(el, selector) {
-            els = el.querySelectorAll(selector);
-            for (var i = 0; i < els.length; i++) {
-                els[i].removeAttribute('disabled');
-            }
-        }
-        function disableChildren(el, selector) {
-            els = el.querySelectorAll(selector);
-            for (var i = 0; i < els.length; i++) {
-                els[i].setAttribute('disabled', 'disabled');
-            }
-        }
-
-        function enableProperties() {
-            enableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker");
-            enableChildren(document, ".colpick");
-        }
-
-        function disableProperties() {
-            disableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker");
-            disableChildren(document, ".colpick");
-            for (var i = 0; i < colorPickers.length; i++) {
-                colorPickers[i].colpickHide();
-            }
-        }
-
-        function showElements(els, show) {
-            for (var i = 0; i < els.length; i++) {
-                els[i].style.display = (show) ? 'table' : 'none';
-            }
-        }
-
-        function createEmitCheckedPropertyUpdateFunction(propertyName) {
-            return function() {
-                EventBridge.emitWebEvent(
-                    '{ "type":"update", "properties":{"' + propertyName + '":' + this.checked + '}}'
-                );
-            };
-        }
-
-        function createEmitCheckedToStringPropertyUpdateFunction(checkboxElement, name, propertyName) {
-            var newString = "";
-            if (checkboxElement.checked) {
-                newString += name + "";
-            } else {
-
-            }
-
-        }
-
-        function createEmitGroupCheckedPropertyUpdateFunction(group, propertyName) {
-            return function () {
-                var properties = {};
-                properties[group] = {};
-                properties[group][propertyName] = this.checked;
-                EventBridge.emitWebEvent(
-                    JSON.stringify({
-                        type: "update",
-                        properties: properties
-                    })
-                );
-            };
-        }
-
-        function createEmitNumberPropertyUpdateFunction(propertyName, decimals) {
-            decimals = decimals == undefined ? 4 : decimals;
-            return function() {
-                var value = parseFloat(this.value).toFixed(decimals);
-
-                EventBridge.emitWebEvent(
-                    '{ "type":"update", "properties":{"' + propertyName + '":' + value + '}}'
-                );
-            };
-        }
-        function createEmitGroupNumberPropertyUpdateFunction(group, propertyName) {
-            return function() {
-                var properties = {};
-                properties[group] = {};
-                properties[group][propertyName] = this.value;
-                EventBridge.emitWebEvent(
-                    JSON.stringify({
-                        type: "update",
-                        properties: properties,
-                    })
-                );
-            };
-        }
-
-
-        function createEmitTextPropertyUpdateFunction(propertyName) {
-            return function() {
-                var properties = {};
-                properties[propertyName] = this.value;
-                EventBridge.emitWebEvent(
-                    JSON.stringify({
-                        type: "update",
-                        properties: properties,
-                    })
-                );
-            };
-        }
-
-        function createEmitGroupTextPropertyUpdateFunction(group,propertyName) {
-            return function() {
-                var properties = {};
-                properties[group] = {};
-                properties[group][propertyName] = this.value;
-                EventBridge.emitWebEvent(
-                    JSON.stringify({
-                        type: "update",
-                        properties: properties,
-                    })
-                );
-            };
-        }
-
-        function createEmitVec3PropertyUpdateFunction(property, elX, elY, elZ) {
-            return function() {
-                var data = {
-                     type: "update",
-                     properties: {
-                     }
-                };
-                data.properties[property] = {
-                     x: elX.value,
-                     y: elY.value,
-                     z: elZ.value,
-                };
-                EventBridge.emitWebEvent(JSON.stringify(data));
-            }
-        };
-
-        function createEmitGroupVec3PropertyUpdateFunction(group, property, elX, elY, elZ) {
-            return function() {
-                var data = {
-                     type: "update",
-                     properties: {
-                     }
-                };
-                data.properties[group] = { };
-                data.properties[group][property] = {
-                     x: elX.value,
-                     y: elY.value,
-                     z: elZ ? elZ.value : 0,
-                };
-                EventBridge.emitWebEvent(JSON.stringify(data));
-            }
-        };
-
-        function createEmitVec3PropertyUpdateFunctionWithMultiplier(property, elX, elY, elZ, multiplier) {
-            return function() {
-                var data = {
-                     type: "update",
-                     properties: {
-                     }
-                };
-                data.properties[property] = {
-                     x: elX.value * multiplier,
-                     y: elY.value * multiplier,
-                     z: elZ.value * multiplier,
-                };
-                EventBridge.emitWebEvent(JSON.stringify(data));
-            }
-        };
-
-        function createEmitColorPropertyUpdateFunction(property, elRed, elGreen, elBlue) {
-            return function() {
-                emitColorPropertyUpdate(property, elRed.value, elGreen.value, elBlue.value);
-            }
-        };
-
-        function emitColorPropertyUpdate(property, red, green, blue, group) {
-                var data = {
-                     type: "update",
-                     properties: {
-                     }
-                };
-                if (group) {
-                    data.properties[group] = { };
-                    data.properties[group][property] = {
-                         red: red,
-                         green: green,
-                         blue: blue,
-                    };
-                } else {
-                    data.properties[property] = {
-                         red: red,
-                         green: green,
-                         blue: blue,
-                    };
-                }
-                EventBridge.emitWebEvent(JSON.stringify(data));
-        };
-
-
-        function createEmitGroupColorPropertyUpdateFunction(group, property, elRed, elGreen, elBlue) {
-            return function() {
-                var data = {
-                     type: "update",
-                     properties: {
-                     }
-                };
-                data.properties[group] = { };
-
-                data.properties[group][property] = {
-                     red: elRed.value,
-                     green: elGreen.value,
-                     blue: elBlue.value,
-                };
-                EventBridge.emitWebEvent(JSON.stringify(data));
-            }
-        };
-
-        function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElement, subPropertyString) {
-            if (subPropertyElement.checked) {
-                if (propertyValue.indexOf(subPropertyString)) {
-                    propertyValue += subPropertyString + ',';
-                } 
-            } else {
-                // We've unchecked, so remove 
-                propertyValue = propertyValue.replace(subPropertyString + ",", "");
-            }
-
-            var _properties ={}
-            _properties[propertyName] = propertyValue;
-            
-            EventBridge.emitWebEvent(
-              JSON.stringify({
-                type: "update",
-                properties: _properties
-              })
-            );
-
-        }
-
-        function userDataChanger(groupName, keyName, checkBoxElement, userDataElement, defaultValue) {
-            var properties = {};
-            var parsedData = {};
-            try {
-                parsedData = JSON.parse(userDataElement.value);
-            } catch(e) {}
-
-            if (!(groupName in parsedData)) {
-                parsedData[groupName] = {}
-            }
-            delete parsedData[groupName][keyName];
-            if (checkBoxElement.checked !== defaultValue) {
-                parsedData[groupName][keyName] = checkBoxElement.checked;
-            }
-
-            if (Object.keys(parsedData[groupName]).length == 0) {
-                delete parsedData[groupName];
-            }
-            if (Object.keys(parsedData).length > 0) {
-                properties['userData'] = JSON.stringify(parsedData);
-            } else {
-                properties['userData'] = '';
-            }
-
-            userDataElement.value = properties['userData'];
-
-            EventBridge.emitWebEvent(
-                JSON.stringify({
-                    type: "update",
-                    properties: properties,
-                })
-            );
-        };
-
-        function setTextareaScrolling(element) {
-            var isScrolling = element.scrollHeight > element.offsetHeight;
-            element.setAttribute("scrolling", isScrolling ? "true" : "false");
-        }
-
-        function loaded() { 
-            openEventBridge(function() {
-                var allSections = [];
-                var elID = document.getElementById("property-id");
-                var elType = document.getElementById("property-type");
-                var elTypeIcon = document.getElementById("type-icon");
-                var elName = document.getElementById("property-name");
-                var elLocked = document.getElementById("property-locked");
-                var elVisible = document.getElementById("property-visible");
-                var elPositionX = document.getElementById("property-pos-x");
-                var elPositionY = document.getElementById("property-pos-y");
-                var elPositionZ = document.getElementById("property-pos-z");
-                var elMoveSelectionToGrid = document.getElementById("move-selection-to-grid");
-                var elMoveAllToGrid = document.getElementById("move-all-to-grid");
-    
-                var elDimensionsX = document.getElementById("property-dim-x");
-                var elDimensionsY = document.getElementById("property-dim-y");
-                var elDimensionsZ = document.getElementById("property-dim-z");
-                var elResetToNaturalDimensions = document.getElementById("reset-to-natural-dimensions");
-                var elRescaleDimensionsPct = document.getElementById("dimension-rescale-pct");
-                var elRescaleDimensionsButton = document.getElementById("dimension-rescale-button");
-    
-                var elParentID = document.getElementById("property-parent-id");
-                var elParentJointIndex = document.getElementById("property-parent-joint-index");
-    
-                var elRegistrationX = document.getElementById("property-reg-x");
-                var elRegistrationY = document.getElementById("property-reg-y");
-                var elRegistrationZ = document.getElementById("property-reg-z");
-    
-                var elRotationX = document.getElementById("property-rot-x");
-                var elRotationY = document.getElementById("property-rot-y");
-                var elRotationZ = document.getElementById("property-rot-z");
-    
-                var elLinearVelocityX = document.getElementById("property-lvel-x");
-                var elLinearVelocityY = document.getElementById("property-lvel-y");
-                var elLinearVelocityZ = document.getElementById("property-lvel-z");
-                var elLinearDamping = document.getElementById("property-ldamping");
-    
-                var elAngularVelocityX = document.getElementById("property-avel-x");
-                var elAngularVelocityY = document.getElementById("property-avel-y");
-                var elAngularVelocityZ = document.getElementById("property-avel-z");
-                var elAngularDamping = document.getElementById("property-adamping");
-    
-                var elRestitution = document.getElementById("property-restitution");
-                var elFriction = document.getElementById("property-friction");
-    
-                var elGravityX = document.getElementById("property-grav-x");
-                var elGravityY = document.getElementById("property-grav-y");
-                var elGravityZ = document.getElementById("property-grav-z");
-    
-                var elAccelerationX = document.getElementById("property-lacc-x");
-                var elAccelerationY = document.getElementById("property-lacc-y");
-                var elAccelerationZ = document.getElementById("property-lacc-z");
-    
-                var elDensity = document.getElementById("property-density");
-                var elCollisionless = document.getElementById("property-collisionless");
-                var elDynamic = document.getElementById("property-dynamic" );
-                var elCollideStatic = document.getElementById("property-collide-static");
-                var elCollideDynamic = document.getElementById("property-collide-dynamic");
-                var elCollideKinematic = document.getElementById("property-collide-kinematic");
-                var elCollideMyAvatar = document.getElementById("property-collide-myAvatar");
-                var elCollideOtherAvatar = document.getElementById("property-collide-otherAvatar");
-                var elCollisionSoundURL = document.getElementById("property-collision-sound-url");
-
-                var elGrabbable = document.getElementById("property-grabbable");
-                var elWantsTrigger = document.getElementById("property-wants-trigger");
-                var elIgnoreIK = document.getElementById("property-ignore-ik");
-
-                var elLifetime = document.getElementById("property-lifetime");
-                var elScriptURL = document.getElementById("property-script-url");
-                /*
-                FIXME: See FIXME for property-script-url.
-                var elScriptTimestamp = document.getElementById("property-script-timestamp");
-                */
-                var elReloadScriptButton = document.getElementById("reload-script-button");
-                var elUserData = document.getElementById("property-user-data");
-    
-                var elColorSections = document.querySelectorAll(".color-section");
-                var elColor = document.getElementById("property-color");
-                var elColorRed = document.getElementById("property-color-red");
-                var elColorGreen = document.getElementById("property-color-green");
-                var elColorBlue = document.getElementById("property-color-blue");
-    
-                var elShapeSections = document.querySelectorAll(".shape-section");
-                allSections.push(elShapeSections);
-                var elShape = document.getElementById("property-shape");
-                
-                var elLightSections = document.querySelectorAll(".light-section");
-                allSections.push(elLightSections);
-                var elLightSpotLight = document.getElementById("property-light-spot-light");
-                var elLightColor = document.getElementById("property-light-color");
-                var elLightColorRed = document.getElementById("property-light-color-red");
-                var elLightColorGreen = document.getElementById("property-light-color-green");
-                var elLightColorBlue = document.getElementById("property-light-color-blue");
-    
-                var elLightIntensity = document.getElementById("property-light-intensity");
-                var elLightFalloffRadius = document.getElementById("property-light-falloff-radius");
-                var elLightExponent = document.getElementById("property-light-exponent");
-                var elLightCutoff = document.getElementById("property-light-cutoff");
-    
-                var elModelSections = document.querySelectorAll(".model-section");
-                allSections.push(elModelSections);
-                var elModelURL = document.getElementById("property-model-url");
-                var elShapeType = document.getElementById("property-shape-type");
-                var elCompoundShapeURL = document.getElementById("property-compound-shape-url");
-                var elModelAnimationURL = document.getElementById("property-model-animation-url");
-                var elModelAnimationPlaying = document.getElementById("property-model-animation-playing");
-                var elModelAnimationFPS = document.getElementById("property-model-animation-fps");
-                var elModelAnimationFrame = document.getElementById("property-model-animation-frame");
-                var elModelAnimationFirstFrame = document.getElementById("property-model-animation-first-frame");
-                var elModelAnimationLastFrame = document.getElementById("property-model-animation-last-frame");
-                var elModelAnimationLoop = document.getElementById("property-model-animation-loop");
-                var elModelAnimationHold = document.getElementById("property-model-animation-hold");
-                var elModelTextures = document.getElementById("property-model-textures");
-                var elModelOriginalTextures = document.getElementById("property-model-original-textures");
-    
-                var elWebSections = document.querySelectorAll(".web-section");
-                allSections.push(elWebSections);
-                var elWebSourceURL = document.getElementById("property-web-source-url");
-
-                var elDescription = document.getElementById("property-description");
-
-                var elHyperlinkHref = document.getElementById("property-hyperlink-href");
-             
-                var elHyperlinkSections = document.querySelectorAll(".hyperlink-section");
-              
-    
-                var elTextSections = document.querySelectorAll(".text-section");
-                allSections.push(elTextSections);
-                var elTextText = document.getElementById("property-text-text");
-                var elTextLineHeight = document.getElementById("property-text-line-height");
-                var elTextTextColor = document.getElementById("property-text-text-color");
-                var elTextFaceCamera = document.getElementById("property-text-face-camera");
-                var elTextTextColorRed = document.getElementById("property-text-text-color-red");
-                var elTextTextColorGreen = document.getElementById("property-text-text-color-green");
-                var elTextTextColorBlue = document.getElementById("property-text-text-color-blue");
-                var elTextBackgroundColor = document.getElementById("property-text-background-color");
-                var elTextBackgroundColorRed = document.getElementById("property-text-background-color-red");
-                var elTextBackgroundColorGreen = document.getElementById("property-text-background-color-green");
-                var elTextBackgroundColorBlue = document.getElementById("property-text-background-color-blue");
-    
-                var elZoneSections = document.querySelectorAll(".zone-section");
-                allSections.push(elZoneSections);
-                var elZoneStageSunModelEnabled = document.getElementById("property-zone-stage-sun-model-enabled");
-    
-                var elZoneKeyLightColor = document.getElementById("property-zone-key-light-color");
-                var elZoneKeyLightColorRed = document.getElementById("property-zone-key-light-color-red");
-                var elZoneKeyLightColorGreen = document.getElementById("property-zone-key-light-color-green");
-                var elZoneKeyLightColorBlue = document.getElementById("property-zone-key-light-color-blue");
-                var elZoneKeyLightIntensity = document.getElementById("property-zone-key-intensity");
-                var elZoneKeyLightAmbientIntensity = document.getElementById("property-zone-key-ambient-intensity");
-                var elZoneKeyLightDirectionX = document.getElementById("property-zone-key-light-direction-x");
-                var elZoneKeyLightDirectionY = document.getElementById("property-zone-key-light-direction-y");
-                var elZoneKeyLightDirectionZ = document.getElementById("property-zone-key-light-direction-z");
-                var elZoneKeyLightAmbientURL = document.getElementById("property-zone-key-ambient-url");
-    
-                var elZoneStageLatitude = document.getElementById("property-zone-stage-latitude");
-                var elZoneStageLongitude = document.getElementById("property-zone-stage-longitude");
-                var elZoneStageAltitude = document.getElementById("property-zone-stage-altitude");
-                var elZoneStageAutomaticHourDay = document.getElementById("property-zone-stage-automatic-hour-day");
-                var elZoneStageDay = document.getElementById("property-zone-stage-day");
-                var elZoneStageHour = document.getElementById("property-zone-stage-hour");
-    
-                var elZoneBackgroundMode = document.getElementById("property-zone-background-mode");
-    
-                var elZoneSkyboxColor = document.getElementById("property-zone-skybox-color");
-                var elZoneSkyboxColorRed = document.getElementById("property-zone-skybox-color-red");
-                var elZoneSkyboxColorGreen = document.getElementById("property-zone-skybox-color-green");
-                var elZoneSkyboxColorBlue = document.getElementById("property-zone-skybox-color-blue");
-                var elZoneSkyboxURL = document.getElementById("property-zone-skybox-url");
-
-                var elZoneFlyingAllowed = document.getElementById("property-zone-flying-allowed");
-                var elZoneGhostingAllowed = document.getElementById("property-zone-ghosting-allowed");
-    
-                var elPolyVoxSections = document.querySelectorAll(".poly-vox-section");
-                allSections.push(elPolyVoxSections);
-                var elVoxelVolumeSizeX = document.getElementById("property-voxel-volume-size-x");
-                var elVoxelVolumeSizeY = document.getElementById("property-voxel-volume-size-y");
-                var elVoxelVolumeSizeZ = document.getElementById("property-voxel-volume-size-z");
-                var elVoxelSurfaceStyle = document.getElementById("property-voxel-surface-style");
-                var elXTextureURL = document.getElementById("property-x-texture-url");
-                var elYTextureURL = document.getElementById("property-y-texture-url");
-                var elZTextureURL = document.getElementById("property-z-texture-url");
-
-                var elPreviewCameraButton = document.getElementById("preview-camera-button");
-
-                if (window.EventBridge !== undefined) {
-                    var properties;
-                    EventBridge.scriptEventReceived.connect(function(data) {
-                        data = JSON.parse(data);
-                        if (data.type == "update") {
-                            if (data.selections.length == 0) {
-                                elTypeIcon.style.display = "none";
-                                elType.innerHTML = "<i>No selection</i>";
-                                elID.innerHTML = "";
-                                disableProperties();
-                            } else if (data.selections.length > 1) {
-                                var selections = data.selections;
-    
-                                var ids = [];
-                                var types = {};
-                                var numTypes = 0;
-    
-                                for (var i = 0; i < selections.length; i++) {
-                                    ids.push(selections[i].id);
-                                    var type = selections[i].properties.type;
-                                    if (types[type] === undefined) {
-                                        types[type] = 0;
-                                        numTypes += 1;
-                                    }
-                                    types[type]++;
-                                }
-
-                                var type;
-                                if (numTypes === 1) {
-                                    type = selections[0].properties.type;
-                                } else {
-                                    type = "Multiple";
-                                }
-                                elType.innerHTML = type + " (" + data.selections.length + ")";
-                                elTypeIcon.innerHTML = ICON_FOR_TYPE[type];
-                                elTypeIcon.style.display = "inline-block";
-
-                                elID.innerHTML = ids.join("<br>");
-
-                                disableProperties();
-                            } else {
-            
-    
-                                properties = data.selections[0].properties;
-
-                                elID.innerHTML = properties.id;
-    
-                                elType.innerHTML = properties.type;
-                                elTypeIcon.innerHTML = ICON_FOR_TYPE[properties.type];
-                                elTypeIcon.style.display = "inline-block";
-
-                                elLocked.checked = properties.locked;
-    
-                                if (properties.locked) {
-                                    disableProperties();
-                                    elLocked.removeAttribute('disabled');
-                                } else {
-                                    enableProperties();
-                                }
-    
-                                elName.value = properties.name;
-    
-                                elVisible.checked = properties.visible;
-    
-                                elPositionX.value = properties.position.x.toFixed(4);
-                                elPositionY.value = properties.position.y.toFixed(4);
-                                elPositionZ.value = properties.position.z.toFixed(4);
-    
-                                elDimensionsX.value = properties.dimensions.x.toFixed(4);
-                                elDimensionsY.value = properties.dimensions.y.toFixed(4);
-                                elDimensionsZ.value = properties.dimensions.z.toFixed(4);
-    
-                                elParentID.value = properties.parentID;
-                                elParentJointIndex.value = properties.parentJointIndex;
-    
-                                elRegistrationX.value = properties.registrationPoint.x.toFixed(4);
-                                elRegistrationY.value = properties.registrationPoint.y.toFixed(4);
-                                elRegistrationZ.value = properties.registrationPoint.z.toFixed(4);
-    
-                                elRotationX.value = properties.rotation.x.toFixed(4);
-                                elRotationY.value = properties.rotation.y.toFixed(4);
-                                elRotationZ.value = properties.rotation.z.toFixed(4);
-    
-                                elLinearVelocityX.value = properties.velocity.x.toFixed(4);
-                                elLinearVelocityY.value = properties.velocity.y.toFixed(4);
-                                elLinearVelocityZ.value = properties.velocity.z.toFixed(4);
-                                elLinearDamping.value = properties.damping.toFixed(2);
-    
-                                elAngularVelocityX.value = (properties.angularVelocity.x * RADIANS_TO_DEGREES).toFixed(4);
-                                elAngularVelocityY.value = (properties.angularVelocity.y * RADIANS_TO_DEGREES).toFixed(4);
-                                elAngularVelocityZ.value = (properties.angularVelocity.z * RADIANS_TO_DEGREES).toFixed(4);
-                                elAngularDamping.value = properties.angularDamping.toFixed(4);
-    
-                                elRestitution.value = properties.restitution.toFixed(4);
-                                elFriction.value = properties.friction.toFixed(4);
-    
-                                elGravityX.value = properties.gravity.x.toFixed(4);
-                                elGravityY.value = properties.gravity.y.toFixed(4);
-                                elGravityZ.value = properties.gravity.z.toFixed(4);
-    
-                                elAccelerationX.value = properties.acceleration.x.toFixed(4);
-                                elAccelerationY.value = properties.acceleration.y.toFixed(4);
-                                elAccelerationZ.value = properties.acceleration.z.toFixed(4);
-    
-                                elDensity.value = properties.density.toFixed(4);
-                                elCollisionless.checked = properties.collisionless;
-                                elDynamic.checked = properties.dynamic;
-
-                                elCollideStatic.checked = properties.collidesWith.indexOf("static") > -1;
-                                elCollideKinematic.checked = properties.collidesWith.indexOf("kinematic") > -1;
-                                elCollideDynamic.checked = properties.collidesWith.indexOf("dynamic") > -1;
-                                elCollideMyAvatar.checked = properties.collidesWith.indexOf("myAvatar") > -1;
-                                elCollideOtherAvatar.checked = properties.collidesWith.indexOf("otherAvatar") > -1;
-
-                                elGrabbable.checked = properties.dynamic;
-                                elWantsTrigger.checked = false;
-                                elIgnoreIK.checked = false;
-                                var parsedUserData = {}
-                                try {
-                                    parsedUserData = JSON.parse(properties.userData);
-                                                            
-                                    if ("grabbableKey" in parsedUserData) {
-                                        if ("grabbable" in parsedUserData["grabbableKey"]) {
-                                            elGrabbable.checked = parsedUserData["grabbableKey"].grabbable;
-                                        }
-                                        if ("wantsTrigger" in parsedUserData["grabbableKey"]) {
-                                            elWantsTrigger.checked = parsedUserData["grabbableKey"].wantsTrigger;
-                                        }
-                                        if ("ignoreIK" in parsedUserData["grabbableKey"]) {
-                                            elIgnoreIK.checked = parsedUserData["grabbableKey"].ignoreIK;
-                                        }
-                                    }
-                                } catch(e) {}
-
-                                elCollisionSoundURL.value = properties.collisionSoundURL;
-                                elLifetime.value = properties.lifetime;
-                                elScriptURL.value = properties.script;
-                                /*
-                                FIXME: See FIXME for property-script-url.
-                                elScriptTimestamp.value = properties.scriptTimestamp;
-                                */
-                                elUserData.value = properties.userData;
-                                setTextareaScrolling(elUserData);
-    
-                                elHyperlinkHref.value = properties.href;
-                                elDescription.value = properties.description;
-    
-                                for (var i = 0; i < allSections.length; i++) {
-                                    for (var j = 0; j < allSections[i].length; j++) {
-                                        allSections[i][j].style.display = 'none';
-                                    }
-                                }                                    
-
-                                for (var i = 0; i < elHyperlinkSections.length; i++) {
-                                    elHyperlinkSections[i].style.display = 'table';
-                                }
-
-                                if (properties.type == "Shape" || properties.type == "Box" || properties.type == "Sphere") {
-                                    for (var i = 0; i < elShapeSections.length; i++) {
-                                        elShapeSections[i].style.display = 'table';
-                                    }
-                                    elShape.value = properties.shape;
-                                    setDropdownText(elShape);
-
-                                } else {
-                                    for (var i = 0; i < elShapeSections.length; i++) {
-                                        elShapeSections[i].style.display = 'none';
-                                    }
-                                }
-                                
-                                if (properties.type == "Shape" || properties.type == "Box" || properties.type == "Sphere" || properties.type == "ParticleEffect") {
-                                    for (var i = 0; i < elColorSections.length; i++) {
-                                        elColorSections[i].style.display = 'table';
-                                    }
-                                    elColorRed.value = properties.color.red;
-                                    elColorGreen.value = properties.color.green;
-                                    elColorBlue.value = properties.color.blue;
-                                    elColor.style.backgroundColor = "rgb(" + properties.color.red  + "," + properties.color.green + "," + properties.color.blue + ")";
-                                } else {
-                                    for (var i = 0; i < elColorSections.length; i++) {
-                                        elColorSections[i].style.display = 'none';
-                                    }
-                                }
-    
-                                if (properties.type == "Model") {
-                                    for (var i = 0; i < elModelSections.length; i++) {
-                                        elModelSections[i].style.display = 'table';
-                                    }
-    
-                                    elModelURL.value = properties.modelURL;
-                                    elShapeType.value = properties.shapeType;
-                                    setDropdownText(elShapeType);
-                                    elCompoundShapeURL.value = properties.compoundShapeURL;
-                                    elModelAnimationURL.value = properties.animation.url;
-                                    elModelAnimationPlaying.checked = properties.animation.running;
-                                    elModelAnimationFPS.value = properties.animation.fps;
-                                    elModelAnimationFrame.value = properties.animation.currentFrame;
-                                    elModelAnimationFirstFrame.value = properties.animation.firstFrame;
-                                    elModelAnimationLastFrame.value = properties.animation.lastFrame;
-                                    elModelAnimationLoop.checked = properties.animation.loop;
-                                    elModelAnimationHold.checked = properties.animation.hold;
-                                    elModelTextures.value = properties.textures;
-                                    setTextareaScrolling(elModelTextures);
-                                    elModelOriginalTextures.value = properties.originalTextures;
-                                    setTextareaScrolling(elModelOriginalTextures);
-                                } else if (properties.type == "Web") {
-                                    for (var i = 0; i < elWebSections.length; i++) {
-                                        elWebSections[i].style.display = 'table';
-                                    }
-                                    for (var i = 0; i < elHyperlinkSections.length; i++) {
-                                        elHyperlinkSections[i].style.display = 'none';
-                                    }
-    
-                                    elWebSourceURL.value = properties.sourceUrl;
-                                } else if (properties.type == "Text") {
-                                    for (var i = 0; i < elTextSections.length; i++) {
-                                        elTextSections[i].style.display = 'table';
-                                    }
-    
-                                    elTextText.value = properties.text;
-                                    elTextLineHeight.value = properties.lineHeight.toFixed(4);
-                                    elTextFaceCamera = properties.faceCamera;
-                                    elTextTextColor.style.backgroundColor = "rgb(" + properties.textColor.red  + "," + properties.textColor.green + "," + properties.textColor.blue + ")";
-                                    elTextTextColorRed.value = properties.textColor.red;
-                                    elTextTextColorGreen.value = properties.textColor.green;
-                                    elTextTextColorBlue.value = properties.textColor.blue;
-                                    elTextBackgroundColorRed.value = properties.backgroundColor.red;
-                                    elTextBackgroundColorGreen.value = properties.backgroundColor.green;
-                                    elTextBackgroundColorBlue.value = properties.backgroundColor.blue;
-                                } else if (properties.type == "Light") {
-                                    for (var i = 0; i < elLightSections.length; i++) {
-                                        elLightSections[i].style.display = 'table';
-                                    }
-    
-                                    elLightSpotLight.checked = properties.isSpotlight;
-    
-                                    elLightColor.style.backgroundColor = "rgb(" + properties.color.red  + "," + properties.color.green + "," + properties.color.blue + ")";
-                                    elLightColorRed.value = properties.color.red;
-                                    elLightColorGreen.value = properties.color.green;
-                                    elLightColorBlue.value = properties.color.blue;
-    
-                                    elLightIntensity.value = properties.intensity.toFixed(1);
-                                    elLightFalloffRadius.value = properties.falloffRadius.toFixed(1);
-                                    elLightExponent.value = properties.exponent.toFixed(2);
-                                    elLightCutoff.value = properties.cutoff.toFixed(2);
-                                } else if (properties.type == "Zone") {
-                                    for (var i = 0; i < elZoneSections.length; i++) {
-                                        elZoneSections[i].style.display = 'table';
-                                    }
-    
-                                    elZoneStageSunModelEnabled.checked = properties.stage.sunModelEnabled;
-                                    elZoneKeyLightColor.style.backgroundColor = "rgb(" + properties.keyLight.color.red  + "," + properties.keyLight.color.green + "," + properties.keyLight.color.blue + ")";
-                                    elZoneKeyLightColorRed.value = properties.keyLight.color.red;
-                                    elZoneKeyLightColorGreen.value = properties.keyLight.color.green;
-                                    elZoneKeyLightColorBlue.value = properties.keyLight.color.blue;
-                                    elZoneKeyLightIntensity.value = properties.keyLight.intensity.toFixed(2);
-                                    elZoneKeyLightAmbientIntensity.value = properties.keyLight.ambientIntensity.toFixed(2);
-                                    elZoneKeyLightDirectionX.value = properties.keyLight.direction.x.toFixed(2);
-                                    elZoneKeyLightDirectionY.value = properties.keyLight.direction.y.toFixed(2);
-                                    elZoneKeyLightAmbientURL.value = properties.keyLight.ambientURL;
-    
-    
-                                    elZoneStageLatitude.value = properties.stage.latitude.toFixed(2);
-                                    elZoneStageLongitude.value = properties.stage.longitude.toFixed(2);
-                                    elZoneStageAltitude.value = properties.stage.altitude.toFixed(2);
-                                    elZoneStageAutomaticHourDay.checked = properties.stage.automaticHourDay;
-                                    elZoneStageDay.value = properties.stage.day;
-                                    elZoneStageHour.value = properties.stage.hour;
-                                    elShapeType.value = properties.shapeType;
-                                    elCompoundShapeURL.value = properties.compoundShapeURL;
-    
-                                    elZoneBackgroundMode.value = properties.backgroundMode;
-                                    setDropdownText(elZoneBackgroundMode);
-    
-                                    elZoneSkyboxColor.style.backgroundColor = "rgb(" + properties.skybox.color.red  + "," + properties.skybox.color.green + "," + properties.skybox.color.blue + ")";
-                                    elZoneSkyboxColorRed.value = properties.skybox.color.red;
-                                    elZoneSkyboxColorGreen.value = properties.skybox.color.green;
-                                    elZoneSkyboxColorBlue.value = properties.skybox.color.blue;
-                                    elZoneSkyboxURL.value = properties.skybox.url;
-
-                                    elZoneFlyingAllowed.checked = properties.flyingAllowed;
-                                    elZoneGhostingAllowed.checked = properties.ghostingAllowed;
-    
-                                    showElements(document.getElementsByClassName('skybox-section'), elZoneBackgroundMode.value == 'skybox');
-                                } else if (properties.type == "PolyVox") {
-                                    for (var i = 0; i < elPolyVoxSections.length; i++) {
-                                        elPolyVoxSections[i].style.display = 'table';
-                                    }
-    
-                                    elVoxelVolumeSizeX.value = properties.voxelVolumeSize.x.toFixed(2);
-                                    elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2);
-                                    elVoxelVolumeSizeZ.value = properties.voxelVolumeSize.z.toFixed(2);
-                                    elVoxelSurfaceStyle.value = properties.voxelSurfaceStyle;
-                                    setDropdownText(elVoxelSurfaceStyle);
-                                    elXTextureURL.value = properties.xTextureURL;
-                                    elYTextureURL.value = properties.yTextureURL;
-                                    elZTextureURL.value = properties.zTextureURL;
-                                }
-                                
-                                var activeElement = document.activeElement;
-                                
-                                if(typeof activeElement.select!=="undefined"){
-                                    activeElement.select();
-                                }
-                            }
-                        }
-                    });
-                }
-    
-                elLocked.addEventListener('change', createEmitCheckedPropertyUpdateFunction('locked'));
-                elName.addEventListener('change', createEmitTextPropertyUpdateFunction('name'));
-                elHyperlinkHref.addEventListener('change', createEmitTextPropertyUpdateFunction('href'));
-                elDescription.addEventListener('change', createEmitTextPropertyUpdateFunction('description'));
-                elVisible.addEventListener('change', createEmitCheckedPropertyUpdateFunction('visible'));
-    
-                var positionChangeFunction = createEmitVec3PropertyUpdateFunction(
-                    'position', elPositionX, elPositionY, elPositionZ);
-                elPositionX.addEventListener('change', positionChangeFunction);
-                elPositionY.addEventListener('change', positionChangeFunction);
-                elPositionZ.addEventListener('change', positionChangeFunction);
-    
-                var dimensionsChangeFunction = createEmitVec3PropertyUpdateFunction(
-                    'dimensions', elDimensionsX, elDimensionsY, elDimensionsZ);
-                elDimensionsX.addEventListener('change', dimensionsChangeFunction);
-                elDimensionsY.addEventListener('change', dimensionsChangeFunction);
-                elDimensionsZ.addEventListener('change', dimensionsChangeFunction);
-    
-                elParentID.addEventListener('change', createEmitTextPropertyUpdateFunction('parentID'));
-                elParentJointIndex.addEventListener('change', createEmitNumberPropertyUpdateFunction('parentJointIndex'));
-    
-                var registrationChangeFunction = createEmitVec3PropertyUpdateFunction(
-                    'registrationPoint', elRegistrationX, elRegistrationY, elRegistrationZ);
-                elRegistrationX.addEventListener('change', registrationChangeFunction);
-                elRegistrationY.addEventListener('change', registrationChangeFunction);
-                elRegistrationZ.addEventListener('change', registrationChangeFunction);
-    
-                var rotationChangeFunction = createEmitVec3PropertyUpdateFunction(
-                    'rotation', elRotationX, elRotationY, elRotationZ);
-                elRotationX.addEventListener('change', rotationChangeFunction);
-                elRotationY.addEventListener('change', rotationChangeFunction);
-                elRotationZ.addEventListener('change', rotationChangeFunction);
-    
-                var velocityChangeFunction = createEmitVec3PropertyUpdateFunction(
-                    'velocity', elLinearVelocityX, elLinearVelocityY, elLinearVelocityZ);
-                elLinearVelocityX.addEventListener('change', velocityChangeFunction);
-                elLinearVelocityY.addEventListener('change', velocityChangeFunction);
-                elLinearVelocityZ.addEventListener('change', velocityChangeFunction);
-                elLinearDamping.addEventListener('change', createEmitNumberPropertyUpdateFunction('damping'));
-    
-                var angularVelocityChangeFunction = createEmitVec3PropertyUpdateFunctionWithMultiplier(
-                    'angularVelocity', elAngularVelocityX, elAngularVelocityY, elAngularVelocityZ, DEGREES_TO_RADIANS);
-                elAngularVelocityX.addEventListener('change', angularVelocityChangeFunction);
-                elAngularVelocityY.addEventListener('change', angularVelocityChangeFunction);
-                elAngularVelocityZ.addEventListener('change', angularVelocityChangeFunction);
-                elAngularDamping.addEventListener('change', createEmitNumberPropertyUpdateFunction('angularDamping'));
-    
-                elRestitution.addEventListener('change', createEmitNumberPropertyUpdateFunction('restitution'));
-                elFriction.addEventListener('change', createEmitNumberPropertyUpdateFunction('friction'));
-    
-                var gravityChangeFunction = createEmitVec3PropertyUpdateFunction(
-                    'gravity', elGravityX, elGravityY, elGravityZ);
-                elGravityX.addEventListener('change', gravityChangeFunction);
-                elGravityY.addEventListener('change', gravityChangeFunction);
-                elGravityZ.addEventListener('change', gravityChangeFunction);
-    
-                var accelerationChangeFunction = createEmitVec3PropertyUpdateFunction(
-                    'acceleration', elAccelerationX, elAccelerationY, elAccelerationZ);
-                elAccelerationX.addEventListener('change', accelerationChangeFunction);
-                elAccelerationY.addEventListener('change', accelerationChangeFunction);
-                elAccelerationZ.addEventListener('change', accelerationChangeFunction);
-    
-                elDensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('density'));
-                elCollisionless.addEventListener('change', createEmitCheckedPropertyUpdateFunction('collisionless'));
-                elDynamic.addEventListener('change', createEmitCheckedPropertyUpdateFunction('dynamic'));
-
-                elCollideDynamic.addEventListener('change', function() {
-                    updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideDynamic, 'dynamic');
-                });
-
-                elCollideKinematic.addEventListener('change', function() {
-                    updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideKinematic, 'kinematic');
-                });
-
-                elCollideStatic.addEventListener('change', function() {
-                    updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideStatic, 'static');
-                });
-                elCollideMyAvatar.addEventListener('change', function() {
-                    updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideMyAvatar, 'myAvatar');
-                });
-                elCollideOtherAvatar.addEventListener('change', function() {
-                    updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideOtherAvatar, 'otherAvatar');
-                });
-
-                elGrabbable.addEventListener('change', function() {
-                    userDataChanger("grabbableKey", "grabbable", elGrabbable, elUserData, properties.dynamic);
-                });
-                elWantsTrigger.addEventListener('change', function() {
-                    userDataChanger("grabbableKey", "wantsTrigger", elWantsTrigger, elUserData, false);
-                });
-                elIgnoreIK.addEventListener('change', function() {
-                    userDataChanger("grabbableKey", "ignoreIK", elIgnoreIK, elUserData, false);
-                });
-
-                elCollisionSoundURL.addEventListener('change', createEmitTextPropertyUpdateFunction('collisionSoundURL'));
-    
-                elLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifetime'));
-                elScriptURL.addEventListener('change', createEmitTextPropertyUpdateFunction('script'));
-                /*
-                FIXME: See FIXME for property-script-url.
-                elScriptTimestamp.addEventListener('change', createEmitNumberPropertyUpdateFunction('scriptTimestamp'));
-                */
-                elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData'));
-    
-                var colorChangeFunction = createEmitColorPropertyUpdateFunction(
-                    'color', elColorRed, elColorGreen, elColorBlue);
-                elColorRed.addEventListener('change', colorChangeFunction);
-                elColorGreen.addEventListener('change', colorChangeFunction);
-                elColorBlue.addEventListener('change', colorChangeFunction);
-                colorPickers.push($('#property-color').colpick({
-                    colorScheme: 'dark',
-                    layout: 'hex',
-                    color: '000000',
-                    onShow: function (colpick) {
-                        $('#property-color').attr('active', 'true');
-                    },
-                    onHide: function (colpick) {
-                        $('#property-color').attr('active', 'false');
-                    },
-                    onSubmit: function (hsb, hex, rgb, el) {
-                        $(el).css('background-color', '#' + hex);
-                        $(el).colpickHide();
-                        emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b);
-                    }
-                }));
-
-                elLightSpotLight.addEventListener('change', createEmitCheckedPropertyUpdateFunction('isSpotlight'));
-    
-                var lightColorChangeFunction = createEmitColorPropertyUpdateFunction(
-                    'color', elLightColorRed, elLightColorGreen, elLightColorBlue);
-                elLightColorRed.addEventListener('change', lightColorChangeFunction);
-                elLightColorGreen.addEventListener('change', lightColorChangeFunction);
-                elLightColorBlue.addEventListener('change', lightColorChangeFunction);
-                colorPickers.push($('#property-light-color').colpick({
-                    colorScheme: 'dark',
-                    layout: 'hex',
-                    color: '000000',
-                    onShow: function (colpick) {
-                        $('#property-light-color').attr('active', 'true');
-                    },
-                    onHide: function (colpick) {
-                        $('#property-light-color').attr('active', 'false');
-                    },
-                    onSubmit: function (hsb, hex, rgb, el) {
-                        $(el).css('background-color', '#' + hex);
-                        $(el).colpickHide();
-                        emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b);
-                    }
-                }));
-    
-                elLightIntensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('intensity', 1));
-                elLightFalloffRadius.addEventListener('change', createEmitNumberPropertyUpdateFunction('falloffRadius', 1));
-                elLightExponent.addEventListener('change', createEmitNumberPropertyUpdateFunction('exponent', 2));
-                elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff', 2));
-    
-                elShape.addEventListener('change', createEmitTextPropertyUpdateFunction('shape'));
-
-                elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl'));
-    
-                elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL'));
-                elShapeType.addEventListener('change', createEmitTextPropertyUpdateFunction('shapeType'));
-                elCompoundShapeURL.addEventListener('change', createEmitTextPropertyUpdateFunction('compoundShapeURL'));
-    
-                elModelAnimationURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('animation', 'url'));
-                elModelAnimationPlaying.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation','running'));
-                elModelAnimationFPS.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation','fps'));
-                elModelAnimationFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'currentFrame'));
-                elModelAnimationFirstFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'firstFrame'));
-                elModelAnimationLastFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'lastFrame'));
-                elModelAnimationLoop.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'loop'));
-                elModelAnimationHold.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'hold'));
-    
-                elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures'));
-    
-                elTextText.addEventListener('change', createEmitTextPropertyUpdateFunction('text'));
-                elTextFaceCamera.addEventListener('change', createEmitCheckedPropertyUpdateFunction('faceCamera'));
-                elTextLineHeight.addEventListener('change', createEmitNumberPropertyUpdateFunction('lineHeight'));
-                var textTextColorChangeFunction = createEmitColorPropertyUpdateFunction(
-                    'textColor', elTextTextColorRed, elTextTextColorGreen, elTextTextColorBlue);
-                elTextTextColorRed.addEventListener('change', textTextColorChangeFunction);
-                elTextTextColorGreen.addEventListener('change', textTextColorChangeFunction);
-                elTextTextColorBlue.addEventListener('change', textTextColorChangeFunction);
-                colorPickers.push($('#property-text-text-color').colpick({
-                    colorScheme:'dark',
-                    layout:'hex',
-                    color: '000000',
-                    onShow: function (colpick) {
-                        $('#property-text-text-color').attr('active', 'true');
-                    },
-                    onHide: function (colpick) {
-                        $('#property-text-text-color').attr('active', 'false');
-                    },
-                    onSubmit: function (hsb, hex, rgb, el) {
-                        $(el).css('background-color', '#'+hex);
-                        $(el).colpickHide();
-                        $(el).attr('active', 'false');
-                        emitColorPropertyUpdate('textColor', rgb.r, rgb.g, rgb.b);
-                    }
-                }));
-    
-                var textBackgroundColorChangeFunction = createEmitColorPropertyUpdateFunction(
-                    'backgroundColor', elTextBackgroundColorRed, elTextBackgroundColorGreen, elTextBackgroundColorBlue);
-                elTextBackgroundColorRed.addEventListener('change', textBackgroundColorChangeFunction);
-                elTextBackgroundColorGreen.addEventListener('change', textBackgroundColorChangeFunction);
-                elTextBackgroundColorBlue.addEventListener('change', textBackgroundColorChangeFunction);
-                colorPickers.push($('#property-text-background-color').colpick({
-                    colorScheme:'dark',
-                    layout:'hex',
-                    color:'000000',
-                    onShow: function (colpick) {
-                        $('#property-text-background-color').attr('active', 'true');
-                    },
-                    onHide: function (colpick) {
-                        $('#property-text-background-color').attr('active', 'false');
-                    },
-                    onSubmit: function (hsb, hex, rgb, el) {
-                        $(el).css('background-color', '#'+hex);
-                        $(el).colpickHide();
-                        emitColorPropertyUpdate('backgroundColor', rgb.r, rgb.g, rgb.b);
-                    }
-                }));
-    
-                elZoneStageSunModelEnabled.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','sunModelEnabled'));
-                colorPickers.push($('#property-zone-key-light-color').colpick({
-                    colorScheme:'dark',
-                    layout:'hex',
-                    color:'000000',
-                    onShow: function (colpick) {
-                        $('#property-zone-key-light-color').attr('active', 'true');
-                    },
-                    onHide: function (colpick) {
-                        $('#property-zone-key-light-color').attr('active', 'false');
-                    },
-                    onSubmit: function (hsb, hex, rgb, el) {
-                        $(el).css('background-color', '#'+hex);
-                        $(el).colpickHide();
-                        emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'keyLight');
-                    }
-                }));
-                var zoneKeyLightColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('keyLight','color', elZoneKeyLightColorRed, elZoneKeyLightColorGreen, elZoneKeyLightColorBlue);
-                elZoneKeyLightColorRed.addEventListener('change', zoneKeyLightColorChangeFunction);
-                elZoneKeyLightColorGreen.addEventListener('change', zoneKeyLightColorChangeFunction);
-                elZoneKeyLightColorBlue.addEventListener('change', zoneKeyLightColorChangeFunction);
-                elZoneKeyLightIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','intensity'));
-                elZoneKeyLightAmbientIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','ambientIntensity'));
-                elZoneKeyLightAmbientURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('keyLight','ambientURL'));
-                var zoneKeyLightDirectionChangeFunction = createEmitGroupVec3PropertyUpdateFunction('keyLight','direction', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY);
-                elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction);
-                elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction);
-
-                elZoneStageLatitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','latitude'));
-                elZoneStageLongitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','longitude'));
-                elZoneStageAltitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','altitude'));
-                elZoneStageAutomaticHourDay.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','automaticHourDay'));
-                elZoneStageDay.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','day'));
-                elZoneStageHour.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','hour'));
-    
-    
-                elZoneBackgroundMode.addEventListener('change', createEmitTextPropertyUpdateFunction('backgroundMode'));
-                var zoneSkyboxColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('skybox','color',
-                    elZoneSkyboxColorRed, elZoneSkyboxColorGreen, elZoneSkyboxColorBlue);
-                elZoneSkyboxColorRed.addEventListener('change', zoneSkyboxColorChangeFunction);
-                elZoneSkyboxColorGreen.addEventListener('change', zoneSkyboxColorChangeFunction);
-                elZoneSkyboxColorBlue.addEventListener('change', zoneSkyboxColorChangeFunction);
-                colorPickers.push($('#property-zone-skybox-color').colpick({
-                    colorScheme:'dark',
-                    layout:'hex',
-                    color:'000000',
-                    onShow: function (colpick) {
-                        $('#property-zone-skybox-color').attr('active', 'true');
-                    },
-                    onHide: function (colpick) {
-                        $('#property-zone-skybox-color').attr('active', 'false');
-                    },
-                    onSubmit: function (hsb, hex, rgb, el) {
-                        $(el).css('background-color', '#'+hex);
-                        $(el).colpickHide();
-                        emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'skybox');
-                    }
-                }));
-    
-                elZoneSkyboxURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('skybox','url'));
-
-                elZoneFlyingAllowed.addEventListener('change', createEmitCheckedPropertyUpdateFunction('flyingAllowed'));
-                elZoneGhostingAllowed.addEventListener('change', createEmitCheckedPropertyUpdateFunction('ghostingAllowed'));
-
-                var voxelVolumeSizeChangeFunction = createEmitVec3PropertyUpdateFunction(
-                    'voxelVolumeSize', elVoxelVolumeSizeX, elVoxelVolumeSizeY, elVoxelVolumeSizeZ);
-                elVoxelVolumeSizeX.addEventListener('change', voxelVolumeSizeChangeFunction);
-                elVoxelVolumeSizeY.addEventListener('change', voxelVolumeSizeChangeFunction);
-                elVoxelVolumeSizeZ.addEventListener('change', voxelVolumeSizeChangeFunction);
-                elVoxelSurfaceStyle.addEventListener('change', createEmitTextPropertyUpdateFunction('voxelSurfaceStyle'));
-                elXTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('xTextureURL'));
-                elYTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('yTextureURL'));
-                elZTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('zTextureURL'));
-    
-                elMoveSelectionToGrid.addEventListener("click", function() {
-                    EventBridge.emitWebEvent(JSON.stringify({
-                        type: "action",
-                        action: "moveSelectionToGrid",
-                    }));
-                });
-                elMoveAllToGrid.addEventListener("click", function() {
-                    EventBridge.emitWebEvent(JSON.stringify({
-                        type: "action",
-                        action: "moveAllToGrid",
-                    }));
-                });
-                elResetToNaturalDimensions.addEventListener("click", function() {
-                    EventBridge.emitWebEvent(JSON.stringify({
-                        type: "action",
-                        action: "resetToNaturalDimensions",
-                    }));
-                });
-                elRescaleDimensionsButton.addEventListener("click", function() {
-                    EventBridge.emitWebEvent(JSON.stringify({
-                        type: "action",
-                        action: "rescaleDimensions",
-                        percentage: parseInt(elRescaleDimensionsPct.value),
-                    }));
-                });
-                /*
-                FIXME: See FIXME for property-script-url.
-                elReloadScriptButton.addEventListener("click", function() {
-                    EventBridge.emitWebEvent(JSON.stringify({
-                        type: "action",
-                        action: "reloadScript"
-                    }));
-                });
-                */
-                elPreviewCameraButton.addEventListener("click", function() {
-                    EventBridge.emitWebEvent(JSON.stringify({
-                        type: "action",
-                        action: "previewCamera"
-                    }));
-                });
-
-                window.onblur = function() {
-                    // Fake a change event
-                    var ev = document.createEvent("HTMLEvents");
-                    ev.initEvent("change", true, true);
-                    document.activeElement.dispatchEvent(ev);
-                }
-    
-                // For input and textarea elements, select all of the text on focus
-                // WebKit-based browsers, such as is used with QWebView, have a quirk
-                // where the mouseup event comes after the focus event, causing the
-                // text to be deselected immediately after selecting all of the text.
-                // To make this work we block the first mouseup event after the elements
-                // received focus.  If we block all mouseup events the user will not
-                // be able to click within the selected text.
-                // We also check to see if the value has changed to make sure we aren't
-                // blocking a mouse-up event when clicking on an input spinner.
-                var els = document.querySelectorAll("input, textarea");
-                for (var i = 0; i < els.length; i++) {
-                    var clicked = false;
-                    var originalText;
-                    els[i].onfocus = function(e) {
-                        originalText = this.value;
-                        this.select();
-                        clicked = false;
-                    };
-                    els[i].onmouseup = function(e) {
-                        if (!clicked && originalText == this.value) {
-                            e.preventDefault();
-                        }
-                        clicked = true;
-                    };
-                }
-            });
-
-            // Collapsible sections
-            var elCollapsible = document.getElementsByClassName("section-header");
-
-            var toggleCollapsedEvent = function (event) {
-                var element = event.target;
-                if (element.nodeName !== "DIV") {
-                    element = element.parentNode;
-                }
-                var isCollapsed = element.getAttribute("collapsed") !== "true";
-                element.setAttribute("collapsed", isCollapsed ? "true" : "false");
-                element.getElementsByTagName("span")[0].textContent = isCollapsed ? "L" : "M";
-            };
-
-            for (var i = 0, length = elCollapsible.length; i < length; i++) {
-                var element = elCollapsible[i];
-                element.addEventListener("click", toggleCollapsedEvent, true);
-            };
-
-
-            // Textarea scrollbars
-            var elTextareas = document.getElementsByTagName("TEXTAREA");
-
-            var textareaOnChangeEvent = function (event) {
-                setTextareaScrolling(event.target);
-            }
-
-            for (var i = 0, length = elTextareas.length; i < length; i++) {
-                var element = elTextareas[i];
-                setTextareaScrolling(element);
-                element.addEventListener("input", textareaOnChangeEvent, false);
-                element.addEventListener("change", textareaOnChangeEvent, false);
-                /* FIXME: Detect and update textarea scrolling attribute on resize. Unfortunately textarea doesn't have a resize
-                event; mouseup is a partial stand-in but doesn't handle resizing if mouse moves outside textarea rectangle. */
-                element.addEventListener("mouseup", textareaOnChangeEvent, false);
-            };
-
-            // Dropdowns
-            // For each dropdown the following replacement is created in place of the oriringal dropdown...
-            // Structure created:
-            //  <dl dropped="true/false">
-            //      <dt name="?" id="?" value="?"><span>display text</span><span>carat</span></dt>
-            //      <dd>
-            //          <ul>
-            //              <li value="??>display text</li>
-            //              <li>...</li>
-            //          </ul>
-            //      </dd>
-            //  </dl>
-
-            function setDropdownText(dropdown) {
-                var lis = dropdown.parentNode.getElementsByTagName("li");
-                var text = "";
-                for (var i = 0; i < lis.length; i++) {
-                    if (lis[i].getAttribute("value") === dropdown.value) {
-                        text = lis[i].textContent;
-                    }
-                }
-                dropdown.firstChild.textContent = text;
-            }
-
-            function toggleDropdown(event) {
-                var element = event.target;
-                if (element.nodeName !== "DT") {
-                    element = element.parentNode;
-                }
-                element = element.parentNode;
-                var isDropped = element.getAttribute("dropped");
-                element.setAttribute("dropped", isDropped !== "true" ? "true" : "false");
-            }
-
-            function setDropdownValue(event) {
-                var dt = event.target.parentNode.parentNode.previousSibling;
-                dt.value = event.target.getAttribute("value");
-                dt.firstChild.textContent = event.target.textContent;
-
-                dt.parentNode.setAttribute("dropped", "false");
-
-                var evt = document.createEvent("HTMLEvents");
-                evt.initEvent("change", true, true);
-                dt.dispatchEvent(evt);
-            }
-
-            var elDropdowns = document.getElementsByTagName("select");
-            for (var i = 0; i < elDropdowns.length; i++) {
-                var options = elDropdowns[i].getElementsByTagName("option");
-                var selectedOption = 0;
-                for (var j = 0; j < options.length; j++) {
-                    if (options[j].getAttribute("selected") === "selected") {
-                        selectedOption = j;
-                    }
-                }
-                var div = elDropdowns[i].parentNode;
-
-                var dl = document.createElement("dl");
-                div.appendChild(dl);
-
-                var dt = document.createElement("dt");
-                dt.name = elDropdowns[i].name;
-                dt.id = elDropdowns[i].id;
-                dt.addEventListener("click", toggleDropdown, true);
-                dl.appendChild(dt);
-
-                var span = document.createElement("span");
-                span.setAttribute("value", options[selectedOption].value);
-                span.textContent = options[selectedOption].firstChild.textContent;
-                dt.appendChild(span);
-
-                var span = document.createElement("span");
-                span.textContent = "5";  // caratDn
-                dt.appendChild(span);
-
-                var dd = document.createElement("dd");
-                dl.appendChild(dd);
-
-                var ul = document.createElement("ul");
-                dd.appendChild(ul);
-
-                for (var j = 0; j < options.length; j++) {
-                    var li = document.createElement("li");
-                    li.setAttribute("value", options[j].value);
-                    li.textContent = options[j].firstChild.textContent;
-                    li.addEventListener("click", setDropdownValue);
-                    ul.appendChild(li);
-                }
-            }
-
-            elDropdowns = document.getElementsByTagName("select");
-            while (elDropdowns.length > 0) {
-                var el = elDropdowns[0];
-                el.parentNode.removeChild(el);
-                elDropdowns = document.getElementsByTagName("select");
-            }
-
-            augmentSpinButtons();
-
-            // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
-            document.addEventListener("contextmenu", function (event) {
-                event.preventDefault();
-            }, false);
-        }
-    </script>
+    <script type="text/javascript" src="js/eventBridgeLoader.js"></script>
+    <script type="text/javascript" src="js/spinButtons.js"></script>
+    <script type="text/javascript" src="js/entityProperties.js"></script>
 </head>
-
 <body onload='loaded();'>
     <div id="properties-list">
         <div id="properties-header">
diff --git a/scripts/system/html/entityProperties.js b/scripts/system/html/entityProperties.js
new file mode 100644
index 0000000000..490579e909
--- /dev/null
+++ b/scripts/system/html/entityProperties.js
@@ -0,0 +1,1317 @@
+//  entityProperties.js
+//
+//  Created by Ryan Huffman on 13 Nov 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
+
+var PI = 3.14159265358979;
+var DEGREES_TO_RADIANS = PI / 180.0;
+var RADIANS_TO_DEGREES = 180.0 / PI;
+var ICON_FOR_TYPE = {
+    Box: "V",
+    Sphere: "n",
+    Shape: "n",
+    ParticleEffect: "&#xe004;",
+    Model: "&#xe008;",
+    Web: "q",
+    Text: "l",
+    Light: "p",
+    Zone: "o",
+    PolyVox: "&#xe005;",
+    Multiple: "&#xe000;"
+}
+
+var colorPickers = [];
+
+debugPrint = function(message) {
+    EventBridge.emitWebEvent(
+        JSON.stringify({
+            type:"print", 
+            message: message
+        })
+    );
+};
+
+function enableChildren(el, selector) {
+    els = el.querySelectorAll(selector);
+    for (var i = 0; i < els.length; i++) {
+        els[i].removeAttribute('disabled');
+    }
+}
+function disableChildren(el, selector) {
+    els = el.querySelectorAll(selector);
+    for (var i = 0; i < els.length; i++) {
+        els[i].setAttribute('disabled', 'disabled');
+    }
+}
+
+function enableProperties() {
+    enableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker");
+    enableChildren(document, ".colpick");
+}
+
+function disableProperties() {
+    disableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker");
+    disableChildren(document, ".colpick");
+    for (var i = 0; i < colorPickers.length; i++) {
+        colorPickers[i].colpickHide();
+    }
+}
+
+function showElements(els, show) {
+    for (var i = 0; i < els.length; i++) {
+        els[i].style.display = (show) ? 'table' : 'none';
+    }
+}
+
+function createEmitCheckedPropertyUpdateFunction(propertyName) {
+    return function() {
+        EventBridge.emitWebEvent(
+            '{ "type":"update", "properties":{"' + propertyName + '":' + this.checked + '}}'
+        );
+    };
+}
+
+function createEmitCheckedToStringPropertyUpdateFunction(checkboxElement, name, propertyName) {
+    var newString = "";
+    if (checkboxElement.checked) {
+        newString += name + "";
+    } else {
+
+    }
+
+}
+
+function createEmitGroupCheckedPropertyUpdateFunction(group, propertyName) {
+    return function () {
+        var properties = {};
+        properties[group] = {};
+        properties[group][propertyName] = this.checked;
+        EventBridge.emitWebEvent(
+            JSON.stringify({
+                type: "update",
+                properties: properties
+            })
+        );
+    };
+}
+
+function createEmitNumberPropertyUpdateFunction(propertyName, decimals) {
+    decimals = decimals == undefined ? 4 : decimals;
+    return function() {
+        var value = parseFloat(this.value).toFixed(decimals);
+
+        EventBridge.emitWebEvent(
+            '{ "type":"update", "properties":{"' + propertyName + '":' + value + '}}'
+        );
+    };
+}
+function createEmitGroupNumberPropertyUpdateFunction(group, propertyName) {
+    return function() {
+        var properties = {};
+        properties[group] = {};
+        properties[group][propertyName] = this.value;
+        EventBridge.emitWebEvent(
+            JSON.stringify({
+                type: "update",
+                properties: properties,
+            })
+        );
+    };
+}
+
+
+function createEmitTextPropertyUpdateFunction(propertyName) {
+    return function() {
+        var properties = {};
+        properties[propertyName] = this.value;
+        EventBridge.emitWebEvent(
+            JSON.stringify({
+                type: "update",
+                properties: properties,
+            })
+        );
+    };
+}
+
+function createEmitGroupTextPropertyUpdateFunction(group,propertyName) {
+    return function() {
+        var properties = {};
+        properties[group] = {};
+        properties[group][propertyName] = this.value;
+        EventBridge.emitWebEvent(
+            JSON.stringify({
+                type: "update",
+                properties: properties,
+            })
+        );
+    };
+}
+
+function createEmitVec3PropertyUpdateFunction(property, elX, elY, elZ) {
+    return function() {
+        var data = {
+             type: "update",
+             properties: {
+             }
+        };
+        data.properties[property] = {
+             x: elX.value,
+             y: elY.value,
+             z: elZ.value,
+        };
+        EventBridge.emitWebEvent(JSON.stringify(data));
+    }
+};
+
+function createEmitGroupVec3PropertyUpdateFunction(group, property, elX, elY, elZ) {
+    return function() {
+        var data = {
+             type: "update",
+             properties: {
+             }
+        };
+        data.properties[group] = { };
+        data.properties[group][property] = {
+             x: elX.value,
+             y: elY.value,
+             z: elZ ? elZ.value : 0,
+        };
+        EventBridge.emitWebEvent(JSON.stringify(data));
+    }
+};
+
+function createEmitVec3PropertyUpdateFunctionWithMultiplier(property, elX, elY, elZ, multiplier) {
+    return function() {
+        var data = {
+             type: "update",
+             properties: {
+             }
+        };
+        data.properties[property] = {
+             x: elX.value * multiplier,
+             y: elY.value * multiplier,
+             z: elZ.value * multiplier,
+        };
+        EventBridge.emitWebEvent(JSON.stringify(data));
+    }
+};
+
+function createEmitColorPropertyUpdateFunction(property, elRed, elGreen, elBlue) {
+    return function() {
+        emitColorPropertyUpdate(property, elRed.value, elGreen.value, elBlue.value);
+    }
+};
+
+function emitColorPropertyUpdate(property, red, green, blue, group) {
+        var data = {
+             type: "update",
+             properties: {
+             }
+        };
+        if (group) {
+            data.properties[group] = { };
+            data.properties[group][property] = {
+                 red: red,
+                 green: green,
+                 blue: blue,
+            };
+        } else {
+            data.properties[property] = {
+                 red: red,
+                 green: green,
+                 blue: blue,
+            };
+        }
+        EventBridge.emitWebEvent(JSON.stringify(data));
+};
+
+
+function createEmitGroupColorPropertyUpdateFunction(group, property, elRed, elGreen, elBlue) {
+    return function() {
+        var data = {
+             type: "update",
+             properties: {
+             }
+        };
+        data.properties[group] = { };
+
+        data.properties[group][property] = {
+             red: elRed.value,
+             green: elGreen.value,
+             blue: elBlue.value,
+        };
+        EventBridge.emitWebEvent(JSON.stringify(data));
+    }
+};
+
+function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElement, subPropertyString) {
+    if (subPropertyElement.checked) {
+        if (propertyValue.indexOf(subPropertyString)) {
+            propertyValue += subPropertyString + ',';
+        } 
+    } else {
+        // We've unchecked, so remove 
+        propertyValue = propertyValue.replace(subPropertyString + ",", "");
+    }
+
+    var _properties ={}
+    _properties[propertyName] = propertyValue;
+    
+    EventBridge.emitWebEvent(
+      JSON.stringify({
+        type: "update",
+        properties: _properties
+      })
+    );
+
+}
+
+function userDataChanger(groupName, keyName, checkBoxElement, userDataElement, defaultValue) {
+    var properties = {};
+    var parsedData = {};
+    try {
+        parsedData = JSON.parse(userDataElement.value);
+    } catch(e) {}
+
+    if (!(groupName in parsedData)) {
+        parsedData[groupName] = {}
+    }
+    delete parsedData[groupName][keyName];
+    if (checkBoxElement.checked !== defaultValue) {
+        parsedData[groupName][keyName] = checkBoxElement.checked;
+    }
+
+    if (Object.keys(parsedData[groupName]).length == 0) {
+        delete parsedData[groupName];
+    }
+    if (Object.keys(parsedData).length > 0) {
+        properties['userData'] = JSON.stringify(parsedData);
+    } else {
+        properties['userData'] = '';
+    }
+
+    userDataElement.value = properties['userData'];
+
+    EventBridge.emitWebEvent(
+        JSON.stringify({
+            type: "update",
+            properties: properties,
+        })
+    );
+};
+
+function setTextareaScrolling(element) {
+    var isScrolling = element.scrollHeight > element.offsetHeight;
+    element.setAttribute("scrolling", isScrolling ? "true" : "false");
+}
+
+function loaded() { 
+    openEventBridge(function() {
+        var allSections = [];
+        var elID = document.getElementById("property-id");
+        var elType = document.getElementById("property-type");
+        var elTypeIcon = document.getElementById("type-icon");
+        var elName = document.getElementById("property-name");
+        var elLocked = document.getElementById("property-locked");
+        var elVisible = document.getElementById("property-visible");
+        var elPositionX = document.getElementById("property-pos-x");
+        var elPositionY = document.getElementById("property-pos-y");
+        var elPositionZ = document.getElementById("property-pos-z");
+        var elMoveSelectionToGrid = document.getElementById("move-selection-to-grid");
+        var elMoveAllToGrid = document.getElementById("move-all-to-grid");
+
+        var elDimensionsX = document.getElementById("property-dim-x");
+        var elDimensionsY = document.getElementById("property-dim-y");
+        var elDimensionsZ = document.getElementById("property-dim-z");
+        var elResetToNaturalDimensions = document.getElementById("reset-to-natural-dimensions");
+        var elRescaleDimensionsPct = document.getElementById("dimension-rescale-pct");
+        var elRescaleDimensionsButton = document.getElementById("dimension-rescale-button");
+
+        var elParentID = document.getElementById("property-parent-id");
+        var elParentJointIndex = document.getElementById("property-parent-joint-index");
+
+        var elRegistrationX = document.getElementById("property-reg-x");
+        var elRegistrationY = document.getElementById("property-reg-y");
+        var elRegistrationZ = document.getElementById("property-reg-z");
+
+        var elRotationX = document.getElementById("property-rot-x");
+        var elRotationY = document.getElementById("property-rot-y");
+        var elRotationZ = document.getElementById("property-rot-z");
+
+        var elLinearVelocityX = document.getElementById("property-lvel-x");
+        var elLinearVelocityY = document.getElementById("property-lvel-y");
+        var elLinearVelocityZ = document.getElementById("property-lvel-z");
+        var elLinearDamping = document.getElementById("property-ldamping");
+
+        var elAngularVelocityX = document.getElementById("property-avel-x");
+        var elAngularVelocityY = document.getElementById("property-avel-y");
+        var elAngularVelocityZ = document.getElementById("property-avel-z");
+        var elAngularDamping = document.getElementById("property-adamping");
+
+        var elRestitution = document.getElementById("property-restitution");
+        var elFriction = document.getElementById("property-friction");
+
+        var elGravityX = document.getElementById("property-grav-x");
+        var elGravityY = document.getElementById("property-grav-y");
+        var elGravityZ = document.getElementById("property-grav-z");
+
+        var elAccelerationX = document.getElementById("property-lacc-x");
+        var elAccelerationY = document.getElementById("property-lacc-y");
+        var elAccelerationZ = document.getElementById("property-lacc-z");
+
+        var elDensity = document.getElementById("property-density");
+        var elCollisionless = document.getElementById("property-collisionless");
+        var elDynamic = document.getElementById("property-dynamic" );
+        var elCollideStatic = document.getElementById("property-collide-static");
+        var elCollideDynamic = document.getElementById("property-collide-dynamic");
+        var elCollideKinematic = document.getElementById("property-collide-kinematic");
+        var elCollideMyAvatar = document.getElementById("property-collide-myAvatar");
+        var elCollideOtherAvatar = document.getElementById("property-collide-otherAvatar");
+        var elCollisionSoundURL = document.getElementById("property-collision-sound-url");
+
+        var elGrabbable = document.getElementById("property-grabbable");
+        var elWantsTrigger = document.getElementById("property-wants-trigger");
+        var elIgnoreIK = document.getElementById("property-ignore-ik");
+
+        var elLifetime = document.getElementById("property-lifetime");
+        var elScriptURL = document.getElementById("property-script-url");
+        /*
+        FIXME: See FIXME for property-script-url.
+        var elScriptTimestamp = document.getElementById("property-script-timestamp");
+        */
+        var elReloadScriptButton = document.getElementById("reload-script-button");
+        var elUserData = document.getElementById("property-user-data");
+
+        var elColorSections = document.querySelectorAll(".color-section");
+        var elColor = document.getElementById("property-color");
+        var elColorRed = document.getElementById("property-color-red");
+        var elColorGreen = document.getElementById("property-color-green");
+        var elColorBlue = document.getElementById("property-color-blue");
+
+        var elShapeSections = document.querySelectorAll(".shape-section");
+        allSections.push(elShapeSections);
+        var elShape = document.getElementById("property-shape");
+        
+        var elLightSections = document.querySelectorAll(".light-section");
+        allSections.push(elLightSections);
+        var elLightSpotLight = document.getElementById("property-light-spot-light");
+        var elLightColor = document.getElementById("property-light-color");
+        var elLightColorRed = document.getElementById("property-light-color-red");
+        var elLightColorGreen = document.getElementById("property-light-color-green");
+        var elLightColorBlue = document.getElementById("property-light-color-blue");
+
+        var elLightIntensity = document.getElementById("property-light-intensity");
+        var elLightFalloffRadius = document.getElementById("property-light-falloff-radius");
+        var elLightExponent = document.getElementById("property-light-exponent");
+        var elLightCutoff = document.getElementById("property-light-cutoff");
+
+        var elModelSections = document.querySelectorAll(".model-section");
+        allSections.push(elModelSections);
+        var elModelURL = document.getElementById("property-model-url");
+        var elShapeType = document.getElementById("property-shape-type");
+        var elCompoundShapeURL = document.getElementById("property-compound-shape-url");
+        var elModelAnimationURL = document.getElementById("property-model-animation-url");
+        var elModelAnimationPlaying = document.getElementById("property-model-animation-playing");
+        var elModelAnimationFPS = document.getElementById("property-model-animation-fps");
+        var elModelAnimationFrame = document.getElementById("property-model-animation-frame");
+        var elModelAnimationFirstFrame = document.getElementById("property-model-animation-first-frame");
+        var elModelAnimationLastFrame = document.getElementById("property-model-animation-last-frame");
+        var elModelAnimationLoop = document.getElementById("property-model-animation-loop");
+        var elModelAnimationHold = document.getElementById("property-model-animation-hold");
+        var elModelTextures = document.getElementById("property-model-textures");
+        var elModelOriginalTextures = document.getElementById("property-model-original-textures");
+
+        var elWebSections = document.querySelectorAll(".web-section");
+        allSections.push(elWebSections);
+        var elWebSourceURL = document.getElementById("property-web-source-url");
+
+        var elDescription = document.getElementById("property-description");
+
+        var elHyperlinkHref = document.getElementById("property-hyperlink-href");
+     
+        var elHyperlinkSections = document.querySelectorAll(".hyperlink-section");
+      
+
+        var elTextSections = document.querySelectorAll(".text-section");
+        allSections.push(elTextSections);
+        var elTextText = document.getElementById("property-text-text");
+        var elTextLineHeight = document.getElementById("property-text-line-height");
+        var elTextTextColor = document.getElementById("property-text-text-color");
+        var elTextFaceCamera = document.getElementById("property-text-face-camera");
+        var elTextTextColorRed = document.getElementById("property-text-text-color-red");
+        var elTextTextColorGreen = document.getElementById("property-text-text-color-green");
+        var elTextTextColorBlue = document.getElementById("property-text-text-color-blue");
+        var elTextBackgroundColor = document.getElementById("property-text-background-color");
+        var elTextBackgroundColorRed = document.getElementById("property-text-background-color-red");
+        var elTextBackgroundColorGreen = document.getElementById("property-text-background-color-green");
+        var elTextBackgroundColorBlue = document.getElementById("property-text-background-color-blue");
+
+        var elZoneSections = document.querySelectorAll(".zone-section");
+        allSections.push(elZoneSections);
+        var elZoneStageSunModelEnabled = document.getElementById("property-zone-stage-sun-model-enabled");
+
+        var elZoneKeyLightColor = document.getElementById("property-zone-key-light-color");
+        var elZoneKeyLightColorRed = document.getElementById("property-zone-key-light-color-red");
+        var elZoneKeyLightColorGreen = document.getElementById("property-zone-key-light-color-green");
+        var elZoneKeyLightColorBlue = document.getElementById("property-zone-key-light-color-blue");
+        var elZoneKeyLightIntensity = document.getElementById("property-zone-key-intensity");
+        var elZoneKeyLightAmbientIntensity = document.getElementById("property-zone-key-ambient-intensity");
+        var elZoneKeyLightDirectionX = document.getElementById("property-zone-key-light-direction-x");
+        var elZoneKeyLightDirectionY = document.getElementById("property-zone-key-light-direction-y");
+        var elZoneKeyLightDirectionZ = document.getElementById("property-zone-key-light-direction-z");
+        var elZoneKeyLightAmbientURL = document.getElementById("property-zone-key-ambient-url");
+
+        var elZoneStageLatitude = document.getElementById("property-zone-stage-latitude");
+        var elZoneStageLongitude = document.getElementById("property-zone-stage-longitude");
+        var elZoneStageAltitude = document.getElementById("property-zone-stage-altitude");
+        var elZoneStageAutomaticHourDay = document.getElementById("property-zone-stage-automatic-hour-day");
+        var elZoneStageDay = document.getElementById("property-zone-stage-day");
+        var elZoneStageHour = document.getElementById("property-zone-stage-hour");
+
+        var elZoneBackgroundMode = document.getElementById("property-zone-background-mode");
+
+        var elZoneSkyboxColor = document.getElementById("property-zone-skybox-color");
+        var elZoneSkyboxColorRed = document.getElementById("property-zone-skybox-color-red");
+        var elZoneSkyboxColorGreen = document.getElementById("property-zone-skybox-color-green");
+        var elZoneSkyboxColorBlue = document.getElementById("property-zone-skybox-color-blue");
+        var elZoneSkyboxURL = document.getElementById("property-zone-skybox-url");
+
+        var elZoneFlyingAllowed = document.getElementById("property-zone-flying-allowed");
+        var elZoneGhostingAllowed = document.getElementById("property-zone-ghosting-allowed");
+
+        var elPolyVoxSections = document.querySelectorAll(".poly-vox-section");
+        allSections.push(elPolyVoxSections);
+        var elVoxelVolumeSizeX = document.getElementById("property-voxel-volume-size-x");
+        var elVoxelVolumeSizeY = document.getElementById("property-voxel-volume-size-y");
+        var elVoxelVolumeSizeZ = document.getElementById("property-voxel-volume-size-z");
+        var elVoxelSurfaceStyle = document.getElementById("property-voxel-surface-style");
+        var elXTextureURL = document.getElementById("property-x-texture-url");
+        var elYTextureURL = document.getElementById("property-y-texture-url");
+        var elZTextureURL = document.getElementById("property-z-texture-url");
+
+        var elPreviewCameraButton = document.getElementById("preview-camera-button");
+
+        if (window.EventBridge !== undefined) {
+            var properties;
+            EventBridge.scriptEventReceived.connect(function(data) {
+                data = JSON.parse(data);
+                if (data.type == "update") {
+                    if (data.selections.length == 0) {
+                        elTypeIcon.style.display = "none";
+                        elType.innerHTML = "<i>No selection</i>";
+                        elID.innerHTML = "";
+                        disableProperties();
+                    } else if (data.selections.length > 1) {
+                        var selections = data.selections;
+
+                        var ids = [];
+                        var types = {};
+                        var numTypes = 0;
+
+                        for (var i = 0; i < selections.length; i++) {
+                            ids.push(selections[i].id);
+                            var type = selections[i].properties.type;
+                            if (types[type] === undefined) {
+                                types[type] = 0;
+                                numTypes += 1;
+                            }
+                            types[type]++;
+                        }
+
+                        var type;
+                        if (numTypes === 1) {
+                            type = selections[0].properties.type;
+                        } else {
+                            type = "Multiple";
+                        }
+                        elType.innerHTML = type + " (" + data.selections.length + ")";
+                        elTypeIcon.innerHTML = ICON_FOR_TYPE[type];
+                        elTypeIcon.style.display = "inline-block";
+
+                        elID.innerHTML = ids.join("<br>");
+
+                        disableProperties();
+                    } else {
+    
+
+                        properties = data.selections[0].properties;
+
+                        elID.innerHTML = properties.id;
+
+                        elType.innerHTML = properties.type;
+                        elTypeIcon.innerHTML = ICON_FOR_TYPE[properties.type];
+                        elTypeIcon.style.display = "inline-block";
+
+                        elLocked.checked = properties.locked;
+
+                        if (properties.locked) {
+                            disableProperties();
+                            elLocked.removeAttribute('disabled');
+                        } else {
+                            enableProperties();
+                        }
+
+                        elName.value = properties.name;
+
+                        elVisible.checked = properties.visible;
+
+                        elPositionX.value = properties.position.x.toFixed(4);
+                        elPositionY.value = properties.position.y.toFixed(4);
+                        elPositionZ.value = properties.position.z.toFixed(4);
+
+                        elDimensionsX.value = properties.dimensions.x.toFixed(4);
+                        elDimensionsY.value = properties.dimensions.y.toFixed(4);
+                        elDimensionsZ.value = properties.dimensions.z.toFixed(4);
+
+                        elParentID.value = properties.parentID;
+                        elParentJointIndex.value = properties.parentJointIndex;
+
+                        elRegistrationX.value = properties.registrationPoint.x.toFixed(4);
+                        elRegistrationY.value = properties.registrationPoint.y.toFixed(4);
+                        elRegistrationZ.value = properties.registrationPoint.z.toFixed(4);
+
+                        elRotationX.value = properties.rotation.x.toFixed(4);
+                        elRotationY.value = properties.rotation.y.toFixed(4);
+                        elRotationZ.value = properties.rotation.z.toFixed(4);
+
+                        elLinearVelocityX.value = properties.velocity.x.toFixed(4);
+                        elLinearVelocityY.value = properties.velocity.y.toFixed(4);
+                        elLinearVelocityZ.value = properties.velocity.z.toFixed(4);
+                        elLinearDamping.value = properties.damping.toFixed(2);
+
+                        elAngularVelocityX.value = (properties.angularVelocity.x * RADIANS_TO_DEGREES).toFixed(4);
+                        elAngularVelocityY.value = (properties.angularVelocity.y * RADIANS_TO_DEGREES).toFixed(4);
+                        elAngularVelocityZ.value = (properties.angularVelocity.z * RADIANS_TO_DEGREES).toFixed(4);
+                        elAngularDamping.value = properties.angularDamping.toFixed(4);
+
+                        elRestitution.value = properties.restitution.toFixed(4);
+                        elFriction.value = properties.friction.toFixed(4);
+
+                        elGravityX.value = properties.gravity.x.toFixed(4);
+                        elGravityY.value = properties.gravity.y.toFixed(4);
+                        elGravityZ.value = properties.gravity.z.toFixed(4);
+
+                        elAccelerationX.value = properties.acceleration.x.toFixed(4);
+                        elAccelerationY.value = properties.acceleration.y.toFixed(4);
+                        elAccelerationZ.value = properties.acceleration.z.toFixed(4);
+
+                        elDensity.value = properties.density.toFixed(4);
+                        elCollisionless.checked = properties.collisionless;
+                        elDynamic.checked = properties.dynamic;
+
+                        elCollideStatic.checked = properties.collidesWith.indexOf("static") > -1;
+                        elCollideKinematic.checked = properties.collidesWith.indexOf("kinematic") > -1;
+                        elCollideDynamic.checked = properties.collidesWith.indexOf("dynamic") > -1;
+                        elCollideMyAvatar.checked = properties.collidesWith.indexOf("myAvatar") > -1;
+                        elCollideOtherAvatar.checked = properties.collidesWith.indexOf("otherAvatar") > -1;
+
+                        elGrabbable.checked = properties.dynamic;
+                        elWantsTrigger.checked = false;
+                        elIgnoreIK.checked = false;
+                        var parsedUserData = {}
+                        try {
+                            parsedUserData = JSON.parse(properties.userData);
+                                                    
+                            if ("grabbableKey" in parsedUserData) {
+                                if ("grabbable" in parsedUserData["grabbableKey"]) {
+                                    elGrabbable.checked = parsedUserData["grabbableKey"].grabbable;
+                                }
+                                if ("wantsTrigger" in parsedUserData["grabbableKey"]) {
+                                    elWantsTrigger.checked = parsedUserData["grabbableKey"].wantsTrigger;
+                                }
+                                if ("ignoreIK" in parsedUserData["grabbableKey"]) {
+                                    elIgnoreIK.checked = parsedUserData["grabbableKey"].ignoreIK;
+                                }
+                            }
+                        } catch(e) {}
+
+                        elCollisionSoundURL.value = properties.collisionSoundURL;
+                        elLifetime.value = properties.lifetime;
+                        elScriptURL.value = properties.script;
+                        /*
+                        FIXME: See FIXME for property-script-url.
+                        elScriptTimestamp.value = properties.scriptTimestamp;
+                        */
+                        elUserData.value = properties.userData;
+                        setTextareaScrolling(elUserData);
+
+                        elHyperlinkHref.value = properties.href;
+                        elDescription.value = properties.description;
+
+                        for (var i = 0; i < allSections.length; i++) {
+                            for (var j = 0; j < allSections[i].length; j++) {
+                                allSections[i][j].style.display = 'none';
+                            }
+                        }                                    
+
+                        for (var i = 0; i < elHyperlinkSections.length; i++) {
+                            elHyperlinkSections[i].style.display = 'table';
+                        }
+
+                        if (properties.type == "Shape" || properties.type == "Box" || properties.type == "Sphere") {
+                            for (var i = 0; i < elShapeSections.length; i++) {
+                                elShapeSections[i].style.display = 'table';
+                            }
+                            elShape.value = properties.shape;
+                            setDropdownText(elShape);
+
+                        } else {
+                            for (var i = 0; i < elShapeSections.length; i++) {
+                                elShapeSections[i].style.display = 'none';
+                            }
+                        }
+                        
+                        if (properties.type == "Shape" || properties.type == "Box" || properties.type == "Sphere" || properties.type == "ParticleEffect") {
+                            for (var i = 0; i < elColorSections.length; i++) {
+                                elColorSections[i].style.display = 'table';
+                            }
+                            elColorRed.value = properties.color.red;
+                            elColorGreen.value = properties.color.green;
+                            elColorBlue.value = properties.color.blue;
+                            elColor.style.backgroundColor = "rgb(" + properties.color.red  + "," + properties.color.green + "," + properties.color.blue + ")";
+                        } else {
+                            for (var i = 0; i < elColorSections.length; i++) {
+                                elColorSections[i].style.display = 'none';
+                            }
+                        }
+
+                        if (properties.type == "Model") {
+                            for (var i = 0; i < elModelSections.length; i++) {
+                                elModelSections[i].style.display = 'table';
+                            }
+
+                            elModelURL.value = properties.modelURL;
+                            elShapeType.value = properties.shapeType;
+                            setDropdownText(elShapeType);
+                            elCompoundShapeURL.value = properties.compoundShapeURL;
+                            elModelAnimationURL.value = properties.animation.url;
+                            elModelAnimationPlaying.checked = properties.animation.running;
+                            elModelAnimationFPS.value = properties.animation.fps;
+                            elModelAnimationFrame.value = properties.animation.currentFrame;
+                            elModelAnimationFirstFrame.value = properties.animation.firstFrame;
+                            elModelAnimationLastFrame.value = properties.animation.lastFrame;
+                            elModelAnimationLoop.checked = properties.animation.loop;
+                            elModelAnimationHold.checked = properties.animation.hold;
+                            elModelTextures.value = properties.textures;
+                            setTextareaScrolling(elModelTextures);
+                            elModelOriginalTextures.value = properties.originalTextures;
+                            setTextareaScrolling(elModelOriginalTextures);
+                        } else if (properties.type == "Web") {
+                            for (var i = 0; i < elWebSections.length; i++) {
+                                elWebSections[i].style.display = 'table';
+                            }
+                            for (var i = 0; i < elHyperlinkSections.length; i++) {
+                                elHyperlinkSections[i].style.display = 'none';
+                            }
+
+                            elWebSourceURL.value = properties.sourceUrl;
+                        } else if (properties.type == "Text") {
+                            for (var i = 0; i < elTextSections.length; i++) {
+                                elTextSections[i].style.display = 'table';
+                            }
+
+                            elTextText.value = properties.text;
+                            elTextLineHeight.value = properties.lineHeight.toFixed(4);
+                            elTextFaceCamera = properties.faceCamera;
+                            elTextTextColor.style.backgroundColor = "rgb(" + properties.textColor.red  + "," + properties.textColor.green + "," + properties.textColor.blue + ")";
+                            elTextTextColorRed.value = properties.textColor.red;
+                            elTextTextColorGreen.value = properties.textColor.green;
+                            elTextTextColorBlue.value = properties.textColor.blue;
+                            elTextBackgroundColorRed.value = properties.backgroundColor.red;
+                            elTextBackgroundColorGreen.value = properties.backgroundColor.green;
+                            elTextBackgroundColorBlue.value = properties.backgroundColor.blue;
+                        } else if (properties.type == "Light") {
+                            for (var i = 0; i < elLightSections.length; i++) {
+                                elLightSections[i].style.display = 'table';
+                            }
+
+                            elLightSpotLight.checked = properties.isSpotlight;
+
+                            elLightColor.style.backgroundColor = "rgb(" + properties.color.red  + "," + properties.color.green + "," + properties.color.blue + ")";
+                            elLightColorRed.value = properties.color.red;
+                            elLightColorGreen.value = properties.color.green;
+                            elLightColorBlue.value = properties.color.blue;
+
+                            elLightIntensity.value = properties.intensity.toFixed(1);
+                            elLightFalloffRadius.value = properties.falloffRadius.toFixed(1);
+                            elLightExponent.value = properties.exponent.toFixed(2);
+                            elLightCutoff.value = properties.cutoff.toFixed(2);
+                        } else if (properties.type == "Zone") {
+                            for (var i = 0; i < elZoneSections.length; i++) {
+                                elZoneSections[i].style.display = 'table';
+                            }
+
+                            elZoneStageSunModelEnabled.checked = properties.stage.sunModelEnabled;
+                            elZoneKeyLightColor.style.backgroundColor = "rgb(" + properties.keyLight.color.red  + "," + properties.keyLight.color.green + "," + properties.keyLight.color.blue + ")";
+                            elZoneKeyLightColorRed.value = properties.keyLight.color.red;
+                            elZoneKeyLightColorGreen.value = properties.keyLight.color.green;
+                            elZoneKeyLightColorBlue.value = properties.keyLight.color.blue;
+                            elZoneKeyLightIntensity.value = properties.keyLight.intensity.toFixed(2);
+                            elZoneKeyLightAmbientIntensity.value = properties.keyLight.ambientIntensity.toFixed(2);
+                            elZoneKeyLightDirectionX.value = properties.keyLight.direction.x.toFixed(2);
+                            elZoneKeyLightDirectionY.value = properties.keyLight.direction.y.toFixed(2);
+                            elZoneKeyLightAmbientURL.value = properties.keyLight.ambientURL;
+
+
+                            elZoneStageLatitude.value = properties.stage.latitude.toFixed(2);
+                            elZoneStageLongitude.value = properties.stage.longitude.toFixed(2);
+                            elZoneStageAltitude.value = properties.stage.altitude.toFixed(2);
+                            elZoneStageAutomaticHourDay.checked = properties.stage.automaticHourDay;
+                            elZoneStageDay.value = properties.stage.day;
+                            elZoneStageHour.value = properties.stage.hour;
+                            elShapeType.value = properties.shapeType;
+                            elCompoundShapeURL.value = properties.compoundShapeURL;
+
+                            elZoneBackgroundMode.value = properties.backgroundMode;
+                            setDropdownText(elZoneBackgroundMode);
+
+                            elZoneSkyboxColor.style.backgroundColor = "rgb(" + properties.skybox.color.red  + "," + properties.skybox.color.green + "," + properties.skybox.color.blue + ")";
+                            elZoneSkyboxColorRed.value = properties.skybox.color.red;
+                            elZoneSkyboxColorGreen.value = properties.skybox.color.green;
+                            elZoneSkyboxColorBlue.value = properties.skybox.color.blue;
+                            elZoneSkyboxURL.value = properties.skybox.url;
+
+                            elZoneFlyingAllowed.checked = properties.flyingAllowed;
+                            elZoneGhostingAllowed.checked = properties.ghostingAllowed;
+
+                            showElements(document.getElementsByClassName('skybox-section'), elZoneBackgroundMode.value == 'skybox');
+                        } else if (properties.type == "PolyVox") {
+                            for (var i = 0; i < elPolyVoxSections.length; i++) {
+                                elPolyVoxSections[i].style.display = 'table';
+                            }
+
+                            elVoxelVolumeSizeX.value = properties.voxelVolumeSize.x.toFixed(2);
+                            elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2);
+                            elVoxelVolumeSizeZ.value = properties.voxelVolumeSize.z.toFixed(2);
+                            elVoxelSurfaceStyle.value = properties.voxelSurfaceStyle;
+                            setDropdownText(elVoxelSurfaceStyle);
+                            elXTextureURL.value = properties.xTextureURL;
+                            elYTextureURL.value = properties.yTextureURL;
+                            elZTextureURL.value = properties.zTextureURL;
+                        }
+                        
+                        var activeElement = document.activeElement;
+                        
+                        if(typeof activeElement.select!=="undefined"){
+                            activeElement.select();
+                        }
+                    }
+                }
+            });
+        }
+
+        elLocked.addEventListener('change', createEmitCheckedPropertyUpdateFunction('locked'));
+        elName.addEventListener('change', createEmitTextPropertyUpdateFunction('name'));
+        elHyperlinkHref.addEventListener('change', createEmitTextPropertyUpdateFunction('href'));
+        elDescription.addEventListener('change', createEmitTextPropertyUpdateFunction('description'));
+        elVisible.addEventListener('change', createEmitCheckedPropertyUpdateFunction('visible'));
+
+        var positionChangeFunction = createEmitVec3PropertyUpdateFunction(
+            'position', elPositionX, elPositionY, elPositionZ);
+        elPositionX.addEventListener('change', positionChangeFunction);
+        elPositionY.addEventListener('change', positionChangeFunction);
+        elPositionZ.addEventListener('change', positionChangeFunction);
+
+        var dimensionsChangeFunction = createEmitVec3PropertyUpdateFunction(
+            'dimensions', elDimensionsX, elDimensionsY, elDimensionsZ);
+        elDimensionsX.addEventListener('change', dimensionsChangeFunction);
+        elDimensionsY.addEventListener('change', dimensionsChangeFunction);
+        elDimensionsZ.addEventListener('change', dimensionsChangeFunction);
+
+        elParentID.addEventListener('change', createEmitTextPropertyUpdateFunction('parentID'));
+        elParentJointIndex.addEventListener('change', createEmitNumberPropertyUpdateFunction('parentJointIndex'));
+
+        var registrationChangeFunction = createEmitVec3PropertyUpdateFunction(
+            'registrationPoint', elRegistrationX, elRegistrationY, elRegistrationZ);
+        elRegistrationX.addEventListener('change', registrationChangeFunction);
+        elRegistrationY.addEventListener('change', registrationChangeFunction);
+        elRegistrationZ.addEventListener('change', registrationChangeFunction);
+
+        var rotationChangeFunction = createEmitVec3PropertyUpdateFunction(
+            'rotation', elRotationX, elRotationY, elRotationZ);
+        elRotationX.addEventListener('change', rotationChangeFunction);
+        elRotationY.addEventListener('change', rotationChangeFunction);
+        elRotationZ.addEventListener('change', rotationChangeFunction);
+
+        var velocityChangeFunction = createEmitVec3PropertyUpdateFunction(
+            'velocity', elLinearVelocityX, elLinearVelocityY, elLinearVelocityZ);
+        elLinearVelocityX.addEventListener('change', velocityChangeFunction);
+        elLinearVelocityY.addEventListener('change', velocityChangeFunction);
+        elLinearVelocityZ.addEventListener('change', velocityChangeFunction);
+        elLinearDamping.addEventListener('change', createEmitNumberPropertyUpdateFunction('damping'));
+
+        var angularVelocityChangeFunction = createEmitVec3PropertyUpdateFunctionWithMultiplier(
+            'angularVelocity', elAngularVelocityX, elAngularVelocityY, elAngularVelocityZ, DEGREES_TO_RADIANS);
+        elAngularVelocityX.addEventListener('change', angularVelocityChangeFunction);
+        elAngularVelocityY.addEventListener('change', angularVelocityChangeFunction);
+        elAngularVelocityZ.addEventListener('change', angularVelocityChangeFunction);
+        elAngularDamping.addEventListener('change', createEmitNumberPropertyUpdateFunction('angularDamping'));
+
+        elRestitution.addEventListener('change', createEmitNumberPropertyUpdateFunction('restitution'));
+        elFriction.addEventListener('change', createEmitNumberPropertyUpdateFunction('friction'));
+
+        var gravityChangeFunction = createEmitVec3PropertyUpdateFunction(
+            'gravity', elGravityX, elGravityY, elGravityZ);
+        elGravityX.addEventListener('change', gravityChangeFunction);
+        elGravityY.addEventListener('change', gravityChangeFunction);
+        elGravityZ.addEventListener('change', gravityChangeFunction);
+
+        var accelerationChangeFunction = createEmitVec3PropertyUpdateFunction(
+            'acceleration', elAccelerationX, elAccelerationY, elAccelerationZ);
+        elAccelerationX.addEventListener('change', accelerationChangeFunction);
+        elAccelerationY.addEventListener('change', accelerationChangeFunction);
+        elAccelerationZ.addEventListener('change', accelerationChangeFunction);
+
+        elDensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('density'));
+        elCollisionless.addEventListener('change', createEmitCheckedPropertyUpdateFunction('collisionless'));
+        elDynamic.addEventListener('change', createEmitCheckedPropertyUpdateFunction('dynamic'));
+
+        elCollideDynamic.addEventListener('change', function() {
+            updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideDynamic, 'dynamic');
+        });
+
+        elCollideKinematic.addEventListener('change', function() {
+            updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideKinematic, 'kinematic');
+        });
+
+        elCollideStatic.addEventListener('change', function() {
+            updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideStatic, 'static');
+        });
+        elCollideMyAvatar.addEventListener('change', function() {
+            updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideMyAvatar, 'myAvatar');
+        });
+        elCollideOtherAvatar.addEventListener('change', function() {
+            updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideOtherAvatar, 'otherAvatar');
+        });
+
+        elGrabbable.addEventListener('change', function() {
+            userDataChanger("grabbableKey", "grabbable", elGrabbable, elUserData, properties.dynamic);
+        });
+        elWantsTrigger.addEventListener('change', function() {
+            userDataChanger("grabbableKey", "wantsTrigger", elWantsTrigger, elUserData, false);
+        });
+        elIgnoreIK.addEventListener('change', function() {
+            userDataChanger("grabbableKey", "ignoreIK", elIgnoreIK, elUserData, false);
+        });
+
+        elCollisionSoundURL.addEventListener('change', createEmitTextPropertyUpdateFunction('collisionSoundURL'));
+
+        elLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifetime'));
+        elScriptURL.addEventListener('change', createEmitTextPropertyUpdateFunction('script'));
+        /*
+        FIXME: See FIXME for property-script-url.
+        elScriptTimestamp.addEventListener('change', createEmitNumberPropertyUpdateFunction('scriptTimestamp'));
+        */
+        elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData'));
+
+        var colorChangeFunction = createEmitColorPropertyUpdateFunction(
+            'color', elColorRed, elColorGreen, elColorBlue);
+        elColorRed.addEventListener('change', colorChangeFunction);
+        elColorGreen.addEventListener('change', colorChangeFunction);
+        elColorBlue.addEventListener('change', colorChangeFunction);
+        colorPickers.push($('#property-color').colpick({
+            colorScheme: 'dark',
+            layout: 'hex',
+            color: '000000',
+            onShow: function (colpick) {
+                $('#property-color').attr('active', 'true');
+            },
+            onHide: function (colpick) {
+                $('#property-color').attr('active', 'false');
+            },
+            onSubmit: function (hsb, hex, rgb, el) {
+                $(el).css('background-color', '#' + hex);
+                $(el).colpickHide();
+                emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b);
+            }
+        }));
+
+        elLightSpotLight.addEventListener('change', createEmitCheckedPropertyUpdateFunction('isSpotlight'));
+
+        var lightColorChangeFunction = createEmitColorPropertyUpdateFunction(
+            'color', elLightColorRed, elLightColorGreen, elLightColorBlue);
+        elLightColorRed.addEventListener('change', lightColorChangeFunction);
+        elLightColorGreen.addEventListener('change', lightColorChangeFunction);
+        elLightColorBlue.addEventListener('change', lightColorChangeFunction);
+        colorPickers.push($('#property-light-color').colpick({
+            colorScheme: 'dark',
+            layout: 'hex',
+            color: '000000',
+            onShow: function (colpick) {
+                $('#property-light-color').attr('active', 'true');
+            },
+            onHide: function (colpick) {
+                $('#property-light-color').attr('active', 'false');
+            },
+            onSubmit: function (hsb, hex, rgb, el) {
+                $(el).css('background-color', '#' + hex);
+                $(el).colpickHide();
+                emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b);
+            }
+        }));
+
+        elLightIntensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('intensity', 1));
+        elLightFalloffRadius.addEventListener('change', createEmitNumberPropertyUpdateFunction('falloffRadius', 1));
+        elLightExponent.addEventListener('change', createEmitNumberPropertyUpdateFunction('exponent', 2));
+        elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff', 2));
+
+        elShape.addEventListener('change', createEmitTextPropertyUpdateFunction('shape'));
+
+        elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl'));
+
+        elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL'));
+        elShapeType.addEventListener('change', createEmitTextPropertyUpdateFunction('shapeType'));
+        elCompoundShapeURL.addEventListener('change', createEmitTextPropertyUpdateFunction('compoundShapeURL'));
+
+        elModelAnimationURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('animation', 'url'));
+        elModelAnimationPlaying.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation','running'));
+        elModelAnimationFPS.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation','fps'));
+        elModelAnimationFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'currentFrame'));
+        elModelAnimationFirstFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'firstFrame'));
+        elModelAnimationLastFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'lastFrame'));
+        elModelAnimationLoop.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'loop'));
+        elModelAnimationHold.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'hold'));
+
+        elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures'));
+
+        elTextText.addEventListener('change', createEmitTextPropertyUpdateFunction('text'));
+        elTextFaceCamera.addEventListener('change', createEmitCheckedPropertyUpdateFunction('faceCamera'));
+        elTextLineHeight.addEventListener('change', createEmitNumberPropertyUpdateFunction('lineHeight'));
+        var textTextColorChangeFunction = createEmitColorPropertyUpdateFunction(
+            'textColor', elTextTextColorRed, elTextTextColorGreen, elTextTextColorBlue);
+        elTextTextColorRed.addEventListener('change', textTextColorChangeFunction);
+        elTextTextColorGreen.addEventListener('change', textTextColorChangeFunction);
+        elTextTextColorBlue.addEventListener('change', textTextColorChangeFunction);
+        colorPickers.push($('#property-text-text-color').colpick({
+            colorScheme:'dark',
+            layout:'hex',
+            color: '000000',
+            onShow: function (colpick) {
+                $('#property-text-text-color').attr('active', 'true');
+            },
+            onHide: function (colpick) {
+                $('#property-text-text-color').attr('active', 'false');
+            },
+            onSubmit: function (hsb, hex, rgb, el) {
+                $(el).css('background-color', '#'+hex);
+                $(el).colpickHide();
+                $(el).attr('active', 'false');
+                emitColorPropertyUpdate('textColor', rgb.r, rgb.g, rgb.b);
+            }
+        }));
+
+        var textBackgroundColorChangeFunction = createEmitColorPropertyUpdateFunction(
+            'backgroundColor', elTextBackgroundColorRed, elTextBackgroundColorGreen, elTextBackgroundColorBlue);
+        elTextBackgroundColorRed.addEventListener('change', textBackgroundColorChangeFunction);
+        elTextBackgroundColorGreen.addEventListener('change', textBackgroundColorChangeFunction);
+        elTextBackgroundColorBlue.addEventListener('change', textBackgroundColorChangeFunction);
+        colorPickers.push($('#property-text-background-color').colpick({
+            colorScheme:'dark',
+            layout:'hex',
+            color:'000000',
+            onShow: function (colpick) {
+                $('#property-text-background-color').attr('active', 'true');
+            },
+            onHide: function (colpick) {
+                $('#property-text-background-color').attr('active', 'false');
+            },
+            onSubmit: function (hsb, hex, rgb, el) {
+                $(el).css('background-color', '#'+hex);
+                $(el).colpickHide();
+                emitColorPropertyUpdate('backgroundColor', rgb.r, rgb.g, rgb.b);
+            }
+        }));
+
+        elZoneStageSunModelEnabled.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','sunModelEnabled'));
+        colorPickers.push($('#property-zone-key-light-color').colpick({
+            colorScheme:'dark',
+            layout:'hex',
+            color:'000000',
+            onShow: function (colpick) {
+                $('#property-zone-key-light-color').attr('active', 'true');
+            },
+            onHide: function (colpick) {
+                $('#property-zone-key-light-color').attr('active', 'false');
+            },
+            onSubmit: function (hsb, hex, rgb, el) {
+                $(el).css('background-color', '#'+hex);
+                $(el).colpickHide();
+                emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'keyLight');
+            }
+        }));
+        var zoneKeyLightColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('keyLight','color', elZoneKeyLightColorRed, elZoneKeyLightColorGreen, elZoneKeyLightColorBlue);
+        elZoneKeyLightColorRed.addEventListener('change', zoneKeyLightColorChangeFunction);
+        elZoneKeyLightColorGreen.addEventListener('change', zoneKeyLightColorChangeFunction);
+        elZoneKeyLightColorBlue.addEventListener('change', zoneKeyLightColorChangeFunction);
+        elZoneKeyLightIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','intensity'));
+        elZoneKeyLightAmbientIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','ambientIntensity'));
+        elZoneKeyLightAmbientURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('keyLight','ambientURL'));
+        var zoneKeyLightDirectionChangeFunction = createEmitGroupVec3PropertyUpdateFunction('keyLight','direction', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY);
+        elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction);
+        elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction);
+
+        elZoneStageLatitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','latitude'));
+        elZoneStageLongitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','longitude'));
+        elZoneStageAltitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','altitude'));
+        elZoneStageAutomaticHourDay.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','automaticHourDay'));
+        elZoneStageDay.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','day'));
+        elZoneStageHour.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','hour'));
+
+
+        elZoneBackgroundMode.addEventListener('change', createEmitTextPropertyUpdateFunction('backgroundMode'));
+        var zoneSkyboxColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('skybox','color',
+            elZoneSkyboxColorRed, elZoneSkyboxColorGreen, elZoneSkyboxColorBlue);
+        elZoneSkyboxColorRed.addEventListener('change', zoneSkyboxColorChangeFunction);
+        elZoneSkyboxColorGreen.addEventListener('change', zoneSkyboxColorChangeFunction);
+        elZoneSkyboxColorBlue.addEventListener('change', zoneSkyboxColorChangeFunction);
+        colorPickers.push($('#property-zone-skybox-color').colpick({
+            colorScheme:'dark',
+            layout:'hex',
+            color:'000000',
+            onShow: function (colpick) {
+                $('#property-zone-skybox-color').attr('active', 'true');
+            },
+            onHide: function (colpick) {
+                $('#property-zone-skybox-color').attr('active', 'false');
+            },
+            onSubmit: function (hsb, hex, rgb, el) {
+                $(el).css('background-color', '#'+hex);
+                $(el).colpickHide();
+                emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'skybox');
+            }
+        }));
+
+        elZoneSkyboxURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('skybox','url'));
+
+        elZoneFlyingAllowed.addEventListener('change', createEmitCheckedPropertyUpdateFunction('flyingAllowed'));
+        elZoneGhostingAllowed.addEventListener('change', createEmitCheckedPropertyUpdateFunction('ghostingAllowed'));
+
+        var voxelVolumeSizeChangeFunction = createEmitVec3PropertyUpdateFunction(
+            'voxelVolumeSize', elVoxelVolumeSizeX, elVoxelVolumeSizeY, elVoxelVolumeSizeZ);
+        elVoxelVolumeSizeX.addEventListener('change', voxelVolumeSizeChangeFunction);
+        elVoxelVolumeSizeY.addEventListener('change', voxelVolumeSizeChangeFunction);
+        elVoxelVolumeSizeZ.addEventListener('change', voxelVolumeSizeChangeFunction);
+        elVoxelSurfaceStyle.addEventListener('change', createEmitTextPropertyUpdateFunction('voxelSurfaceStyle'));
+        elXTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('xTextureURL'));
+        elYTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('yTextureURL'));
+        elZTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('zTextureURL'));
+
+        elMoveSelectionToGrid.addEventListener("click", function() {
+            EventBridge.emitWebEvent(JSON.stringify({
+                type: "action",
+                action: "moveSelectionToGrid",
+            }));
+        });
+        elMoveAllToGrid.addEventListener("click", function() {
+            EventBridge.emitWebEvent(JSON.stringify({
+                type: "action",
+                action: "moveAllToGrid",
+            }));
+        });
+        elResetToNaturalDimensions.addEventListener("click", function() {
+            EventBridge.emitWebEvent(JSON.stringify({
+                type: "action",
+                action: "resetToNaturalDimensions",
+            }));
+        });
+        elRescaleDimensionsButton.addEventListener("click", function() {
+            EventBridge.emitWebEvent(JSON.stringify({
+                type: "action",
+                action: "rescaleDimensions",
+                percentage: parseInt(elRescaleDimensionsPct.value),
+            }));
+        });
+        /*
+        FIXME: See FIXME for property-script-url.
+        elReloadScriptButton.addEventListener("click", function() {
+            EventBridge.emitWebEvent(JSON.stringify({
+                type: "action",
+                action: "reloadScript"
+            }));
+        });
+        */
+        elPreviewCameraButton.addEventListener("click", function() {
+            EventBridge.emitWebEvent(JSON.stringify({
+                type: "action",
+                action: "previewCamera"
+            }));
+        });
+
+        window.onblur = function() {
+            // Fake a change event
+            var ev = document.createEvent("HTMLEvents");
+            ev.initEvent("change", true, true);
+            document.activeElement.dispatchEvent(ev);
+        }
+
+        // For input and textarea elements, select all of the text on focus
+        // WebKit-based browsers, such as is used with QWebView, have a quirk
+        // where the mouseup event comes after the focus event, causing the
+        // text to be deselected immediately after selecting all of the text.
+        // To make this work we block the first mouseup event after the elements
+        // received focus.  If we block all mouseup events the user will not
+        // be able to click within the selected text.
+        // We also check to see if the value has changed to make sure we aren't
+        // blocking a mouse-up event when clicking on an input spinner.
+        var els = document.querySelectorAll("input, textarea");
+        for (var i = 0; i < els.length; i++) {
+            var clicked = false;
+            var originalText;
+            els[i].onfocus = function(e) {
+                originalText = this.value;
+                this.select();
+                clicked = false;
+            };
+            els[i].onmouseup = function(e) {
+                if (!clicked && originalText == this.value) {
+                    e.preventDefault();
+                }
+                clicked = true;
+            };
+        }
+    });
+
+    // Collapsible sections
+    var elCollapsible = document.getElementsByClassName("section-header");
+
+    var toggleCollapsedEvent = function (event) {
+        var element = event.target;
+        if (element.nodeName !== "DIV") {
+            element = element.parentNode;
+        }
+        var isCollapsed = element.getAttribute("collapsed") !== "true";
+        element.setAttribute("collapsed", isCollapsed ? "true" : "false");
+        element.getElementsByTagName("span")[0].textContent = isCollapsed ? "L" : "M";
+    };
+
+    for (var i = 0, length = elCollapsible.length; i < length; i++) {
+        var element = elCollapsible[i];
+        element.addEventListener("click", toggleCollapsedEvent, true);
+    };
+
+
+    // Textarea scrollbars
+    var elTextareas = document.getElementsByTagName("TEXTAREA");
+
+    var textareaOnChangeEvent = function (event) {
+        setTextareaScrolling(event.target);
+    }
+
+    for (var i = 0, length = elTextareas.length; i < length; i++) {
+        var element = elTextareas[i];
+        setTextareaScrolling(element);
+        element.addEventListener("input", textareaOnChangeEvent, false);
+        element.addEventListener("change", textareaOnChangeEvent, false);
+        /* FIXME: Detect and update textarea scrolling attribute on resize. Unfortunately textarea doesn't have a resize
+        event; mouseup is a partial stand-in but doesn't handle resizing if mouse moves outside textarea rectangle. */
+        element.addEventListener("mouseup", textareaOnChangeEvent, false);
+    };
+
+    // Dropdowns
+    // For each dropdown the following replacement is created in place of the oriringal dropdown...
+    // Structure created:
+    //  <dl dropped="true/false">
+    //      <dt name="?" id="?" value="?"><span>display text</span><span>carat</span></dt>
+    //      <dd>
+    //          <ul>
+    //              <li value="??>display text</li>
+    //              <li>...</li>
+    //          </ul>
+    //      </dd>
+    //  </dl>
+
+    function setDropdownText(dropdown) {
+        var lis = dropdown.parentNode.getElementsByTagName("li");
+        var text = "";
+        for (var i = 0; i < lis.length; i++) {
+            if (lis[i].getAttribute("value") === dropdown.value) {
+                text = lis[i].textContent;
+            }
+        }
+        dropdown.firstChild.textContent = text;
+    }
+
+    function toggleDropdown(event) {
+        var element = event.target;
+        if (element.nodeName !== "DT") {
+            element = element.parentNode;
+        }
+        element = element.parentNode;
+        var isDropped = element.getAttribute("dropped");
+        element.setAttribute("dropped", isDropped !== "true" ? "true" : "false");
+    }
+
+    function setDropdownValue(event) {
+        var dt = event.target.parentNode.parentNode.previousSibling;
+        dt.value = event.target.getAttribute("value");
+        dt.firstChild.textContent = event.target.textContent;
+
+        dt.parentNode.setAttribute("dropped", "false");
+
+        var evt = document.createEvent("HTMLEvents");
+        evt.initEvent("change", true, true);
+        dt.dispatchEvent(evt);
+    }
+
+    var elDropdowns = document.getElementsByTagName("select");
+    for (var i = 0; i < elDropdowns.length; i++) {
+        var options = elDropdowns[i].getElementsByTagName("option");
+        var selectedOption = 0;
+        for (var j = 0; j < options.length; j++) {
+            if (options[j].getAttribute("selected") === "selected") {
+                selectedOption = j;
+            }
+        }
+        var div = elDropdowns[i].parentNode;
+
+        var dl = document.createElement("dl");
+        div.appendChild(dl);
+
+        var dt = document.createElement("dt");
+        dt.name = elDropdowns[i].name;
+        dt.id = elDropdowns[i].id;
+        dt.addEventListener("click", toggleDropdown, true);
+        dl.appendChild(dt);
+
+        var span = document.createElement("span");
+        span.setAttribute("value", options[selectedOption].value);
+        span.textContent = options[selectedOption].firstChild.textContent;
+        dt.appendChild(span);
+
+        var span = document.createElement("span");
+        span.textContent = "5";  // caratDn
+        dt.appendChild(span);
+
+        var dd = document.createElement("dd");
+        dl.appendChild(dd);
+
+        var ul = document.createElement("ul");
+        dd.appendChild(ul);
+
+        for (var j = 0; j < options.length; j++) {
+            var li = document.createElement("li");
+            li.setAttribute("value", options[j].value);
+            li.textContent = options[j].firstChild.textContent;
+            li.addEventListener("click", setDropdownValue);
+            ul.appendChild(li);
+        }
+    }
+
+    elDropdowns = document.getElementsByTagName("select");
+    while (elDropdowns.length > 0) {
+        var el = elDropdowns[0];
+        el.parentNode.removeChild(el);
+        elDropdowns = document.getElementsByTagName("select");
+    }
+
+    augmentSpinButtons();
+
+    // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
+    document.addEventListener("contextmenu", function (event) {
+        event.preventDefault();
+    }, false);
+}
+
diff --git a/scripts/system/html/gridControls.html b/scripts/system/html/gridControls.html
index 4b18a8555b..f7f206d702 100644
--- a/scripts/system/html/gridControls.html
+++ b/scripts/system/html/gridControls.html
@@ -10,144 +10,14 @@
 
 <html>
 <head>
-    <link rel="stylesheet" type="text/css" href="edit-style.css">
+    <link rel="stylesheet" type="text/css" href="css/edit-style.css">
     <link rel="stylesheet" type="text/css" href="css/colpick.css">
-    <script src="jquery-2.1.4.min.js"></script>
-    <script src="colpick.js"></script>
+    <script src="js/jquery-2.1.4.min.js"></script>
+    <script src="js/colpick.js"></script>
     <script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
-    <script type="text/javascript" src="eventBridgeLoader.js"></script>
-    <script type="text/javascript" src="spinButtons.js"></script>
-    <script>
-        function loaded() {
-            openEventBridge(function() {
-                elPosY = document.getElementById("horiz-y");
-                elMinorSpacing = document.getElementById("minor-spacing");
-                elMajorSpacing = document.getElementById("major-spacing");
-                elSnapToGrid = document.getElementById("snap-to-grid");
-                elHorizontalGridVisible = document.getElementById("horiz-grid-visible");
-                elMoveToSelection = document.getElementById("move-to-selection");
-                elMoveToAvatar = document.getElementById("move-to-avatar");
-    
-                if (window.EventBridge !== undefined) {
-                    EventBridge.scriptEventReceived.connect(function(data) {
-                        data = JSON.parse(data);
-
-                        if (data.origin) {
-                            var origin = data.origin;
-                            elPosY.value = origin.y;
-                        }
-
-                        if (data.minorGridEvery !== undefined) {
-                            elMinorSpacing.value = data.minorGridEvery;
-                        }
-    
-                        if (data.majorGridEvery !== undefined) {
-                            elMajorSpacing.value = data.majorGridEvery;
-                        }
-    
-                        if (data.gridColor) {
-                            gridColor = data.gridColor;
-                        }
-    
-                        if (data.snapToGrid !== undefined) {
-                            elSnapToGrid.checked = data.snapToGrid == true;
-                        }
-    
-                        if (data.visible !== undefined) {
-                            elHorizontalGridVisible.checked = data.visible == true;
-                        }
-                    });
-    
-                    function emitUpdate() {
-                        EventBridge.emitWebEvent(JSON.stringify({
-                            type: "update",
-                            origin: {
-                                y: elPosY.value,
-                            },
-                            minorGridEvery: elMinorSpacing.value,
-                            majorGridEvery: elMajorSpacing.value,
-                            gridColor: gridColor,
-                            snapToGrid: elSnapToGrid.checked,
-                            visible: elHorizontalGridVisible.checked,
-                        }));
-                    }
-    
-                }
-    
-                elPosY.addEventListener("change", emitUpdate);
-                elMinorSpacing.addEventListener("change", emitUpdate);
-                elMajorSpacing.addEventListener("change", emitUpdate);
-                elSnapToGrid.addEventListener("change", emitUpdate);
-                elHorizontalGridVisible.addEventListener("change", emitUpdate);
-    
-                elMoveToAvatar.addEventListener("click", function() {
-                    EventBridge.emitWebEvent(JSON.stringify({
-                        type: "action",
-                        action: "moveToAvatar",
-                    }));
-                });
-                elMoveToSelection.addEventListener("click", function() {
-                    EventBridge.emitWebEvent(JSON.stringify({
-                        type: "action",
-                        action: "moveToSelection",
-                    }));
-                });
-    
-                var gridColor = { red: 255, green: 255, blue: 255 };
-                var elColor = document.getElementById("grid-color");
-                var elColorRed = document.getElementById("grid-color-red");
-                var elColorGreen = document.getElementById("grid-color-green");
-                var elColorBlue = document.getElementById("grid-color-blue");
-                elColor.style.backgroundColor = "rgb(" + gridColor.red + "," + gridColor.green + "," + gridColor.blue + ")";
-                elColorRed.value = gridColor.red;
-                elColorGreen.value = gridColor.green;
-                elColorBlue.value = gridColor.blue;
-
-                var colorChangeFunction = function () {
-                    gridColor = { red: elColorRed.value, green: elColorGreen.value, blue: elColorBlue.value };
-                    elColor.style.backgroundColor = "rgb(" + gridColor.red + "," + gridColor.green + "," + gridColor.blue + ")";
-                    emitUpdate();
-                };
-
-                var colorPickFunction = function (red, green, blue) {
-                    elColorRed.value = red;
-                    elColorGreen.value = green;
-                    elColorBlue.value = blue;
-                    gridColor = { red: red, green: green, blue: blue };
-                    emitUpdate();
-                }
-
-                elColorRed.addEventListener('change', colorChangeFunction);
-                elColorGreen.addEventListener('change', colorChangeFunction);
-                elColorBlue.addEventListener('change', colorChangeFunction);
-                $('#grid-color').colpick({
-                    colorScheme: 'dark',
-                    layout: 'hex',
-                    color: { r: gridColor.red, g: gridColor.green, b: gridColor.blue },
-                    onShow: function (colpick) {
-                        $('#grid-color').attr('active', 'true');
-                    },
-                    onHide: function (colpick) {
-                        $('#grid-color').attr('active', 'false');
-                    },
-                    onSubmit: function (hsb, hex, rgb, el) {
-                        $(el).css('background-color', '#' + hex);
-                        $(el).colpickHide();
-                        colorPickFunction(rgb.r, rgb.g, rgb.b);
-                    }
-                });
-
-                augmentSpinButtons();
-
-                EventBridge.emitWebEvent(JSON.stringify({ type: 'init' }));
-            });
-
-            // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
-            document.addEventListener("contextmenu", function (event) {
-                event.preventDefault();
-            }, false);
-        }
-    </script>
+    <script type="text/javascript" src="js/eventBridgeLoader.js"></script>
+    <script type="text/javascript" src="js/spinButtons.js"></script>
+    <script type="text/javascript" src="js/gridControls.js"></script>
 </head>
 <body onload='loaded();'>
     <div id="grid-section">
diff --git a/scripts/system/html/colpick.js b/scripts/system/html/js/colpick.js
similarity index 100%
rename from scripts/system/html/colpick.js
rename to scripts/system/html/js/colpick.js
diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js
new file mode 100644
index 0000000000..8e5e190068
--- /dev/null
+++ b/scripts/system/html/js/entityList.js
@@ -0,0 +1,310 @@
+//  entityList.js
+//
+//  Created by Ryan Huffman on 19 Nov 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
+
+var entities = {};
+var selectedEntities = [];
+var currentSortColumn = 'type';
+var currentSortOrder = 'des';
+var entityList = null;
+var refreshEntityListTimer = null;
+const ASCENDING_STRING = '&#x25BE;';
+const DESCENDING_STRING = '&#x25B4;';
+const LOCKED_GLYPH = "&#xe006;";
+const VISIBLE_GLYPH = "&#xe007;";
+const DELETE = 46; // Key code for the delete key.
+const MAX_ITEMS = Number.MAX_VALUE; // Used to set the max length of the list of discovered entities.
+
+debugPrint = function (message) {
+    console.log(message);
+};
+
+function loaded() {
+  openEventBridge(function() { 
+      entityList = new List('entity-list', { valueNames: ['name', 'type', 'url', 'locked', 'visible'], page: MAX_ITEMS});
+      entityList.clear();
+      elEntityTable = document.getElementById("entity-table");
+      elEntityTableBody = document.getElementById("entity-table-body");
+      elRefresh = document.getElementById("refresh");
+      elToggleLocked = document.getElementById("locked");
+      elToggleVisible = document.getElementById("visible");
+      elDelete = document.getElementById("delete");
+      elTeleport = document.getElementById("teleport");
+      elRadius = document.getElementById("radius");
+      elFooter = document.getElementById("footer-text");
+      elNoEntitiesMessage = document.getElementById("no-entities");
+      elNoEntitiesRadius = document.getElementById("no-entities-radius");
+      elEntityTableScroll = document.getElementById("entity-table-scroll");
+      
+      document.getElementById("entity-name").onclick = function() {
+          setSortColumn('name');
+      };
+      document.getElementById("entity-type").onclick = function() {
+          setSortColumn('type');
+      };
+      document.getElementById("entity-url").onclick = function() {
+          setSortColumn('url');
+      };
+      document.getElementById("entity-locked").onclick = function () {
+          setSortColumn('locked');
+      };
+      document.getElementById("entity-visible").onclick = function () {
+          setSortColumn('visible');
+      };
+      
+      function onRowClicked(clickEvent) {
+          var id = this.dataset.entityId;
+          var selection = [this.dataset.entityId];
+          if (clickEvent.ctrlKey) {
+              selection = selection.concat(selectedEntities);
+          } else if (clickEvent.shiftKey && selectedEntities.length > 0) {
+              var previousItemFound = -1;
+              var clickedItemFound = -1; 
+              for (var entity in entityList.visibleItems) {
+                  if (clickedItemFound === -1 && this.dataset.entityId == entityList.visibleItems[entity].values().id) {
+                      clickedItemFound = entity;
+                  } else if(previousItemFound === -1 && selectedEntities[0] == entityList.visibleItems[entity].values().id) {
+                      previousItemFound = entity;
+                  }
+              }
+              if (previousItemFound !== -1 && clickedItemFound !== -1) {
+                  var betweenItems = [];
+                  var toItem = Math.max(previousItemFound, clickedItemFound);
+                  // skip first and last item in this loop, we add them to selection after the loop
+                  for (var i = (Math.min(previousItemFound, clickedItemFound) + 1); i < toItem; i++) {
+                      entityList.visibleItems[i].elm.className = 'selected';
+                      betweenItems.push(entityList.visibleItems[i].values().id);
+                  }
+                  if (previousItemFound > clickedItemFound) {
+                      // always make sure that we add the items in the right order
+                      betweenItems.reverse();
+                  }
+                  selection = selection.concat(betweenItems, selectedEntities);
+              }
+          }
+      
+          selectedEntities = selection;
+      
+          this.className = 'selected';
+      
+          EventBridge.emitWebEvent(JSON.stringify({
+              type: "selectionUpdate",
+              focus: false,
+              entityIds: selection,
+          }));
+      }
+      
+      function onRowDoubleClicked() {
+          EventBridge.emitWebEvent(JSON.stringify({
+              type: "selectionUpdate",
+              focus: true,
+              entityIds: [this.dataset.entityId],
+          }));
+      }
+      
+      function addEntity(id, name, type, url, locked, visible) {
+          var urlParts = url.split('/');
+          var filename = urlParts[urlParts.length - 1];
+
+          if (entities[id] === undefined) {
+              entityList.add([{ id: id, name: name, type: type, url: filename, locked: locked, visible: visible }],
+                  function (items) {
+                      var currentElement = items[0].elm;
+                      var id = items[0]._values.id;
+                      entities[id] = {
+                          id: id,
+                          name: name,
+                          el: currentElement,
+                          item: items[0]
+                      };
+                      currentElement.setAttribute('id', 'entity_' + id);
+                      currentElement.setAttribute('title', url);
+                      currentElement.dataset.entityId = id;
+                      currentElement.onclick = onRowClicked;
+                      currentElement.ondblclick = onRowDoubleClicked;
+              });
+      
+              if (refreshEntityListTimer) {
+                  clearTimeout(refreshEntityListTimer);
+              }
+              refreshEntityListTimer = setTimeout(refreshEntityListObject, 50);
+          } else {
+              var item = entities[id].item;
+              item.values({ name: name, url: filename, locked: locked, visible: visible });
+          }
+      }
+      
+      function clearEntities() {
+          entities = {};
+          entityList.clear();
+      }
+      
+      var elSortOrder = {
+          name: document.querySelector('#entity-name .sort-order'),
+          type: document.querySelector('#entity-type .sort-order'),
+          url: document.querySelector('#entity-url .sort-order'),
+          locked: document.querySelector('#entity-locked .sort-order'),
+          visible: document.querySelector('#entity-visible .sort-order')
+      }
+      function setSortColumn(column) {
+          if (currentSortColumn == column) {
+              currentSortOrder = currentSortOrder == "asc" ? "desc" : "asc";
+          } else {
+              elSortOrder[currentSortColumn].innerHTML = "";
+              currentSortColumn = column;
+              currentSortOrder = "asc";
+          }
+          elSortOrder[column].innerHTML = currentSortOrder == "asc" ? ASCENDING_STRING : DESCENDING_STRING;
+          entityList.sort(currentSortColumn, { order: currentSortOrder });
+      }
+      setSortColumn('type');
+      
+      function refreshEntities() {
+          clearEntities();
+          EventBridge.emitWebEvent(JSON.stringify({ type: 'refresh' }));
+      }
+      
+      function refreshEntityListObject() {
+          refreshEntityListTimer = null;
+          entityList.sort(currentSortColumn, { order: currentSortOrder });
+          entityList.search(document.getElementById("filter").value);
+      }
+      
+      function updateSelectedEntities(selectedEntities) {
+          var notFound = false;
+          for (var id in entities) {
+              entities[id].el.className = '';
+          }
+          for (var i = 0; i < selectedEntities.length; i++) {
+              var id = selectedEntities[i];
+              if (id in entities) {
+                  var entity = entities[id];
+                  entity.el.className = 'selected';
+              } else {
+                  notFound = true;
+              }
+          }
+
+          if (selectedEntities.length > 1) {
+              elFooter.firstChild.nodeValue = selectedEntities.length + " entities selected";
+          } else if (selectedEntities.length === 1) {
+              elFooter.firstChild.nodeValue = "1 entity selected";
+          } else if (entityList.visibleItems.length === 1) {
+              elFooter.firstChild.nodeValue = "1 entity found";
+          } else {
+              elFooter.firstChild.nodeValue = entityList.visibleItems.length + " entities found";
+          }
+
+          // HACK: Fixes the footer and header text sometimes not displaying after adding or deleting entities.
+          // The problem appears to be a bug in the Qt HTML/CSS rendering (Qt 5.5).
+          document.getElementById("radius").focus();
+          document.getElementById("radius").blur();
+
+          return notFound;
+      }
+      
+      elRefresh.onclick = function() {
+          refreshEntities();
+      }
+      elToggleLocked.onclick = function () {
+          EventBridge.emitWebEvent(JSON.stringify({ type: 'toggleLocked' }));
+      }
+      elToggleVisible.onclick = function () {
+          EventBridge.emitWebEvent(JSON.stringify({ type: 'toggleVisible' }));
+      }
+      elTeleport.onclick = function () {
+          EventBridge.emitWebEvent(JSON.stringify({ type: 'teleport' }));
+      }
+      elDelete.onclick = function() {
+          EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' }));
+          refreshEntities();
+      }
+      
+      document.addEventListener("keydown", function (keyDownEvent) {
+          if (keyDownEvent.target.nodeName === "INPUT") {
+              return;
+          }
+          var keyCode = keyDownEvent.keyCode;
+          if (keyCode === DELETE) {
+              EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' }));
+              refreshEntities();
+          }
+      }, false);
+      
+      elRadius.onchange = function () {
+          elRadius.value = Math.max(elRadius.value, 0);
+          EventBridge.emitWebEvent(JSON.stringify({ type: 'radius', radius: elRadius.value }));
+          refreshEntities();
+          elNoEntitiesRadius.firstChild.nodeValue = elRadius.value;
+      }
+      
+      if (window.EventBridge !== undefined) {
+          EventBridge.scriptEventReceived.connect(function(data) {
+              data = JSON.parse(data);
+      
+              if (data.type === "clearEntityList") {
+                  clearEntities();
+              } else if (data.type == "selectionUpdate") {
+                  var notFound = updateSelectedEntities(data.selectedIDs);
+                  if (notFound) {
+                      refreshEntities();
+                  }
+              } else if (data.type == "update") {
+                  var newEntities = data.entities;
+                  if (newEntities.length == 0) {
+                      elNoEntitiesMessage.style.display = "block";
+                      elFooter.firstChild.nodeValue = "0 entities found";
+                  } else {
+                      elNoEntitiesMessage.style.display = "none";
+                      for (var i = 0; i < newEntities.length; i++) {
+                          var id = newEntities[i].id;
+                          addEntity(id, newEntities[i].name, newEntities[i].type, newEntities[i].url,
+                              newEntities[i].locked ? LOCKED_GLYPH : null,
+                              newEntities[i].visible ? VISIBLE_GLYPH : null);
+                      }
+                      updateSelectedEntities(data.selectedIDs);
+                      resize();
+                  }
+              }
+          });
+          setTimeout(refreshEntities, 1000);
+      }
+
+      function resize() {
+          // Take up available window space
+          elEntityTableScroll.style.height = window.innerHeight - 207;
+
+          var tds = document.querySelectorAll("#entity-table-body tr:first-child td");
+          var ths = document.querySelectorAll("#entity-table thead th");
+          if (tds.length >= ths.length) {
+              // Update the widths of the header cells to match the body
+              for (var i = 0; i < ths.length; i++) {
+                  ths[i].width = tds[i].offsetWidth;
+              }
+          } else {
+              // Reasonable widths if nothing is displayed
+              var tableWidth = document.getElementById("entity-table").offsetWidth;
+              ths[0].width = 0.16 * tableWidth;
+              ths[1].width = 0.34 * tableWidth;
+              ths[2].width = 0.34 * tableWidth;
+              ths[3].width = 0.08 * tableWidth;
+              ths[4].width = 0.08 * tableWidth;
+          }
+      };
+
+      window.onresize = resize;
+      resize();
+  });
+
+  augmentSpinButtons();
+
+  // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
+  document.addEventListener("contextmenu", function (event) {
+      event.preventDefault();
+  }, false);
+}
+
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js
new file mode 100644
index 0000000000..490579e909
--- /dev/null
+++ b/scripts/system/html/js/entityProperties.js
@@ -0,0 +1,1317 @@
+//  entityProperties.js
+//
+//  Created by Ryan Huffman on 13 Nov 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
+
+var PI = 3.14159265358979;
+var DEGREES_TO_RADIANS = PI / 180.0;
+var RADIANS_TO_DEGREES = 180.0 / PI;
+var ICON_FOR_TYPE = {
+    Box: "V",
+    Sphere: "n",
+    Shape: "n",
+    ParticleEffect: "&#xe004;",
+    Model: "&#xe008;",
+    Web: "q",
+    Text: "l",
+    Light: "p",
+    Zone: "o",
+    PolyVox: "&#xe005;",
+    Multiple: "&#xe000;"
+}
+
+var colorPickers = [];
+
+debugPrint = function(message) {
+    EventBridge.emitWebEvent(
+        JSON.stringify({
+            type:"print", 
+            message: message
+        })
+    );
+};
+
+function enableChildren(el, selector) {
+    els = el.querySelectorAll(selector);
+    for (var i = 0; i < els.length; i++) {
+        els[i].removeAttribute('disabled');
+    }
+}
+function disableChildren(el, selector) {
+    els = el.querySelectorAll(selector);
+    for (var i = 0; i < els.length; i++) {
+        els[i].setAttribute('disabled', 'disabled');
+    }
+}
+
+function enableProperties() {
+    enableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker");
+    enableChildren(document, ".colpick");
+}
+
+function disableProperties() {
+    disableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker");
+    disableChildren(document, ".colpick");
+    for (var i = 0; i < colorPickers.length; i++) {
+        colorPickers[i].colpickHide();
+    }
+}
+
+function showElements(els, show) {
+    for (var i = 0; i < els.length; i++) {
+        els[i].style.display = (show) ? 'table' : 'none';
+    }
+}
+
+function createEmitCheckedPropertyUpdateFunction(propertyName) {
+    return function() {
+        EventBridge.emitWebEvent(
+            '{ "type":"update", "properties":{"' + propertyName + '":' + this.checked + '}}'
+        );
+    };
+}
+
+function createEmitCheckedToStringPropertyUpdateFunction(checkboxElement, name, propertyName) {
+    var newString = "";
+    if (checkboxElement.checked) {
+        newString += name + "";
+    } else {
+
+    }
+
+}
+
+function createEmitGroupCheckedPropertyUpdateFunction(group, propertyName) {
+    return function () {
+        var properties = {};
+        properties[group] = {};
+        properties[group][propertyName] = this.checked;
+        EventBridge.emitWebEvent(
+            JSON.stringify({
+                type: "update",
+                properties: properties
+            })
+        );
+    };
+}
+
+function createEmitNumberPropertyUpdateFunction(propertyName, decimals) {
+    decimals = decimals == undefined ? 4 : decimals;
+    return function() {
+        var value = parseFloat(this.value).toFixed(decimals);
+
+        EventBridge.emitWebEvent(
+            '{ "type":"update", "properties":{"' + propertyName + '":' + value + '}}'
+        );
+    };
+}
+function createEmitGroupNumberPropertyUpdateFunction(group, propertyName) {
+    return function() {
+        var properties = {};
+        properties[group] = {};
+        properties[group][propertyName] = this.value;
+        EventBridge.emitWebEvent(
+            JSON.stringify({
+                type: "update",
+                properties: properties,
+            })
+        );
+    };
+}
+
+
+function createEmitTextPropertyUpdateFunction(propertyName) {
+    return function() {
+        var properties = {};
+        properties[propertyName] = this.value;
+        EventBridge.emitWebEvent(
+            JSON.stringify({
+                type: "update",
+                properties: properties,
+            })
+        );
+    };
+}
+
+function createEmitGroupTextPropertyUpdateFunction(group,propertyName) {
+    return function() {
+        var properties = {};
+        properties[group] = {};
+        properties[group][propertyName] = this.value;
+        EventBridge.emitWebEvent(
+            JSON.stringify({
+                type: "update",
+                properties: properties,
+            })
+        );
+    };
+}
+
+function createEmitVec3PropertyUpdateFunction(property, elX, elY, elZ) {
+    return function() {
+        var data = {
+             type: "update",
+             properties: {
+             }
+        };
+        data.properties[property] = {
+             x: elX.value,
+             y: elY.value,
+             z: elZ.value,
+        };
+        EventBridge.emitWebEvent(JSON.stringify(data));
+    }
+};
+
+function createEmitGroupVec3PropertyUpdateFunction(group, property, elX, elY, elZ) {
+    return function() {
+        var data = {
+             type: "update",
+             properties: {
+             }
+        };
+        data.properties[group] = { };
+        data.properties[group][property] = {
+             x: elX.value,
+             y: elY.value,
+             z: elZ ? elZ.value : 0,
+        };
+        EventBridge.emitWebEvent(JSON.stringify(data));
+    }
+};
+
+function createEmitVec3PropertyUpdateFunctionWithMultiplier(property, elX, elY, elZ, multiplier) {
+    return function() {
+        var data = {
+             type: "update",
+             properties: {
+             }
+        };
+        data.properties[property] = {
+             x: elX.value * multiplier,
+             y: elY.value * multiplier,
+             z: elZ.value * multiplier,
+        };
+        EventBridge.emitWebEvent(JSON.stringify(data));
+    }
+};
+
+function createEmitColorPropertyUpdateFunction(property, elRed, elGreen, elBlue) {
+    return function() {
+        emitColorPropertyUpdate(property, elRed.value, elGreen.value, elBlue.value);
+    }
+};
+
+function emitColorPropertyUpdate(property, red, green, blue, group) {
+        var data = {
+             type: "update",
+             properties: {
+             }
+        };
+        if (group) {
+            data.properties[group] = { };
+            data.properties[group][property] = {
+                 red: red,
+                 green: green,
+                 blue: blue,
+            };
+        } else {
+            data.properties[property] = {
+                 red: red,
+                 green: green,
+                 blue: blue,
+            };
+        }
+        EventBridge.emitWebEvent(JSON.stringify(data));
+};
+
+
+function createEmitGroupColorPropertyUpdateFunction(group, property, elRed, elGreen, elBlue) {
+    return function() {
+        var data = {
+             type: "update",
+             properties: {
+             }
+        };
+        data.properties[group] = { };
+
+        data.properties[group][property] = {
+             red: elRed.value,
+             green: elGreen.value,
+             blue: elBlue.value,
+        };
+        EventBridge.emitWebEvent(JSON.stringify(data));
+    }
+};
+
+function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElement, subPropertyString) {
+    if (subPropertyElement.checked) {
+        if (propertyValue.indexOf(subPropertyString)) {
+            propertyValue += subPropertyString + ',';
+        } 
+    } else {
+        // We've unchecked, so remove 
+        propertyValue = propertyValue.replace(subPropertyString + ",", "");
+    }
+
+    var _properties ={}
+    _properties[propertyName] = propertyValue;
+    
+    EventBridge.emitWebEvent(
+      JSON.stringify({
+        type: "update",
+        properties: _properties
+      })
+    );
+
+}
+
+function userDataChanger(groupName, keyName, checkBoxElement, userDataElement, defaultValue) {
+    var properties = {};
+    var parsedData = {};
+    try {
+        parsedData = JSON.parse(userDataElement.value);
+    } catch(e) {}
+
+    if (!(groupName in parsedData)) {
+        parsedData[groupName] = {}
+    }
+    delete parsedData[groupName][keyName];
+    if (checkBoxElement.checked !== defaultValue) {
+        parsedData[groupName][keyName] = checkBoxElement.checked;
+    }
+
+    if (Object.keys(parsedData[groupName]).length == 0) {
+        delete parsedData[groupName];
+    }
+    if (Object.keys(parsedData).length > 0) {
+        properties['userData'] = JSON.stringify(parsedData);
+    } else {
+        properties['userData'] = '';
+    }
+
+    userDataElement.value = properties['userData'];
+
+    EventBridge.emitWebEvent(
+        JSON.stringify({
+            type: "update",
+            properties: properties,
+        })
+    );
+};
+
+function setTextareaScrolling(element) {
+    var isScrolling = element.scrollHeight > element.offsetHeight;
+    element.setAttribute("scrolling", isScrolling ? "true" : "false");
+}
+
+function loaded() { 
+    openEventBridge(function() {
+        var allSections = [];
+        var elID = document.getElementById("property-id");
+        var elType = document.getElementById("property-type");
+        var elTypeIcon = document.getElementById("type-icon");
+        var elName = document.getElementById("property-name");
+        var elLocked = document.getElementById("property-locked");
+        var elVisible = document.getElementById("property-visible");
+        var elPositionX = document.getElementById("property-pos-x");
+        var elPositionY = document.getElementById("property-pos-y");
+        var elPositionZ = document.getElementById("property-pos-z");
+        var elMoveSelectionToGrid = document.getElementById("move-selection-to-grid");
+        var elMoveAllToGrid = document.getElementById("move-all-to-grid");
+
+        var elDimensionsX = document.getElementById("property-dim-x");
+        var elDimensionsY = document.getElementById("property-dim-y");
+        var elDimensionsZ = document.getElementById("property-dim-z");
+        var elResetToNaturalDimensions = document.getElementById("reset-to-natural-dimensions");
+        var elRescaleDimensionsPct = document.getElementById("dimension-rescale-pct");
+        var elRescaleDimensionsButton = document.getElementById("dimension-rescale-button");
+
+        var elParentID = document.getElementById("property-parent-id");
+        var elParentJointIndex = document.getElementById("property-parent-joint-index");
+
+        var elRegistrationX = document.getElementById("property-reg-x");
+        var elRegistrationY = document.getElementById("property-reg-y");
+        var elRegistrationZ = document.getElementById("property-reg-z");
+
+        var elRotationX = document.getElementById("property-rot-x");
+        var elRotationY = document.getElementById("property-rot-y");
+        var elRotationZ = document.getElementById("property-rot-z");
+
+        var elLinearVelocityX = document.getElementById("property-lvel-x");
+        var elLinearVelocityY = document.getElementById("property-lvel-y");
+        var elLinearVelocityZ = document.getElementById("property-lvel-z");
+        var elLinearDamping = document.getElementById("property-ldamping");
+
+        var elAngularVelocityX = document.getElementById("property-avel-x");
+        var elAngularVelocityY = document.getElementById("property-avel-y");
+        var elAngularVelocityZ = document.getElementById("property-avel-z");
+        var elAngularDamping = document.getElementById("property-adamping");
+
+        var elRestitution = document.getElementById("property-restitution");
+        var elFriction = document.getElementById("property-friction");
+
+        var elGravityX = document.getElementById("property-grav-x");
+        var elGravityY = document.getElementById("property-grav-y");
+        var elGravityZ = document.getElementById("property-grav-z");
+
+        var elAccelerationX = document.getElementById("property-lacc-x");
+        var elAccelerationY = document.getElementById("property-lacc-y");
+        var elAccelerationZ = document.getElementById("property-lacc-z");
+
+        var elDensity = document.getElementById("property-density");
+        var elCollisionless = document.getElementById("property-collisionless");
+        var elDynamic = document.getElementById("property-dynamic" );
+        var elCollideStatic = document.getElementById("property-collide-static");
+        var elCollideDynamic = document.getElementById("property-collide-dynamic");
+        var elCollideKinematic = document.getElementById("property-collide-kinematic");
+        var elCollideMyAvatar = document.getElementById("property-collide-myAvatar");
+        var elCollideOtherAvatar = document.getElementById("property-collide-otherAvatar");
+        var elCollisionSoundURL = document.getElementById("property-collision-sound-url");
+
+        var elGrabbable = document.getElementById("property-grabbable");
+        var elWantsTrigger = document.getElementById("property-wants-trigger");
+        var elIgnoreIK = document.getElementById("property-ignore-ik");
+
+        var elLifetime = document.getElementById("property-lifetime");
+        var elScriptURL = document.getElementById("property-script-url");
+        /*
+        FIXME: See FIXME for property-script-url.
+        var elScriptTimestamp = document.getElementById("property-script-timestamp");
+        */
+        var elReloadScriptButton = document.getElementById("reload-script-button");
+        var elUserData = document.getElementById("property-user-data");
+
+        var elColorSections = document.querySelectorAll(".color-section");
+        var elColor = document.getElementById("property-color");
+        var elColorRed = document.getElementById("property-color-red");
+        var elColorGreen = document.getElementById("property-color-green");
+        var elColorBlue = document.getElementById("property-color-blue");
+
+        var elShapeSections = document.querySelectorAll(".shape-section");
+        allSections.push(elShapeSections);
+        var elShape = document.getElementById("property-shape");
+        
+        var elLightSections = document.querySelectorAll(".light-section");
+        allSections.push(elLightSections);
+        var elLightSpotLight = document.getElementById("property-light-spot-light");
+        var elLightColor = document.getElementById("property-light-color");
+        var elLightColorRed = document.getElementById("property-light-color-red");
+        var elLightColorGreen = document.getElementById("property-light-color-green");
+        var elLightColorBlue = document.getElementById("property-light-color-blue");
+
+        var elLightIntensity = document.getElementById("property-light-intensity");
+        var elLightFalloffRadius = document.getElementById("property-light-falloff-radius");
+        var elLightExponent = document.getElementById("property-light-exponent");
+        var elLightCutoff = document.getElementById("property-light-cutoff");
+
+        var elModelSections = document.querySelectorAll(".model-section");
+        allSections.push(elModelSections);
+        var elModelURL = document.getElementById("property-model-url");
+        var elShapeType = document.getElementById("property-shape-type");
+        var elCompoundShapeURL = document.getElementById("property-compound-shape-url");
+        var elModelAnimationURL = document.getElementById("property-model-animation-url");
+        var elModelAnimationPlaying = document.getElementById("property-model-animation-playing");
+        var elModelAnimationFPS = document.getElementById("property-model-animation-fps");
+        var elModelAnimationFrame = document.getElementById("property-model-animation-frame");
+        var elModelAnimationFirstFrame = document.getElementById("property-model-animation-first-frame");
+        var elModelAnimationLastFrame = document.getElementById("property-model-animation-last-frame");
+        var elModelAnimationLoop = document.getElementById("property-model-animation-loop");
+        var elModelAnimationHold = document.getElementById("property-model-animation-hold");
+        var elModelTextures = document.getElementById("property-model-textures");
+        var elModelOriginalTextures = document.getElementById("property-model-original-textures");
+
+        var elWebSections = document.querySelectorAll(".web-section");
+        allSections.push(elWebSections);
+        var elWebSourceURL = document.getElementById("property-web-source-url");
+
+        var elDescription = document.getElementById("property-description");
+
+        var elHyperlinkHref = document.getElementById("property-hyperlink-href");
+     
+        var elHyperlinkSections = document.querySelectorAll(".hyperlink-section");
+      
+
+        var elTextSections = document.querySelectorAll(".text-section");
+        allSections.push(elTextSections);
+        var elTextText = document.getElementById("property-text-text");
+        var elTextLineHeight = document.getElementById("property-text-line-height");
+        var elTextTextColor = document.getElementById("property-text-text-color");
+        var elTextFaceCamera = document.getElementById("property-text-face-camera");
+        var elTextTextColorRed = document.getElementById("property-text-text-color-red");
+        var elTextTextColorGreen = document.getElementById("property-text-text-color-green");
+        var elTextTextColorBlue = document.getElementById("property-text-text-color-blue");
+        var elTextBackgroundColor = document.getElementById("property-text-background-color");
+        var elTextBackgroundColorRed = document.getElementById("property-text-background-color-red");
+        var elTextBackgroundColorGreen = document.getElementById("property-text-background-color-green");
+        var elTextBackgroundColorBlue = document.getElementById("property-text-background-color-blue");
+
+        var elZoneSections = document.querySelectorAll(".zone-section");
+        allSections.push(elZoneSections);
+        var elZoneStageSunModelEnabled = document.getElementById("property-zone-stage-sun-model-enabled");
+
+        var elZoneKeyLightColor = document.getElementById("property-zone-key-light-color");
+        var elZoneKeyLightColorRed = document.getElementById("property-zone-key-light-color-red");
+        var elZoneKeyLightColorGreen = document.getElementById("property-zone-key-light-color-green");
+        var elZoneKeyLightColorBlue = document.getElementById("property-zone-key-light-color-blue");
+        var elZoneKeyLightIntensity = document.getElementById("property-zone-key-intensity");
+        var elZoneKeyLightAmbientIntensity = document.getElementById("property-zone-key-ambient-intensity");
+        var elZoneKeyLightDirectionX = document.getElementById("property-zone-key-light-direction-x");
+        var elZoneKeyLightDirectionY = document.getElementById("property-zone-key-light-direction-y");
+        var elZoneKeyLightDirectionZ = document.getElementById("property-zone-key-light-direction-z");
+        var elZoneKeyLightAmbientURL = document.getElementById("property-zone-key-ambient-url");
+
+        var elZoneStageLatitude = document.getElementById("property-zone-stage-latitude");
+        var elZoneStageLongitude = document.getElementById("property-zone-stage-longitude");
+        var elZoneStageAltitude = document.getElementById("property-zone-stage-altitude");
+        var elZoneStageAutomaticHourDay = document.getElementById("property-zone-stage-automatic-hour-day");
+        var elZoneStageDay = document.getElementById("property-zone-stage-day");
+        var elZoneStageHour = document.getElementById("property-zone-stage-hour");
+
+        var elZoneBackgroundMode = document.getElementById("property-zone-background-mode");
+
+        var elZoneSkyboxColor = document.getElementById("property-zone-skybox-color");
+        var elZoneSkyboxColorRed = document.getElementById("property-zone-skybox-color-red");
+        var elZoneSkyboxColorGreen = document.getElementById("property-zone-skybox-color-green");
+        var elZoneSkyboxColorBlue = document.getElementById("property-zone-skybox-color-blue");
+        var elZoneSkyboxURL = document.getElementById("property-zone-skybox-url");
+
+        var elZoneFlyingAllowed = document.getElementById("property-zone-flying-allowed");
+        var elZoneGhostingAllowed = document.getElementById("property-zone-ghosting-allowed");
+
+        var elPolyVoxSections = document.querySelectorAll(".poly-vox-section");
+        allSections.push(elPolyVoxSections);
+        var elVoxelVolumeSizeX = document.getElementById("property-voxel-volume-size-x");
+        var elVoxelVolumeSizeY = document.getElementById("property-voxel-volume-size-y");
+        var elVoxelVolumeSizeZ = document.getElementById("property-voxel-volume-size-z");
+        var elVoxelSurfaceStyle = document.getElementById("property-voxel-surface-style");
+        var elXTextureURL = document.getElementById("property-x-texture-url");
+        var elYTextureURL = document.getElementById("property-y-texture-url");
+        var elZTextureURL = document.getElementById("property-z-texture-url");
+
+        var elPreviewCameraButton = document.getElementById("preview-camera-button");
+
+        if (window.EventBridge !== undefined) {
+            var properties;
+            EventBridge.scriptEventReceived.connect(function(data) {
+                data = JSON.parse(data);
+                if (data.type == "update") {
+                    if (data.selections.length == 0) {
+                        elTypeIcon.style.display = "none";
+                        elType.innerHTML = "<i>No selection</i>";
+                        elID.innerHTML = "";
+                        disableProperties();
+                    } else if (data.selections.length > 1) {
+                        var selections = data.selections;
+
+                        var ids = [];
+                        var types = {};
+                        var numTypes = 0;
+
+                        for (var i = 0; i < selections.length; i++) {
+                            ids.push(selections[i].id);
+                            var type = selections[i].properties.type;
+                            if (types[type] === undefined) {
+                                types[type] = 0;
+                                numTypes += 1;
+                            }
+                            types[type]++;
+                        }
+
+                        var type;
+                        if (numTypes === 1) {
+                            type = selections[0].properties.type;
+                        } else {
+                            type = "Multiple";
+                        }
+                        elType.innerHTML = type + " (" + data.selections.length + ")";
+                        elTypeIcon.innerHTML = ICON_FOR_TYPE[type];
+                        elTypeIcon.style.display = "inline-block";
+
+                        elID.innerHTML = ids.join("<br>");
+
+                        disableProperties();
+                    } else {
+    
+
+                        properties = data.selections[0].properties;
+
+                        elID.innerHTML = properties.id;
+
+                        elType.innerHTML = properties.type;
+                        elTypeIcon.innerHTML = ICON_FOR_TYPE[properties.type];
+                        elTypeIcon.style.display = "inline-block";
+
+                        elLocked.checked = properties.locked;
+
+                        if (properties.locked) {
+                            disableProperties();
+                            elLocked.removeAttribute('disabled');
+                        } else {
+                            enableProperties();
+                        }
+
+                        elName.value = properties.name;
+
+                        elVisible.checked = properties.visible;
+
+                        elPositionX.value = properties.position.x.toFixed(4);
+                        elPositionY.value = properties.position.y.toFixed(4);
+                        elPositionZ.value = properties.position.z.toFixed(4);
+
+                        elDimensionsX.value = properties.dimensions.x.toFixed(4);
+                        elDimensionsY.value = properties.dimensions.y.toFixed(4);
+                        elDimensionsZ.value = properties.dimensions.z.toFixed(4);
+
+                        elParentID.value = properties.parentID;
+                        elParentJointIndex.value = properties.parentJointIndex;
+
+                        elRegistrationX.value = properties.registrationPoint.x.toFixed(4);
+                        elRegistrationY.value = properties.registrationPoint.y.toFixed(4);
+                        elRegistrationZ.value = properties.registrationPoint.z.toFixed(4);
+
+                        elRotationX.value = properties.rotation.x.toFixed(4);
+                        elRotationY.value = properties.rotation.y.toFixed(4);
+                        elRotationZ.value = properties.rotation.z.toFixed(4);
+
+                        elLinearVelocityX.value = properties.velocity.x.toFixed(4);
+                        elLinearVelocityY.value = properties.velocity.y.toFixed(4);
+                        elLinearVelocityZ.value = properties.velocity.z.toFixed(4);
+                        elLinearDamping.value = properties.damping.toFixed(2);
+
+                        elAngularVelocityX.value = (properties.angularVelocity.x * RADIANS_TO_DEGREES).toFixed(4);
+                        elAngularVelocityY.value = (properties.angularVelocity.y * RADIANS_TO_DEGREES).toFixed(4);
+                        elAngularVelocityZ.value = (properties.angularVelocity.z * RADIANS_TO_DEGREES).toFixed(4);
+                        elAngularDamping.value = properties.angularDamping.toFixed(4);
+
+                        elRestitution.value = properties.restitution.toFixed(4);
+                        elFriction.value = properties.friction.toFixed(4);
+
+                        elGravityX.value = properties.gravity.x.toFixed(4);
+                        elGravityY.value = properties.gravity.y.toFixed(4);
+                        elGravityZ.value = properties.gravity.z.toFixed(4);
+
+                        elAccelerationX.value = properties.acceleration.x.toFixed(4);
+                        elAccelerationY.value = properties.acceleration.y.toFixed(4);
+                        elAccelerationZ.value = properties.acceleration.z.toFixed(4);
+
+                        elDensity.value = properties.density.toFixed(4);
+                        elCollisionless.checked = properties.collisionless;
+                        elDynamic.checked = properties.dynamic;
+
+                        elCollideStatic.checked = properties.collidesWith.indexOf("static") > -1;
+                        elCollideKinematic.checked = properties.collidesWith.indexOf("kinematic") > -1;
+                        elCollideDynamic.checked = properties.collidesWith.indexOf("dynamic") > -1;
+                        elCollideMyAvatar.checked = properties.collidesWith.indexOf("myAvatar") > -1;
+                        elCollideOtherAvatar.checked = properties.collidesWith.indexOf("otherAvatar") > -1;
+
+                        elGrabbable.checked = properties.dynamic;
+                        elWantsTrigger.checked = false;
+                        elIgnoreIK.checked = false;
+                        var parsedUserData = {}
+                        try {
+                            parsedUserData = JSON.parse(properties.userData);
+                                                    
+                            if ("grabbableKey" in parsedUserData) {
+                                if ("grabbable" in parsedUserData["grabbableKey"]) {
+                                    elGrabbable.checked = parsedUserData["grabbableKey"].grabbable;
+                                }
+                                if ("wantsTrigger" in parsedUserData["grabbableKey"]) {
+                                    elWantsTrigger.checked = parsedUserData["grabbableKey"].wantsTrigger;
+                                }
+                                if ("ignoreIK" in parsedUserData["grabbableKey"]) {
+                                    elIgnoreIK.checked = parsedUserData["grabbableKey"].ignoreIK;
+                                }
+                            }
+                        } catch(e) {}
+
+                        elCollisionSoundURL.value = properties.collisionSoundURL;
+                        elLifetime.value = properties.lifetime;
+                        elScriptURL.value = properties.script;
+                        /*
+                        FIXME: See FIXME for property-script-url.
+                        elScriptTimestamp.value = properties.scriptTimestamp;
+                        */
+                        elUserData.value = properties.userData;
+                        setTextareaScrolling(elUserData);
+
+                        elHyperlinkHref.value = properties.href;
+                        elDescription.value = properties.description;
+
+                        for (var i = 0; i < allSections.length; i++) {
+                            for (var j = 0; j < allSections[i].length; j++) {
+                                allSections[i][j].style.display = 'none';
+                            }
+                        }                                    
+
+                        for (var i = 0; i < elHyperlinkSections.length; i++) {
+                            elHyperlinkSections[i].style.display = 'table';
+                        }
+
+                        if (properties.type == "Shape" || properties.type == "Box" || properties.type == "Sphere") {
+                            for (var i = 0; i < elShapeSections.length; i++) {
+                                elShapeSections[i].style.display = 'table';
+                            }
+                            elShape.value = properties.shape;
+                            setDropdownText(elShape);
+
+                        } else {
+                            for (var i = 0; i < elShapeSections.length; i++) {
+                                elShapeSections[i].style.display = 'none';
+                            }
+                        }
+                        
+                        if (properties.type == "Shape" || properties.type == "Box" || properties.type == "Sphere" || properties.type == "ParticleEffect") {
+                            for (var i = 0; i < elColorSections.length; i++) {
+                                elColorSections[i].style.display = 'table';
+                            }
+                            elColorRed.value = properties.color.red;
+                            elColorGreen.value = properties.color.green;
+                            elColorBlue.value = properties.color.blue;
+                            elColor.style.backgroundColor = "rgb(" + properties.color.red  + "," + properties.color.green + "," + properties.color.blue + ")";
+                        } else {
+                            for (var i = 0; i < elColorSections.length; i++) {
+                                elColorSections[i].style.display = 'none';
+                            }
+                        }
+
+                        if (properties.type == "Model") {
+                            for (var i = 0; i < elModelSections.length; i++) {
+                                elModelSections[i].style.display = 'table';
+                            }
+
+                            elModelURL.value = properties.modelURL;
+                            elShapeType.value = properties.shapeType;
+                            setDropdownText(elShapeType);
+                            elCompoundShapeURL.value = properties.compoundShapeURL;
+                            elModelAnimationURL.value = properties.animation.url;
+                            elModelAnimationPlaying.checked = properties.animation.running;
+                            elModelAnimationFPS.value = properties.animation.fps;
+                            elModelAnimationFrame.value = properties.animation.currentFrame;
+                            elModelAnimationFirstFrame.value = properties.animation.firstFrame;
+                            elModelAnimationLastFrame.value = properties.animation.lastFrame;
+                            elModelAnimationLoop.checked = properties.animation.loop;
+                            elModelAnimationHold.checked = properties.animation.hold;
+                            elModelTextures.value = properties.textures;
+                            setTextareaScrolling(elModelTextures);
+                            elModelOriginalTextures.value = properties.originalTextures;
+                            setTextareaScrolling(elModelOriginalTextures);
+                        } else if (properties.type == "Web") {
+                            for (var i = 0; i < elWebSections.length; i++) {
+                                elWebSections[i].style.display = 'table';
+                            }
+                            for (var i = 0; i < elHyperlinkSections.length; i++) {
+                                elHyperlinkSections[i].style.display = 'none';
+                            }
+
+                            elWebSourceURL.value = properties.sourceUrl;
+                        } else if (properties.type == "Text") {
+                            for (var i = 0; i < elTextSections.length; i++) {
+                                elTextSections[i].style.display = 'table';
+                            }
+
+                            elTextText.value = properties.text;
+                            elTextLineHeight.value = properties.lineHeight.toFixed(4);
+                            elTextFaceCamera = properties.faceCamera;
+                            elTextTextColor.style.backgroundColor = "rgb(" + properties.textColor.red  + "," + properties.textColor.green + "," + properties.textColor.blue + ")";
+                            elTextTextColorRed.value = properties.textColor.red;
+                            elTextTextColorGreen.value = properties.textColor.green;
+                            elTextTextColorBlue.value = properties.textColor.blue;
+                            elTextBackgroundColorRed.value = properties.backgroundColor.red;
+                            elTextBackgroundColorGreen.value = properties.backgroundColor.green;
+                            elTextBackgroundColorBlue.value = properties.backgroundColor.blue;
+                        } else if (properties.type == "Light") {
+                            for (var i = 0; i < elLightSections.length; i++) {
+                                elLightSections[i].style.display = 'table';
+                            }
+
+                            elLightSpotLight.checked = properties.isSpotlight;
+
+                            elLightColor.style.backgroundColor = "rgb(" + properties.color.red  + "," + properties.color.green + "," + properties.color.blue + ")";
+                            elLightColorRed.value = properties.color.red;
+                            elLightColorGreen.value = properties.color.green;
+                            elLightColorBlue.value = properties.color.blue;
+
+                            elLightIntensity.value = properties.intensity.toFixed(1);
+                            elLightFalloffRadius.value = properties.falloffRadius.toFixed(1);
+                            elLightExponent.value = properties.exponent.toFixed(2);
+                            elLightCutoff.value = properties.cutoff.toFixed(2);
+                        } else if (properties.type == "Zone") {
+                            for (var i = 0; i < elZoneSections.length; i++) {
+                                elZoneSections[i].style.display = 'table';
+                            }
+
+                            elZoneStageSunModelEnabled.checked = properties.stage.sunModelEnabled;
+                            elZoneKeyLightColor.style.backgroundColor = "rgb(" + properties.keyLight.color.red  + "," + properties.keyLight.color.green + "," + properties.keyLight.color.blue + ")";
+                            elZoneKeyLightColorRed.value = properties.keyLight.color.red;
+                            elZoneKeyLightColorGreen.value = properties.keyLight.color.green;
+                            elZoneKeyLightColorBlue.value = properties.keyLight.color.blue;
+                            elZoneKeyLightIntensity.value = properties.keyLight.intensity.toFixed(2);
+                            elZoneKeyLightAmbientIntensity.value = properties.keyLight.ambientIntensity.toFixed(2);
+                            elZoneKeyLightDirectionX.value = properties.keyLight.direction.x.toFixed(2);
+                            elZoneKeyLightDirectionY.value = properties.keyLight.direction.y.toFixed(2);
+                            elZoneKeyLightAmbientURL.value = properties.keyLight.ambientURL;
+
+
+                            elZoneStageLatitude.value = properties.stage.latitude.toFixed(2);
+                            elZoneStageLongitude.value = properties.stage.longitude.toFixed(2);
+                            elZoneStageAltitude.value = properties.stage.altitude.toFixed(2);
+                            elZoneStageAutomaticHourDay.checked = properties.stage.automaticHourDay;
+                            elZoneStageDay.value = properties.stage.day;
+                            elZoneStageHour.value = properties.stage.hour;
+                            elShapeType.value = properties.shapeType;
+                            elCompoundShapeURL.value = properties.compoundShapeURL;
+
+                            elZoneBackgroundMode.value = properties.backgroundMode;
+                            setDropdownText(elZoneBackgroundMode);
+
+                            elZoneSkyboxColor.style.backgroundColor = "rgb(" + properties.skybox.color.red  + "," + properties.skybox.color.green + "," + properties.skybox.color.blue + ")";
+                            elZoneSkyboxColorRed.value = properties.skybox.color.red;
+                            elZoneSkyboxColorGreen.value = properties.skybox.color.green;
+                            elZoneSkyboxColorBlue.value = properties.skybox.color.blue;
+                            elZoneSkyboxURL.value = properties.skybox.url;
+
+                            elZoneFlyingAllowed.checked = properties.flyingAllowed;
+                            elZoneGhostingAllowed.checked = properties.ghostingAllowed;
+
+                            showElements(document.getElementsByClassName('skybox-section'), elZoneBackgroundMode.value == 'skybox');
+                        } else if (properties.type == "PolyVox") {
+                            for (var i = 0; i < elPolyVoxSections.length; i++) {
+                                elPolyVoxSections[i].style.display = 'table';
+                            }
+
+                            elVoxelVolumeSizeX.value = properties.voxelVolumeSize.x.toFixed(2);
+                            elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2);
+                            elVoxelVolumeSizeZ.value = properties.voxelVolumeSize.z.toFixed(2);
+                            elVoxelSurfaceStyle.value = properties.voxelSurfaceStyle;
+                            setDropdownText(elVoxelSurfaceStyle);
+                            elXTextureURL.value = properties.xTextureURL;
+                            elYTextureURL.value = properties.yTextureURL;
+                            elZTextureURL.value = properties.zTextureURL;
+                        }
+                        
+                        var activeElement = document.activeElement;
+                        
+                        if(typeof activeElement.select!=="undefined"){
+                            activeElement.select();
+                        }
+                    }
+                }
+            });
+        }
+
+        elLocked.addEventListener('change', createEmitCheckedPropertyUpdateFunction('locked'));
+        elName.addEventListener('change', createEmitTextPropertyUpdateFunction('name'));
+        elHyperlinkHref.addEventListener('change', createEmitTextPropertyUpdateFunction('href'));
+        elDescription.addEventListener('change', createEmitTextPropertyUpdateFunction('description'));
+        elVisible.addEventListener('change', createEmitCheckedPropertyUpdateFunction('visible'));
+
+        var positionChangeFunction = createEmitVec3PropertyUpdateFunction(
+            'position', elPositionX, elPositionY, elPositionZ);
+        elPositionX.addEventListener('change', positionChangeFunction);
+        elPositionY.addEventListener('change', positionChangeFunction);
+        elPositionZ.addEventListener('change', positionChangeFunction);
+
+        var dimensionsChangeFunction = createEmitVec3PropertyUpdateFunction(
+            'dimensions', elDimensionsX, elDimensionsY, elDimensionsZ);
+        elDimensionsX.addEventListener('change', dimensionsChangeFunction);
+        elDimensionsY.addEventListener('change', dimensionsChangeFunction);
+        elDimensionsZ.addEventListener('change', dimensionsChangeFunction);
+
+        elParentID.addEventListener('change', createEmitTextPropertyUpdateFunction('parentID'));
+        elParentJointIndex.addEventListener('change', createEmitNumberPropertyUpdateFunction('parentJointIndex'));
+
+        var registrationChangeFunction = createEmitVec3PropertyUpdateFunction(
+            'registrationPoint', elRegistrationX, elRegistrationY, elRegistrationZ);
+        elRegistrationX.addEventListener('change', registrationChangeFunction);
+        elRegistrationY.addEventListener('change', registrationChangeFunction);
+        elRegistrationZ.addEventListener('change', registrationChangeFunction);
+
+        var rotationChangeFunction = createEmitVec3PropertyUpdateFunction(
+            'rotation', elRotationX, elRotationY, elRotationZ);
+        elRotationX.addEventListener('change', rotationChangeFunction);
+        elRotationY.addEventListener('change', rotationChangeFunction);
+        elRotationZ.addEventListener('change', rotationChangeFunction);
+
+        var velocityChangeFunction = createEmitVec3PropertyUpdateFunction(
+            'velocity', elLinearVelocityX, elLinearVelocityY, elLinearVelocityZ);
+        elLinearVelocityX.addEventListener('change', velocityChangeFunction);
+        elLinearVelocityY.addEventListener('change', velocityChangeFunction);
+        elLinearVelocityZ.addEventListener('change', velocityChangeFunction);
+        elLinearDamping.addEventListener('change', createEmitNumberPropertyUpdateFunction('damping'));
+
+        var angularVelocityChangeFunction = createEmitVec3PropertyUpdateFunctionWithMultiplier(
+            'angularVelocity', elAngularVelocityX, elAngularVelocityY, elAngularVelocityZ, DEGREES_TO_RADIANS);
+        elAngularVelocityX.addEventListener('change', angularVelocityChangeFunction);
+        elAngularVelocityY.addEventListener('change', angularVelocityChangeFunction);
+        elAngularVelocityZ.addEventListener('change', angularVelocityChangeFunction);
+        elAngularDamping.addEventListener('change', createEmitNumberPropertyUpdateFunction('angularDamping'));
+
+        elRestitution.addEventListener('change', createEmitNumberPropertyUpdateFunction('restitution'));
+        elFriction.addEventListener('change', createEmitNumberPropertyUpdateFunction('friction'));
+
+        var gravityChangeFunction = createEmitVec3PropertyUpdateFunction(
+            'gravity', elGravityX, elGravityY, elGravityZ);
+        elGravityX.addEventListener('change', gravityChangeFunction);
+        elGravityY.addEventListener('change', gravityChangeFunction);
+        elGravityZ.addEventListener('change', gravityChangeFunction);
+
+        var accelerationChangeFunction = createEmitVec3PropertyUpdateFunction(
+            'acceleration', elAccelerationX, elAccelerationY, elAccelerationZ);
+        elAccelerationX.addEventListener('change', accelerationChangeFunction);
+        elAccelerationY.addEventListener('change', accelerationChangeFunction);
+        elAccelerationZ.addEventListener('change', accelerationChangeFunction);
+
+        elDensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('density'));
+        elCollisionless.addEventListener('change', createEmitCheckedPropertyUpdateFunction('collisionless'));
+        elDynamic.addEventListener('change', createEmitCheckedPropertyUpdateFunction('dynamic'));
+
+        elCollideDynamic.addEventListener('change', function() {
+            updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideDynamic, 'dynamic');
+        });
+
+        elCollideKinematic.addEventListener('change', function() {
+            updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideKinematic, 'kinematic');
+        });
+
+        elCollideStatic.addEventListener('change', function() {
+            updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideStatic, 'static');
+        });
+        elCollideMyAvatar.addEventListener('change', function() {
+            updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideMyAvatar, 'myAvatar');
+        });
+        elCollideOtherAvatar.addEventListener('change', function() {
+            updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideOtherAvatar, 'otherAvatar');
+        });
+
+        elGrabbable.addEventListener('change', function() {
+            userDataChanger("grabbableKey", "grabbable", elGrabbable, elUserData, properties.dynamic);
+        });
+        elWantsTrigger.addEventListener('change', function() {
+            userDataChanger("grabbableKey", "wantsTrigger", elWantsTrigger, elUserData, false);
+        });
+        elIgnoreIK.addEventListener('change', function() {
+            userDataChanger("grabbableKey", "ignoreIK", elIgnoreIK, elUserData, false);
+        });
+
+        elCollisionSoundURL.addEventListener('change', createEmitTextPropertyUpdateFunction('collisionSoundURL'));
+
+        elLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifetime'));
+        elScriptURL.addEventListener('change', createEmitTextPropertyUpdateFunction('script'));
+        /*
+        FIXME: See FIXME for property-script-url.
+        elScriptTimestamp.addEventListener('change', createEmitNumberPropertyUpdateFunction('scriptTimestamp'));
+        */
+        elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData'));
+
+        var colorChangeFunction = createEmitColorPropertyUpdateFunction(
+            'color', elColorRed, elColorGreen, elColorBlue);
+        elColorRed.addEventListener('change', colorChangeFunction);
+        elColorGreen.addEventListener('change', colorChangeFunction);
+        elColorBlue.addEventListener('change', colorChangeFunction);
+        colorPickers.push($('#property-color').colpick({
+            colorScheme: 'dark',
+            layout: 'hex',
+            color: '000000',
+            onShow: function (colpick) {
+                $('#property-color').attr('active', 'true');
+            },
+            onHide: function (colpick) {
+                $('#property-color').attr('active', 'false');
+            },
+            onSubmit: function (hsb, hex, rgb, el) {
+                $(el).css('background-color', '#' + hex);
+                $(el).colpickHide();
+                emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b);
+            }
+        }));
+
+        elLightSpotLight.addEventListener('change', createEmitCheckedPropertyUpdateFunction('isSpotlight'));
+
+        var lightColorChangeFunction = createEmitColorPropertyUpdateFunction(
+            'color', elLightColorRed, elLightColorGreen, elLightColorBlue);
+        elLightColorRed.addEventListener('change', lightColorChangeFunction);
+        elLightColorGreen.addEventListener('change', lightColorChangeFunction);
+        elLightColorBlue.addEventListener('change', lightColorChangeFunction);
+        colorPickers.push($('#property-light-color').colpick({
+            colorScheme: 'dark',
+            layout: 'hex',
+            color: '000000',
+            onShow: function (colpick) {
+                $('#property-light-color').attr('active', 'true');
+            },
+            onHide: function (colpick) {
+                $('#property-light-color').attr('active', 'false');
+            },
+            onSubmit: function (hsb, hex, rgb, el) {
+                $(el).css('background-color', '#' + hex);
+                $(el).colpickHide();
+                emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b);
+            }
+        }));
+
+        elLightIntensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('intensity', 1));
+        elLightFalloffRadius.addEventListener('change', createEmitNumberPropertyUpdateFunction('falloffRadius', 1));
+        elLightExponent.addEventListener('change', createEmitNumberPropertyUpdateFunction('exponent', 2));
+        elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff', 2));
+
+        elShape.addEventListener('change', createEmitTextPropertyUpdateFunction('shape'));
+
+        elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl'));
+
+        elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL'));
+        elShapeType.addEventListener('change', createEmitTextPropertyUpdateFunction('shapeType'));
+        elCompoundShapeURL.addEventListener('change', createEmitTextPropertyUpdateFunction('compoundShapeURL'));
+
+        elModelAnimationURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('animation', 'url'));
+        elModelAnimationPlaying.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation','running'));
+        elModelAnimationFPS.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation','fps'));
+        elModelAnimationFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'currentFrame'));
+        elModelAnimationFirstFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'firstFrame'));
+        elModelAnimationLastFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'lastFrame'));
+        elModelAnimationLoop.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'loop'));
+        elModelAnimationHold.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'hold'));
+
+        elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures'));
+
+        elTextText.addEventListener('change', createEmitTextPropertyUpdateFunction('text'));
+        elTextFaceCamera.addEventListener('change', createEmitCheckedPropertyUpdateFunction('faceCamera'));
+        elTextLineHeight.addEventListener('change', createEmitNumberPropertyUpdateFunction('lineHeight'));
+        var textTextColorChangeFunction = createEmitColorPropertyUpdateFunction(
+            'textColor', elTextTextColorRed, elTextTextColorGreen, elTextTextColorBlue);
+        elTextTextColorRed.addEventListener('change', textTextColorChangeFunction);
+        elTextTextColorGreen.addEventListener('change', textTextColorChangeFunction);
+        elTextTextColorBlue.addEventListener('change', textTextColorChangeFunction);
+        colorPickers.push($('#property-text-text-color').colpick({
+            colorScheme:'dark',
+            layout:'hex',
+            color: '000000',
+            onShow: function (colpick) {
+                $('#property-text-text-color').attr('active', 'true');
+            },
+            onHide: function (colpick) {
+                $('#property-text-text-color').attr('active', 'false');
+            },
+            onSubmit: function (hsb, hex, rgb, el) {
+                $(el).css('background-color', '#'+hex);
+                $(el).colpickHide();
+                $(el).attr('active', 'false');
+                emitColorPropertyUpdate('textColor', rgb.r, rgb.g, rgb.b);
+            }
+        }));
+
+        var textBackgroundColorChangeFunction = createEmitColorPropertyUpdateFunction(
+            'backgroundColor', elTextBackgroundColorRed, elTextBackgroundColorGreen, elTextBackgroundColorBlue);
+        elTextBackgroundColorRed.addEventListener('change', textBackgroundColorChangeFunction);
+        elTextBackgroundColorGreen.addEventListener('change', textBackgroundColorChangeFunction);
+        elTextBackgroundColorBlue.addEventListener('change', textBackgroundColorChangeFunction);
+        colorPickers.push($('#property-text-background-color').colpick({
+            colorScheme:'dark',
+            layout:'hex',
+            color:'000000',
+            onShow: function (colpick) {
+                $('#property-text-background-color').attr('active', 'true');
+            },
+            onHide: function (colpick) {
+                $('#property-text-background-color').attr('active', 'false');
+            },
+            onSubmit: function (hsb, hex, rgb, el) {
+                $(el).css('background-color', '#'+hex);
+                $(el).colpickHide();
+                emitColorPropertyUpdate('backgroundColor', rgb.r, rgb.g, rgb.b);
+            }
+        }));
+
+        elZoneStageSunModelEnabled.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','sunModelEnabled'));
+        colorPickers.push($('#property-zone-key-light-color').colpick({
+            colorScheme:'dark',
+            layout:'hex',
+            color:'000000',
+            onShow: function (colpick) {
+                $('#property-zone-key-light-color').attr('active', 'true');
+            },
+            onHide: function (colpick) {
+                $('#property-zone-key-light-color').attr('active', 'false');
+            },
+            onSubmit: function (hsb, hex, rgb, el) {
+                $(el).css('background-color', '#'+hex);
+                $(el).colpickHide();
+                emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'keyLight');
+            }
+        }));
+        var zoneKeyLightColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('keyLight','color', elZoneKeyLightColorRed, elZoneKeyLightColorGreen, elZoneKeyLightColorBlue);
+        elZoneKeyLightColorRed.addEventListener('change', zoneKeyLightColorChangeFunction);
+        elZoneKeyLightColorGreen.addEventListener('change', zoneKeyLightColorChangeFunction);
+        elZoneKeyLightColorBlue.addEventListener('change', zoneKeyLightColorChangeFunction);
+        elZoneKeyLightIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','intensity'));
+        elZoneKeyLightAmbientIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','ambientIntensity'));
+        elZoneKeyLightAmbientURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('keyLight','ambientURL'));
+        var zoneKeyLightDirectionChangeFunction = createEmitGroupVec3PropertyUpdateFunction('keyLight','direction', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY);
+        elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction);
+        elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction);
+
+        elZoneStageLatitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','latitude'));
+        elZoneStageLongitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','longitude'));
+        elZoneStageAltitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','altitude'));
+        elZoneStageAutomaticHourDay.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','automaticHourDay'));
+        elZoneStageDay.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','day'));
+        elZoneStageHour.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','hour'));
+
+
+        elZoneBackgroundMode.addEventListener('change', createEmitTextPropertyUpdateFunction('backgroundMode'));
+        var zoneSkyboxColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('skybox','color',
+            elZoneSkyboxColorRed, elZoneSkyboxColorGreen, elZoneSkyboxColorBlue);
+        elZoneSkyboxColorRed.addEventListener('change', zoneSkyboxColorChangeFunction);
+        elZoneSkyboxColorGreen.addEventListener('change', zoneSkyboxColorChangeFunction);
+        elZoneSkyboxColorBlue.addEventListener('change', zoneSkyboxColorChangeFunction);
+        colorPickers.push($('#property-zone-skybox-color').colpick({
+            colorScheme:'dark',
+            layout:'hex',
+            color:'000000',
+            onShow: function (colpick) {
+                $('#property-zone-skybox-color').attr('active', 'true');
+            },
+            onHide: function (colpick) {
+                $('#property-zone-skybox-color').attr('active', 'false');
+            },
+            onSubmit: function (hsb, hex, rgb, el) {
+                $(el).css('background-color', '#'+hex);
+                $(el).colpickHide();
+                emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'skybox');
+            }
+        }));
+
+        elZoneSkyboxURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('skybox','url'));
+
+        elZoneFlyingAllowed.addEventListener('change', createEmitCheckedPropertyUpdateFunction('flyingAllowed'));
+        elZoneGhostingAllowed.addEventListener('change', createEmitCheckedPropertyUpdateFunction('ghostingAllowed'));
+
+        var voxelVolumeSizeChangeFunction = createEmitVec3PropertyUpdateFunction(
+            'voxelVolumeSize', elVoxelVolumeSizeX, elVoxelVolumeSizeY, elVoxelVolumeSizeZ);
+        elVoxelVolumeSizeX.addEventListener('change', voxelVolumeSizeChangeFunction);
+        elVoxelVolumeSizeY.addEventListener('change', voxelVolumeSizeChangeFunction);
+        elVoxelVolumeSizeZ.addEventListener('change', voxelVolumeSizeChangeFunction);
+        elVoxelSurfaceStyle.addEventListener('change', createEmitTextPropertyUpdateFunction('voxelSurfaceStyle'));
+        elXTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('xTextureURL'));
+        elYTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('yTextureURL'));
+        elZTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('zTextureURL'));
+
+        elMoveSelectionToGrid.addEventListener("click", function() {
+            EventBridge.emitWebEvent(JSON.stringify({
+                type: "action",
+                action: "moveSelectionToGrid",
+            }));
+        });
+        elMoveAllToGrid.addEventListener("click", function() {
+            EventBridge.emitWebEvent(JSON.stringify({
+                type: "action",
+                action: "moveAllToGrid",
+            }));
+        });
+        elResetToNaturalDimensions.addEventListener("click", function() {
+            EventBridge.emitWebEvent(JSON.stringify({
+                type: "action",
+                action: "resetToNaturalDimensions",
+            }));
+        });
+        elRescaleDimensionsButton.addEventListener("click", function() {
+            EventBridge.emitWebEvent(JSON.stringify({
+                type: "action",
+                action: "rescaleDimensions",
+                percentage: parseInt(elRescaleDimensionsPct.value),
+            }));
+        });
+        /*
+        FIXME: See FIXME for property-script-url.
+        elReloadScriptButton.addEventListener("click", function() {
+            EventBridge.emitWebEvent(JSON.stringify({
+                type: "action",
+                action: "reloadScript"
+            }));
+        });
+        */
+        elPreviewCameraButton.addEventListener("click", function() {
+            EventBridge.emitWebEvent(JSON.stringify({
+                type: "action",
+                action: "previewCamera"
+            }));
+        });
+
+        window.onblur = function() {
+            // Fake a change event
+            var ev = document.createEvent("HTMLEvents");
+            ev.initEvent("change", true, true);
+            document.activeElement.dispatchEvent(ev);
+        }
+
+        // For input and textarea elements, select all of the text on focus
+        // WebKit-based browsers, such as is used with QWebView, have a quirk
+        // where the mouseup event comes after the focus event, causing the
+        // text to be deselected immediately after selecting all of the text.
+        // To make this work we block the first mouseup event after the elements
+        // received focus.  If we block all mouseup events the user will not
+        // be able to click within the selected text.
+        // We also check to see if the value has changed to make sure we aren't
+        // blocking a mouse-up event when clicking on an input spinner.
+        var els = document.querySelectorAll("input, textarea");
+        for (var i = 0; i < els.length; i++) {
+            var clicked = false;
+            var originalText;
+            els[i].onfocus = function(e) {
+                originalText = this.value;
+                this.select();
+                clicked = false;
+            };
+            els[i].onmouseup = function(e) {
+                if (!clicked && originalText == this.value) {
+                    e.preventDefault();
+                }
+                clicked = true;
+            };
+        }
+    });
+
+    // Collapsible sections
+    var elCollapsible = document.getElementsByClassName("section-header");
+
+    var toggleCollapsedEvent = function (event) {
+        var element = event.target;
+        if (element.nodeName !== "DIV") {
+            element = element.parentNode;
+        }
+        var isCollapsed = element.getAttribute("collapsed") !== "true";
+        element.setAttribute("collapsed", isCollapsed ? "true" : "false");
+        element.getElementsByTagName("span")[0].textContent = isCollapsed ? "L" : "M";
+    };
+
+    for (var i = 0, length = elCollapsible.length; i < length; i++) {
+        var element = elCollapsible[i];
+        element.addEventListener("click", toggleCollapsedEvent, true);
+    };
+
+
+    // Textarea scrollbars
+    var elTextareas = document.getElementsByTagName("TEXTAREA");
+
+    var textareaOnChangeEvent = function (event) {
+        setTextareaScrolling(event.target);
+    }
+
+    for (var i = 0, length = elTextareas.length; i < length; i++) {
+        var element = elTextareas[i];
+        setTextareaScrolling(element);
+        element.addEventListener("input", textareaOnChangeEvent, false);
+        element.addEventListener("change", textareaOnChangeEvent, false);
+        /* FIXME: Detect and update textarea scrolling attribute on resize. Unfortunately textarea doesn't have a resize
+        event; mouseup is a partial stand-in but doesn't handle resizing if mouse moves outside textarea rectangle. */
+        element.addEventListener("mouseup", textareaOnChangeEvent, false);
+    };
+
+    // Dropdowns
+    // For each dropdown the following replacement is created in place of the oriringal dropdown...
+    // Structure created:
+    //  <dl dropped="true/false">
+    //      <dt name="?" id="?" value="?"><span>display text</span><span>carat</span></dt>
+    //      <dd>
+    //          <ul>
+    //              <li value="??>display text</li>
+    //              <li>...</li>
+    //          </ul>
+    //      </dd>
+    //  </dl>
+
+    function setDropdownText(dropdown) {
+        var lis = dropdown.parentNode.getElementsByTagName("li");
+        var text = "";
+        for (var i = 0; i < lis.length; i++) {
+            if (lis[i].getAttribute("value") === dropdown.value) {
+                text = lis[i].textContent;
+            }
+        }
+        dropdown.firstChild.textContent = text;
+    }
+
+    function toggleDropdown(event) {
+        var element = event.target;
+        if (element.nodeName !== "DT") {
+            element = element.parentNode;
+        }
+        element = element.parentNode;
+        var isDropped = element.getAttribute("dropped");
+        element.setAttribute("dropped", isDropped !== "true" ? "true" : "false");
+    }
+
+    function setDropdownValue(event) {
+        var dt = event.target.parentNode.parentNode.previousSibling;
+        dt.value = event.target.getAttribute("value");
+        dt.firstChild.textContent = event.target.textContent;
+
+        dt.parentNode.setAttribute("dropped", "false");
+
+        var evt = document.createEvent("HTMLEvents");
+        evt.initEvent("change", true, true);
+        dt.dispatchEvent(evt);
+    }
+
+    var elDropdowns = document.getElementsByTagName("select");
+    for (var i = 0; i < elDropdowns.length; i++) {
+        var options = elDropdowns[i].getElementsByTagName("option");
+        var selectedOption = 0;
+        for (var j = 0; j < options.length; j++) {
+            if (options[j].getAttribute("selected") === "selected") {
+                selectedOption = j;
+            }
+        }
+        var div = elDropdowns[i].parentNode;
+
+        var dl = document.createElement("dl");
+        div.appendChild(dl);
+
+        var dt = document.createElement("dt");
+        dt.name = elDropdowns[i].name;
+        dt.id = elDropdowns[i].id;
+        dt.addEventListener("click", toggleDropdown, true);
+        dl.appendChild(dt);
+
+        var span = document.createElement("span");
+        span.setAttribute("value", options[selectedOption].value);
+        span.textContent = options[selectedOption].firstChild.textContent;
+        dt.appendChild(span);
+
+        var span = document.createElement("span");
+        span.textContent = "5";  // caratDn
+        dt.appendChild(span);
+
+        var dd = document.createElement("dd");
+        dl.appendChild(dd);
+
+        var ul = document.createElement("ul");
+        dd.appendChild(ul);
+
+        for (var j = 0; j < options.length; j++) {
+            var li = document.createElement("li");
+            li.setAttribute("value", options[j].value);
+            li.textContent = options[j].firstChild.textContent;
+            li.addEventListener("click", setDropdownValue);
+            ul.appendChild(li);
+        }
+    }
+
+    elDropdowns = document.getElementsByTagName("select");
+    while (elDropdowns.length > 0) {
+        var el = elDropdowns[0];
+        el.parentNode.removeChild(el);
+        elDropdowns = document.getElementsByTagName("select");
+    }
+
+    augmentSpinButtons();
+
+    // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
+    document.addEventListener("contextmenu", function (event) {
+        event.preventDefault();
+    }, false);
+}
+
diff --git a/scripts/system/html/eventBridgeLoader.js b/scripts/system/html/js/eventBridgeLoader.js
similarity index 100%
rename from scripts/system/html/eventBridgeLoader.js
rename to scripts/system/html/js/eventBridgeLoader.js
diff --git a/scripts/system/html/js/gridControls.js b/scripts/system/html/js/gridControls.js
new file mode 100644
index 0000000000..cc268bcbff
--- /dev/null
+++ b/scripts/system/html/js/gridControls.js
@@ -0,0 +1,138 @@
+//  gridControls.js
+//
+//  Created by Ryan Huffman on 6 Nov 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
+
+function loaded() {
+    openEventBridge(function() {
+        elPosY = document.getElementById("horiz-y");
+        elMinorSpacing = document.getElementById("minor-spacing");
+        elMajorSpacing = document.getElementById("major-spacing");
+        elSnapToGrid = document.getElementById("snap-to-grid");
+        elHorizontalGridVisible = document.getElementById("horiz-grid-visible");
+        elMoveToSelection = document.getElementById("move-to-selection");
+        elMoveToAvatar = document.getElementById("move-to-avatar");
+
+        if (window.EventBridge !== undefined) {
+            EventBridge.scriptEventReceived.connect(function(data) {
+                data = JSON.parse(data);
+
+                if (data.origin) {
+                    var origin = data.origin;
+                    elPosY.value = origin.y;
+                }
+
+                if (data.minorGridEvery !== undefined) {
+                    elMinorSpacing.value = data.minorGridEvery;
+                }
+
+                if (data.majorGridEvery !== undefined) {
+                    elMajorSpacing.value = data.majorGridEvery;
+                }
+
+                if (data.gridColor) {
+                    gridColor = data.gridColor;
+                }
+
+                if (data.snapToGrid !== undefined) {
+                    elSnapToGrid.checked = data.snapToGrid == true;
+                }
+
+                if (data.visible !== undefined) {
+                    elHorizontalGridVisible.checked = data.visible == true;
+                }
+            });
+
+            function emitUpdate() {
+                EventBridge.emitWebEvent(JSON.stringify({
+                    type: "update",
+                    origin: {
+                        y: elPosY.value,
+                    },
+                    minorGridEvery: elMinorSpacing.value,
+                    majorGridEvery: elMajorSpacing.value,
+                    gridColor: gridColor,
+                    snapToGrid: elSnapToGrid.checked,
+                    visible: elHorizontalGridVisible.checked,
+                }));
+            }
+
+        }
+
+        elPosY.addEventListener("change", emitUpdate);
+        elMinorSpacing.addEventListener("change", emitUpdate);
+        elMajorSpacing.addEventListener("change", emitUpdate);
+        elSnapToGrid.addEventListener("change", emitUpdate);
+        elHorizontalGridVisible.addEventListener("change", emitUpdate);
+
+        elMoveToAvatar.addEventListener("click", function() {
+            EventBridge.emitWebEvent(JSON.stringify({
+                type: "action",
+                action: "moveToAvatar",
+            }));
+        });
+        elMoveToSelection.addEventListener("click", function() {
+            EventBridge.emitWebEvent(JSON.stringify({
+                type: "action",
+                action: "moveToSelection",
+            }));
+        });
+
+        var gridColor = { red: 255, green: 255, blue: 255 };
+        var elColor = document.getElementById("grid-color");
+        var elColorRed = document.getElementById("grid-color-red");
+        var elColorGreen = document.getElementById("grid-color-green");
+        var elColorBlue = document.getElementById("grid-color-blue");
+        elColor.style.backgroundColor = "rgb(" + gridColor.red + "," + gridColor.green + "," + gridColor.blue + ")";
+        elColorRed.value = gridColor.red;
+        elColorGreen.value = gridColor.green;
+        elColorBlue.value = gridColor.blue;
+
+        var colorChangeFunction = function () {
+            gridColor = { red: elColorRed.value, green: elColorGreen.value, blue: elColorBlue.value };
+            elColor.style.backgroundColor = "rgb(" + gridColor.red + "," + gridColor.green + "," + gridColor.blue + ")";
+            emitUpdate();
+        };
+
+        var colorPickFunction = function (red, green, blue) {
+            elColorRed.value = red;
+            elColorGreen.value = green;
+            elColorBlue.value = blue;
+            gridColor = { red: red, green: green, blue: blue };
+            emitUpdate();
+        }
+
+        elColorRed.addEventListener('change', colorChangeFunction);
+        elColorGreen.addEventListener('change', colorChangeFunction);
+        elColorBlue.addEventListener('change', colorChangeFunction);
+        $('#grid-color').colpick({
+            colorScheme: 'dark',
+            layout: 'hex',
+            color: { r: gridColor.red, g: gridColor.green, b: gridColor.blue },
+            onShow: function (colpick) {
+                $('#grid-color').attr('active', 'true');
+            },
+            onHide: function (colpick) {
+                $('#grid-color').attr('active', 'false');
+            },
+            onSubmit: function (hsb, hex, rgb, el) {
+                $(el).css('background-color', '#' + hex);
+                $(el).colpickHide();
+                colorPickFunction(rgb.r, rgb.g, rgb.b);
+            }
+        });
+
+        augmentSpinButtons();
+
+        EventBridge.emitWebEvent(JSON.stringify({ type: 'init' }));
+    });
+
+    // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
+    document.addEventListener("contextmenu", function (event) {
+        event.preventDefault();
+    }, false);
+}
+
diff --git a/scripts/system/html/jquery-2.1.4.min.js b/scripts/system/html/js/jquery-2.1.4.min.js
similarity index 100%
rename from scripts/system/html/jquery-2.1.4.min.js
rename to scripts/system/html/js/jquery-2.1.4.min.js
diff --git a/scripts/system/html/list.min.js b/scripts/system/html/js/list.min.js
similarity index 100%
rename from scripts/system/html/list.min.js
rename to scripts/system/html/js/list.min.js
diff --git a/scripts/system/html/spinButtons.js b/scripts/system/html/js/spinButtons.js
similarity index 100%
rename from scripts/system/html/spinButtons.js
rename to scripts/system/html/js/spinButtons.js

From d5b822ddbe50fad78a2d17f086f75291e086e8a9 Mon Sep 17 00:00:00 2001
From: Zach Pomerantz <zach@highfidelity.io>
Date: Tue, 9 Aug 2016 15:20:56 -0700
Subject: [PATCH 69/78] remove missing camera selectors

---
 scripts/system/html/entityProperties.js    | 1317 --------------------
 scripts/system/html/js/entityProperties.js |    8 -
 2 files changed, 1325 deletions(-)
 delete mode 100644 scripts/system/html/entityProperties.js

diff --git a/scripts/system/html/entityProperties.js b/scripts/system/html/entityProperties.js
deleted file mode 100644
index 490579e909..0000000000
--- a/scripts/system/html/entityProperties.js
+++ /dev/null
@@ -1,1317 +0,0 @@
-//  entityProperties.js
-//
-//  Created by Ryan Huffman on 13 Nov 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
-
-var PI = 3.14159265358979;
-var DEGREES_TO_RADIANS = PI / 180.0;
-var RADIANS_TO_DEGREES = 180.0 / PI;
-var ICON_FOR_TYPE = {
-    Box: "V",
-    Sphere: "n",
-    Shape: "n",
-    ParticleEffect: "&#xe004;",
-    Model: "&#xe008;",
-    Web: "q",
-    Text: "l",
-    Light: "p",
-    Zone: "o",
-    PolyVox: "&#xe005;",
-    Multiple: "&#xe000;"
-}
-
-var colorPickers = [];
-
-debugPrint = function(message) {
-    EventBridge.emitWebEvent(
-        JSON.stringify({
-            type:"print", 
-            message: message
-        })
-    );
-};
-
-function enableChildren(el, selector) {
-    els = el.querySelectorAll(selector);
-    for (var i = 0; i < els.length; i++) {
-        els[i].removeAttribute('disabled');
-    }
-}
-function disableChildren(el, selector) {
-    els = el.querySelectorAll(selector);
-    for (var i = 0; i < els.length; i++) {
-        els[i].setAttribute('disabled', 'disabled');
-    }
-}
-
-function enableProperties() {
-    enableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker");
-    enableChildren(document, ".colpick");
-}
-
-function disableProperties() {
-    disableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker");
-    disableChildren(document, ".colpick");
-    for (var i = 0; i < colorPickers.length; i++) {
-        colorPickers[i].colpickHide();
-    }
-}
-
-function showElements(els, show) {
-    for (var i = 0; i < els.length; i++) {
-        els[i].style.display = (show) ? 'table' : 'none';
-    }
-}
-
-function createEmitCheckedPropertyUpdateFunction(propertyName) {
-    return function() {
-        EventBridge.emitWebEvent(
-            '{ "type":"update", "properties":{"' + propertyName + '":' + this.checked + '}}'
-        );
-    };
-}
-
-function createEmitCheckedToStringPropertyUpdateFunction(checkboxElement, name, propertyName) {
-    var newString = "";
-    if (checkboxElement.checked) {
-        newString += name + "";
-    } else {
-
-    }
-
-}
-
-function createEmitGroupCheckedPropertyUpdateFunction(group, propertyName) {
-    return function () {
-        var properties = {};
-        properties[group] = {};
-        properties[group][propertyName] = this.checked;
-        EventBridge.emitWebEvent(
-            JSON.stringify({
-                type: "update",
-                properties: properties
-            })
-        );
-    };
-}
-
-function createEmitNumberPropertyUpdateFunction(propertyName, decimals) {
-    decimals = decimals == undefined ? 4 : decimals;
-    return function() {
-        var value = parseFloat(this.value).toFixed(decimals);
-
-        EventBridge.emitWebEvent(
-            '{ "type":"update", "properties":{"' + propertyName + '":' + value + '}}'
-        );
-    };
-}
-function createEmitGroupNumberPropertyUpdateFunction(group, propertyName) {
-    return function() {
-        var properties = {};
-        properties[group] = {};
-        properties[group][propertyName] = this.value;
-        EventBridge.emitWebEvent(
-            JSON.stringify({
-                type: "update",
-                properties: properties,
-            })
-        );
-    };
-}
-
-
-function createEmitTextPropertyUpdateFunction(propertyName) {
-    return function() {
-        var properties = {};
-        properties[propertyName] = this.value;
-        EventBridge.emitWebEvent(
-            JSON.stringify({
-                type: "update",
-                properties: properties,
-            })
-        );
-    };
-}
-
-function createEmitGroupTextPropertyUpdateFunction(group,propertyName) {
-    return function() {
-        var properties = {};
-        properties[group] = {};
-        properties[group][propertyName] = this.value;
-        EventBridge.emitWebEvent(
-            JSON.stringify({
-                type: "update",
-                properties: properties,
-            })
-        );
-    };
-}
-
-function createEmitVec3PropertyUpdateFunction(property, elX, elY, elZ) {
-    return function() {
-        var data = {
-             type: "update",
-             properties: {
-             }
-        };
-        data.properties[property] = {
-             x: elX.value,
-             y: elY.value,
-             z: elZ.value,
-        };
-        EventBridge.emitWebEvent(JSON.stringify(data));
-    }
-};
-
-function createEmitGroupVec3PropertyUpdateFunction(group, property, elX, elY, elZ) {
-    return function() {
-        var data = {
-             type: "update",
-             properties: {
-             }
-        };
-        data.properties[group] = { };
-        data.properties[group][property] = {
-             x: elX.value,
-             y: elY.value,
-             z: elZ ? elZ.value : 0,
-        };
-        EventBridge.emitWebEvent(JSON.stringify(data));
-    }
-};
-
-function createEmitVec3PropertyUpdateFunctionWithMultiplier(property, elX, elY, elZ, multiplier) {
-    return function() {
-        var data = {
-             type: "update",
-             properties: {
-             }
-        };
-        data.properties[property] = {
-             x: elX.value * multiplier,
-             y: elY.value * multiplier,
-             z: elZ.value * multiplier,
-        };
-        EventBridge.emitWebEvent(JSON.stringify(data));
-    }
-};
-
-function createEmitColorPropertyUpdateFunction(property, elRed, elGreen, elBlue) {
-    return function() {
-        emitColorPropertyUpdate(property, elRed.value, elGreen.value, elBlue.value);
-    }
-};
-
-function emitColorPropertyUpdate(property, red, green, blue, group) {
-        var data = {
-             type: "update",
-             properties: {
-             }
-        };
-        if (group) {
-            data.properties[group] = { };
-            data.properties[group][property] = {
-                 red: red,
-                 green: green,
-                 blue: blue,
-            };
-        } else {
-            data.properties[property] = {
-                 red: red,
-                 green: green,
-                 blue: blue,
-            };
-        }
-        EventBridge.emitWebEvent(JSON.stringify(data));
-};
-
-
-function createEmitGroupColorPropertyUpdateFunction(group, property, elRed, elGreen, elBlue) {
-    return function() {
-        var data = {
-             type: "update",
-             properties: {
-             }
-        };
-        data.properties[group] = { };
-
-        data.properties[group][property] = {
-             red: elRed.value,
-             green: elGreen.value,
-             blue: elBlue.value,
-        };
-        EventBridge.emitWebEvent(JSON.stringify(data));
-    }
-};
-
-function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElement, subPropertyString) {
-    if (subPropertyElement.checked) {
-        if (propertyValue.indexOf(subPropertyString)) {
-            propertyValue += subPropertyString + ',';
-        } 
-    } else {
-        // We've unchecked, so remove 
-        propertyValue = propertyValue.replace(subPropertyString + ",", "");
-    }
-
-    var _properties ={}
-    _properties[propertyName] = propertyValue;
-    
-    EventBridge.emitWebEvent(
-      JSON.stringify({
-        type: "update",
-        properties: _properties
-      })
-    );
-
-}
-
-function userDataChanger(groupName, keyName, checkBoxElement, userDataElement, defaultValue) {
-    var properties = {};
-    var parsedData = {};
-    try {
-        parsedData = JSON.parse(userDataElement.value);
-    } catch(e) {}
-
-    if (!(groupName in parsedData)) {
-        parsedData[groupName] = {}
-    }
-    delete parsedData[groupName][keyName];
-    if (checkBoxElement.checked !== defaultValue) {
-        parsedData[groupName][keyName] = checkBoxElement.checked;
-    }
-
-    if (Object.keys(parsedData[groupName]).length == 0) {
-        delete parsedData[groupName];
-    }
-    if (Object.keys(parsedData).length > 0) {
-        properties['userData'] = JSON.stringify(parsedData);
-    } else {
-        properties['userData'] = '';
-    }
-
-    userDataElement.value = properties['userData'];
-
-    EventBridge.emitWebEvent(
-        JSON.stringify({
-            type: "update",
-            properties: properties,
-        })
-    );
-};
-
-function setTextareaScrolling(element) {
-    var isScrolling = element.scrollHeight > element.offsetHeight;
-    element.setAttribute("scrolling", isScrolling ? "true" : "false");
-}
-
-function loaded() { 
-    openEventBridge(function() {
-        var allSections = [];
-        var elID = document.getElementById("property-id");
-        var elType = document.getElementById("property-type");
-        var elTypeIcon = document.getElementById("type-icon");
-        var elName = document.getElementById("property-name");
-        var elLocked = document.getElementById("property-locked");
-        var elVisible = document.getElementById("property-visible");
-        var elPositionX = document.getElementById("property-pos-x");
-        var elPositionY = document.getElementById("property-pos-y");
-        var elPositionZ = document.getElementById("property-pos-z");
-        var elMoveSelectionToGrid = document.getElementById("move-selection-to-grid");
-        var elMoveAllToGrid = document.getElementById("move-all-to-grid");
-
-        var elDimensionsX = document.getElementById("property-dim-x");
-        var elDimensionsY = document.getElementById("property-dim-y");
-        var elDimensionsZ = document.getElementById("property-dim-z");
-        var elResetToNaturalDimensions = document.getElementById("reset-to-natural-dimensions");
-        var elRescaleDimensionsPct = document.getElementById("dimension-rescale-pct");
-        var elRescaleDimensionsButton = document.getElementById("dimension-rescale-button");
-
-        var elParentID = document.getElementById("property-parent-id");
-        var elParentJointIndex = document.getElementById("property-parent-joint-index");
-
-        var elRegistrationX = document.getElementById("property-reg-x");
-        var elRegistrationY = document.getElementById("property-reg-y");
-        var elRegistrationZ = document.getElementById("property-reg-z");
-
-        var elRotationX = document.getElementById("property-rot-x");
-        var elRotationY = document.getElementById("property-rot-y");
-        var elRotationZ = document.getElementById("property-rot-z");
-
-        var elLinearVelocityX = document.getElementById("property-lvel-x");
-        var elLinearVelocityY = document.getElementById("property-lvel-y");
-        var elLinearVelocityZ = document.getElementById("property-lvel-z");
-        var elLinearDamping = document.getElementById("property-ldamping");
-
-        var elAngularVelocityX = document.getElementById("property-avel-x");
-        var elAngularVelocityY = document.getElementById("property-avel-y");
-        var elAngularVelocityZ = document.getElementById("property-avel-z");
-        var elAngularDamping = document.getElementById("property-adamping");
-
-        var elRestitution = document.getElementById("property-restitution");
-        var elFriction = document.getElementById("property-friction");
-
-        var elGravityX = document.getElementById("property-grav-x");
-        var elGravityY = document.getElementById("property-grav-y");
-        var elGravityZ = document.getElementById("property-grav-z");
-
-        var elAccelerationX = document.getElementById("property-lacc-x");
-        var elAccelerationY = document.getElementById("property-lacc-y");
-        var elAccelerationZ = document.getElementById("property-lacc-z");
-
-        var elDensity = document.getElementById("property-density");
-        var elCollisionless = document.getElementById("property-collisionless");
-        var elDynamic = document.getElementById("property-dynamic" );
-        var elCollideStatic = document.getElementById("property-collide-static");
-        var elCollideDynamic = document.getElementById("property-collide-dynamic");
-        var elCollideKinematic = document.getElementById("property-collide-kinematic");
-        var elCollideMyAvatar = document.getElementById("property-collide-myAvatar");
-        var elCollideOtherAvatar = document.getElementById("property-collide-otherAvatar");
-        var elCollisionSoundURL = document.getElementById("property-collision-sound-url");
-
-        var elGrabbable = document.getElementById("property-grabbable");
-        var elWantsTrigger = document.getElementById("property-wants-trigger");
-        var elIgnoreIK = document.getElementById("property-ignore-ik");
-
-        var elLifetime = document.getElementById("property-lifetime");
-        var elScriptURL = document.getElementById("property-script-url");
-        /*
-        FIXME: See FIXME for property-script-url.
-        var elScriptTimestamp = document.getElementById("property-script-timestamp");
-        */
-        var elReloadScriptButton = document.getElementById("reload-script-button");
-        var elUserData = document.getElementById("property-user-data");
-
-        var elColorSections = document.querySelectorAll(".color-section");
-        var elColor = document.getElementById("property-color");
-        var elColorRed = document.getElementById("property-color-red");
-        var elColorGreen = document.getElementById("property-color-green");
-        var elColorBlue = document.getElementById("property-color-blue");
-
-        var elShapeSections = document.querySelectorAll(".shape-section");
-        allSections.push(elShapeSections);
-        var elShape = document.getElementById("property-shape");
-        
-        var elLightSections = document.querySelectorAll(".light-section");
-        allSections.push(elLightSections);
-        var elLightSpotLight = document.getElementById("property-light-spot-light");
-        var elLightColor = document.getElementById("property-light-color");
-        var elLightColorRed = document.getElementById("property-light-color-red");
-        var elLightColorGreen = document.getElementById("property-light-color-green");
-        var elLightColorBlue = document.getElementById("property-light-color-blue");
-
-        var elLightIntensity = document.getElementById("property-light-intensity");
-        var elLightFalloffRadius = document.getElementById("property-light-falloff-radius");
-        var elLightExponent = document.getElementById("property-light-exponent");
-        var elLightCutoff = document.getElementById("property-light-cutoff");
-
-        var elModelSections = document.querySelectorAll(".model-section");
-        allSections.push(elModelSections);
-        var elModelURL = document.getElementById("property-model-url");
-        var elShapeType = document.getElementById("property-shape-type");
-        var elCompoundShapeURL = document.getElementById("property-compound-shape-url");
-        var elModelAnimationURL = document.getElementById("property-model-animation-url");
-        var elModelAnimationPlaying = document.getElementById("property-model-animation-playing");
-        var elModelAnimationFPS = document.getElementById("property-model-animation-fps");
-        var elModelAnimationFrame = document.getElementById("property-model-animation-frame");
-        var elModelAnimationFirstFrame = document.getElementById("property-model-animation-first-frame");
-        var elModelAnimationLastFrame = document.getElementById("property-model-animation-last-frame");
-        var elModelAnimationLoop = document.getElementById("property-model-animation-loop");
-        var elModelAnimationHold = document.getElementById("property-model-animation-hold");
-        var elModelTextures = document.getElementById("property-model-textures");
-        var elModelOriginalTextures = document.getElementById("property-model-original-textures");
-
-        var elWebSections = document.querySelectorAll(".web-section");
-        allSections.push(elWebSections);
-        var elWebSourceURL = document.getElementById("property-web-source-url");
-
-        var elDescription = document.getElementById("property-description");
-
-        var elHyperlinkHref = document.getElementById("property-hyperlink-href");
-     
-        var elHyperlinkSections = document.querySelectorAll(".hyperlink-section");
-      
-
-        var elTextSections = document.querySelectorAll(".text-section");
-        allSections.push(elTextSections);
-        var elTextText = document.getElementById("property-text-text");
-        var elTextLineHeight = document.getElementById("property-text-line-height");
-        var elTextTextColor = document.getElementById("property-text-text-color");
-        var elTextFaceCamera = document.getElementById("property-text-face-camera");
-        var elTextTextColorRed = document.getElementById("property-text-text-color-red");
-        var elTextTextColorGreen = document.getElementById("property-text-text-color-green");
-        var elTextTextColorBlue = document.getElementById("property-text-text-color-blue");
-        var elTextBackgroundColor = document.getElementById("property-text-background-color");
-        var elTextBackgroundColorRed = document.getElementById("property-text-background-color-red");
-        var elTextBackgroundColorGreen = document.getElementById("property-text-background-color-green");
-        var elTextBackgroundColorBlue = document.getElementById("property-text-background-color-blue");
-
-        var elZoneSections = document.querySelectorAll(".zone-section");
-        allSections.push(elZoneSections);
-        var elZoneStageSunModelEnabled = document.getElementById("property-zone-stage-sun-model-enabled");
-
-        var elZoneKeyLightColor = document.getElementById("property-zone-key-light-color");
-        var elZoneKeyLightColorRed = document.getElementById("property-zone-key-light-color-red");
-        var elZoneKeyLightColorGreen = document.getElementById("property-zone-key-light-color-green");
-        var elZoneKeyLightColorBlue = document.getElementById("property-zone-key-light-color-blue");
-        var elZoneKeyLightIntensity = document.getElementById("property-zone-key-intensity");
-        var elZoneKeyLightAmbientIntensity = document.getElementById("property-zone-key-ambient-intensity");
-        var elZoneKeyLightDirectionX = document.getElementById("property-zone-key-light-direction-x");
-        var elZoneKeyLightDirectionY = document.getElementById("property-zone-key-light-direction-y");
-        var elZoneKeyLightDirectionZ = document.getElementById("property-zone-key-light-direction-z");
-        var elZoneKeyLightAmbientURL = document.getElementById("property-zone-key-ambient-url");
-
-        var elZoneStageLatitude = document.getElementById("property-zone-stage-latitude");
-        var elZoneStageLongitude = document.getElementById("property-zone-stage-longitude");
-        var elZoneStageAltitude = document.getElementById("property-zone-stage-altitude");
-        var elZoneStageAutomaticHourDay = document.getElementById("property-zone-stage-automatic-hour-day");
-        var elZoneStageDay = document.getElementById("property-zone-stage-day");
-        var elZoneStageHour = document.getElementById("property-zone-stage-hour");
-
-        var elZoneBackgroundMode = document.getElementById("property-zone-background-mode");
-
-        var elZoneSkyboxColor = document.getElementById("property-zone-skybox-color");
-        var elZoneSkyboxColorRed = document.getElementById("property-zone-skybox-color-red");
-        var elZoneSkyboxColorGreen = document.getElementById("property-zone-skybox-color-green");
-        var elZoneSkyboxColorBlue = document.getElementById("property-zone-skybox-color-blue");
-        var elZoneSkyboxURL = document.getElementById("property-zone-skybox-url");
-
-        var elZoneFlyingAllowed = document.getElementById("property-zone-flying-allowed");
-        var elZoneGhostingAllowed = document.getElementById("property-zone-ghosting-allowed");
-
-        var elPolyVoxSections = document.querySelectorAll(".poly-vox-section");
-        allSections.push(elPolyVoxSections);
-        var elVoxelVolumeSizeX = document.getElementById("property-voxel-volume-size-x");
-        var elVoxelVolumeSizeY = document.getElementById("property-voxel-volume-size-y");
-        var elVoxelVolumeSizeZ = document.getElementById("property-voxel-volume-size-z");
-        var elVoxelSurfaceStyle = document.getElementById("property-voxel-surface-style");
-        var elXTextureURL = document.getElementById("property-x-texture-url");
-        var elYTextureURL = document.getElementById("property-y-texture-url");
-        var elZTextureURL = document.getElementById("property-z-texture-url");
-
-        var elPreviewCameraButton = document.getElementById("preview-camera-button");
-
-        if (window.EventBridge !== undefined) {
-            var properties;
-            EventBridge.scriptEventReceived.connect(function(data) {
-                data = JSON.parse(data);
-                if (data.type == "update") {
-                    if (data.selections.length == 0) {
-                        elTypeIcon.style.display = "none";
-                        elType.innerHTML = "<i>No selection</i>";
-                        elID.innerHTML = "";
-                        disableProperties();
-                    } else if (data.selections.length > 1) {
-                        var selections = data.selections;
-
-                        var ids = [];
-                        var types = {};
-                        var numTypes = 0;
-
-                        for (var i = 0; i < selections.length; i++) {
-                            ids.push(selections[i].id);
-                            var type = selections[i].properties.type;
-                            if (types[type] === undefined) {
-                                types[type] = 0;
-                                numTypes += 1;
-                            }
-                            types[type]++;
-                        }
-
-                        var type;
-                        if (numTypes === 1) {
-                            type = selections[0].properties.type;
-                        } else {
-                            type = "Multiple";
-                        }
-                        elType.innerHTML = type + " (" + data.selections.length + ")";
-                        elTypeIcon.innerHTML = ICON_FOR_TYPE[type];
-                        elTypeIcon.style.display = "inline-block";
-
-                        elID.innerHTML = ids.join("<br>");
-
-                        disableProperties();
-                    } else {
-    
-
-                        properties = data.selections[0].properties;
-
-                        elID.innerHTML = properties.id;
-
-                        elType.innerHTML = properties.type;
-                        elTypeIcon.innerHTML = ICON_FOR_TYPE[properties.type];
-                        elTypeIcon.style.display = "inline-block";
-
-                        elLocked.checked = properties.locked;
-
-                        if (properties.locked) {
-                            disableProperties();
-                            elLocked.removeAttribute('disabled');
-                        } else {
-                            enableProperties();
-                        }
-
-                        elName.value = properties.name;
-
-                        elVisible.checked = properties.visible;
-
-                        elPositionX.value = properties.position.x.toFixed(4);
-                        elPositionY.value = properties.position.y.toFixed(4);
-                        elPositionZ.value = properties.position.z.toFixed(4);
-
-                        elDimensionsX.value = properties.dimensions.x.toFixed(4);
-                        elDimensionsY.value = properties.dimensions.y.toFixed(4);
-                        elDimensionsZ.value = properties.dimensions.z.toFixed(4);
-
-                        elParentID.value = properties.parentID;
-                        elParentJointIndex.value = properties.parentJointIndex;
-
-                        elRegistrationX.value = properties.registrationPoint.x.toFixed(4);
-                        elRegistrationY.value = properties.registrationPoint.y.toFixed(4);
-                        elRegistrationZ.value = properties.registrationPoint.z.toFixed(4);
-
-                        elRotationX.value = properties.rotation.x.toFixed(4);
-                        elRotationY.value = properties.rotation.y.toFixed(4);
-                        elRotationZ.value = properties.rotation.z.toFixed(4);
-
-                        elLinearVelocityX.value = properties.velocity.x.toFixed(4);
-                        elLinearVelocityY.value = properties.velocity.y.toFixed(4);
-                        elLinearVelocityZ.value = properties.velocity.z.toFixed(4);
-                        elLinearDamping.value = properties.damping.toFixed(2);
-
-                        elAngularVelocityX.value = (properties.angularVelocity.x * RADIANS_TO_DEGREES).toFixed(4);
-                        elAngularVelocityY.value = (properties.angularVelocity.y * RADIANS_TO_DEGREES).toFixed(4);
-                        elAngularVelocityZ.value = (properties.angularVelocity.z * RADIANS_TO_DEGREES).toFixed(4);
-                        elAngularDamping.value = properties.angularDamping.toFixed(4);
-
-                        elRestitution.value = properties.restitution.toFixed(4);
-                        elFriction.value = properties.friction.toFixed(4);
-
-                        elGravityX.value = properties.gravity.x.toFixed(4);
-                        elGravityY.value = properties.gravity.y.toFixed(4);
-                        elGravityZ.value = properties.gravity.z.toFixed(4);
-
-                        elAccelerationX.value = properties.acceleration.x.toFixed(4);
-                        elAccelerationY.value = properties.acceleration.y.toFixed(4);
-                        elAccelerationZ.value = properties.acceleration.z.toFixed(4);
-
-                        elDensity.value = properties.density.toFixed(4);
-                        elCollisionless.checked = properties.collisionless;
-                        elDynamic.checked = properties.dynamic;
-
-                        elCollideStatic.checked = properties.collidesWith.indexOf("static") > -1;
-                        elCollideKinematic.checked = properties.collidesWith.indexOf("kinematic") > -1;
-                        elCollideDynamic.checked = properties.collidesWith.indexOf("dynamic") > -1;
-                        elCollideMyAvatar.checked = properties.collidesWith.indexOf("myAvatar") > -1;
-                        elCollideOtherAvatar.checked = properties.collidesWith.indexOf("otherAvatar") > -1;
-
-                        elGrabbable.checked = properties.dynamic;
-                        elWantsTrigger.checked = false;
-                        elIgnoreIK.checked = false;
-                        var parsedUserData = {}
-                        try {
-                            parsedUserData = JSON.parse(properties.userData);
-                                                    
-                            if ("grabbableKey" in parsedUserData) {
-                                if ("grabbable" in parsedUserData["grabbableKey"]) {
-                                    elGrabbable.checked = parsedUserData["grabbableKey"].grabbable;
-                                }
-                                if ("wantsTrigger" in parsedUserData["grabbableKey"]) {
-                                    elWantsTrigger.checked = parsedUserData["grabbableKey"].wantsTrigger;
-                                }
-                                if ("ignoreIK" in parsedUserData["grabbableKey"]) {
-                                    elIgnoreIK.checked = parsedUserData["grabbableKey"].ignoreIK;
-                                }
-                            }
-                        } catch(e) {}
-
-                        elCollisionSoundURL.value = properties.collisionSoundURL;
-                        elLifetime.value = properties.lifetime;
-                        elScriptURL.value = properties.script;
-                        /*
-                        FIXME: See FIXME for property-script-url.
-                        elScriptTimestamp.value = properties.scriptTimestamp;
-                        */
-                        elUserData.value = properties.userData;
-                        setTextareaScrolling(elUserData);
-
-                        elHyperlinkHref.value = properties.href;
-                        elDescription.value = properties.description;
-
-                        for (var i = 0; i < allSections.length; i++) {
-                            for (var j = 0; j < allSections[i].length; j++) {
-                                allSections[i][j].style.display = 'none';
-                            }
-                        }                                    
-
-                        for (var i = 0; i < elHyperlinkSections.length; i++) {
-                            elHyperlinkSections[i].style.display = 'table';
-                        }
-
-                        if (properties.type == "Shape" || properties.type == "Box" || properties.type == "Sphere") {
-                            for (var i = 0; i < elShapeSections.length; i++) {
-                                elShapeSections[i].style.display = 'table';
-                            }
-                            elShape.value = properties.shape;
-                            setDropdownText(elShape);
-
-                        } else {
-                            for (var i = 0; i < elShapeSections.length; i++) {
-                                elShapeSections[i].style.display = 'none';
-                            }
-                        }
-                        
-                        if (properties.type == "Shape" || properties.type == "Box" || properties.type == "Sphere" || properties.type == "ParticleEffect") {
-                            for (var i = 0; i < elColorSections.length; i++) {
-                                elColorSections[i].style.display = 'table';
-                            }
-                            elColorRed.value = properties.color.red;
-                            elColorGreen.value = properties.color.green;
-                            elColorBlue.value = properties.color.blue;
-                            elColor.style.backgroundColor = "rgb(" + properties.color.red  + "," + properties.color.green + "," + properties.color.blue + ")";
-                        } else {
-                            for (var i = 0; i < elColorSections.length; i++) {
-                                elColorSections[i].style.display = 'none';
-                            }
-                        }
-
-                        if (properties.type == "Model") {
-                            for (var i = 0; i < elModelSections.length; i++) {
-                                elModelSections[i].style.display = 'table';
-                            }
-
-                            elModelURL.value = properties.modelURL;
-                            elShapeType.value = properties.shapeType;
-                            setDropdownText(elShapeType);
-                            elCompoundShapeURL.value = properties.compoundShapeURL;
-                            elModelAnimationURL.value = properties.animation.url;
-                            elModelAnimationPlaying.checked = properties.animation.running;
-                            elModelAnimationFPS.value = properties.animation.fps;
-                            elModelAnimationFrame.value = properties.animation.currentFrame;
-                            elModelAnimationFirstFrame.value = properties.animation.firstFrame;
-                            elModelAnimationLastFrame.value = properties.animation.lastFrame;
-                            elModelAnimationLoop.checked = properties.animation.loop;
-                            elModelAnimationHold.checked = properties.animation.hold;
-                            elModelTextures.value = properties.textures;
-                            setTextareaScrolling(elModelTextures);
-                            elModelOriginalTextures.value = properties.originalTextures;
-                            setTextareaScrolling(elModelOriginalTextures);
-                        } else if (properties.type == "Web") {
-                            for (var i = 0; i < elWebSections.length; i++) {
-                                elWebSections[i].style.display = 'table';
-                            }
-                            for (var i = 0; i < elHyperlinkSections.length; i++) {
-                                elHyperlinkSections[i].style.display = 'none';
-                            }
-
-                            elWebSourceURL.value = properties.sourceUrl;
-                        } else if (properties.type == "Text") {
-                            for (var i = 0; i < elTextSections.length; i++) {
-                                elTextSections[i].style.display = 'table';
-                            }
-
-                            elTextText.value = properties.text;
-                            elTextLineHeight.value = properties.lineHeight.toFixed(4);
-                            elTextFaceCamera = properties.faceCamera;
-                            elTextTextColor.style.backgroundColor = "rgb(" + properties.textColor.red  + "," + properties.textColor.green + "," + properties.textColor.blue + ")";
-                            elTextTextColorRed.value = properties.textColor.red;
-                            elTextTextColorGreen.value = properties.textColor.green;
-                            elTextTextColorBlue.value = properties.textColor.blue;
-                            elTextBackgroundColorRed.value = properties.backgroundColor.red;
-                            elTextBackgroundColorGreen.value = properties.backgroundColor.green;
-                            elTextBackgroundColorBlue.value = properties.backgroundColor.blue;
-                        } else if (properties.type == "Light") {
-                            for (var i = 0; i < elLightSections.length; i++) {
-                                elLightSections[i].style.display = 'table';
-                            }
-
-                            elLightSpotLight.checked = properties.isSpotlight;
-
-                            elLightColor.style.backgroundColor = "rgb(" + properties.color.red  + "," + properties.color.green + "," + properties.color.blue + ")";
-                            elLightColorRed.value = properties.color.red;
-                            elLightColorGreen.value = properties.color.green;
-                            elLightColorBlue.value = properties.color.blue;
-
-                            elLightIntensity.value = properties.intensity.toFixed(1);
-                            elLightFalloffRadius.value = properties.falloffRadius.toFixed(1);
-                            elLightExponent.value = properties.exponent.toFixed(2);
-                            elLightCutoff.value = properties.cutoff.toFixed(2);
-                        } else if (properties.type == "Zone") {
-                            for (var i = 0; i < elZoneSections.length; i++) {
-                                elZoneSections[i].style.display = 'table';
-                            }
-
-                            elZoneStageSunModelEnabled.checked = properties.stage.sunModelEnabled;
-                            elZoneKeyLightColor.style.backgroundColor = "rgb(" + properties.keyLight.color.red  + "," + properties.keyLight.color.green + "," + properties.keyLight.color.blue + ")";
-                            elZoneKeyLightColorRed.value = properties.keyLight.color.red;
-                            elZoneKeyLightColorGreen.value = properties.keyLight.color.green;
-                            elZoneKeyLightColorBlue.value = properties.keyLight.color.blue;
-                            elZoneKeyLightIntensity.value = properties.keyLight.intensity.toFixed(2);
-                            elZoneKeyLightAmbientIntensity.value = properties.keyLight.ambientIntensity.toFixed(2);
-                            elZoneKeyLightDirectionX.value = properties.keyLight.direction.x.toFixed(2);
-                            elZoneKeyLightDirectionY.value = properties.keyLight.direction.y.toFixed(2);
-                            elZoneKeyLightAmbientURL.value = properties.keyLight.ambientURL;
-
-
-                            elZoneStageLatitude.value = properties.stage.latitude.toFixed(2);
-                            elZoneStageLongitude.value = properties.stage.longitude.toFixed(2);
-                            elZoneStageAltitude.value = properties.stage.altitude.toFixed(2);
-                            elZoneStageAutomaticHourDay.checked = properties.stage.automaticHourDay;
-                            elZoneStageDay.value = properties.stage.day;
-                            elZoneStageHour.value = properties.stage.hour;
-                            elShapeType.value = properties.shapeType;
-                            elCompoundShapeURL.value = properties.compoundShapeURL;
-
-                            elZoneBackgroundMode.value = properties.backgroundMode;
-                            setDropdownText(elZoneBackgroundMode);
-
-                            elZoneSkyboxColor.style.backgroundColor = "rgb(" + properties.skybox.color.red  + "," + properties.skybox.color.green + "," + properties.skybox.color.blue + ")";
-                            elZoneSkyboxColorRed.value = properties.skybox.color.red;
-                            elZoneSkyboxColorGreen.value = properties.skybox.color.green;
-                            elZoneSkyboxColorBlue.value = properties.skybox.color.blue;
-                            elZoneSkyboxURL.value = properties.skybox.url;
-
-                            elZoneFlyingAllowed.checked = properties.flyingAllowed;
-                            elZoneGhostingAllowed.checked = properties.ghostingAllowed;
-
-                            showElements(document.getElementsByClassName('skybox-section'), elZoneBackgroundMode.value == 'skybox');
-                        } else if (properties.type == "PolyVox") {
-                            for (var i = 0; i < elPolyVoxSections.length; i++) {
-                                elPolyVoxSections[i].style.display = 'table';
-                            }
-
-                            elVoxelVolumeSizeX.value = properties.voxelVolumeSize.x.toFixed(2);
-                            elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2);
-                            elVoxelVolumeSizeZ.value = properties.voxelVolumeSize.z.toFixed(2);
-                            elVoxelSurfaceStyle.value = properties.voxelSurfaceStyle;
-                            setDropdownText(elVoxelSurfaceStyle);
-                            elXTextureURL.value = properties.xTextureURL;
-                            elYTextureURL.value = properties.yTextureURL;
-                            elZTextureURL.value = properties.zTextureURL;
-                        }
-                        
-                        var activeElement = document.activeElement;
-                        
-                        if(typeof activeElement.select!=="undefined"){
-                            activeElement.select();
-                        }
-                    }
-                }
-            });
-        }
-
-        elLocked.addEventListener('change', createEmitCheckedPropertyUpdateFunction('locked'));
-        elName.addEventListener('change', createEmitTextPropertyUpdateFunction('name'));
-        elHyperlinkHref.addEventListener('change', createEmitTextPropertyUpdateFunction('href'));
-        elDescription.addEventListener('change', createEmitTextPropertyUpdateFunction('description'));
-        elVisible.addEventListener('change', createEmitCheckedPropertyUpdateFunction('visible'));
-
-        var positionChangeFunction = createEmitVec3PropertyUpdateFunction(
-            'position', elPositionX, elPositionY, elPositionZ);
-        elPositionX.addEventListener('change', positionChangeFunction);
-        elPositionY.addEventListener('change', positionChangeFunction);
-        elPositionZ.addEventListener('change', positionChangeFunction);
-
-        var dimensionsChangeFunction = createEmitVec3PropertyUpdateFunction(
-            'dimensions', elDimensionsX, elDimensionsY, elDimensionsZ);
-        elDimensionsX.addEventListener('change', dimensionsChangeFunction);
-        elDimensionsY.addEventListener('change', dimensionsChangeFunction);
-        elDimensionsZ.addEventListener('change', dimensionsChangeFunction);
-
-        elParentID.addEventListener('change', createEmitTextPropertyUpdateFunction('parentID'));
-        elParentJointIndex.addEventListener('change', createEmitNumberPropertyUpdateFunction('parentJointIndex'));
-
-        var registrationChangeFunction = createEmitVec3PropertyUpdateFunction(
-            'registrationPoint', elRegistrationX, elRegistrationY, elRegistrationZ);
-        elRegistrationX.addEventListener('change', registrationChangeFunction);
-        elRegistrationY.addEventListener('change', registrationChangeFunction);
-        elRegistrationZ.addEventListener('change', registrationChangeFunction);
-
-        var rotationChangeFunction = createEmitVec3PropertyUpdateFunction(
-            'rotation', elRotationX, elRotationY, elRotationZ);
-        elRotationX.addEventListener('change', rotationChangeFunction);
-        elRotationY.addEventListener('change', rotationChangeFunction);
-        elRotationZ.addEventListener('change', rotationChangeFunction);
-
-        var velocityChangeFunction = createEmitVec3PropertyUpdateFunction(
-            'velocity', elLinearVelocityX, elLinearVelocityY, elLinearVelocityZ);
-        elLinearVelocityX.addEventListener('change', velocityChangeFunction);
-        elLinearVelocityY.addEventListener('change', velocityChangeFunction);
-        elLinearVelocityZ.addEventListener('change', velocityChangeFunction);
-        elLinearDamping.addEventListener('change', createEmitNumberPropertyUpdateFunction('damping'));
-
-        var angularVelocityChangeFunction = createEmitVec3PropertyUpdateFunctionWithMultiplier(
-            'angularVelocity', elAngularVelocityX, elAngularVelocityY, elAngularVelocityZ, DEGREES_TO_RADIANS);
-        elAngularVelocityX.addEventListener('change', angularVelocityChangeFunction);
-        elAngularVelocityY.addEventListener('change', angularVelocityChangeFunction);
-        elAngularVelocityZ.addEventListener('change', angularVelocityChangeFunction);
-        elAngularDamping.addEventListener('change', createEmitNumberPropertyUpdateFunction('angularDamping'));
-
-        elRestitution.addEventListener('change', createEmitNumberPropertyUpdateFunction('restitution'));
-        elFriction.addEventListener('change', createEmitNumberPropertyUpdateFunction('friction'));
-
-        var gravityChangeFunction = createEmitVec3PropertyUpdateFunction(
-            'gravity', elGravityX, elGravityY, elGravityZ);
-        elGravityX.addEventListener('change', gravityChangeFunction);
-        elGravityY.addEventListener('change', gravityChangeFunction);
-        elGravityZ.addEventListener('change', gravityChangeFunction);
-
-        var accelerationChangeFunction = createEmitVec3PropertyUpdateFunction(
-            'acceleration', elAccelerationX, elAccelerationY, elAccelerationZ);
-        elAccelerationX.addEventListener('change', accelerationChangeFunction);
-        elAccelerationY.addEventListener('change', accelerationChangeFunction);
-        elAccelerationZ.addEventListener('change', accelerationChangeFunction);
-
-        elDensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('density'));
-        elCollisionless.addEventListener('change', createEmitCheckedPropertyUpdateFunction('collisionless'));
-        elDynamic.addEventListener('change', createEmitCheckedPropertyUpdateFunction('dynamic'));
-
-        elCollideDynamic.addEventListener('change', function() {
-            updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideDynamic, 'dynamic');
-        });
-
-        elCollideKinematic.addEventListener('change', function() {
-            updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideKinematic, 'kinematic');
-        });
-
-        elCollideStatic.addEventListener('change', function() {
-            updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideStatic, 'static');
-        });
-        elCollideMyAvatar.addEventListener('change', function() {
-            updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideMyAvatar, 'myAvatar');
-        });
-        elCollideOtherAvatar.addEventListener('change', function() {
-            updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideOtherAvatar, 'otherAvatar');
-        });
-
-        elGrabbable.addEventListener('change', function() {
-            userDataChanger("grabbableKey", "grabbable", elGrabbable, elUserData, properties.dynamic);
-        });
-        elWantsTrigger.addEventListener('change', function() {
-            userDataChanger("grabbableKey", "wantsTrigger", elWantsTrigger, elUserData, false);
-        });
-        elIgnoreIK.addEventListener('change', function() {
-            userDataChanger("grabbableKey", "ignoreIK", elIgnoreIK, elUserData, false);
-        });
-
-        elCollisionSoundURL.addEventListener('change', createEmitTextPropertyUpdateFunction('collisionSoundURL'));
-
-        elLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifetime'));
-        elScriptURL.addEventListener('change', createEmitTextPropertyUpdateFunction('script'));
-        /*
-        FIXME: See FIXME for property-script-url.
-        elScriptTimestamp.addEventListener('change', createEmitNumberPropertyUpdateFunction('scriptTimestamp'));
-        */
-        elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData'));
-
-        var colorChangeFunction = createEmitColorPropertyUpdateFunction(
-            'color', elColorRed, elColorGreen, elColorBlue);
-        elColorRed.addEventListener('change', colorChangeFunction);
-        elColorGreen.addEventListener('change', colorChangeFunction);
-        elColorBlue.addEventListener('change', colorChangeFunction);
-        colorPickers.push($('#property-color').colpick({
-            colorScheme: 'dark',
-            layout: 'hex',
-            color: '000000',
-            onShow: function (colpick) {
-                $('#property-color').attr('active', 'true');
-            },
-            onHide: function (colpick) {
-                $('#property-color').attr('active', 'false');
-            },
-            onSubmit: function (hsb, hex, rgb, el) {
-                $(el).css('background-color', '#' + hex);
-                $(el).colpickHide();
-                emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b);
-            }
-        }));
-
-        elLightSpotLight.addEventListener('change', createEmitCheckedPropertyUpdateFunction('isSpotlight'));
-
-        var lightColorChangeFunction = createEmitColorPropertyUpdateFunction(
-            'color', elLightColorRed, elLightColorGreen, elLightColorBlue);
-        elLightColorRed.addEventListener('change', lightColorChangeFunction);
-        elLightColorGreen.addEventListener('change', lightColorChangeFunction);
-        elLightColorBlue.addEventListener('change', lightColorChangeFunction);
-        colorPickers.push($('#property-light-color').colpick({
-            colorScheme: 'dark',
-            layout: 'hex',
-            color: '000000',
-            onShow: function (colpick) {
-                $('#property-light-color').attr('active', 'true');
-            },
-            onHide: function (colpick) {
-                $('#property-light-color').attr('active', 'false');
-            },
-            onSubmit: function (hsb, hex, rgb, el) {
-                $(el).css('background-color', '#' + hex);
-                $(el).colpickHide();
-                emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b);
-            }
-        }));
-
-        elLightIntensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('intensity', 1));
-        elLightFalloffRadius.addEventListener('change', createEmitNumberPropertyUpdateFunction('falloffRadius', 1));
-        elLightExponent.addEventListener('change', createEmitNumberPropertyUpdateFunction('exponent', 2));
-        elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff', 2));
-
-        elShape.addEventListener('change', createEmitTextPropertyUpdateFunction('shape'));
-
-        elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl'));
-
-        elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL'));
-        elShapeType.addEventListener('change', createEmitTextPropertyUpdateFunction('shapeType'));
-        elCompoundShapeURL.addEventListener('change', createEmitTextPropertyUpdateFunction('compoundShapeURL'));
-
-        elModelAnimationURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('animation', 'url'));
-        elModelAnimationPlaying.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation','running'));
-        elModelAnimationFPS.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation','fps'));
-        elModelAnimationFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'currentFrame'));
-        elModelAnimationFirstFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'firstFrame'));
-        elModelAnimationLastFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'lastFrame'));
-        elModelAnimationLoop.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'loop'));
-        elModelAnimationHold.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'hold'));
-
-        elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures'));
-
-        elTextText.addEventListener('change', createEmitTextPropertyUpdateFunction('text'));
-        elTextFaceCamera.addEventListener('change', createEmitCheckedPropertyUpdateFunction('faceCamera'));
-        elTextLineHeight.addEventListener('change', createEmitNumberPropertyUpdateFunction('lineHeight'));
-        var textTextColorChangeFunction = createEmitColorPropertyUpdateFunction(
-            'textColor', elTextTextColorRed, elTextTextColorGreen, elTextTextColorBlue);
-        elTextTextColorRed.addEventListener('change', textTextColorChangeFunction);
-        elTextTextColorGreen.addEventListener('change', textTextColorChangeFunction);
-        elTextTextColorBlue.addEventListener('change', textTextColorChangeFunction);
-        colorPickers.push($('#property-text-text-color').colpick({
-            colorScheme:'dark',
-            layout:'hex',
-            color: '000000',
-            onShow: function (colpick) {
-                $('#property-text-text-color').attr('active', 'true');
-            },
-            onHide: function (colpick) {
-                $('#property-text-text-color').attr('active', 'false');
-            },
-            onSubmit: function (hsb, hex, rgb, el) {
-                $(el).css('background-color', '#'+hex);
-                $(el).colpickHide();
-                $(el).attr('active', 'false');
-                emitColorPropertyUpdate('textColor', rgb.r, rgb.g, rgb.b);
-            }
-        }));
-
-        var textBackgroundColorChangeFunction = createEmitColorPropertyUpdateFunction(
-            'backgroundColor', elTextBackgroundColorRed, elTextBackgroundColorGreen, elTextBackgroundColorBlue);
-        elTextBackgroundColorRed.addEventListener('change', textBackgroundColorChangeFunction);
-        elTextBackgroundColorGreen.addEventListener('change', textBackgroundColorChangeFunction);
-        elTextBackgroundColorBlue.addEventListener('change', textBackgroundColorChangeFunction);
-        colorPickers.push($('#property-text-background-color').colpick({
-            colorScheme:'dark',
-            layout:'hex',
-            color:'000000',
-            onShow: function (colpick) {
-                $('#property-text-background-color').attr('active', 'true');
-            },
-            onHide: function (colpick) {
-                $('#property-text-background-color').attr('active', 'false');
-            },
-            onSubmit: function (hsb, hex, rgb, el) {
-                $(el).css('background-color', '#'+hex);
-                $(el).colpickHide();
-                emitColorPropertyUpdate('backgroundColor', rgb.r, rgb.g, rgb.b);
-            }
-        }));
-
-        elZoneStageSunModelEnabled.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','sunModelEnabled'));
-        colorPickers.push($('#property-zone-key-light-color').colpick({
-            colorScheme:'dark',
-            layout:'hex',
-            color:'000000',
-            onShow: function (colpick) {
-                $('#property-zone-key-light-color').attr('active', 'true');
-            },
-            onHide: function (colpick) {
-                $('#property-zone-key-light-color').attr('active', 'false');
-            },
-            onSubmit: function (hsb, hex, rgb, el) {
-                $(el).css('background-color', '#'+hex);
-                $(el).colpickHide();
-                emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'keyLight');
-            }
-        }));
-        var zoneKeyLightColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('keyLight','color', elZoneKeyLightColorRed, elZoneKeyLightColorGreen, elZoneKeyLightColorBlue);
-        elZoneKeyLightColorRed.addEventListener('change', zoneKeyLightColorChangeFunction);
-        elZoneKeyLightColorGreen.addEventListener('change', zoneKeyLightColorChangeFunction);
-        elZoneKeyLightColorBlue.addEventListener('change', zoneKeyLightColorChangeFunction);
-        elZoneKeyLightIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','intensity'));
-        elZoneKeyLightAmbientIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','ambientIntensity'));
-        elZoneKeyLightAmbientURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('keyLight','ambientURL'));
-        var zoneKeyLightDirectionChangeFunction = createEmitGroupVec3PropertyUpdateFunction('keyLight','direction', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY);
-        elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction);
-        elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction);
-
-        elZoneStageLatitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','latitude'));
-        elZoneStageLongitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','longitude'));
-        elZoneStageAltitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','altitude'));
-        elZoneStageAutomaticHourDay.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','automaticHourDay'));
-        elZoneStageDay.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','day'));
-        elZoneStageHour.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','hour'));
-
-
-        elZoneBackgroundMode.addEventListener('change', createEmitTextPropertyUpdateFunction('backgroundMode'));
-        var zoneSkyboxColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('skybox','color',
-            elZoneSkyboxColorRed, elZoneSkyboxColorGreen, elZoneSkyboxColorBlue);
-        elZoneSkyboxColorRed.addEventListener('change', zoneSkyboxColorChangeFunction);
-        elZoneSkyboxColorGreen.addEventListener('change', zoneSkyboxColorChangeFunction);
-        elZoneSkyboxColorBlue.addEventListener('change', zoneSkyboxColorChangeFunction);
-        colorPickers.push($('#property-zone-skybox-color').colpick({
-            colorScheme:'dark',
-            layout:'hex',
-            color:'000000',
-            onShow: function (colpick) {
-                $('#property-zone-skybox-color').attr('active', 'true');
-            },
-            onHide: function (colpick) {
-                $('#property-zone-skybox-color').attr('active', 'false');
-            },
-            onSubmit: function (hsb, hex, rgb, el) {
-                $(el).css('background-color', '#'+hex);
-                $(el).colpickHide();
-                emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'skybox');
-            }
-        }));
-
-        elZoneSkyboxURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('skybox','url'));
-
-        elZoneFlyingAllowed.addEventListener('change', createEmitCheckedPropertyUpdateFunction('flyingAllowed'));
-        elZoneGhostingAllowed.addEventListener('change', createEmitCheckedPropertyUpdateFunction('ghostingAllowed'));
-
-        var voxelVolumeSizeChangeFunction = createEmitVec3PropertyUpdateFunction(
-            'voxelVolumeSize', elVoxelVolumeSizeX, elVoxelVolumeSizeY, elVoxelVolumeSizeZ);
-        elVoxelVolumeSizeX.addEventListener('change', voxelVolumeSizeChangeFunction);
-        elVoxelVolumeSizeY.addEventListener('change', voxelVolumeSizeChangeFunction);
-        elVoxelVolumeSizeZ.addEventListener('change', voxelVolumeSizeChangeFunction);
-        elVoxelSurfaceStyle.addEventListener('change', createEmitTextPropertyUpdateFunction('voxelSurfaceStyle'));
-        elXTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('xTextureURL'));
-        elYTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('yTextureURL'));
-        elZTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('zTextureURL'));
-
-        elMoveSelectionToGrid.addEventListener("click", function() {
-            EventBridge.emitWebEvent(JSON.stringify({
-                type: "action",
-                action: "moveSelectionToGrid",
-            }));
-        });
-        elMoveAllToGrid.addEventListener("click", function() {
-            EventBridge.emitWebEvent(JSON.stringify({
-                type: "action",
-                action: "moveAllToGrid",
-            }));
-        });
-        elResetToNaturalDimensions.addEventListener("click", function() {
-            EventBridge.emitWebEvent(JSON.stringify({
-                type: "action",
-                action: "resetToNaturalDimensions",
-            }));
-        });
-        elRescaleDimensionsButton.addEventListener("click", function() {
-            EventBridge.emitWebEvent(JSON.stringify({
-                type: "action",
-                action: "rescaleDimensions",
-                percentage: parseInt(elRescaleDimensionsPct.value),
-            }));
-        });
-        /*
-        FIXME: See FIXME for property-script-url.
-        elReloadScriptButton.addEventListener("click", function() {
-            EventBridge.emitWebEvent(JSON.stringify({
-                type: "action",
-                action: "reloadScript"
-            }));
-        });
-        */
-        elPreviewCameraButton.addEventListener("click", function() {
-            EventBridge.emitWebEvent(JSON.stringify({
-                type: "action",
-                action: "previewCamera"
-            }));
-        });
-
-        window.onblur = function() {
-            // Fake a change event
-            var ev = document.createEvent("HTMLEvents");
-            ev.initEvent("change", true, true);
-            document.activeElement.dispatchEvent(ev);
-        }
-
-        // For input and textarea elements, select all of the text on focus
-        // WebKit-based browsers, such as is used with QWebView, have a quirk
-        // where the mouseup event comes after the focus event, causing the
-        // text to be deselected immediately after selecting all of the text.
-        // To make this work we block the first mouseup event after the elements
-        // received focus.  If we block all mouseup events the user will not
-        // be able to click within the selected text.
-        // We also check to see if the value has changed to make sure we aren't
-        // blocking a mouse-up event when clicking on an input spinner.
-        var els = document.querySelectorAll("input, textarea");
-        for (var i = 0; i < els.length; i++) {
-            var clicked = false;
-            var originalText;
-            els[i].onfocus = function(e) {
-                originalText = this.value;
-                this.select();
-                clicked = false;
-            };
-            els[i].onmouseup = function(e) {
-                if (!clicked && originalText == this.value) {
-                    e.preventDefault();
-                }
-                clicked = true;
-            };
-        }
-    });
-
-    // Collapsible sections
-    var elCollapsible = document.getElementsByClassName("section-header");
-
-    var toggleCollapsedEvent = function (event) {
-        var element = event.target;
-        if (element.nodeName !== "DIV") {
-            element = element.parentNode;
-        }
-        var isCollapsed = element.getAttribute("collapsed") !== "true";
-        element.setAttribute("collapsed", isCollapsed ? "true" : "false");
-        element.getElementsByTagName("span")[0].textContent = isCollapsed ? "L" : "M";
-    };
-
-    for (var i = 0, length = elCollapsible.length; i < length; i++) {
-        var element = elCollapsible[i];
-        element.addEventListener("click", toggleCollapsedEvent, true);
-    };
-
-
-    // Textarea scrollbars
-    var elTextareas = document.getElementsByTagName("TEXTAREA");
-
-    var textareaOnChangeEvent = function (event) {
-        setTextareaScrolling(event.target);
-    }
-
-    for (var i = 0, length = elTextareas.length; i < length; i++) {
-        var element = elTextareas[i];
-        setTextareaScrolling(element);
-        element.addEventListener("input", textareaOnChangeEvent, false);
-        element.addEventListener("change", textareaOnChangeEvent, false);
-        /* FIXME: Detect and update textarea scrolling attribute on resize. Unfortunately textarea doesn't have a resize
-        event; mouseup is a partial stand-in but doesn't handle resizing if mouse moves outside textarea rectangle. */
-        element.addEventListener("mouseup", textareaOnChangeEvent, false);
-    };
-
-    // Dropdowns
-    // For each dropdown the following replacement is created in place of the oriringal dropdown...
-    // Structure created:
-    //  <dl dropped="true/false">
-    //      <dt name="?" id="?" value="?"><span>display text</span><span>carat</span></dt>
-    //      <dd>
-    //          <ul>
-    //              <li value="??>display text</li>
-    //              <li>...</li>
-    //          </ul>
-    //      </dd>
-    //  </dl>
-
-    function setDropdownText(dropdown) {
-        var lis = dropdown.parentNode.getElementsByTagName("li");
-        var text = "";
-        for (var i = 0; i < lis.length; i++) {
-            if (lis[i].getAttribute("value") === dropdown.value) {
-                text = lis[i].textContent;
-            }
-        }
-        dropdown.firstChild.textContent = text;
-    }
-
-    function toggleDropdown(event) {
-        var element = event.target;
-        if (element.nodeName !== "DT") {
-            element = element.parentNode;
-        }
-        element = element.parentNode;
-        var isDropped = element.getAttribute("dropped");
-        element.setAttribute("dropped", isDropped !== "true" ? "true" : "false");
-    }
-
-    function setDropdownValue(event) {
-        var dt = event.target.parentNode.parentNode.previousSibling;
-        dt.value = event.target.getAttribute("value");
-        dt.firstChild.textContent = event.target.textContent;
-
-        dt.parentNode.setAttribute("dropped", "false");
-
-        var evt = document.createEvent("HTMLEvents");
-        evt.initEvent("change", true, true);
-        dt.dispatchEvent(evt);
-    }
-
-    var elDropdowns = document.getElementsByTagName("select");
-    for (var i = 0; i < elDropdowns.length; i++) {
-        var options = elDropdowns[i].getElementsByTagName("option");
-        var selectedOption = 0;
-        for (var j = 0; j < options.length; j++) {
-            if (options[j].getAttribute("selected") === "selected") {
-                selectedOption = j;
-            }
-        }
-        var div = elDropdowns[i].parentNode;
-
-        var dl = document.createElement("dl");
-        div.appendChild(dl);
-
-        var dt = document.createElement("dt");
-        dt.name = elDropdowns[i].name;
-        dt.id = elDropdowns[i].id;
-        dt.addEventListener("click", toggleDropdown, true);
-        dl.appendChild(dt);
-
-        var span = document.createElement("span");
-        span.setAttribute("value", options[selectedOption].value);
-        span.textContent = options[selectedOption].firstChild.textContent;
-        dt.appendChild(span);
-
-        var span = document.createElement("span");
-        span.textContent = "5";  // caratDn
-        dt.appendChild(span);
-
-        var dd = document.createElement("dd");
-        dl.appendChild(dd);
-
-        var ul = document.createElement("ul");
-        dd.appendChild(ul);
-
-        for (var j = 0; j < options.length; j++) {
-            var li = document.createElement("li");
-            li.setAttribute("value", options[j].value);
-            li.textContent = options[j].firstChild.textContent;
-            li.addEventListener("click", setDropdownValue);
-            ul.appendChild(li);
-        }
-    }
-
-    elDropdowns = document.getElementsByTagName("select");
-    while (elDropdowns.length > 0) {
-        var el = elDropdowns[0];
-        el.parentNode.removeChild(el);
-        elDropdowns = document.getElementsByTagName("select");
-    }
-
-    augmentSpinButtons();
-
-    // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
-    document.addEventListener("contextmenu", function (event) {
-        event.preventDefault();
-    }, false);
-}
-
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js
index 490579e909..5898f85e90 100644
--- a/scripts/system/html/js/entityProperties.js
+++ b/scripts/system/html/js/entityProperties.js
@@ -492,8 +492,6 @@ function loaded() {
         var elYTextureURL = document.getElementById("property-y-texture-url");
         var elZTextureURL = document.getElementById("property-z-texture-url");
 
-        var elPreviewCameraButton = document.getElementById("preview-camera-button");
-
         if (window.EventBridge !== undefined) {
             var properties;
             EventBridge.scriptEventReceived.connect(function(data) {
@@ -1133,12 +1131,6 @@ function loaded() {
             }));
         });
         */
-        elPreviewCameraButton.addEventListener("click", function() {
-            EventBridge.emitWebEvent(JSON.stringify({
-                type: "action",
-                action: "previewCamera"
-            }));
-        });
 
         window.onblur = function() {
             // Fake a change event

From afe76c69d21ad158f5452465c833b91ce2e26e9e Mon Sep 17 00:00:00 2001
From: David Rowe <david@ctrlaltstudio.com>
Date: Wed, 10 Aug 2016 10:41:16 +1200
Subject: [PATCH 70/78] Fix crash when adding new attachment

---
 libraries/avatars/src/AvatarData.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp
index dee0d1cb20..d0c7b3912c 100644
--- a/libraries/avatars/src/AvatarData.cpp
+++ b/libraries/avatars/src/AvatarData.cpp
@@ -1638,7 +1638,9 @@ void AvatarData::setAttachmentsVariant(const QVariantList& variant) {
     for (const auto& attachmentVar : variant) {
         AttachmentData attachment;
         attachment.fromVariant(attachmentVar);
-        newAttachments.append(attachment);
+        if (!attachment.modelURL.isEmpty()) {
+            newAttachments.append(attachment);
+        }
     }
     setAttachmentData(newAttachments);
 }

From 8e56e0bf18782af197a0dc475486141fa081f262 Mon Sep 17 00:00:00 2001
From: David Rowe <david@ctrlaltstudio.com>
Date: Wed, 10 Aug 2016 10:41:37 +1200
Subject: [PATCH 71/78] Fix QML warning

---
 interface/resources/qml/hifi/dialogs/attachments/Attachment.qml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml
index 04e3934535..6d371741ea 100644
--- a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml
+++ b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml
@@ -24,6 +24,7 @@ Item {
     Rectangle { color: hifi.colors.baseGray; anchors.fill: parent; radius: 4 }
 
     Component.onCompleted: {
+        jointChooser.model = MyAvatar.jointNames;
         completed = true;
     }
 
@@ -82,7 +83,6 @@ Item {
             HifiControls.ComboBox {
                 id: jointChooser;
                 anchors { bottom: parent.bottom; left: parent.left; right: parent.right }
-                model: MyAvatar.jointNames
                 colorScheme: hifi.colorSchemes.dark
                 currentIndex: attachment ? model.indexOf(attachment.jointName) : -1
                 onCurrentIndexChanged: {

From 2360e90d395c5e9906188cfa3070b1913efc3909 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Tue, 9 Aug 2016 15:48:31 -0700
Subject: [PATCH 72/78] simplify valid check for rotation

---
 scripts/system/controllers/teleport.js | 22 ++++++----------------
 1 file changed, 6 insertions(+), 16 deletions(-)

diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js
index 5ec429fae0..5a1ae7e5ee 100644
--- a/scripts/system/controllers/teleport.js
+++ b/scripts/system/controllers/teleport.js
@@ -234,17 +234,12 @@ function Teleporter() {
     this.rightRay = function() {
         var pose = Controller.getPoseValue(Controller.Standard.RightHand);
         var rightPosition = pose.valid ? Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position) : MyAvatar.getHeadPosition();
-        var rightRotation = pose.valid ? Quat.multiply(MyAvatar.orientation, pose.rotation) : MyAvatar.headOrientation;
-
-        var rightFinal = Quat.multiply(rightRotation, Quat.angleAxis(-90, {
-            x: 1,
-            y: 0,
-            z: 0
-        }));
+        var rightRotation = pose.valid ? Quat.multiply(MyAvatar.orientation, pose.rotation) :
+                                         Quat.multiply(MyAvatar.headOrientation, Quat.angleAxis(-90, {x: 1, y: 0, z: 0}));
 
         var rightPickRay = {
             origin: rightPosition,
-            direction: Quat.getUp(pose.valid ? rightRotation : rightFinal),
+            direction: Quat.getUp(rightRotation),
         };
 
         this.rightPickRay = rightPickRay;
@@ -287,17 +282,12 @@ function Teleporter() {
     this.leftRay = function() {
         var pose = Controller.getPoseValue(Controller.Standard.LeftHand);
         var leftPosition = pose.valid ? Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position) : MyAvatar.getHeadPosition();
-        var leftRotation = pose.valid ? Quat.multiply(MyAvatar.orientation, pose.rotation) : MyAvatar.headOrientation;
-
-        var leftFinal = Quat.multiply(leftRotation, Quat.angleAxis(-90, {
-            x: 1,
-            y: 0,
-            z: 0
-        }));
+        var leftRotation = pose.valid ? Quat.multiply(MyAvatar.orientation, pose.rotation) :
+                                        Quat.multiply(MyAvatar.headOrientation, Quat.angleAxis(-90, {x: 1, y: 0, z: 0}));
 
         var leftPickRay = {
             origin: leftPosition,
-            direction: Quat.getUp(pose.valid ? leftRotation : leftFinal),
+            direction: Quat.getUp(leftRotation),
         };
 
         this.leftPickRay = leftPickRay;

From 50a4c8b4965fc7d6957554d7dbff221fa2949b33 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Tue, 9 Aug 2016 18:02:56 -0700
Subject: [PATCH 73/78] mini mirror fades with overlays

---
 interface/src/ui/ApplicationOverlay.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp
index 6d5df31766..888529da5c 100644
--- a/interface/src/ui/ApplicationOverlay.cpp
+++ b/interface/src/ui/ApplicationOverlay.cpp
@@ -28,6 +28,8 @@
 #include "Util.h"
 #include "ui/Stats.h"
 #include "ui/AvatarInputs.h"
+#include "OffscreenUi.h"
+#include <QQmlContext>
 
 const vec4 CONNECTION_STATUS_BORDER_COLOR{ 1.0f, 0.0f, 0.0f, 0.8f };
 static const float ORTHO_NEAR_CLIP = -1000.0f;
@@ -177,13 +179,11 @@ void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) {
         glm::vec2 texCoordMinCorner(0.0f, 0.0f);
         glm::vec2 texCoordMaxCorner(viewport.width() * renderRatio / float(selfieTexture->getWidth()), viewport.height() * renderRatio / float(selfieTexture->getHeight()));
 
-
-        geometryCache->useSimpleDrawPipeline(batch, true);
         batch.setResourceTexture(0, selfieTexture);
-        geometryCache->renderQuad(batch, bottomLeft, topRight, texCoordMinCorner, texCoordMaxCorner, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); 
+        float alpha = DependencyManager::get<OffscreenUi>()->getDesktop()->property("unpinnedAlpha").toFloat();
+        geometryCache->renderQuad(batch, bottomLeft, topRight, texCoordMinCorner, texCoordMaxCorner, glm::vec4(1.0f, 1.0f, 1.0f, alpha));
 
         batch.setResourceTexture(0, renderArgs->_whiteTexture);
-        geometryCache->useSimpleDrawPipeline(batch, false);
     }
 }
 

From 9e20d922416e5278518fe3e097e1a77b0612a6e1 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Wed, 10 Aug 2016 10:35:18 -0700
Subject: [PATCH 74/78] CR changes

---
 libraries/gpu/src/gpu/Shader.h               | 2 +-
 libraries/render-utils/src/AnimDebugDraw.cpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h
index a741eafd40..dcec023013 100755
--- a/libraries/gpu/src/gpu/Shader.h
+++ b/libraries/gpu/src/gpu/Shader.h
@@ -58,7 +58,7 @@ public:
         Slot(const Slot& s) : _name(s._name), _location(s._location), _element(s._element), _resourceType(s._resourceType), _size(s._size) {}
         Slot(Slot&& s) : _name(s._name), _location(s._location), _element(s._element), _resourceType(s._resourceType), _size(s._size) {}
         Slot(const std::string& name, int32 location, const Element& element, uint16 resourceType = Resource::BUFFER, uint32 size = 0) :
-        _name(name), _location(location), _element(element), _resourceType(resourceType), _size(size) {}
+            _name(name), _location(location), _element(element), _resourceType(resourceType), _size(size) {}
         Slot(const std::string& name) : _name(name) {}
 
         Slot& operator= (const Slot& s) {
diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp
index f1443f7e4d..b905928423 100644
--- a/libraries/render-utils/src/AnimDebugDraw.cpp
+++ b/libraries/render-utils/src/AnimDebugDraw.cpp
@@ -7,6 +7,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include <qmath.h>
+
 #include "animdebugdraw_vert.h"
 #include "animdebugdraw_frag.h"
 #include <gpu/Batch.h>
@@ -15,8 +17,6 @@
 #include "GLMHelpers.h"
 #include "DebugDraw.h"
 
-#include <qmath.h>
-
 #include "AnimDebugDraw.h"
 
 struct Vertex {

From e35d453b37782f27e98a5b5e2d77f716b009632e Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Wed, 10 Aug 2016 10:36:37 -0700
Subject: [PATCH 75/78] ...missing space

---
 libraries/gpu/src/gpu/Shader.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h
index dcec023013..ebe0bc83df 100755
--- a/libraries/gpu/src/gpu/Shader.h
+++ b/libraries/gpu/src/gpu/Shader.h
@@ -58,7 +58,7 @@ public:
         Slot(const Slot& s) : _name(s._name), _location(s._location), _element(s._element), _resourceType(s._resourceType), _size(s._size) {}
         Slot(Slot&& s) : _name(s._name), _location(s._location), _element(s._element), _resourceType(s._resourceType), _size(s._size) {}
         Slot(const std::string& name, int32 location, const Element& element, uint16 resourceType = Resource::BUFFER, uint32 size = 0) :
-            _name(name), _location(location), _element(element), _resourceType(resourceType), _size(size) {}
+             _name(name), _location(location), _element(element), _resourceType(resourceType), _size(size) {}
         Slot(const std::string& name) : _name(name) {}
 
         Slot& operator= (const Slot& s) {

From d8545bc7a3bbfd319ef11212d580bbed4d990d67 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Wed, 10 Aug 2016 13:49:35 -0700
Subject: [PATCH 76/78] fix procedural transparency

---
 .../src/RenderableShapeEntityItem.cpp         |  5 -----
 .../procedural/src/procedural/Procedural.cpp  | 21 +++++++++++++++----
 .../procedural/src/procedural/Procedural.h    | 10 ++++++---
 .../src/procedural/ProceduralSkybox.cpp       |  3 ++-
 4 files changed, 26 insertions(+), 13 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
index 73c4d99b5e..e14def114d 100644
--- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
@@ -91,11 +91,6 @@ void RenderableShapeEntityItem::render(RenderArgs* args) {
         _procedural.reset(new Procedural(getUserData()));
         _procedural->_vertexSource = simple_vert;
         _procedural->_fragmentSource = simple_frag;
-        _procedural->_state->setCullMode(gpu::State::CULL_NONE);
-        _procedural->_state->setDepthTest(true, true, gpu::LESS_EQUAL);
-        _procedural->_state->setBlendFunction(true,
-            gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
-            gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
     }
 
     gpu::Batch& batch = *args->_batch;
diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp
index cd9edb6621..79a90ef72d 100644
--- a/libraries/procedural/src/procedural/Procedural.cpp
+++ b/libraries/procedural/src/procedural/Procedural.cpp
@@ -63,7 +63,19 @@ QJsonValue Procedural::getProceduralData(const QString& proceduralJson) {
     return doc.object()[PROCEDURAL_USER_DATA_KEY];
 }
 
-Procedural::Procedural() : _state { std::make_shared<gpu::State>() } {
+Procedural::Procedural() {
+    _opaqueState->setCullMode(gpu::State::CULL_NONE);
+    _opaqueState->setDepthTest(true, true, gpu::LESS_EQUAL);
+    _opaqueState->setBlendFunction(false,
+        gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
+        gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
+
+    _transparentState->setCullMode(gpu::State::CULL_NONE);
+    _transparentState->setDepthTest(true, true, gpu::LESS_EQUAL);
+    _transparentState->setBlendFunction(true,
+        gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
+        gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
+
     _proceduralDataDirty = false;
 }
 
@@ -230,7 +242,7 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm
         _shaderSource = _networkShader->_source;
     }
 
-    if (!_pipeline || _shaderDirty) {
+    if (!_opaquePipeline || !_transparentPipeline || _shaderDirty) {
         if (!_vertexShader) {
             _vertexShader = gpu::Shader::createVertex(_vertexSource);
         }
@@ -268,7 +280,8 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm
         slotBindings.insert(gpu::Shader::Binding(std::string("iChannel3"), 3));
         gpu::Shader::makeProgram(*_shader, slotBindings);
 
-        _pipeline = gpu::Pipeline::create(_shader, _state);
+        _opaquePipeline = gpu::Pipeline::create(_shader, _opaqueState);
+        _transparentPipeline = gpu::Pipeline::create(_shader, _transparentState);
         for (size_t i = 0; i < NUM_STANDARD_UNIFORMS; ++i) {
             const std::string& name = STANDARD_UNIFORM_NAMES[i];
             _standardUniformSlots[i] = _shader->getUniforms().findLocation(name);
@@ -277,7 +290,7 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm
         _frameCount = 0;
     }
 
-    batch.setPipeline(_pipeline);
+    batch.setPipeline(isFading() ? _transparentPipeline : _opaquePipeline);
 
     if (_shaderDirty || _uniformsDirty) {
         setupUniforms();
diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h
index c2939e4a01..fdca85bbae 100644
--- a/libraries/procedural/src/procedural/Procedural.h
+++ b/libraries/procedural/src/procedural/Procedural.h
@@ -43,15 +43,17 @@ public:
 
     glm::vec4 getColor(const glm::vec4& entityColor);
     quint64 getFadeStartTime() { return _fadeStartTime; }
-    bool isFading() { return _isFading; }
+    bool isFading() { return _doesFade && _isFading; }
     void setIsFading(bool isFading) { _isFading = isFading; }
+    void setDoesFade(bool doesFade) { _doesFade = doesFade; }
 
     uint8_t _version { 1 };
 
     std::string _vertexSource;
     std::string _fragmentSource;
 
-    gpu::StatePointer _state;
+    gpu::StatePointer _opaqueState { std::make_shared<gpu::State>() };
+    gpu::StatePointer _transparentState { std::make_shared<gpu::State>() };
 
     enum StandardUniforms {
         DATE,
@@ -89,7 +91,8 @@ protected:
     UniformLambdas _uniforms;
     int32_t _standardUniformSlots[NUM_STANDARD_UNIFORMS];
     NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS];
-    gpu::PipelinePointer _pipeline;
+    gpu::PipelinePointer _opaquePipeline;
+    gpu::PipelinePointer _transparentPipeline;
     gpu::ShaderPointer _vertexShader;
     gpu::ShaderPointer _fragmentShader;
     gpu::ShaderPointer _shader;
@@ -113,6 +116,7 @@ private:
     quint64 _fadeStartTime;
     bool _hasStartedFade { false };
     bool _isFading { false };
+    bool _doesFade { true };
 };
 
 #endif
diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp
index 9e9a26d902..843df3aa8d 100644
--- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp
+++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp
@@ -22,7 +22,8 @@ ProceduralSkybox::ProceduralSkybox() : model::Skybox() {
     _procedural._vertexSource = skybox_vert;
     _procedural._fragmentSource = skybox_frag;
     // Adjust the pipeline state for background using the stencil test
-    _procedural._state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
+    _procedural.setDoesFade(false);
+    _procedural._opaqueState->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
 }
 
 void ProceduralSkybox::clear() {

From 5bac88c00431b60f3c9484d28d7c204663e48964 Mon Sep 17 00:00:00 2001
From: Stephen Birarda <commit@birarda.com>
Date: Thu, 11 Aug 2016 10:52:36 -0700
Subject: [PATCH 77/78] default domain-server permissions to connect for
 standard

---
 domain-server/src/DomainServerSettingsManager.cpp | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp
index 47187fac5c..c7944bbcad 100644
--- a/domain-server/src/DomainServerSettingsManager.cpp
+++ b/domain-server/src/DomainServerSettingsManager.cpp
@@ -532,9 +532,12 @@ void DomainServerSettingsManager::unpackPermissions() {
             // we don't have permissions for one of the standard groups, so we'll add them now
             NodePermissionsPointer perms { new NodePermissions(standardKey) };
 
-            // the localhost user is granted all permissions by default
             if (standardKey == NodePermissions::standardNameLocalhost) {
+                // the localhost user is granted all permissions by default
                 perms->setAll(true);
+            } else {
+                // anonymous, logged in, and friend users get connect permissions by default
+                perms->set(NodePermissions::Permission::canConnectToDomain);
             }
 
             // add the permissions to the standard map

From 0f988fb209cdc93a6a848495a6f66e7346e23fc9 Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@brown.edu>
Date: Thu, 11 Aug 2016 13:05:16 -0700
Subject: [PATCH 78/78] don't always mark _shaderDirty when user data changes
 unless url actually changes

---
 libraries/procedural/src/procedural/Procedural.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp
index 79a90ef72d..27662e96d0 100644
--- a/libraries/procedural/src/procedural/Procedural.cpp
+++ b/libraries/procedural/src/procedural/Procedural.cpp
@@ -115,6 +115,11 @@ bool Procedural::parseUrl(const QUrl& shaderUrl) {
         return false;
     }
 
+    // If the URL hasn't changed, don't mark the shader as dirty
+    if (_shaderUrl == shaderUrl) {
+        return true;
+    }
+
     _shaderUrl = shaderUrl;
     _shaderDirty = true;