diff --git a/gz3d/build/gz3d.js b/gz3d/build/gz3d.js
index 09b199b8296af9149d3443a8f89aa16395a6163d..38c6e53e7d24c2160dc5fc55983338ffa4bf07eb 100644
--- a/gz3d/build/gz3d.js
+++ b/gz3d/build/gz3d.js
@@ -1,5 +1,7 @@
 var GZ3D = GZ3D || {
-  REVISION : '1'
+  REVISION : '1',
+  assetsPath: 'http://localhost:8080/assets',
+  webSocketUrl: 'ws://localhost:7681'
 };
 
 /*global $:false */
@@ -9,7 +11,7 @@ var guiEvents = new EventEmitter2({ verbose: true });
 
 var emUnits = function(value)
     {
-      return value*parseFloat($('body').css('font-size'));
+      return value*parseFloat($('#gz3d-body').css('font-size'));
     };
 
 var isTouchDevice = 'ontouchstart' in window || 'onmsgesturechange' in window;
@@ -29,160 +31,15 @@ var tabColors = {selected: 'rgb(34, 170, 221)', unselected: 'rgb(42, 42, 42)'};
 
 var modelList =
   [
-    {path:'buildings', title:'Buildings',
-    examplePath1:'fast_food', examplePath2:'kitchen_dining', examplePath3:'house_1', models:
+    {path:'virtual_room', title:'Virtual Room Objects',
+    examplePath1:'library_model',  examplePath2:'hosta_potted_plant',  examplePath3:'vr_lamp', models:
     [
-      {modelPath:'fast_food', modelTitle:'Fast Food'},
-      {modelPath:'gas_station', modelTitle:'Gas Station'},
-      {modelPath:'house_1', modelTitle:'House 1'},
-      {modelPath:'house_2', modelTitle:'House 2'},
-      {modelPath:'house_3', modelTitle:'House 3'},
-      {modelPath:'iss', modelTitle:'International Space Station'},
-      {modelPath:'iss_half', modelTitle:'ISS half'},
-      {modelPath:'kitchen_dining', modelTitle:'Kitchen and Dining'},
-      {modelPath:'office_building', modelTitle:'Office Building'},
-      {modelPath:'powerplant', modelTitle:'Power Plant'},
-      {modelPath:'starting_pen', modelTitle:'Starting Pen'},
-      {modelPath:'willowgarage', modelTitle:'Willow Garage'}
-    ]},
-
-    {path:'furniture', title:'Furniture',
-    examplePath1:'hinged_door', examplePath2:'bookshelf', examplePath3:'table', models:
-    [
-      {modelPath:'bookshelf', modelTitle:'Book Shelf'},
-      {modelPath:'cabinet', modelTitle:'Cabinet'},
-      {modelPath:'drc_practice_door_4x8', modelTitle:'4x8 Doorway'},
-      {modelPath:'drc_practice_ladder', modelTitle:'Ladder'},
-      {modelPath:'hinged_door', modelTitle:'Hinged Door'},
-      {modelPath:'table', modelTitle:'Table'},
-      {modelPath:'table_marble', modelTitle:'Table Marble'},
-
-      {modelPath:'drc_practice_ball_valve', modelTitle:'Ball Valve'},
-      {modelPath:'drc_practice_handle_wheel_valve', modelTitle:'Handle Wheel Valve'},
-      {modelPath:'drc_practice_hand_wheel_valve', modelTitle:'Hand Wheel Valve'},
-      {modelPath:'drc_practice_wheel_valve', modelTitle:'Wheel Valve'},
-      {modelPath:'drc_practice_wheel_valve_large', modelTitle:'Wheel Valve Large'},
-      {modelPath:'door_handle', modelTitle:'Door Handle'},
-
-      {modelPath:'drc_practice_ball_valve_wall', modelTitle:'Wall (Ball Valve)'},
-      {modelPath:'drc_practice_handle_wheel_valve_wall', modelTitle:'Wall (Handle Wheel Valve)'},
-      {modelPath:'drc_practice_hand_wheel_valve_wall', modelTitle:'Wall (Hand Wheel Valve)'},
-      {modelPath:'drc_practice_valve_wall', modelTitle:'Wall (Valve)'},
-      {modelPath:'drc_practice_wheel_valve_wall', modelTitle:'Wall (Wheel Valve)'},
-      {modelPath:'drc_practice_wheel_valve_large_wall', modelTitle:'Wall (Wheel Valve Large)'},
-      {modelPath:'grey_wall', modelTitle:'Grey Wall'},
-      {modelPath:'asphalt_plane', modelTitle:'Asphalt Plane'},
-      {modelPath:'drc_practice_base_4x8', modelTitle:'Debris base'},
-      {modelPath:'ground_plane', modelTitle:'Ground Plane'},
-      {modelPath:'nist_maze_wall_120', modelTitle:'120 Maze Wall'},
-      {modelPath:'nist_maze_wall_240', modelTitle:'240 Maze Wall'},
-      {modelPath:'nist_maze_wall_triple_holes_120', modelTitle:'120 Maze Wall Triple Holes'},
-      {modelPath:'nist_simple_ramp_120', modelTitle:'Simple Ramp'},
-      {modelPath:'nist_stairs_120', modelTitle:'Stairs'}
-    ]},
-
-    {path:'kitchen', title:'Kitchen',
-    examplePath1:'saucepan',  examplePath2:'beer',  examplePath3:'bowl', models:
-    [
-      {modelPath:'beer', modelTitle:'Beer'},
-      {modelPath:'bowl', modelTitle:'Bowl'},
-      {modelPath:'coke_can', modelTitle:'Coke Can'},
-      {modelPath:'saucepan', modelTitle:'Saucepan'}
-    ]},
-
-    {path:'robocup', title:'Robocup', examplePath1:'robocup_3Dsim_ball',
-    examplePath2:'robocup14_spl_goal', examplePath3:'robocup09_spl_field', models:
-    [
-      {modelPath:'robocup09_spl_field', modelTitle:'2009 SPL Field'},
-      {modelPath:'robocup14_spl_field', modelTitle:'2014 SPL Field'},
-      {modelPath:'robocup_3Dsim_field', modelTitle:'3D Sim. Field'},
-      {modelPath:'robocup14_spl_goal', modelTitle:'SPL Goal'},
-      {modelPath:'robocup_3Dsim_goal', modelTitle:'3D Sim. Goal'},
-      {modelPath:'robocup_spl_ball', modelTitle:'SPL Ball'},
-      {modelPath:'robocup_3Dsim_ball', modelTitle:'3D Sim. Ball'}
-    ]},
-
-    {path:'robots', title:'Robots',
-    examplePath1:'pioneer3at', examplePath2:'turtlebot', examplePath3:'pr2', models:
-    [
-      {modelPath:'create', modelTitle:'Create'},
-      {modelPath:'husky', modelTitle:'Husky'},
-      {modelPath:'irobot_hand', modelTitle:'iRobot Hand'},
-      {modelPath:'pioneer2dx', modelTitle:'Pioneer 2DX'},
-      {modelPath:'pioneer3at', modelTitle:'Pioneer 3AT'},
-      {modelPath:'pr2', modelTitle:'PR2'},
-      {modelPath:'robonaut', modelTitle:'Robonaut'},
-      {modelPath:'simple_arm', modelTitle:'Simple Arm'},
-      {modelPath:'simple_arm_gripper', modelTitle:'Simple Arm and Gripper'},
-      {modelPath:'simple_gripper', modelTitle:'Simple Gripper'},
-      {modelPath:'turtlebot', modelTitle:'TurtleBot'},
-      {modelPath:'youbot', modelTitle:'YouBot'}
-    ]},
-
-    {path:'sensors', title:'Sensors',
-    examplePath1:'camera', examplePath2:'hokuyo', examplePath3:'kinect', models:
-    [
-      {modelPath:'camera', modelTitle:'Camera'},
-      {modelPath:'stereo_camera', modelTitle:'Stereo Camera'},
-      {modelPath:'hokuyo', modelTitle:'Hokuyo'},
-      {modelPath:'kinect', modelTitle:'Kinect'}
-    ]},
-
-    {path:'street', title:'Street', examplePath1:'dumpster',
-    examplePath2:'drc_practice_angled_barrier_45', examplePath3:'fire_hydrant', models:
-    [
-      {modelPath:'cinder_block', modelTitle:'Cinder Block'},
-      {modelPath:'cinder_block_2', modelTitle:'Cinder Block 2'},
-      {modelPath:'cinder_block_wide', modelTitle:'Cinder Block Wide'},
-      {modelPath:'construction_barrel', modelTitle:'Construction Barrel'},
-      {modelPath:'construction_cone', modelTitle:'Construction Cone'},
-      {modelPath:'drc_practice_angled_barrier_45', modelTitle:'Angled Barrier 45'},
-      {modelPath:'drc_practice_angled_barrier_135', modelTitle:'Angled Barrier 135'},
-      {modelPath:'drc_practice_block_wall', modelTitle:'Block Wall'},
-      {modelPath:'drc_practice_orange_jersey_barrier', modelTitle:'Jersey Barrier (Orange)'},
-      {modelPath:'drc_practice_white_jersey_barrier', modelTitle:'Jersey Barrier (White)'},
-      {modelPath:'drc_practice_truss', modelTitle:'Truss'},
-      {modelPath:'drc_practice_yellow_parking_block', modelTitle:'Parking Block'},
-      {modelPath:'dumpster', modelTitle:'Dumpster'},
-      {modelPath:'fire_hydrant', modelTitle:'Fire Hydrant'},
-      {modelPath:'jersey_barrier', modelTitle:'Jersey Barrier'},
-      {modelPath:'lamp_post', modelTitle:'Lamp Post'},
-      {modelPath:'mailbox', modelTitle:'Mailbox'},
-      {modelPath:'mud_box', modelTitle:'Mud Box'},
-      {modelPath:'nist_fiducial_barrel', modelTitle:'Fiducial Barrel'},
-      {modelPath:'speed_limit_sign', modelTitle:'Speed Limit Sign'},
-      {modelPath:'stop_sign', modelTitle:'Stop Sign'}
-
-    ]},
-
-    {path:'tools', title:'Tools', examplePath1:'hammer',
-    examplePath2:'polaris_ranger_ev', examplePath3:'cordless_drill', models:
-    [
-      {modelPath:'cordless_drill', modelTitle:'Cordless Drill'},
-      {modelPath:'fire_hose_long', modelTitle:'Fire Hose'},
-      {modelPath:'fire_hose_long_curled', modelTitle:'Fire Hose Long Curled'},
-      {modelPath:'hammer', modelTitle:'Hammer'},
-      {modelPath:'monkey_wrench', modelTitle:'Monkey Wrench'},
-      {modelPath:'polaris_ranger_ev', modelTitle:'Polaris Ranger EV'},
-      {modelPath:'polaris_ranger_xp900', modelTitle:'Polaris Ranger XP900'},
-      {modelPath:'polaris_ranger_xp900_no_roll_cage', modelTitle:'Polaris Ranger without roll cage'},
-      {modelPath:'utility_cart', modelTitle:'Utility Cart'}
-    ]},
-
-    {path:'misc', title:'Misc.', examplePath1:'brick_box_3x1x3',
-    examplePath2:'drc_practice_4x4x20', examplePath3:'double_pendulum_with_base', models:
-    [
-      {modelPath:'double_pendulum_with_base', modelTitle:'Double Pendulum With Base'},
-      {modelPath:'breakable_test', modelTitle:'Breakable_test'},
-      {modelPath:'brick_box_3x1x3', modelTitle:'Brick Box 3x1x3'},
-      {modelPath:'cube_20k', modelTitle:'Cube 20k'},
-      {modelPath:'drc_practice_2x4', modelTitle:'2x4 Lumber'},
-      {modelPath:'drc_practice_2x6', modelTitle:'2x6 Lumber'},
-      {modelPath:'drc_practice_4x4x20', modelTitle:'4x4x20 Lumber'},
-      {modelPath:'drc_practice_4x4x40', modelTitle:'4x4x40 Lumber'},
-      {modelPath:'drc_practice_blue_cylinder', modelTitle:'Blue Cylinder'},
-      {modelPath:'drc_practice_wood_slats', modelTitle:'Wood Slats'},
-      {modelPath:'nist_elevated_floor_120', modelTitle:'Elevated Floor 120'}
+      {modelPath:'library_model', modelTitle:'Library'},
+      {modelPath:'hosta_potted_plant', modelTitle:'Hosta Plant'},
+      {modelPath:'vr_lamp', modelTitle:'Stand Lamp'},
+      {modelPath:'vr_screen', modelTitle:'Virtual Screen'},
+      {modelPath:'viz_poster', modelTitle:'Poster 1'},
+      {modelPath:'viz_poster_2', modelTitle:'Poster 2'}
     ]}
   ];
 
@@ -191,7 +48,7 @@ $(function()
   //Initialize
   if ('ontouchstart' in window || 'onmsgesturechange' in window)
   {
-    $('body').addClass('isTouchDevice');
+    $('#gz3d-body').addClass('isTouchDevice');
   }
 
   // Toggle items
@@ -491,6 +348,14 @@ $(function()
         guiEvents.emit('show_orbit_indicator');
         guiEvents.emit('closeTabs', false);
       });
+  $('#view-shadows').click(function()
+      {
+        guiEvents.emit('show_shadows', 'toggle');
+      });
+  $('#view-camera-sensors').click(function()
+      {
+        guiEvents.emit('show_camera_sensors', 'toggle');
+      });
   $( '#snap-to-grid' ).click(function() {
     guiEvents.emit('snap_to_grid');
     guiEvents.emit('closeTabs', false);
@@ -505,13 +370,12 @@ $(function()
   });
 
   // Disable Esc key to close panel
-  $('body').on('keyup', function(event)
-      {
-        if (event.which === 27)
-        {
-          return false;
-        }
-      });
+    $('#gz3d-body').on('keyup', function(event) {
+    if (event.which === 27)
+    {
+      return false;
+    }
+  });
 
   // Object menu
   $( '#view-transparent' ).click(function() {
@@ -839,6 +703,73 @@ GZ3D.Gui.prototype.init = function()
       }
   );
 
+  guiEvents.on('show_shadows', function(option)
+      {
+          if (option === 'show')
+          {
+              //that.emitter.emit('setShadows', true);
+              that.scene.setShadowMaps(true);
+          }
+          else if (option === 'hide')
+          {
+              //that.emitter.emit('setShadows', false);
+              that.scene.setShadowMaps(false);
+          }
+          else if (option === 'toggle')
+          {
+              var shadowsEnabled = that.scene.renderer.shadowMapEnabled;
+              //that.emitter.emit('setShadows', !shadowsEnabled);
+              that.scene.setShadowMaps(!shadowsEnabled);
+          }
+
+          if(!that.scene.renderer.shadowMapEnabled)
+          {
+              $('#view-shadows').buttonMarkup({icon: 'false'});
+              guiEvents.emit('notification_popup','Disabling shadows');
+          }
+          else
+          {
+              $('#view-shadows').buttonMarkup({icon: 'check'});
+              guiEvents.emit('notification_popup','Enabling shadows');
+          }
+      }
+  );
+
+    guiEvents.on('show_camera_sensors', function(option)
+        {
+            var camerasShown = false;
+            if (option === 'show')
+            {
+                that.scene.viewManager.showCameras(true);
+            }
+            else if (option === 'hide')
+            {
+                that.scene.viewManager.showCameras(false);
+            }
+            else if (option === 'toggle')
+            {
+                if (that.scene.viewManager.views.length > 1) {
+                    camerasShown = that.scene.viewManager.views[1].active;
+                    that.scene.viewManager.showCameras(!camerasShown);
+                }
+            }
+
+            if (that.scene.viewManager.views.length > 1) {
+                camerasShown = that.scene.viewManager.views[1].active;
+            }
+            if(!camerasShown)
+            {
+                $('#view-camera-sensors').buttonMarkup({icon: 'false'});
+                guiEvents.emit('notification_popup','Disabling camera views');
+            }
+            else
+            {
+                $('#view-camera-sensors').buttonMarkup({icon: 'check'});
+                guiEvents.emit('notification_popup','Enabling camera views');
+            }
+        }
+    );
+
   guiEvents.on('pause', function(paused)
       {
         that.emitter.emit('pause', paused);
@@ -1081,7 +1012,7 @@ GZ3D.Gui.prototype.init = function()
           $( '#notification-popup' ).html(' '+notification+' ');
           $( '#notification-popup' ).popup('open', {
               y:window.innerHeight-50});
-          
+
           if (duration === undefined)
           {
             duration = 2000;
@@ -1140,7 +1071,8 @@ GZ3D.Gui.prototype.init = function()
         lastOpenMenu[parentId] = id;
 
         $('.leftPanels').hide();
-        $('#'+id).show();
+        //$('#'+id).show(); //defaults as flex, but block is needed
+        $('#'+id).css('display','block');
 
         $('.tab').css('border-left-color', tabColors.unselected);
         $('#'+parentId+'Tab').css('border-left-color', tabColors.selected);
@@ -1609,17 +1541,20 @@ GZ3D.Gui.prototype.setModelStats = function(stats, action)
               return e.shortName === LinkShortName;
             });
 
-        if (link[0].self_collide)
-        {
-          link[0].self_collide = this.trueOrFalse(stats.link[0].self_collide);
-        }
-        if (link[0].gravity)
+        if (link[0])
         {
-          link[0].gravity = this.trueOrFalse(stats.link[0].gravity);
-        }
-        if (link[0].kinematic)
-        {
-          link[0].kinematic = this.trueOrFalse(stats.link[0].kinematic);
+          if (link[0].self_collide)
+          {
+            link[0].self_collide = this.trueOrFalse(stats.link[0].self_collide);
+          }
+          if (link[0].gravity)
+          {
+            link[0].gravity = this.trueOrFalse(stats.link[0].gravity);
+          }
+          if (link[0].kinematic)
+          {
+            link[0].kinematic = this.trueOrFalse(stats.link[0].kinematic);
+          }
         }
       }
 
@@ -1696,10 +1631,10 @@ GZ3D.Gui.prototype.setLightStats = function(stats, action)
       var thumbnail;
       switch(type)
       {
-        case 2:
+        case GZ3D.LIGHT_SPOT:
             thumbnail = 'style/images/spotlight.png';
             break;
-        case 3:
+        case GZ3D.LIGHT_DIRECTIONAL:
             thumbnail = 'style/images/directionallight.png';
             break;
         default:
@@ -1801,7 +1736,10 @@ GZ3D.Gui.prototype.findModelThumbnail = function(instanceName)
 GZ3D.Gui.prototype.updateStats = function()
 {
   var tree = angular.element($('#treeMenu')).scope();
-  tree.updateStats();
+  if (typeof(tree) !== 'undefined' && typeof tree.updateStats !== 'undefined')
+  {
+    tree.updateStats();
+  }
 };
 
 /**
@@ -1926,10 +1864,12 @@ GZ3D.Gui.prototype.formatStats = function(stats)
     colorHex = {};
     for (comp in diffuse)
     {
-      colorHex[comp] = diffuse[comp].toString(16);
-      if (colorHex[comp].length === 1)
-      {
-        colorHex[comp] = '0' + colorHex[comp];
+      if (diffuse.hasOwnProperty(comp)) {
+        colorHex[comp] = diffuse[comp].toString(16);
+        if (colorHex[comp].length === 1)
+        {
+          colorHex[comp] = '0' + colorHex[comp];
+        }
       }
     }
     color.diffuse = '#' + colorHex['r'] + colorHex['g'] + colorHex['b'];
@@ -1942,10 +1882,12 @@ GZ3D.Gui.prototype.formatStats = function(stats)
     colorHex = {};
     for (comp in specular)
     {
-      colorHex[comp] = specular[comp].toString(16);
-      if (colorHex[comp].length === 1)
-      {
-        colorHex[comp] = '0' + colorHex[comp];
+      if (specular.hasOwnProperty(comp)) {
+        colorHex[comp] = specular[comp].toString(16);
+        if (colorHex[comp].length === 1)
+        {
+          colorHex[comp] = '0' + colorHex[comp];
+        }
       }
     }
     color.specular = '#' + colorHex['r'] + colorHex['g'] + colorHex['b'];
@@ -2079,6 +2021,8 @@ GZ3D.Gui.prototype.deleteFromStats = function(type, name)
 
 //var GAZEBO_MODEL_DATABASE_URI='http://gazebosim.org/models';
 
+THREE.ImageUtils.crossOrigin = 'anonymous'; // needed to allow cross-origin loading of textures
+
 GZ3D.GZIface = function(scene, gui)
 {
   this.scene = scene;
@@ -2094,6 +2038,13 @@ GZ3D.GZIface = function(scene, gui)
   this.numConnectionTrials = 0;
   this.maxConnectionTrials = 30; // try to connect 30 times
   this.timeToSleepBtwTrials = 1000; // wait 1 second between connection trials
+
+  this.assetProgressData = {};
+  this.assetProgressData.assets = [];
+  this.assetProgressData.prepared = false;
+  this.assetProgressCallback = undefined;
+
+  this.webSocketConnectionCallbacks = [];
 };
 
 GZ3D.GZIface.prototype.init = function()
@@ -2104,11 +2055,35 @@ GZ3D.GZIface.prototype.init = function()
   this.connect();
 };
 
+GZ3D.GZIface.prototype.setAssetProgressCallback = function(callback)
+{
+  this.assetProgressCallback = callback;
+};
+
+GZ3D.GZIface.prototype.registerWebSocketConnectionCallback = function(callback) {
+  this.webSocketConnectionCallbacks.push(callback);
+};
+
 GZ3D.GZIface.prototype.connect = function()
 {
   // connect to websocket
+  var url = GZ3D.webSocketUrl;
+  if (!localStorage.getItem('localmode.forceuser')) {
+    var token = [];
+    if (localStorage.getItem('tokens-neurorobotics-ui@https://services.humanbrainproject.eu/oidc')) {
+      try {
+        token = JSON.parse(localStorage.getItem('tokens-neurorobotics-ui@https://services.humanbrainproject.eu/oidc'));
+      } catch(e) {
+        token[0] = { access_token : 'notoken' };
+      }
+      url = url + '/?token=' + token[0].access_token;
+    } else {
+      url = 'ws://' + location.hostname + ':7681';
+    }
+  }
+
   this.webSocket = new ROSLIB.Ros({
-    url : 'ws://' + location.hostname + ':7681'
+    url : url
   });
 
   var that = this;
@@ -2118,6 +2093,9 @@ GZ3D.GZIface.prototype.connect = function()
   this.webSocket.on('error', function() {
     that.onError();
   });
+  this.webSocket.on('close', function() {
+    console.log('Connection closed to websocket server: ' + that.webSocket.socket.url);
+  });
 
   this.numConnectionTrials++;
 };
@@ -2142,6 +2120,8 @@ GZ3D.GZIface.prototype.onError = function()
 
 GZ3D.GZIface.prototype.onConnected = function()
 {
+  console.log('Connected to websocket server: ' + this.webSocket.socket.url);
+
   this.isConnected = true;
   this.emitter.emit('connection');
 
@@ -2163,6 +2143,11 @@ GZ3D.GZIface.prototype.onConnected = function()
 
   setInterval(publishHeartbeat, 5000);
 
+  // call all the registered callbacks since we are connected now
+  this.webSocketConnectionCallbacks.forEach(function(callback) {
+    callback();
+  });
+
   var statusTopic = new ROSLIB.Topic({
     ros: this.webSocket,
     name: '~/status',
@@ -2208,7 +2193,7 @@ GZ3D.GZIface.prototype.onConnected = function()
 
     if (message.grid === true)
     {
-      this.gui.guiEvents.emit('show_grid', 'show');
+      //this.gui.guiEvents.emit('show_grid', 'show'); // do not show grid by default for now
     }
 
     if (message.ambient)
@@ -2248,6 +2233,7 @@ GZ3D.GZIface.prototype.onConnected = function()
       this.gui.setModelStats(model, 'update');
     }
 
+    this.assetProgressData.prepared = true;
     this.gui.setSceneStats(message);
     this.sceneTopic.unsubscribe();
   };
@@ -2356,6 +2342,8 @@ GZ3D.GZIface.prototype.onConnected = function()
         }
         i++;
       }
+    } else {
+      this.updateModelFromMsg(this.scene.getByName(message.name), message);
     }
     this.gui.setModelStats(message, 'update');
   };
@@ -2478,7 +2466,7 @@ GZ3D.GZIface.prototype.onConnected = function()
     messageType : 'light',
   });
 
-  var publishEntityModify = function(entity)
+  var createEntityModifyMessage = function(entity)
   {
     var matrix = entity.matrixWorld;
     var translation = new THREE.Vector3();
@@ -2505,43 +2493,89 @@ GZ3D.GZIface.prototype.onConnected = function()
         z: quaternion.z
       }
     };
-    if (entity.children[0] &&
-        entity.children[0] instanceof THREE.Light)
+    return entityMsg;
+  };
+
+  /*
+  TODO: (Sandro Weber)
+  The following functions are used to change all lights at the same time through the light slider of the NRP.
+  Gazebo only knows attenuation factors, not intensity, so we manipulate diffuse color for now.
+  Probably replaced / revamped after a dedicated edit tab is introduced to allow manipulation of lights directly.
+   */
+  var createEntityModifyMessageWithLight = function(entity, diffuse)
+  {
+    var entityMsg = createEntityModifyMessage(entity);
+
+    var lightObj = entity.children[0];
+
+    if (diffuse === undefined) {
+      diffuse = lightObj.color;
+    }
+    entityMsg.diffuse =
     {
-      entityMsg.diffuse =
-      {
-        r: entity.children[0].color.r,
-        g: entity.children[0].color.g,
-        b: entity.children[0].color.b
-      };
-      entityMsg.specular =
-      {
-        r: entity.serverProperties.specular.r,
-        g: entity.serverProperties.specular.g,
-        b: entity.serverProperties.specular.b
-      };
-      entityMsg.direction = entity.direction;
-      entityMsg.range = entity.children[0].distance;
-      entityMsg.attenuation_constant = entity.serverProperties.attenuation_constant;
-      entityMsg.attenuation_linear = entity.serverProperties.attenuation_linear;
-      entityMsg.attenuation_quadratic = entity.serverProperties.attenuation_quadratic;
+      r: diffuse.r,
+      g: diffuse.g,
+      b: diffuse.b
+    };
+    entityMsg.specular =
+    {
+      r: entity.serverProperties.specular.r,
+      g: entity.serverProperties.specular.g,
+      b: entity.serverProperties.specular.b
+    };
+    entityMsg.direction = entity.direction;
+    entityMsg.range = lightObj.distance;
+
+    entityMsg.attenuation_constant = entity.serverProperties.attenuation_constant;
+    entityMsg.attenuation_linear = entity.serverProperties.attenuation_linear;
+    entityMsg.attenuation_quadratic = entity.serverProperties.attenuation_quadratic;
+
+    return entityMsg;
+  };
 
-      that.lightModifyTopic.publish(entityMsg);
+  var publishEntityModify = function(entity)
+  {
+    var lightObj = entity.children[0];
+    if (lightObj && lightObj instanceof THREE.Light)
+    {
+      that.lightModifyTopic.publish(createEntityModifyMessageWithLight(entity, undefined));
     }
     else
     {
-      that.modelModifyTopic.publish(entityMsg);
+      that.modelModifyTopic.publish(createEntityModifyMessage(entity));
     }
   };
 
   this.scene.emitter.on('entityChanged', publishEntityModify);
 
-  // Link messages - for modifying links
-  this.linkModifyTopic = new ROSLIB.Topic({
-    ros : this.webSocket,
-    name : '~/link',
-    messageType : 'link',
-  });
+  var publishLightModify = function(ratio)
+  {
+    var lights = [];
+    that.scene.scene.traverse(function(node) {
+      if (node instanceof THREE.Light) {
+        lights.push(node);
+      }
+    });
+
+    var numberOfLights = lights.length;
+    for (var i = 0; i < numberOfLights; i+=1) {
+      if( lights[i] instanceof THREE.AmbientLight ) { // we don't change ambient lights
+        continue;
+      }
+      var entity = that.scene.getByName(lights[i].name);
+      var newDiffuse = new THREE.Color();
+      newDiffuse.r = THREE.Math.clamp(ratio * entity.serverProperties.initial.diffuse.r, 0, 1);
+      newDiffuse.g = THREE.Math.clamp(ratio * entity.serverProperties.initial.diffuse.g, 0, 1);
+      newDiffuse.b = THREE.Math.clamp(ratio * entity.serverProperties.initial.diffuse.b, 0, 1);
+
+      that.lightModifyTopic.publish(createEntityModifyMessageWithLight(entity, newDiffuse));
+    }
+  };
+
+  this.scene.emitter.on('lightChanged', publishLightModify);
+  /*
+  end of light slider change functions
+   */
 
   var publishLinkModify = function(entity, type)
   {
@@ -2808,6 +2842,16 @@ GZ3D.GZIface.prototype.createModelFromMsg = function(model)
         }
       }
     }
+
+    for (var i = 0; i < link.sensor.length; ++i) {
+      var sensor = link.sensor[i];
+
+      var sensorObj = this.createSensorFromMsg(sensor);
+      if (sensorObj && !sensorObj.parent)
+      {
+        linkObj.add(sensorObj);
+      }
+    }
   }
   if (model.joint)
   {
@@ -2817,6 +2861,24 @@ GZ3D.GZIface.prototype.createModelFromMsg = function(model)
   return modelObj;
 };
 
+// This method uses code also to be found at GZ3D.GZIface.prototype.createModelFromMsg.
+// Currently not everything is handled for an update, but this method was introduced to handle
+// the updates of colors of objects; if there should be more functionality one could consider
+// merging the two methods and extracting the different things to parameters (or any other means
+// of configuration).
+GZ3D.GZIface.prototype.updateModelFromMsg = function (modelObj, modelMsg) {
+  for (var j = 0; j < modelMsg.link.length; ++j) {
+    var link = modelMsg.link[j];
+    var linkObj = modelObj.children[j];
+
+    for (var k = 0; k < link.visual.length; ++k) {
+      var visual = link.visual[k];
+      var visualObj = linkObj.getObjectByName(visual.name);
+      this.updateVisualFromMsg(visualObj, visual);
+    }
+  }
+};
+
 GZ3D.GZIface.prototype.createVisualFromMsg = function(visual)
 {
   if (visual.geometry)
@@ -2839,21 +2901,32 @@ GZ3D.GZIface.prototype.createVisualFromMsg = function(visual)
   }
 };
 
+GZ3D.GZIface.prototype.updateVisualFromMsg = function (visualObj, visual) {
+  if (visual.geometry) {
+    var obj = visualObj.children[0];
+    var mat = this.parseMaterial(visual.material);
+
+    if (obj && mat) {
+      this.scene.setMaterial(obj, mat);
+    }
+  }
+};
+
 GZ3D.GZIface.prototype.createLightFromMsg = function(light)
 {
   var obj, range, direction;
 
-  if (light.type === 1)
+  if (light.type === this.scene.LIGHT_POINT)
   {
     direction = null;
     range = light.range;
   }
-  else if (light.type === 2)
+  else if (light.type === this.scene.LIGHT_SPOT)
   {
     direction = light.direction;
     range = light.range;
   }
-  else if (light.type === 3)
+  else if (light.type === this.scene.LIGHT_DIRECTIONAL)
   {
     direction = light.direction;
     range = null;
@@ -2891,9 +2964,66 @@ GZ3D.GZIface.prototype.createRoadsFromMsg = function(roads)
   return roadObj;
 };
 
+GZ3D.GZIface.prototype.createSensorFromMsg = function(sensor)
+{
+  var sensorObj = new THREE.Object3D();
+  sensorObj.name = sensor.name;
+
+  if (sensor.pose) {
+    this.scene.setPose(sensorObj, sensor.pose.position, sensor.pose.orientation);
+  }
+
+  if (sensor.type === 'camera') {
+    // if we have a camera sensor we have a potential view that could be rendered
+    // camera parameters are not published by gazebo right now, so hardcoded until fixed
+    var displayParams = {
+      left: '5%',
+      top: '5%',
+      width: '20%',
+      height: '20%',
+      adjustable: true
+    };
+    var cameraParams = {
+      width: 960,
+      height: 600,
+      fov: 60,
+      near: 0.1,
+      far: 100
+    };
+    var viewName = 'view_' + sensor.name;
+    var view = this.scene.viewManager.createView(viewName, displayParams, cameraParams);
+    if (!view) {
+      console.error('GZ3D.GZIface.createSensorFromMsg() - failed to create view ' + viewName);
+      return;
+    }
+
+    // There is a problem with the width and height; it should be read from the "sensor" object. Also see:
+    // https://bitbucket.org/osrf/gazebo/issues/1663/sensor-camera-elements-from-sdf-not-being
+
+    // camera sensors defined in gazebo .sdf seem to look along positive x axis, so need to adjust the rotation here
+    view.camera.rotateOnAxis(new THREE.Vector3(0, 1, 0), -Math.PI / 2);
+    view.camera.rotateOnAxis(new THREE.Vector3(0, 0, 1), -Math.PI / 2);
+
+    // set view inactive and hide at start
+    this.scene.viewManager.setViewVisibility(view, false);
+
+    // visualization - Deactivated since it causes the robot to be very big and the user
+    // can't barely select other objects on the scene. Reactivate for debug purposes !
+    // var cameraHelper = new THREE.CameraHelper(view.camera);
+    // view.camera.add( cameraHelper );
+
+    view.type = sensor.type;
+    console.log('view type: ' + view.type);
+
+    sensorObj.add(view.camera);
+  }
+
+  return sensorObj;
+};
+
 GZ3D.GZIface.prototype.parseUri = function(uri)
 {
-  var uriPath = 'assets';
+  var uriPath = GZ3D.assetsPath;
   var idx = uri.indexOf('://');
   if (idx > 0)
   {
@@ -2905,7 +3035,7 @@ GZ3D.GZIface.prototype.parseUri = function(uri)
 GZ3D.GZIface.prototype.createGeom = function(geom, material, parent)
 {
   var obj;
-  var uriPath = 'assets';
+  var uriPath = GZ3D.assetsPath;
   var that = this;
   var mat = this.parseMaterial(material);
   if (geom.box)
@@ -2985,26 +3115,48 @@ GZ3D.GZIface.prototype.createGeom = function(geom, material, parent)
 
         var modelUri = uriPath + '/' + modelName;
         // Use coarse version on touch devices
-        if (modelUri.indexOf('.dae') !== -1 && isTouchDevice)
+        if (modelUri.indexOf('.dae') !== -1 /*&& isTouchDevice*/) // Modified for HBP, we do use coarse models all the time
         {
           modelUri = modelUri.substring(0,modelUri.indexOf('.dae'));
 
-          var checkModel = new XMLHttpRequest();
-          checkModel.open('HEAD', modelUri+'_coarse.dae', false);
-          checkModel.send();
-          if (checkModel.status === 404)
+          if(modelUri.indexOf('_coarse') !== -1) //dae is already a coarse model
           {
             modelUri = modelUri+'.dae';
           }
-          else
-          {
-            modelUri = modelUri+'_coarse.dae';
+          else { // check if a coarse version is available
+            var checkModel = new XMLHttpRequest();
+            // We use a double technique to disable the cache for these requests:
+            // 1. We create a custom url by adding the time as a parameter.
+            // 2. We add the If-Modified-Since header with a date far in the future (end of the HBP project)
+            // Since browsers and servers vary in their behaviour, we use both of these tricks.
+            // PS: These requests do not load the dae files, they just verify if they exist on the server
+            // so that we can choose between coarse or reqular models.
+            checkModel.open('HEAD', modelUri+'_coarse.dae?timestamp=' + new Date().getTime(), false);
+            checkModel.setRequestHeader('If-Modified-Since', 'Sat, 1 Jan 2026 00:00:00 GMT');
+
+            try { checkModel.send(); } catch(err) { console.log(modelUri + ': no coarse version'); }
+
+            if (checkModel.status === 404) {
+              modelUri = modelUri+'.dae';
+            }
+            else {
+              modelUri = modelUri+'_coarse.dae';
+            }
           }
         }
 
         var materialName = parent.name + '::' + modelUri;
         this.entityMaterial[materialName] = mat;
 
+        // Progress update: Add this asset to the assetProgressArray
+        var element = {};
+        element.id = parent.name;
+        element.url = modelUri;
+        element.progress = 0;
+        element.totalSize = 0;
+        element.done = false;
+        this.assetProgressData.assets.push(element);
+
         this.scene.loadMesh(modelUri, submesh,
             centerSubmesh, function(dae) {
               if (that.entityMaterial[materialName])
@@ -3023,6 +3175,19 @@ GZ3D.GZIface.prototype.createGeom = function(geom, material, parent)
               }
               parent.add(dae);
               loadGeom(parent);
+
+              // Progress update: execute callback
+              element.done = true;
+              if (that.assetProgressCallback) {
+                that.assetProgressCallback(that.assetProgressData);
+              }
+            }, function(progress){
+              element.progress = progress.loaded;
+              element.totalSize = progress.total;
+              element.error = progress.error;
+              if (that.assetProgressCallback) {
+                that.assetProgressCallback(that.assetProgressData);
+              }
             });
       }
     }
@@ -3098,7 +3263,7 @@ GZ3D.GZIface.prototype.createGeom = function(geom, material, parent)
           allChildren[c].castShadow = false;
           allChildren[c].receiveShadow = false;
 
-          allChildren[c].visible = this.scene.showCollisions;
+          allChildren[c].visible = that.scene.showCollisions;
         }
       }
     }
@@ -3156,7 +3321,7 @@ GZ3D.GZIface.prototype.parseMaterial = function(material)
     return null;
   }
 
-  var uriPath = 'assets';
+  var uriPath = GZ3D.assetsPath;//'assets';
   var texture;
   var normalMap;
   var textureUri;
@@ -4011,10 +4176,10 @@ GZ3D.Manipulator = function(camera, mobile, domElement, doc)
     }
 
     this.object.updateMatrixWorld();
-    worldPosition.getPositionFromMatrix(this.object.matrixWorld);
+    worldPosition.setFromMatrixPosition(this.object.matrixWorld);
 
     this.camera.updateMatrixWorld();
-    camPosition.getPositionFromMatrix(this.camera.matrixWorld);
+    camPosition.setFromMatrixPosition(this.camera.matrixWorld);
 
     scale = worldPosition.distanceTo(camPosition) / 6 * this.scale;
     this.gizmo.position.copy(worldPosition);
@@ -4266,7 +4431,7 @@ GZ3D.Manipulator = function(camera, mobile, domElement, doc)
         worldRotationMatrix.extractRotation(scope.object.matrixWorld);
 
         parentRotationMatrix.extractRotation(scope.object.parent.matrixWorld);
-        parentScale.getScaleFromMatrix(tempMatrix.getInverse(
+        parentScale.setFromMatrixScale(tempMatrix.getInverse(
             scope.object.parent.matrixWorld));
 
         offset.copy(planeIntersect.point);
@@ -4381,7 +4546,7 @@ GZ3D.Manipulator = function(camera, mobile, domElement, doc)
 
           parentRotationMatrix.extractRotation(
               scope.object.parent.matrixWorld);
-          parentScale.getScaleFromMatrix(tempMatrix.getInverse(
+          parentScale.setFromMatrixScale(tempMatrix.getInverse(
               scope.object.parent.matrixWorld));
 
           offset.copy(planeIntersect.point);
@@ -4677,6 +4842,287 @@ GZ3D.Manipulator = function(camera, mobile, domElement, doc)
 GZ3D.Manipulator.prototype = Object.create(THREE.EventDispatcher.prototype);
 
 
+/**
+ * Created by Sandro Weber (webers@in.tum.de).
+ */
+
+GZ3D.MULTIVIEW_MAX_VIEW_COUNT = 10;
+
+/**
+ * GZ3D.MULTIVIEW_RENDER_VIEWPORTS uses view containers as transparent references to determine viewports for rendering (one single canvas).
+ * This is broken in combination with shadowmaps at the moment. See renderToViewport() method.
+ *
+ * @type {number}
+ */
+GZ3D.MULTIVIEW_RENDER_VIEWPORTS = 1;
+/**
+ * GZ3D.MULTIVIEW_RENDER_COPY2CANVAS renders views offscreen then copies into the view containers (multiple separate canvases).
+ * @type {number}
+ */
+GZ3D.MULTIVIEW_RENDER_COPY2CANVAS = 2;
+
+GZ3D.MultiView = function(gz3dScene, mainContainer, callbackCreateRenderContainer)
+{
+    this.gz3dScene = gz3dScene;
+    this.mainContainer = mainContainer;
+    this.createRenderContainerCallback = callbackCreateRenderContainer;
+
+    this.init();
+};
+
+GZ3D.MultiView.prototype.init = function()
+{
+    this.views = [];
+
+    this.mainContainer.style.zIndex = 0;
+
+    this.renderMethod = GZ3D.MULTIVIEW_RENDER_COPY2CANVAS;
+    if (this.renderMethod === GZ3D.MULTIVIEW_RENDER_VIEWPORTS) {
+        this.mainContainer.appendChild(this.gz3dScene.getDomElement());
+    }
+};
+
+GZ3D.MultiView.prototype.setCallbackCreateRenderContainer = function(callback)
+{
+    this.createRenderContainerCallback = callback;
+};
+
+/**
+ *
+ * @param name
+ * @param displayParams {left, top, width, height, zIndex, adjustable}
+ * @param cameraParams {width, height, fov, near, far}
+ */
+GZ3D.MultiView.prototype.createView = function(name, displayParams, cameraParams)
+{
+    if (this.getViewByName(name) !== undefined) {
+        console.error('GZ3D.MultiView.createView() - a view of that name already exists (' + name + ')');
+        return null;
+    }
+
+    if (this.views.length >= this.MULTIVIEW_MAX_VIEW_COUNT) {
+        console.warn('GZ3D.MultiView.createView() - creating new view will exceed MULTIVIEW_MAX_VIEW_COUNT(' + this.MULTIVIEW_MAX_VIEW_COUNT + '). This may cause z-ordering issues.');
+    }
+
+    var container = this.createViewContainer(displayParams, name);
+    if (container === null) {
+        return;
+    }
+
+    // camera
+    var camera = new THREE.PerspectiveCamera(cameraParams.fov, cameraParams.width / cameraParams.height, cameraParams.near, cameraParams.far);
+    camera.name = name;
+
+    // assemble view
+    var view = {
+        name: name,
+        active: true,
+        container: container,
+        camera: camera
+    };
+
+    this.views.push(view);
+    this.mainContainer.appendChild(view.container);
+
+    return view;
+};
+
+GZ3D.MultiView.prototype.createViewContainer = function(displayParams, name)
+{
+    if (this.createRenderContainerCallback === undefined) {
+        console.error('GZ3D.MultiView.createViewContainer() - no callback for creating view reference container defined');
+        return null;
+    }
+
+    // container div
+    var viewContainer = this.createRenderContainerCallback(displayParams.adjustable, name);
+    if (!viewContainer) {
+        console.error('GZ3D.MultiView.createViewContainer() - could not create view container via callback');
+        return null;
+    }
+
+    // positioning
+    viewContainer.style.position = 'absolute';
+    viewContainer.style.left = displayParams.left;
+    viewContainer.style.top = displayParams.top;
+    // There is a problem with the width and height; it should be read from the "sensor" object. Also see:
+    // https://bitbucket.org/osrf/gazebo/issues/1663/sensor-camera-elements-from-sdf-not-being
+    // Here we use (preliminary) percentual width.
+    viewContainer.style.width = displayParams.width;
+    viewContainer.style.height = displayParams.height;
+
+    // We set 50px as a min-width for now and set the min height accordingly
+    viewContainer.style.minWidth = '50px';
+    viewContainer.style.minHeight = (50 * (parseInt(displayParams.width, 10)/parseInt(displayParams.height, 10))) + 'px';
+    viewContainer.style.maxWidth = '100%';
+    viewContainer.style.maxHeight = '100%';
+
+    // transparent view-container so we can render viewport with one renderer in the same context
+    // view-container is only taken as reference for viewport
+    viewContainer.style.boxShadow = '0px 0px 0px 3px rgba(0,0,0,0.3)';
+    viewContainer.style.borderRadius = '2px';
+    viewContainer.style.background = 'rgba(0,0,0,0)';
+
+    if (this.renderMethod === GZ3D.MULTIVIEW_RENDER_COPY2CANVAS) {
+        // canvas
+        viewContainer.canvas = document.createElement('canvas');
+        viewContainer.appendChild( viewContainer.canvas );
+        viewContainer.canvas.style.width = '100%';
+        viewContainer.canvas.style.height = '100%';
+    }
+
+    // z-index
+    var zIndexTop = parseInt(this.mainContainer.style.zIndex, 10) + this.views.length + 1;
+    viewContainer.style.zIndex = (displayParams.zIndex !== undefined) ? displayParams.zIndex : zIndexTop;
+
+    return viewContainer;
+};
+
+GZ3D.MultiView.prototype.getViewByName = function(name)
+{
+    for (var i = 0; i < this.views.length; i = i+1) {
+        if (this.views[i].name === name) {
+            return this.views[i];
+        }
+    }
+
+    return undefined;
+};
+
+GZ3D.MultiView.prototype.setViewVisibility = function(view, visible)
+{
+    view.active = visible;
+    if (view.active) {
+        view.container.style.visibility = 'visible';
+    } else {
+        view.container.style.visibility = 'hidden';
+    }
+};
+
+GZ3D.MultiView.prototype.updateCamera = function(view)
+{
+    view.camera.aspect = view.container.clientWidth / view.container.clientHeight;
+    view.camera.updateProjectionMatrix();
+};
+
+GZ3D.MultiView.prototype.getViewport = function(view)
+{
+    var viewport = {
+        x: view.container.offsetLeft,
+        y: this.mainContainer.clientHeight - view.container.clientHeight - view.container.offsetTop,
+        w: view.container.clientWidth,
+        h: view.container.clientHeight
+    };
+
+    return viewport;
+};
+
+GZ3D.MultiView.prototype.setWindowSize = function(width, height)
+{
+
+};
+
+GZ3D.MultiView.prototype.renderViews = function()
+{
+    // sort views into rendering order
+    this.views.sort(function(a, b) {
+       return a.container.style.zIndex - b.container.style.zIndex;
+    });
+
+    for (var i = 0, l = this.views.length; i < l; i = i+1) {
+        var view = this.views[i];
+
+        if (view.active) {
+            switch(this.renderMethod) {
+                case GZ3D.MULTIVIEW_RENDER_VIEWPORTS:
+                    this.renderToViewport(view);
+                    break;
+                case GZ3D.MULTIVIEW_RENDER_COPY2CANVAS:
+                    this.renderAndCopyToCanvas(view);
+                    break;
+            }
+        }
+    }
+};
+
+/**
+ * !IMPORTANT!
+ * https://github.com/mrdoob/three.js/issues/3532
+ * This is at the moment not a good idea, see issue above. ScissorTest will scramble shadowmaps.
+ *
+ * @param view
+ */
+GZ3D.MultiView.prototype.renderToViewport = function(view)
+{
+    this.updateCamera(view);  //TODO: better solution with resize callback, also adjust camera helper
+
+    var webglRenderer = this.gz3dScene.renderer;
+
+    var viewport = this.getViewport(view);
+    webglRenderer.setViewport( viewport.x, viewport.y, viewport.w, viewport.h );
+    webglRenderer.setScissor( viewport.x, viewport.y, viewport.w, viewport.h );
+    webglRenderer.enableScissorTest ( true );
+
+    if (this.gz3dScene.effectsEnabled) {
+        this.gz3dScene.scene.overrideMaterial = this.depthMaterial;
+        this.gz3dScene.renderer.render(this.gz3dScene.scene, view.camera, this.depthTarget);
+        this.gz3dScene.scene.overrideMaterial = null;
+        this.gz3dScene.composer.render();
+    } else {
+        webglRenderer.render(this.gz3dScene.scene, view.camera);
+    }
+};
+
+GZ3D.MultiView.prototype.renderAndCopyToCanvas = function(view)
+{
+    this.updateCamera(view);  //TODO: better solution with resize callback, also adjust camera helper
+
+    var webglRenderer = this.gz3dScene.renderer;
+
+    var width = view.container.canvas.clientWidth;
+    var height = view.container.canvas.clientHeight;
+    view.container.canvas.width = width;
+    view.container.canvas.height = height;
+
+    if (webglRenderer.context.canvas.width < width) {
+        webglRenderer.context.canvas.width = width;
+    }
+    if (webglRenderer.context.canvas.height < height) {
+        webglRenderer.context.canvas.height = height;
+    }
+    webglRenderer.setViewport( 0, 0, width, height );
+
+    if (this.gz3dScene.effectsEnabled) {
+        this.gz3dScene.scene.overrideMaterial = this.depthMaterial;
+        this.gz3dScene.renderer.render(this.gz3dScene.scene, view.camera, this.depthTarget);
+        this.gz3dScene.scene.overrideMaterial = null;
+        this.gz3dScene.composer.render();
+    } else {
+        webglRenderer.render(this.gz3dScene.scene, view.camera);
+    }
+
+    // copy rendered image over to view canvas
+    var srcX = 0;
+    var srcY = webglRenderer.context.canvas.height - height + 1;
+    var dstX = 0;
+    var dstY = 0;
+    // no cleaner solution right now, should be coming though: https://github.com/mrdoob/three.js/pull/6723#issuecomment-134129027
+    // this kills performance a little ...
+    if ( srcX >= 0 && srcY >= 0 && width >= 1 && height >= 1 ) {
+        view.container.canvas.getContext('2d').drawImage( webglRenderer.context.canvas,
+            srcX, srcY, width, height,
+            dstX, dstY, width, height );
+    }
+};
+
+GZ3D.MultiView.prototype.showCameras = function(show)
+{
+    for (var i = 0; i < this.views.length; i = i+1) {
+        if (this.views[i].type === 'camera') {
+            this.setViewVisibility(this.views[i], show);
+        }
+    }
+};
 /**
  * Radial menu for an object
  * @constructor
@@ -5079,8 +5525,6 @@ GZ3D.RadialMenu.prototype.setNumberOfItems = function(number)
   this.offset = this.numberOfItems - 1 - Math.floor(this.numberOfItems/2);
 };
 
-
-
 /**
  * The scene is where everything is placed, from objects, to lights and cameras.
  * @constructor
@@ -5090,6 +5534,11 @@ GZ3D.Scene = function()
   this.init();
 };
 
+GZ3D.Scene.prototype.LIGHT_POINT = 1;
+GZ3D.Scene.prototype.LIGHT_SPOT = 2;
+GZ3D.Scene.prototype.LIGHT_DIRECTIONAL = 3;
+GZ3D.Scene.prototype.LIGHT_UNKNOWN = 4;
+
 /**
  * Initialize scene
  */
@@ -5113,16 +5562,38 @@ GZ3D.Scene.prototype.init = function()
   this.renderer.setSize( window.innerWidth, window.innerHeight);
 
   // shadows
-  this.renderer.shadowMapEnabled = true;
+  this.renderer.shadowMapEnabled = false;
   this.renderer.shadowMapType = THREE.PCFSoftShadowMap;
 
+  this.container = document.getElementById( 'container' );
+
+  // create views manager
+  this.viewManager = new GZ3D.MultiView(this, this.container, function(adjustable, name){
+    return document.createElement('div');
+  });
+  // create main view
+  var displayParams = {
+    left: '0px',
+    top: '0px',
+    width: '100%',
+    height: '100%',
+    adjustable: false
+  };
+  var cameraParams = {
+    width: 960,
+    height: 600,
+    fov: 60,
+    near: 0.1,
+    far: 100
+  };
+  this.viewManager.createView('main_view', displayParams, cameraParams);
+
   // lights
   this.ambient = new THREE.AmbientLight( 0x666666 );
   this.scene.add(this.ambient);
 
   // camera
-  this.camera = new THREE.PerspectiveCamera(
-      60, window.innerWidth / window.innerHeight, 0.1, 1000 );
+  this.camera = this.viewManager.getViewByName('main_view').camera;
   this.defaultCameraPosition = new THREE.Vector3(0, -5, 5);
   this.resetView();
 
@@ -5181,7 +5652,12 @@ GZ3D.Scene.prototype.init = function()
 
   this.timeDown = null;
 
-  this.controls = new THREE.OrbitControls(this.camera);
+  var domElementForKeyBindings = document.getElementsByTagName('body')[0];
+  this.controls = new THREE.FirstPersonControls(this.camera, this.container, domElementForKeyBindings);
+  if (this.controls instanceof THREE.FirstPersonControls) {
+    this.controls.movementSpeed = 0.2;
+    this.controls.lookSpeed = 0.005;
+  }
   if (this.controls.targetIndicator !== undefined) {
     this.scene.add(this.controls.targetIndicator);
   }
@@ -5550,7 +6026,7 @@ GZ3D.Scene.prototype.onPointerDown = function(event)
  */
 GZ3D.Scene.prototype.onPointerUp = function(event)
 {
-  event.preventDefault();
+  //event.preventDefault();
 
   // Clicks (<150ms) outside any models trigger view mode
   var millisecs = new Date().getTime();
@@ -5754,7 +6230,7 @@ GZ3D.Scene.prototype.getRayCastModel = function(pos, intersect)
  */
 GZ3D.Scene.prototype.getDomElement = function()
 {
-  return this.renderer.domElement;
+  return this.container;
 };
 
 /**
@@ -5784,17 +6260,7 @@ GZ3D.Scene.prototype.render = function()
   this.modelManipulator.update();
   this.radialMenu.update();
 
-  if (this.effectsEnabled)
-  {
-    this.scene.overrideMaterial = this.depthMaterial;
-    this.renderer.render(this.scene, this.camera, this.depthTarget);
-    this.scene.overrideMaterial = null;
-    this.composer.render();
-  }
-  else
-  {
-    this.renderer.render(this.scene, this.camera);
-  }
+  this.viewManager.renderViews();
 };
 
 /**
@@ -5808,6 +6274,11 @@ GZ3D.Scene.prototype.setWindowSize = function(width, height)
   this.camera.updateProjectionMatrix();
 
   this.renderer.setSize( width, height);
+  this.renderer.context.canvas.width = width;
+  this.renderer.context.canvas.height = height;
+
+  this.viewManager.setWindowSize(width, height);
+
   this.render();
 };
 
@@ -5996,6 +6467,8 @@ GZ3D.Scene.prototype.createBox = function(width, height, depth)
  * @param {} attenuation_constant
  * @param {} attenuation_linear
  * @param {} attenuation_quadratic
+ * @param {} spot_angle
+ * @param {} spot_falloff
  * @returns {THREE.Object3D}
  */
 GZ3D.Scene.prototype.createLight = function(type, diffuse, intensity, pose,
@@ -6024,6 +6497,10 @@ GZ3D.Scene.prototype.createLight = function(type, diffuse, intensity, pose,
     specular = color.clone();
   }
 
+  if (typeof(specular) === 'undefined') {
+    specular = 0xffffff;
+  }
+
   var matrixWorld;
 
   if (pose)
@@ -6047,17 +6524,17 @@ GZ3D.Scene.prototype.createLight = function(type, diffuse, intensity, pose,
   }
 
   var elements;
-  if (type === 1)
+  if (type === this.LIGHT_POINT)
   {
     elements = this.createPointLight(obj, diffuse, intensity,
         distance, cast_shadows);
   }
-  else if (type === 2)
+  else if (type === this.LIGHT_SPOT)
   {
     elements = this.createSpotLight(obj, diffuse, intensity,
         distance, cast_shadows, spot_angle, spot_falloff);
   }
-  else if (type === 3)
+  else if (type === this.LIGHT_DIRECTIONAL)
   {
     elements = this.createDirectionalLight(obj, diffuse, intensity,
         cast_shadows);
@@ -6092,13 +6569,16 @@ GZ3D.Scene.prototype.createLight = function(type, diffuse, intensity, pose,
   obj.serverProperties.attenuation_constant = attenuation_constant;
   obj.serverProperties.attenuation_linear = attenuation_linear;
   obj.serverProperties.attenuation_quadratic = attenuation_quadratic;
+  obj.serverProperties.initial = {};
+  obj.serverProperties.initial.diffuse = diffuse;
 
-  obj.add(lightObj);
-  obj.add(helper);
-
+  helper.visible = false;
   lightObj.up = new THREE.Vector3(0,0,1);
   lightObj.shadowBias = -0.0005;
 
+  obj.add(lightObj);
+  obj.add(helper);
+
   return obj;
 };
 
@@ -6565,7 +7045,7 @@ GZ3D.Scene.prototype.loadHeightmap = function(heights, width, height,
  * @param {function} callback
  */
 GZ3D.Scene.prototype.loadMesh = function(uri, submesh, centerSubmesh,
-    callback)
+    callback, progressCallback)
 {
   var uriPath = uri.substring(0, uri.lastIndexOf('/'));
   var uriFile = uri.substring(uri.lastIndexOf('/') + 1);
@@ -6573,7 +7053,7 @@ GZ3D.Scene.prototype.loadMesh = function(uri, submesh, centerSubmesh,
   // load urdf model
   if (uriFile.substr(-4).toLowerCase() === '.dae')
   {
-    return this.loadCollada(uri, submesh, centerSubmesh, callback);
+    return this.loadCollada(uri, submesh, centerSubmesh, callback, progressCallback);
   }
   else if (uriFile.substr(-5).toLowerCase() === '.urdf')
   {
@@ -6619,7 +7099,7 @@ GZ3D.Scene.prototype.loadMesh = function(uri, submesh, centerSubmesh,
  * @param {function} callback
  */
 GZ3D.Scene.prototype.loadCollada = function(uri, submesh, centerSubmesh,
-    callback)
+    callback, progressCallback)
 {
   var dae;
   var mesh = null;
@@ -6642,6 +7122,7 @@ GZ3D.Scene.prototype.loadCollada = function(uri, submesh, centerSubmesh,
   var thatSubmesh = submesh;
   var thatCenterSubmesh = centerSubmesh;
 
+  var that = this;
   loader.load(uri, function(collada)
   {
     // check for a scale factor
@@ -6653,13 +7134,22 @@ GZ3D.Scene.prototype.loadCollada = function(uri, submesh, centerSubmesh,
 
     dae = collada.scene;
     dae.updateMatrix();
-    this.scene.prepareColladaMesh(dae);
-    this.scene.meshes[thatURI] = dae;
+    that.prepareColladaMesh(dae);
+    that.meshes[thatURI] = dae;
     dae = dae.clone();
-    this.scene.useColladaSubMesh(dae, thatSubmesh, centerSubmesh);
+    that.useColladaSubMesh(dae, thatSubmesh, centerSubmesh);
 
     dae.name = uri;
     callback(dae);
+  },function(progress) {
+    if (progressCallback !== undefined) {
+      progress.error = false;
+      progressCallback(progress);
+    }
+  },function(){
+    if (progressCallback !== undefined) {
+      progressCallback({ total: 0, loaded: 0, error: true });
+    }
   });
 };
 
@@ -6872,7 +7362,7 @@ GZ3D.Scene.prototype.setManipulationMode = function(mode)
   }
   else
   {
-    // Toggle manipulaion space (world / local)
+    // Toggle manipulation space (world / local)
     if (this.modelManipulator.mode === this.manipulationMode)
     {
       this.modelManipulator.space =
@@ -7465,7 +7955,7 @@ GZ3D.Scene.prototype.updateLight = function(entity, msg)
     this.setPose(entity, msg.pose.position, msg.pose.orientation);
     entity.matrixWorldNeedsUpdate = true;
 
-    if (entity.direction)
+    if (entity.direction && lightObj.target)
     {
       dir = new THREE.Vector3(entity.direction.x, entity.direction.y,
           entity.direction.z);
@@ -7507,6 +7997,18 @@ GZ3D.Scene.prototype.updateLight = function(entity, msg)
     entity.serverProperties.attenuation_quadratic = msg.attenuation_quadratic;
     lightObj.intensity = lightObj.intensity/(1+msg.attenuation_quadratic);
   }
+  if (msg.attenuation_linear && msg.attenuation_quadratic)
+  {
+    // equation taken from
+    // http://wiki.blender.org/index.php/Doc:2.6/Manual/Lighting/Lights/Light_Attenuation
+    var E = 1;
+    var D = 1;
+    var r = 1;
+    var L = msg.attenuation_linear;
+    var Q = msg.attenuation_quadratic;
+    lightObj.intensity = E*(D/(D+L*r))*(Math.pow(D,2)/(Math.pow(D,2)+Q*Math.pow(r,2)));
+  }
+
 
   if (lightObj instanceof THREE.SpotLight) {
     if (msg.spot_outer_angle) {
@@ -7518,7 +8020,7 @@ GZ3D.Scene.prototype.updateLight = function(entity, msg)
     }
   }
 
-  if (msg.direction)
+  if (msg.direction && lightObj.target)
   {
     dir = new THREE.Vector3(msg.direction.x, msg.direction.y,
         msg.direction.z);
@@ -7531,6 +8033,29 @@ GZ3D.Scene.prototype.updateLight = function(entity, msg)
   }
 };
 
+GZ3D.Scene.prototype.setShadowMaps = function(enabled) {
+  this.renderer.shadowMapEnabled = enabled;
+
+  var that = this;
+  this.scene.traverse(function(node) {
+    if (enabled) {
+      if (node.material) {
+        node.material.needsUpdate = true;
+        if (node.material.materials) {
+          for (var i = 0; i < node.material.materials.length; i = i+1) {
+            node.material.materials[i].needsUpdate = true;
+          }
+        }
+      }
+    } else {
+      if (node instanceof THREE.Light) {
+        if (node.shadowMap) {
+          that.renderer.clearTarget( node.shadowMap );
+        }
+      }
+    }
+  });
+};
 /**
  * SDF parser constructor initializes SDF parser with the given parameters
  * and defines a DOM parser function to parse SDF XML files
@@ -7542,7 +8067,7 @@ GZ3D.SdfParser = function(scene, gui, gziface)
 {
   // set the sdf version
   this.SDF_VERSION = 1.5;
-  this.MATERIAL_ROOT = 'assets/';
+  this.MATERIAL_ROOT = GZ3D.assetsPath + '/';
 
   // set the xml parser function
   this.parseXML = function(xmlStr) {
@@ -8072,7 +8597,7 @@ GZ3D.SdfParser.prototype.createGeom = function(geom, mat, parent)
           allChildren[c].castShadow = false;
           allChildren[c].receiveShadow = false;
 
-          allChildren[c].visible = this.scene.showCollisions;
+          allChildren[c].visible = that.scene.showCollisions;
         }
         break;
       }
diff --git a/gz3d/client/index.html b/gz3d/client/index.html
index 2dd977d34d2e2fc9464126d02bb1b4b653c38900..45175460bd6b5e0b4504899b88c5545ba69efdb2 100644
--- a/gz3d/client/index.html
+++ b/gz3d/client/index.html
@@ -114,7 +114,7 @@
   </script>
 
   <body>
-    <div data-role="page" data-theme="b" data-content-theme="c">
+    <div id="gz3d-body" data-role="page" data-theme="b" data-content-theme="c">
 
       <!-- left panel -->
       <div id="leftPanel" class="gzGUI">
@@ -139,6 +139,8 @@
                     <li id='view-grid' data-icon="false" data-iconpos="right" style="border: none !important;"><a href="#"><span class="collapsible_item">Grid</span></a></li>
                     <li id='view-collisions' data-icon="false" data-iconpos="right" style="border: none !important;"><a href="#"><span class="collapsible_item">Collisions</span></a></li>
                     <li id='view-orbit-indicator' data-icon="false" data-iconpos="right" style="border: none !important;"><a href="#"><span class="collapsible_item">Orbit Indicator</span></a></li>
+                    <li id='view-shadows' data-icon="false" data-iconpos="right" style="border: none !important;"><a href="#"><span class="collapsible_item">Shadows</span></a></li>
+                    <li id='view-camera-sensors' data-icon="false" data-iconpos="right" style="border: none !important;"><a href="#"><span class="collapsible_item">Camera Sensors</span></a></li>
                   </ul>
               </div>
               <div data-role="collapsible" data-inset="false" data-iconpos="left" data-collapsed-icon="carat-r" data-expanded-icon="carat-d">
@@ -1135,7 +1137,7 @@
       <!-- header -->
 
       <!-- content -->
-      <div data-role="content" id="container">
+      <div data-role="content" id="container" style="position: absolute">
         <div data-role="popup" id="notification-popup" data-history="false"></div>
         <div data-role="popup" data-corners="false" id="model-popup" data-history="false">
           <ul data-role="listview">
@@ -1165,12 +1167,21 @@
       function init()
       {
         scene = new GZ3D.Scene();
+        scene.viewManager.setCallbackCreateRenderContainer(function(adjustable, name) {
+          /*if (adjustable) {
+            return $compile('<div movable resizeable keep-aspect-ratio class="camera-view"><div class="camera-view-label">' + name + '</div></div>')($rootScope)[0];
+          } else {
+            return $compile('<div keep-aspect-ratio class="camera-view"><div class="camera-view-label">' + name + '</div></div>')($rootScope)[0];
+          }*/
+          return document.createElement('div');
+        });
+
         gui = new GZ3D.Gui(scene);
         iface = new GZ3D.GZIface(scene, gui);
         sdfparser = new GZ3D.SdfParser(scene, gui, iface);
 
-        container = document.getElementById( 'container' );
-        container.appendChild(scene.getDomElement());
+        //container = document.getElementById( 'container' );
+        //container.appendChild(scene.getDomElement());
 
         // FPS indicator
         stats = new Stats();
diff --git a/gz3d/client/js/include/FirstPersonControls.js b/gz3d/client/js/include/FirstPersonControls.js
index 35a202ed19869d807d232809fb98e656e8179299..ab61a1b45387740c13cde55691164f37f7e67372 100644
--- a/gz3d/client/js/include/FirstPersonControls.js
+++ b/gz3d/client/js/include/FirstPersonControls.js
@@ -273,7 +273,7 @@ THREE.FirstPersonControls = function(object, domElement, domElementForKeyBinding
     vecForward.normalize();
 
     this.zenith = Math.acos(-vecForward.z);
-    this.azimuth = Math.atan(vecForward.y / vecForward.x) + Math.PI;
+    this.azimuth = Math.atan2(vecForward.y, vecForward.x)  + Math.PI;
   };
 
   function bind(scope, fn) {
diff --git a/gz3d/src/gz.js b/gz3d/src/gz.js
index e8dd0aec0149d76faae58cb7cb9805f32b730069..54989411226664c2788855f82f86f69e4bb538fd 100644
--- a/gz3d/src/gz.js
+++ b/gz3d/src/gz.js
@@ -1,3 +1,5 @@
 var GZ3D = GZ3D || {
-  REVISION : '1'
+  REVISION : '1',
+  assetsPath: 'http://localhost:8080/assets',
+  webSocketUrl: 'ws://localhost:7681'
 };
\ No newline at end of file
diff --git a/gz3d/src/gzgui.js b/gz3d/src/gzgui.js
index 559504cba34f546a21b26132605701df715dfa13..81ae97170e9111e5c06f3f4b2bb84d31c918b69d 100644
--- a/gz3d/src/gzgui.js
+++ b/gz3d/src/gzgui.js
@@ -5,7 +5,7 @@ var guiEvents = new EventEmitter2({ verbose: true });
 
 var emUnits = function(value)
     {
-      return value*parseFloat($('body').css('font-size'));
+      return value*parseFloat($('#gz3d-body').css('font-size'));
     };
 
 var isTouchDevice = 'ontouchstart' in window || 'onmsgesturechange' in window;
@@ -25,160 +25,15 @@ var tabColors = {selected: 'rgb(34, 170, 221)', unselected: 'rgb(42, 42, 42)'};
 
 var modelList =
   [
-    {path:'buildings', title:'Buildings',
-    examplePath1:'fast_food', examplePath2:'kitchen_dining', examplePath3:'house_1', models:
+    {path:'virtual_room', title:'Virtual Room Objects',
+    examplePath1:'library_model',  examplePath2:'hosta_potted_plant',  examplePath3:'vr_lamp', models:
     [
-      {modelPath:'fast_food', modelTitle:'Fast Food'},
-      {modelPath:'gas_station', modelTitle:'Gas Station'},
-      {modelPath:'house_1', modelTitle:'House 1'},
-      {modelPath:'house_2', modelTitle:'House 2'},
-      {modelPath:'house_3', modelTitle:'House 3'},
-      {modelPath:'iss', modelTitle:'International Space Station'},
-      {modelPath:'iss_half', modelTitle:'ISS half'},
-      {modelPath:'kitchen_dining', modelTitle:'Kitchen and Dining'},
-      {modelPath:'office_building', modelTitle:'Office Building'},
-      {modelPath:'powerplant', modelTitle:'Power Plant'},
-      {modelPath:'starting_pen', modelTitle:'Starting Pen'},
-      {modelPath:'willowgarage', modelTitle:'Willow Garage'}
-    ]},
-
-    {path:'furniture', title:'Furniture',
-    examplePath1:'hinged_door', examplePath2:'bookshelf', examplePath3:'table', models:
-    [
-      {modelPath:'bookshelf', modelTitle:'Book Shelf'},
-      {modelPath:'cabinet', modelTitle:'Cabinet'},
-      {modelPath:'drc_practice_door_4x8', modelTitle:'4x8 Doorway'},
-      {modelPath:'drc_practice_ladder', modelTitle:'Ladder'},
-      {modelPath:'hinged_door', modelTitle:'Hinged Door'},
-      {modelPath:'table', modelTitle:'Table'},
-      {modelPath:'table_marble', modelTitle:'Table Marble'},
-
-      {modelPath:'drc_practice_ball_valve', modelTitle:'Ball Valve'},
-      {modelPath:'drc_practice_handle_wheel_valve', modelTitle:'Handle Wheel Valve'},
-      {modelPath:'drc_practice_hand_wheel_valve', modelTitle:'Hand Wheel Valve'},
-      {modelPath:'drc_practice_wheel_valve', modelTitle:'Wheel Valve'},
-      {modelPath:'drc_practice_wheel_valve_large', modelTitle:'Wheel Valve Large'},
-      {modelPath:'door_handle', modelTitle:'Door Handle'},
-
-      {modelPath:'drc_practice_ball_valve_wall', modelTitle:'Wall (Ball Valve)'},
-      {modelPath:'drc_practice_handle_wheel_valve_wall', modelTitle:'Wall (Handle Wheel Valve)'},
-      {modelPath:'drc_practice_hand_wheel_valve_wall', modelTitle:'Wall (Hand Wheel Valve)'},
-      {modelPath:'drc_practice_valve_wall', modelTitle:'Wall (Valve)'},
-      {modelPath:'drc_practice_wheel_valve_wall', modelTitle:'Wall (Wheel Valve)'},
-      {modelPath:'drc_practice_wheel_valve_large_wall', modelTitle:'Wall (Wheel Valve Large)'},
-      {modelPath:'grey_wall', modelTitle:'Grey Wall'},
-      {modelPath:'asphalt_plane', modelTitle:'Asphalt Plane'},
-      {modelPath:'drc_practice_base_4x8', modelTitle:'Debris base'},
-      {modelPath:'ground_plane', modelTitle:'Ground Plane'},
-      {modelPath:'nist_maze_wall_120', modelTitle:'120 Maze Wall'},
-      {modelPath:'nist_maze_wall_240', modelTitle:'240 Maze Wall'},
-      {modelPath:'nist_maze_wall_triple_holes_120', modelTitle:'120 Maze Wall Triple Holes'},
-      {modelPath:'nist_simple_ramp_120', modelTitle:'Simple Ramp'},
-      {modelPath:'nist_stairs_120', modelTitle:'Stairs'}
-    ]},
-
-    {path:'kitchen', title:'Kitchen',
-    examplePath1:'saucepan',  examplePath2:'beer',  examplePath3:'bowl', models:
-    [
-      {modelPath:'beer', modelTitle:'Beer'},
-      {modelPath:'bowl', modelTitle:'Bowl'},
-      {modelPath:'coke_can', modelTitle:'Coke Can'},
-      {modelPath:'saucepan', modelTitle:'Saucepan'}
-    ]},
-
-    {path:'robocup', title:'Robocup', examplePath1:'robocup_3Dsim_ball',
-    examplePath2:'robocup14_spl_goal', examplePath3:'robocup09_spl_field', models:
-    [
-      {modelPath:'robocup09_spl_field', modelTitle:'2009 SPL Field'},
-      {modelPath:'robocup14_spl_field', modelTitle:'2014 SPL Field'},
-      {modelPath:'robocup_3Dsim_field', modelTitle:'3D Sim. Field'},
-      {modelPath:'robocup14_spl_goal', modelTitle:'SPL Goal'},
-      {modelPath:'robocup_3Dsim_goal', modelTitle:'3D Sim. Goal'},
-      {modelPath:'robocup_spl_ball', modelTitle:'SPL Ball'},
-      {modelPath:'robocup_3Dsim_ball', modelTitle:'3D Sim. Ball'}
-    ]},
-
-    {path:'robots', title:'Robots',
-    examplePath1:'pioneer3at', examplePath2:'turtlebot', examplePath3:'pr2', models:
-    [
-      {modelPath:'create', modelTitle:'Create'},
-      {modelPath:'husky', modelTitle:'Husky'},
-      {modelPath:'irobot_hand', modelTitle:'iRobot Hand'},
-      {modelPath:'pioneer2dx', modelTitle:'Pioneer 2DX'},
-      {modelPath:'pioneer3at', modelTitle:'Pioneer 3AT'},
-      {modelPath:'pr2', modelTitle:'PR2'},
-      {modelPath:'robonaut', modelTitle:'Robonaut'},
-      {modelPath:'simple_arm', modelTitle:'Simple Arm'},
-      {modelPath:'simple_arm_gripper', modelTitle:'Simple Arm and Gripper'},
-      {modelPath:'simple_gripper', modelTitle:'Simple Gripper'},
-      {modelPath:'turtlebot', modelTitle:'TurtleBot'},
-      {modelPath:'youbot', modelTitle:'YouBot'}
-    ]},
-
-    {path:'sensors', title:'Sensors',
-    examplePath1:'camera', examplePath2:'hokuyo', examplePath3:'kinect', models:
-    [
-      {modelPath:'camera', modelTitle:'Camera'},
-      {modelPath:'stereo_camera', modelTitle:'Stereo Camera'},
-      {modelPath:'hokuyo', modelTitle:'Hokuyo'},
-      {modelPath:'kinect', modelTitle:'Kinect'}
-    ]},
-
-    {path:'street', title:'Street', examplePath1:'dumpster',
-    examplePath2:'drc_practice_angled_barrier_45', examplePath3:'fire_hydrant', models:
-    [
-      {modelPath:'cinder_block', modelTitle:'Cinder Block'},
-      {modelPath:'cinder_block_2', modelTitle:'Cinder Block 2'},
-      {modelPath:'cinder_block_wide', modelTitle:'Cinder Block Wide'},
-      {modelPath:'construction_barrel', modelTitle:'Construction Barrel'},
-      {modelPath:'construction_cone', modelTitle:'Construction Cone'},
-      {modelPath:'drc_practice_angled_barrier_45', modelTitle:'Angled Barrier 45'},
-      {modelPath:'drc_practice_angled_barrier_135', modelTitle:'Angled Barrier 135'},
-      {modelPath:'drc_practice_block_wall', modelTitle:'Block Wall'},
-      {modelPath:'drc_practice_orange_jersey_barrier', modelTitle:'Jersey Barrier (Orange)'},
-      {modelPath:'drc_practice_white_jersey_barrier', modelTitle:'Jersey Barrier (White)'},
-      {modelPath:'drc_practice_truss', modelTitle:'Truss'},
-      {modelPath:'drc_practice_yellow_parking_block', modelTitle:'Parking Block'},
-      {modelPath:'dumpster', modelTitle:'Dumpster'},
-      {modelPath:'fire_hydrant', modelTitle:'Fire Hydrant'},
-      {modelPath:'jersey_barrier', modelTitle:'Jersey Barrier'},
-      {modelPath:'lamp_post', modelTitle:'Lamp Post'},
-      {modelPath:'mailbox', modelTitle:'Mailbox'},
-      {modelPath:'mud_box', modelTitle:'Mud Box'},
-      {modelPath:'nist_fiducial_barrel', modelTitle:'Fiducial Barrel'},
-      {modelPath:'speed_limit_sign', modelTitle:'Speed Limit Sign'},
-      {modelPath:'stop_sign', modelTitle:'Stop Sign'}
-
-    ]},
-
-    {path:'tools', title:'Tools', examplePath1:'hammer',
-    examplePath2:'polaris_ranger_ev', examplePath3:'cordless_drill', models:
-    [
-      {modelPath:'cordless_drill', modelTitle:'Cordless Drill'},
-      {modelPath:'fire_hose_long', modelTitle:'Fire Hose'},
-      {modelPath:'fire_hose_long_curled', modelTitle:'Fire Hose Long Curled'},
-      {modelPath:'hammer', modelTitle:'Hammer'},
-      {modelPath:'monkey_wrench', modelTitle:'Monkey Wrench'},
-      {modelPath:'polaris_ranger_ev', modelTitle:'Polaris Ranger EV'},
-      {modelPath:'polaris_ranger_xp900', modelTitle:'Polaris Ranger XP900'},
-      {modelPath:'polaris_ranger_xp900_no_roll_cage', modelTitle:'Polaris Ranger without roll cage'},
-      {modelPath:'utility_cart', modelTitle:'Utility Cart'}
-    ]},
-
-    {path:'misc', title:'Misc.', examplePath1:'brick_box_3x1x3',
-    examplePath2:'drc_practice_4x4x20', examplePath3:'double_pendulum_with_base', models:
-    [
-      {modelPath:'double_pendulum_with_base', modelTitle:'Double Pendulum With Base'},
-      {modelPath:'breakable_test', modelTitle:'Breakable_test'},
-      {modelPath:'brick_box_3x1x3', modelTitle:'Brick Box 3x1x3'},
-      {modelPath:'cube_20k', modelTitle:'Cube 20k'},
-      {modelPath:'drc_practice_2x4', modelTitle:'2x4 Lumber'},
-      {modelPath:'drc_practice_2x6', modelTitle:'2x6 Lumber'},
-      {modelPath:'drc_practice_4x4x20', modelTitle:'4x4x20 Lumber'},
-      {modelPath:'drc_practice_4x4x40', modelTitle:'4x4x40 Lumber'},
-      {modelPath:'drc_practice_blue_cylinder', modelTitle:'Blue Cylinder'},
-      {modelPath:'drc_practice_wood_slats', modelTitle:'Wood Slats'},
-      {modelPath:'nist_elevated_floor_120', modelTitle:'Elevated Floor 120'}
+      {modelPath:'library_model', modelTitle:'Library'},
+      {modelPath:'hosta_potted_plant', modelTitle:'Hosta Plant'},
+      {modelPath:'vr_lamp', modelTitle:'Stand Lamp'},
+      {modelPath:'vr_screen', modelTitle:'Virtual Screen'},
+      {modelPath:'viz_poster', modelTitle:'Poster 1'},
+      {modelPath:'viz_poster_2', modelTitle:'Poster 2'}
     ]}
   ];
 
@@ -187,7 +42,7 @@ $(function()
   //Initialize
   if ('ontouchstart' in window || 'onmsgesturechange' in window)
   {
-    $('body').addClass('isTouchDevice');
+    $('#gz3d-body').addClass('isTouchDevice');
   }
 
   // Toggle items
@@ -487,6 +342,14 @@ $(function()
         guiEvents.emit('show_orbit_indicator');
         guiEvents.emit('closeTabs', false);
       });
+  $('#view-shadows').click(function()
+      {
+        guiEvents.emit('show_shadows', 'toggle');
+      });
+  $('#view-camera-sensors').click(function()
+      {
+        guiEvents.emit('show_camera_sensors', 'toggle');
+      });
   $( '#snap-to-grid' ).click(function() {
     guiEvents.emit('snap_to_grid');
     guiEvents.emit('closeTabs', false);
@@ -501,13 +364,12 @@ $(function()
   });
 
   // Disable Esc key to close panel
-  $('body').on('keyup', function(event)
-      {
-        if (event.which === 27)
-        {
-          return false;
-        }
-      });
+    $('#gz3d-body').on('keyup', function(event) {
+    if (event.which === 27)
+    {
+      return false;
+    }
+  });
 
   // Object menu
   $( '#view-transparent' ).click(function() {
@@ -835,6 +697,73 @@ GZ3D.Gui.prototype.init = function()
       }
   );
 
+  guiEvents.on('show_shadows', function(option)
+      {
+          if (option === 'show')
+          {
+              //that.emitter.emit('setShadows', true);
+              that.scene.setShadowMaps(true);
+          }
+          else if (option === 'hide')
+          {
+              //that.emitter.emit('setShadows', false);
+              that.scene.setShadowMaps(false);
+          }
+          else if (option === 'toggle')
+          {
+              var shadowsEnabled = that.scene.renderer.shadowMapEnabled;
+              //that.emitter.emit('setShadows', !shadowsEnabled);
+              that.scene.setShadowMaps(!shadowsEnabled);
+          }
+
+          if(!that.scene.renderer.shadowMapEnabled)
+          {
+              $('#view-shadows').buttonMarkup({icon: 'false'});
+              guiEvents.emit('notification_popup','Disabling shadows');
+          }
+          else
+          {
+              $('#view-shadows').buttonMarkup({icon: 'check'});
+              guiEvents.emit('notification_popup','Enabling shadows');
+          }
+      }
+  );
+
+    guiEvents.on('show_camera_sensors', function(option)
+        {
+            var camerasShown = false;
+            if (option === 'show')
+            {
+                that.scene.viewManager.showCameras(true);
+            }
+            else if (option === 'hide')
+            {
+                that.scene.viewManager.showCameras(false);
+            }
+            else if (option === 'toggle')
+            {
+                if (that.scene.viewManager.views.length > 1) {
+                    camerasShown = that.scene.viewManager.views[1].active;
+                    that.scene.viewManager.showCameras(!camerasShown);
+                }
+            }
+
+            if (that.scene.viewManager.views.length > 1) {
+                camerasShown = that.scene.viewManager.views[1].active;
+            }
+            if(!camerasShown)
+            {
+                $('#view-camera-sensors').buttonMarkup({icon: 'false'});
+                guiEvents.emit('notification_popup','Disabling camera views');
+            }
+            else
+            {
+                $('#view-camera-sensors').buttonMarkup({icon: 'check'});
+                guiEvents.emit('notification_popup','Enabling camera views');
+            }
+        }
+    );
+
   guiEvents.on('pause', function(paused)
       {
         that.emitter.emit('pause', paused);
@@ -1077,7 +1006,7 @@ GZ3D.Gui.prototype.init = function()
           $( '#notification-popup' ).html('&nbsp;'+notification+'&nbsp;');
           $( '#notification-popup' ).popup('open', {
               y:window.innerHeight-50});
-          
+
           if (duration === undefined)
           {
             duration = 2000;
@@ -1136,7 +1065,8 @@ GZ3D.Gui.prototype.init = function()
         lastOpenMenu[parentId] = id;
 
         $('.leftPanels').hide();
-        $('#'+id).show();
+        //$('#'+id).show(); //defaults as flex, but block is needed
+        $('#'+id).css('display','block');
 
         $('.tab').css('border-left-color', tabColors.unselected);
         $('#'+parentId+'Tab').css('border-left-color', tabColors.selected);
@@ -1605,17 +1535,20 @@ GZ3D.Gui.prototype.setModelStats = function(stats, action)
               return e.shortName === LinkShortName;
             });
 
-        if (link[0].self_collide)
+        if (link[0])
         {
-          link[0].self_collide = this.trueOrFalse(stats.link[0].self_collide);
-        }
-        if (link[0].gravity)
-        {
-          link[0].gravity = this.trueOrFalse(stats.link[0].gravity);
-        }
-        if (link[0].kinematic)
-        {
-          link[0].kinematic = this.trueOrFalse(stats.link[0].kinematic);
+          if (link[0].self_collide)
+          {
+            link[0].self_collide = this.trueOrFalse(stats.link[0].self_collide);
+          }
+          if (link[0].gravity)
+          {
+            link[0].gravity = this.trueOrFalse(stats.link[0].gravity);
+          }
+          if (link[0].kinematic)
+          {
+            link[0].kinematic = this.trueOrFalse(stats.link[0].kinematic);
+          }
         }
       }
 
@@ -1692,10 +1625,10 @@ GZ3D.Gui.prototype.setLightStats = function(stats, action)
       var thumbnail;
       switch(type)
       {
-        case 2:
+        case GZ3D.LIGHT_SPOT:
             thumbnail = 'style/images/spotlight.png';
             break;
-        case 3:
+        case GZ3D.LIGHT_DIRECTIONAL:
             thumbnail = 'style/images/directionallight.png';
             break;
         default:
@@ -1797,7 +1730,10 @@ GZ3D.Gui.prototype.findModelThumbnail = function(instanceName)
 GZ3D.Gui.prototype.updateStats = function()
 {
   var tree = angular.element($('#treeMenu')).scope();
-  tree.updateStats();
+  if (typeof(tree) !== 'undefined' && typeof tree.updateStats !== 'undefined')
+  {
+    tree.updateStats();
+  }
 };
 
 /**
@@ -1922,10 +1858,12 @@ GZ3D.Gui.prototype.formatStats = function(stats)
     colorHex = {};
     for (comp in diffuse)
     {
-      colorHex[comp] = diffuse[comp].toString(16);
-      if (colorHex[comp].length === 1)
-      {
-        colorHex[comp] = '0' + colorHex[comp];
+      if (diffuse.hasOwnProperty(comp)) {
+        colorHex[comp] = diffuse[comp].toString(16);
+        if (colorHex[comp].length === 1)
+        {
+          colorHex[comp] = '0' + colorHex[comp];
+        }
       }
     }
     color.diffuse = '#' + colorHex['r'] + colorHex['g'] + colorHex['b'];
@@ -1938,10 +1876,12 @@ GZ3D.Gui.prototype.formatStats = function(stats)
     colorHex = {};
     for (comp in specular)
     {
-      colorHex[comp] = specular[comp].toString(16);
-      if (colorHex[comp].length === 1)
-      {
-        colorHex[comp] = '0' + colorHex[comp];
+      if (specular.hasOwnProperty(comp)) {
+        colorHex[comp] = specular[comp].toString(16);
+        if (colorHex[comp].length === 1)
+        {
+          colorHex[comp] = '0' + colorHex[comp];
+        }
       }
     }
     color.specular = '#' + colorHex['r'] + colorHex['g'] + colorHex['b'];
diff --git a/gz3d/src/gziface.js b/gz3d/src/gziface.js
index e97d0e54ff7413747d719c3a07c153bd9b76a1d8..5e28028356bef6362bd97774cdd8f4bd7f5e525c 100644
--- a/gz3d/src/gziface.js
+++ b/gz3d/src/gziface.js
@@ -1,5 +1,7 @@
 //var GAZEBO_MODEL_DATABASE_URI='http://gazebosim.org/models';
 
+THREE.ImageUtils.crossOrigin = 'anonymous'; // needed to allow cross-origin loading of textures
+
 GZ3D.GZIface = function(scene, gui)
 {
   this.scene = scene;
@@ -15,6 +17,13 @@ GZ3D.GZIface = function(scene, gui)
   this.numConnectionTrials = 0;
   this.maxConnectionTrials = 30; // try to connect 30 times
   this.timeToSleepBtwTrials = 1000; // wait 1 second between connection trials
+
+  this.assetProgressData = {};
+  this.assetProgressData.assets = [];
+  this.assetProgressData.prepared = false;
+  this.assetProgressCallback = undefined;
+
+  this.webSocketConnectionCallbacks = [];
 };
 
 GZ3D.GZIface.prototype.init = function()
@@ -25,11 +34,35 @@ GZ3D.GZIface.prototype.init = function()
   this.connect();
 };
 
+GZ3D.GZIface.prototype.setAssetProgressCallback = function(callback)
+{
+  this.assetProgressCallback = callback;
+};
+
+GZ3D.GZIface.prototype.registerWebSocketConnectionCallback = function(callback) {
+  this.webSocketConnectionCallbacks.push(callback);
+};
+
 GZ3D.GZIface.prototype.connect = function()
 {
   // connect to websocket
+  var url = GZ3D.webSocketUrl;
+  if (!localStorage.getItem('localmode.forceuser')) {
+    var token = [];
+    if (localStorage.getItem('tokens-neurorobotics-ui@https://services.humanbrainproject.eu/oidc')) {
+      try {
+        token = JSON.parse(localStorage.getItem('tokens-neurorobotics-ui@https://services.humanbrainproject.eu/oidc'));
+      } catch(e) {
+        token[0] = { access_token : 'notoken' };
+      }
+      url = url + '/?token=' + token[0].access_token;
+    } else {
+      url = 'ws://' + location.hostname + ':7681';
+    }
+  }
+
   this.webSocket = new ROSLIB.Ros({
-    url : 'ws://' + location.hostname + ':7681'
+    url : url
   });
 
   var that = this;
@@ -39,6 +72,9 @@ GZ3D.GZIface.prototype.connect = function()
   this.webSocket.on('error', function() {
     that.onError();
   });
+  this.webSocket.on('close', function() {
+    console.log('Connection closed to websocket server: ' + that.webSocket.socket.url);
+  });
 
   this.numConnectionTrials++;
 };
@@ -63,6 +99,8 @@ GZ3D.GZIface.prototype.onError = function()
 
 GZ3D.GZIface.prototype.onConnected = function()
 {
+  console.log('Connected to websocket server: ' + this.webSocket.socket.url);
+
   this.isConnected = true;
   this.emitter.emit('connection');
 
@@ -84,6 +122,11 @@ GZ3D.GZIface.prototype.onConnected = function()
 
   setInterval(publishHeartbeat, 5000);
 
+  // call all the registered callbacks since we are connected now
+  this.webSocketConnectionCallbacks.forEach(function(callback) {
+    callback();
+  });
+
   var statusTopic = new ROSLIB.Topic({
     ros: this.webSocket,
     name: '~/status',
@@ -129,7 +172,7 @@ GZ3D.GZIface.prototype.onConnected = function()
 
     if (message.grid === true)
     {
-      this.gui.guiEvents.emit('show_grid', 'show');
+      //this.gui.guiEvents.emit('show_grid', 'show'); // do not show grid by default for now
     }
 
     if (message.ambient)
@@ -169,6 +212,7 @@ GZ3D.GZIface.prototype.onConnected = function()
       this.gui.setModelStats(model, 'update');
     }
 
+    this.assetProgressData.prepared = true;
     this.gui.setSceneStats(message);
     this.sceneTopic.unsubscribe();
   };
@@ -277,6 +321,8 @@ GZ3D.GZIface.prototype.onConnected = function()
         }
         i++;
       }
+    } else {
+      this.updateModelFromMsg(this.scene.getByName(message.name), message);
     }
     this.gui.setModelStats(message, 'update');
   };
@@ -399,7 +445,7 @@ GZ3D.GZIface.prototype.onConnected = function()
     messageType : 'light',
   });
 
-  var publishEntityModify = function(entity)
+  var createEntityModifyMessage = function(entity)
   {
     var matrix = entity.matrixWorld;
     var translation = new THREE.Vector3();
@@ -426,43 +472,89 @@ GZ3D.GZIface.prototype.onConnected = function()
         z: quaternion.z
       }
     };
-    if (entity.children[0] &&
-        entity.children[0] instanceof THREE.Light)
+    return entityMsg;
+  };
+
+  /*
+  TODO: (Sandro Weber)
+  The following functions are used to change all lights at the same time through the light slider of the NRP.
+  Gazebo only knows attenuation factors, not intensity, so we manipulate diffuse color for now.
+  Probably replaced / revamped after a dedicated edit tab is introduced to allow manipulation of lights directly.
+   */
+  var createEntityModifyMessageWithLight = function(entity, diffuse)
+  {
+    var entityMsg = createEntityModifyMessage(entity);
+
+    var lightObj = entity.children[0];
+
+    if (diffuse === undefined) {
+      diffuse = lightObj.color;
+    }
+    entityMsg.diffuse =
     {
-      entityMsg.diffuse =
-      {
-        r: entity.children[0].color.r,
-        g: entity.children[0].color.g,
-        b: entity.children[0].color.b
-      };
-      entityMsg.specular =
-      {
-        r: entity.serverProperties.specular.r,
-        g: entity.serverProperties.specular.g,
-        b: entity.serverProperties.specular.b
-      };
-      entityMsg.direction = entity.direction;
-      entityMsg.range = entity.children[0].distance;
-      entityMsg.attenuation_constant = entity.serverProperties.attenuation_constant;
-      entityMsg.attenuation_linear = entity.serverProperties.attenuation_linear;
-      entityMsg.attenuation_quadratic = entity.serverProperties.attenuation_quadratic;
-
-      that.lightModifyTopic.publish(entityMsg);
+      r: diffuse.r,
+      g: diffuse.g,
+      b: diffuse.b
+    };
+    entityMsg.specular =
+    {
+      r: entity.serverProperties.specular.r,
+      g: entity.serverProperties.specular.g,
+      b: entity.serverProperties.specular.b
+    };
+    entityMsg.direction = entity.direction;
+    entityMsg.range = lightObj.distance;
+
+    entityMsg.attenuation_constant = entity.serverProperties.attenuation_constant;
+    entityMsg.attenuation_linear = entity.serverProperties.attenuation_linear;
+    entityMsg.attenuation_quadratic = entity.serverProperties.attenuation_quadratic;
+
+    return entityMsg;
+  };
+
+  var publishEntityModify = function(entity)
+  {
+    var lightObj = entity.children[0];
+    if (lightObj && lightObj instanceof THREE.Light)
+    {
+      that.lightModifyTopic.publish(createEntityModifyMessageWithLight(entity, undefined));
     }
     else
     {
-      that.modelModifyTopic.publish(entityMsg);
+      that.modelModifyTopic.publish(createEntityModifyMessage(entity));
     }
   };
 
   this.scene.emitter.on('entityChanged', publishEntityModify);
 
-  // Link messages - for modifying links
-  this.linkModifyTopic = new ROSLIB.Topic({
-    ros : this.webSocket,
-    name : '~/link',
-    messageType : 'link',
-  });
+  var publishLightModify = function(ratio)
+  {
+    var lights = [];
+    that.scene.scene.traverse(function(node) {
+      if (node instanceof THREE.Light) {
+        lights.push(node);
+      }
+    });
+
+    var numberOfLights = lights.length;
+    for (var i = 0; i < numberOfLights; i+=1) {
+      if( lights[i] instanceof THREE.AmbientLight ) { // we don't change ambient lights
+        continue;
+      }
+      var entity = that.scene.getByName(lights[i].name);
+      var newDiffuse = new THREE.Color();
+      newDiffuse.r = THREE.Math.clamp(ratio * entity.serverProperties.initial.diffuse.r, 0, 1);
+      newDiffuse.g = THREE.Math.clamp(ratio * entity.serverProperties.initial.diffuse.g, 0, 1);
+      newDiffuse.b = THREE.Math.clamp(ratio * entity.serverProperties.initial.diffuse.b, 0, 1);
+
+      that.lightModifyTopic.publish(createEntityModifyMessageWithLight(entity, newDiffuse));
+    }
+  };
+
+  this.scene.emitter.on('lightChanged', publishLightModify);
+  /*
+  end of light slider change functions
+   */
 
   var publishLinkModify = function(entity, type)
   {
@@ -729,6 +821,16 @@ GZ3D.GZIface.prototype.createModelFromMsg = function(model)
         }
       }
     }
+
+    for (var i = 0; i < link.sensor.length; ++i) {
+      var sensor = link.sensor[i];
+
+      var sensorObj = this.createSensorFromMsg(sensor);
+      if (sensorObj && !sensorObj.parent)
+      {
+        linkObj.add(sensorObj);
+      }
+    }
   }
   if (model.joint)
   {
@@ -738,6 +840,24 @@ GZ3D.GZIface.prototype.createModelFromMsg = function(model)
   return modelObj;
 };
 
+// This method uses code also to be found at GZ3D.GZIface.prototype.createModelFromMsg.
+// Currently not everything is handled for an update, but this method was introduced to handle
+// the updates of colors of objects; if there should be more functionality one could consider
+// merging the two methods and extracting the different things to parameters (or any other means
+// of configuration).
+GZ3D.GZIface.prototype.updateModelFromMsg = function (modelObj, modelMsg) {
+  for (var j = 0; j < modelMsg.link.length; ++j) {
+    var link = modelMsg.link[j];
+    var linkObj = modelObj.children[j];
+
+    for (var k = 0; k < link.visual.length; ++k) {
+      var visual = link.visual[k];
+      var visualObj = linkObj.getObjectByName(visual.name);
+      this.updateVisualFromMsg(visualObj, visual);
+    }
+  }
+};
+
 GZ3D.GZIface.prototype.createVisualFromMsg = function(visual)
 {
   if (visual.geometry)
@@ -760,21 +880,32 @@ GZ3D.GZIface.prototype.createVisualFromMsg = function(visual)
   }
 };
 
+GZ3D.GZIface.prototype.updateVisualFromMsg = function (visualObj, visual) {
+  if (visual.geometry) {
+    var obj = visualObj.children[0];
+    var mat = this.parseMaterial(visual.material);
+
+    if (obj && mat) {
+      this.scene.setMaterial(obj, mat);
+    }
+  }
+};
+
 GZ3D.GZIface.prototype.createLightFromMsg = function(light)
 {
   var obj, range, direction;
 
-  if (light.type === 1)
+  if (light.type === this.scene.LIGHT_POINT)
   {
     direction = null;
     range = light.range;
   }
-  else if (light.type === 2)
+  else if (light.type === this.scene.LIGHT_SPOT)
   {
     direction = light.direction;
     range = light.range;
   }
-  else if (light.type === 3)
+  else if (light.type === this.scene.LIGHT_DIRECTIONAL)
   {
     direction = light.direction;
     range = null;
@@ -812,9 +943,66 @@ GZ3D.GZIface.prototype.createRoadsFromMsg = function(roads)
   return roadObj;
 };
 
+GZ3D.GZIface.prototype.createSensorFromMsg = function(sensor)
+{
+  var sensorObj = new THREE.Object3D();
+  sensorObj.name = sensor.name;
+
+  if (sensor.pose) {
+    this.scene.setPose(sensorObj, sensor.pose.position, sensor.pose.orientation);
+  }
+
+  if (sensor.type === 'camera') {
+    // if we have a camera sensor we have a potential view that could be rendered
+    // camera parameters are not published by gazebo right now, so hardcoded until fixed
+    var displayParams = {
+      left: '5%',
+      top: '5%',
+      width: '20%',
+      height: '20%',
+      adjustable: true
+    };
+    var cameraParams = {
+      width: 960,
+      height: 600,
+      fov: 60,
+      near: 0.1,
+      far: 100
+    };
+    var viewName = 'view_' + sensor.name;
+    var view = this.scene.viewManager.createView(viewName, displayParams, cameraParams);
+    if (!view) {
+      console.error('GZ3D.GZIface.createSensorFromMsg() - failed to create view ' + viewName);
+      return;
+    }
+
+    // There is a problem with the width and height; it should be read from the "sensor" object. Also see:
+    // https://bitbucket.org/osrf/gazebo/issues/1663/sensor-camera-elements-from-sdf-not-being
+
+    // camera sensors defined in gazebo .sdf seem to look along positive x axis, so need to adjust the rotation here
+    view.camera.rotateOnAxis(new THREE.Vector3(0, 1, 0), -Math.PI / 2);
+    view.camera.rotateOnAxis(new THREE.Vector3(0, 0, 1), -Math.PI / 2);
+
+    // set view inactive and hide at start
+    this.scene.viewManager.setViewVisibility(view, false);
+
+    // visualization - Deactivated since it causes the robot to be very big and the user
+    // can't barely select other objects on the scene. Reactivate for debug purposes !
+    // var cameraHelper = new THREE.CameraHelper(view.camera);
+    // view.camera.add( cameraHelper );
+
+    view.type = sensor.type;
+    console.log('view type: ' + view.type);
+
+    sensorObj.add(view.camera);
+  }
+
+  return sensorObj;
+};
+
 GZ3D.GZIface.prototype.parseUri = function(uri)
 {
-  var uriPath = 'assets';
+  var uriPath = GZ3D.assetsPath;
   var idx = uri.indexOf('://');
   if (idx > 0)
   {
@@ -826,7 +1014,7 @@ GZ3D.GZIface.prototype.parseUri = function(uri)
 GZ3D.GZIface.prototype.createGeom = function(geom, material, parent)
 {
   var obj;
-  var uriPath = 'assets';
+  var uriPath = GZ3D.assetsPath;
   var that = this;
   var mat = this.parseMaterial(material);
   if (geom.box)
@@ -906,26 +1094,48 @@ GZ3D.GZIface.prototype.createGeom = function(geom, material, parent)
 
         var modelUri = uriPath + '/' + modelName;
         // Use coarse version on touch devices
-        if (modelUri.indexOf('.dae') !== -1 && isTouchDevice)
+        if (modelUri.indexOf('.dae') !== -1 /*&& isTouchDevice*/) // Modified for HBP, we do use coarse models all the time
         {
           modelUri = modelUri.substring(0,modelUri.indexOf('.dae'));
 
-          var checkModel = new XMLHttpRequest();
-          checkModel.open('HEAD', modelUri+'_coarse.dae', false);
-          checkModel.send();
-          if (checkModel.status === 404)
+          if(modelUri.indexOf('_coarse') !== -1) //dae is already a coarse model
           {
             modelUri = modelUri+'.dae';
           }
-          else
-          {
-            modelUri = modelUri+'_coarse.dae';
+          else { // check if a coarse version is available
+            var checkModel = new XMLHttpRequest();
+            // We use a double technique to disable the cache for these requests:
+            // 1. We create a custom url by adding the time as a parameter.
+            // 2. We add the If-Modified-Since header with a date far in the future (end of the HBP project)
+            // Since browsers and servers vary in their behaviour, we use both of these tricks.
+            // PS: These requests do not load the dae files, they just verify if they exist on the server
+            // so that we can choose between coarse or reqular models.
+            checkModel.open('HEAD', modelUri+'_coarse.dae?timestamp=' + new Date().getTime(), false);
+            checkModel.setRequestHeader('If-Modified-Since', 'Sat, 1 Jan 2026 00:00:00 GMT');
+
+            try { checkModel.send(); } catch(err) { console.log(modelUri + ': no coarse version'); }
+
+            if (checkModel.status === 404) {
+              modelUri = modelUri+'.dae';
+            }
+            else {
+              modelUri = modelUri+'_coarse.dae';
+            }
           }
         }
 
         var materialName = parent.name + '::' + modelUri;
         this.entityMaterial[materialName] = mat;
 
+        // Progress update: Add this asset to the assetProgressArray
+        var element = {};
+        element.id = parent.name;
+        element.url = modelUri;
+        element.progress = 0;
+        element.totalSize = 0;
+        element.done = false;
+        this.assetProgressData.assets.push(element);
+
         this.scene.loadMesh(modelUri, submesh,
             centerSubmesh, function(dae) {
               if (that.entityMaterial[materialName])
@@ -944,6 +1154,19 @@ GZ3D.GZIface.prototype.createGeom = function(geom, material, parent)
               }
               parent.add(dae);
               loadGeom(parent);
+
+              // Progress update: execute callback
+              element.done = true;
+              if (that.assetProgressCallback) {
+                that.assetProgressCallback(that.assetProgressData);
+              }
+            }, function(progress){
+              element.progress = progress.loaded;
+              element.totalSize = progress.total;
+              element.error = progress.error;
+              if (that.assetProgressCallback) {
+                that.assetProgressCallback(that.assetProgressData);
+              }
             });
       }
     }
@@ -1019,7 +1242,7 @@ GZ3D.GZIface.prototype.createGeom = function(geom, material, parent)
           allChildren[c].castShadow = false;
           allChildren[c].receiveShadow = false;
 
-          allChildren[c].visible = this.scene.showCollisions;
+          allChildren[c].visible = that.scene.showCollisions;
         }
       }
     }
@@ -1077,7 +1300,7 @@ GZ3D.GZIface.prototype.parseMaterial = function(material)
     return null;
   }
 
-  var uriPath = 'assets';
+  var uriPath = GZ3D.assetsPath;//'assets';
   var texture;
   var normalMap;
   var textureUri;
diff --git a/gz3d/src/gzmanipulator.js b/gz3d/src/gzmanipulator.js
index 84e9cadfbd303cdb19667bda4075bdf3d3c0542d..e48ee73f46322a29e2d2b9872b605566ab35a64e 100644
--- a/gz3d/src/gzmanipulator.js
+++ b/gz3d/src/gzmanipulator.js
@@ -520,10 +520,10 @@ GZ3D.Manipulator = function(camera, mobile, domElement, doc)
     }
 
     this.object.updateMatrixWorld();
-    worldPosition.getPositionFromMatrix(this.object.matrixWorld);
+    worldPosition.setFromMatrixPosition(this.object.matrixWorld);
 
     this.camera.updateMatrixWorld();
-    camPosition.getPositionFromMatrix(this.camera.matrixWorld);
+    camPosition.setFromMatrixPosition(this.camera.matrixWorld);
 
     scale = worldPosition.distanceTo(camPosition) / 6 * this.scale;
     this.gizmo.position.copy(worldPosition);
@@ -775,7 +775,7 @@ GZ3D.Manipulator = function(camera, mobile, domElement, doc)
         worldRotationMatrix.extractRotation(scope.object.matrixWorld);
 
         parentRotationMatrix.extractRotation(scope.object.parent.matrixWorld);
-        parentScale.getScaleFromMatrix(tempMatrix.getInverse(
+        parentScale.setFromMatrixScale(tempMatrix.getInverse(
             scope.object.parent.matrixWorld));
 
         offset.copy(planeIntersect.point);
@@ -890,7 +890,7 @@ GZ3D.Manipulator = function(camera, mobile, domElement, doc)
 
           parentRotationMatrix.extractRotation(
               scope.object.parent.matrixWorld);
-          parentScale.getScaleFromMatrix(tempMatrix.getInverse(
+          parentScale.setFromMatrixScale(tempMatrix.getInverse(
               scope.object.parent.matrixWorld));
 
           offset.copy(planeIntersect.point);
diff --git a/gz3d/src/gzmultiview.js b/gz3d/src/gzmultiview.js
new file mode 100644
index 0000000000000000000000000000000000000000..16aaa50efd37119940004316ea4d95248e4dd786
--- /dev/null
+++ b/gz3d/src/gzmultiview.js
@@ -0,0 +1,281 @@
+/**
+ * Created by Sandro Weber (webers@in.tum.de).
+ */
+
+GZ3D.MULTIVIEW_MAX_VIEW_COUNT = 10;
+
+/**
+ * GZ3D.MULTIVIEW_RENDER_VIEWPORTS uses view containers as transparent references to determine viewports for rendering (one single canvas).
+ * This is broken in combination with shadowmaps at the moment. See renderToViewport() method.
+ *
+ * @type {number}
+ */
+GZ3D.MULTIVIEW_RENDER_VIEWPORTS = 1;
+/**
+ * GZ3D.MULTIVIEW_RENDER_COPY2CANVAS renders views offscreen then copies into the view containers (multiple separate canvases).
+ * @type {number}
+ */
+GZ3D.MULTIVIEW_RENDER_COPY2CANVAS = 2;
+
+GZ3D.MultiView = function(gz3dScene, mainContainer, callbackCreateRenderContainer)
+{
+    this.gz3dScene = gz3dScene;
+    this.mainContainer = mainContainer;
+    this.createRenderContainerCallback = callbackCreateRenderContainer;
+
+    this.init();
+};
+
+GZ3D.MultiView.prototype.init = function()
+{
+    this.views = [];
+
+    this.mainContainer.style.zIndex = 0;
+
+    this.renderMethod = GZ3D.MULTIVIEW_RENDER_COPY2CANVAS;
+    if (this.renderMethod === GZ3D.MULTIVIEW_RENDER_VIEWPORTS) {
+        this.mainContainer.appendChild(this.gz3dScene.getDomElement());
+    }
+};
+
+GZ3D.MultiView.prototype.setCallbackCreateRenderContainer = function(callback)
+{
+    this.createRenderContainerCallback = callback;
+};
+
+/**
+ *
+ * @param name
+ * @param displayParams {left, top, width, height, zIndex, adjustable}
+ * @param cameraParams {width, height, fov, near, far}
+ */
+GZ3D.MultiView.prototype.createView = function(name, displayParams, cameraParams)
+{
+    if (this.getViewByName(name) !== undefined) {
+        console.error('GZ3D.MultiView.createView() - a view of that name already exists (' + name + ')');
+        return null;
+    }
+
+    if (this.views.length >= this.MULTIVIEW_MAX_VIEW_COUNT) {
+        console.warn('GZ3D.MultiView.createView() - creating new view will exceed MULTIVIEW_MAX_VIEW_COUNT(' + this.MULTIVIEW_MAX_VIEW_COUNT + '). This may cause z-ordering issues.');
+    }
+
+    var container = this.createViewContainer(displayParams, name);
+    if (container === null) {
+        return;
+    }
+
+    // camera
+    var camera = new THREE.PerspectiveCamera(cameraParams.fov, cameraParams.width / cameraParams.height, cameraParams.near, cameraParams.far);
+    camera.name = name;
+
+    // assemble view
+    var view = {
+        name: name,
+        active: true,
+        container: container,
+        camera: camera
+    };
+
+    this.views.push(view);
+    this.mainContainer.appendChild(view.container);
+
+    return view;
+};
+
+GZ3D.MultiView.prototype.createViewContainer = function(displayParams, name)
+{
+    if (this.createRenderContainerCallback === undefined) {
+        console.error('GZ3D.MultiView.createViewContainer() - no callback for creating view reference container defined');
+        return null;
+    }
+
+    // container div
+    var viewContainer = this.createRenderContainerCallback(displayParams.adjustable, name);
+    if (!viewContainer) {
+        console.error('GZ3D.MultiView.createViewContainer() - could not create view container via callback');
+        return null;
+    }
+
+    // positioning
+    viewContainer.style.position = 'absolute';
+    viewContainer.style.left = displayParams.left;
+    viewContainer.style.top = displayParams.top;
+    // There is a problem with the width and height; it should be read from the "sensor" object. Also see:
+    // https://bitbucket.org/osrf/gazebo/issues/1663/sensor-camera-elements-from-sdf-not-being
+    // Here we use (preliminary) percentual width.
+    viewContainer.style.width = displayParams.width;
+    viewContainer.style.height = displayParams.height;
+
+    // We set 50px as a min-width for now and set the min height accordingly
+    viewContainer.style.minWidth = '50px';
+    viewContainer.style.minHeight = (50 * (parseInt(displayParams.width, 10)/parseInt(displayParams.height, 10))) + 'px';
+    viewContainer.style.maxWidth = '100%';
+    viewContainer.style.maxHeight = '100%';
+
+    // transparent view-container so we can render viewport with one renderer in the same context
+    // view-container is only taken as reference for viewport
+    viewContainer.style.boxShadow = '0px 0px 0px 3px rgba(0,0,0,0.3)';
+    viewContainer.style.borderRadius = '2px';
+    viewContainer.style.background = 'rgba(0,0,0,0)';
+
+    if (this.renderMethod === GZ3D.MULTIVIEW_RENDER_COPY2CANVAS) {
+        // canvas
+        viewContainer.canvas = document.createElement('canvas');
+        viewContainer.appendChild( viewContainer.canvas );
+        viewContainer.canvas.style.width = '100%';
+        viewContainer.canvas.style.height = '100%';
+    }
+
+    // z-index
+    var zIndexTop = parseInt(this.mainContainer.style.zIndex, 10) + this.views.length + 1;
+    viewContainer.style.zIndex = (displayParams.zIndex !== undefined) ? displayParams.zIndex : zIndexTop;
+
+    return viewContainer;
+};
+
+GZ3D.MultiView.prototype.getViewByName = function(name)
+{
+    for (var i = 0; i < this.views.length; i = i+1) {
+        if (this.views[i].name === name) {
+            return this.views[i];
+        }
+    }
+
+    return undefined;
+};
+
+GZ3D.MultiView.prototype.setViewVisibility = function(view, visible)
+{
+    view.active = visible;
+    if (view.active) {
+        view.container.style.visibility = 'visible';
+    } else {
+        view.container.style.visibility = 'hidden';
+    }
+};
+
+GZ3D.MultiView.prototype.updateCamera = function(view)
+{
+    view.camera.aspect = view.container.clientWidth / view.container.clientHeight;
+    view.camera.updateProjectionMatrix();
+};
+
+GZ3D.MultiView.prototype.getViewport = function(view)
+{
+    var viewport = {
+        x: view.container.offsetLeft,
+        y: this.mainContainer.clientHeight - view.container.clientHeight - view.container.offsetTop,
+        w: view.container.clientWidth,
+        h: view.container.clientHeight
+    };
+
+    return viewport;
+};
+
+GZ3D.MultiView.prototype.setWindowSize = function(width, height)
+{
+
+};
+
+GZ3D.MultiView.prototype.renderViews = function()
+{
+    // sort views into rendering order
+    this.views.sort(function(a, b) {
+       return a.container.style.zIndex - b.container.style.zIndex;
+    });
+
+    for (var i = 0, l = this.views.length; i < l; i = i+1) {
+        var view = this.views[i];
+
+        if (view.active) {
+            switch(this.renderMethod) {
+                case GZ3D.MULTIVIEW_RENDER_VIEWPORTS:
+                    this.renderToViewport(view);
+                    break;
+                case GZ3D.MULTIVIEW_RENDER_COPY2CANVAS:
+                    this.renderAndCopyToCanvas(view);
+                    break;
+            }
+        }
+    }
+};
+
+/**
+ * !IMPORTANT!
+ * https://github.com/mrdoob/three.js/issues/3532
+ * This is at the moment not a good idea, see issue above. ScissorTest will scramble shadowmaps.
+ *
+ * @param view
+ */
+GZ3D.MultiView.prototype.renderToViewport = function(view)
+{
+    this.updateCamera(view);  //TODO: better solution with resize callback, also adjust camera helper
+
+    var webglRenderer = this.gz3dScene.renderer;
+
+    var viewport = this.getViewport(view);
+    webglRenderer.setViewport( viewport.x, viewport.y, viewport.w, viewport.h );
+    webglRenderer.setScissor( viewport.x, viewport.y, viewport.w, viewport.h );
+    webglRenderer.enableScissorTest ( true );
+
+    if (this.gz3dScene.effectsEnabled) {
+        this.gz3dScene.scene.overrideMaterial = this.depthMaterial;
+        this.gz3dScene.renderer.render(this.gz3dScene.scene, view.camera, this.depthTarget);
+        this.gz3dScene.scene.overrideMaterial = null;
+        this.gz3dScene.composer.render();
+    } else {
+        webglRenderer.render(this.gz3dScene.scene, view.camera);
+    }
+};
+
+GZ3D.MultiView.prototype.renderAndCopyToCanvas = function(view)
+{
+    this.updateCamera(view);  //TODO: better solution with resize callback, also adjust camera helper
+
+    var webglRenderer = this.gz3dScene.renderer;
+
+    var width = view.container.canvas.clientWidth;
+    var height = view.container.canvas.clientHeight;
+    view.container.canvas.width = width;
+    view.container.canvas.height = height;
+
+    if (webglRenderer.context.canvas.width < width) {
+        webglRenderer.context.canvas.width = width;
+    }
+    if (webglRenderer.context.canvas.height < height) {
+        webglRenderer.context.canvas.height = height;
+    }
+    webglRenderer.setViewport( 0, 0, width, height );
+
+    if (this.gz3dScene.effectsEnabled) {
+        this.gz3dScene.scene.overrideMaterial = this.depthMaterial;
+        this.gz3dScene.renderer.render(this.gz3dScene.scene, view.camera, this.depthTarget);
+        this.gz3dScene.scene.overrideMaterial = null;
+        this.gz3dScene.composer.render();
+    } else {
+        webglRenderer.render(this.gz3dScene.scene, view.camera);
+    }
+
+    // copy rendered image over to view canvas
+    var srcX = 0;
+    var srcY = webglRenderer.context.canvas.height - height + 1;
+    var dstX = 0;
+    var dstY = 0;
+    // no cleaner solution right now, should be coming though: https://github.com/mrdoob/three.js/pull/6723#issuecomment-134129027
+    // this kills performance a little ...
+    if ( srcX >= 0 && srcY >= 0 && width >= 1 && height >= 1 ) {
+        view.container.canvas.getContext('2d').drawImage( webglRenderer.context.canvas,
+            srcX, srcY, width, height,
+            dstX, dstY, width, height );
+    }
+};
+
+GZ3D.MultiView.prototype.showCameras = function(show)
+{
+    for (var i = 0; i < this.views.length; i = i+1) {
+        if (this.views[i].type === 'camera') {
+            this.setViewVisibility(this.views[i], show);
+        }
+    }
+};
\ No newline at end of file
diff --git a/gz3d/src/gzscene.js b/gz3d/src/gzscene.js
index d8a6c0943bdfec4c97378193999a00551369e1b8..fb41118728f91a7a9c8242d45a1033310f244da1 100644
--- a/gz3d/src/gzscene.js
+++ b/gz3d/src/gzscene.js
@@ -1,5 +1,3 @@
-
-
 /**
  * The scene is where everything is placed, from objects, to lights and cameras.
  * @constructor
@@ -9,6 +7,11 @@ GZ3D.Scene = function()
   this.init();
 };
 
+GZ3D.Scene.prototype.LIGHT_POINT = 1;
+GZ3D.Scene.prototype.LIGHT_SPOT = 2;
+GZ3D.Scene.prototype.LIGHT_DIRECTIONAL = 3;
+GZ3D.Scene.prototype.LIGHT_UNKNOWN = 4;
+
 /**
  * Initialize scene
  */
@@ -32,16 +35,38 @@ GZ3D.Scene.prototype.init = function()
   this.renderer.setSize( window.innerWidth, window.innerHeight);
 
   // shadows
-  this.renderer.shadowMapEnabled = true;
+  this.renderer.shadowMapEnabled = false;
   this.renderer.shadowMapType = THREE.PCFSoftShadowMap;
 
+  this.container = document.getElementById( 'container' );
+
+  // create views manager
+  this.viewManager = new GZ3D.MultiView(this, this.container, function(adjustable, name){
+    return document.createElement('div');
+  });
+  // create main view
+  var displayParams = {
+    left: '0px',
+    top: '0px',
+    width: '100%',
+    height: '100%',
+    adjustable: false
+  };
+  var cameraParams = {
+    width: 960,
+    height: 600,
+    fov: 60,
+    near: 0.1,
+    far: 100
+  };
+  this.viewManager.createView('main_view', displayParams, cameraParams);
+
   // lights
   this.ambient = new THREE.AmbientLight( 0x666666 );
   this.scene.add(this.ambient);
 
   // camera
-  this.camera = new THREE.PerspectiveCamera(
-      60, window.innerWidth / window.innerHeight, 0.1, 1000 );
+  this.camera = this.viewManager.getViewByName('main_view').camera;
   this.defaultCameraPosition = new THREE.Vector3(0, -5, 5);
   this.resetView();
 
@@ -100,7 +125,12 @@ GZ3D.Scene.prototype.init = function()
 
   this.timeDown = null;
 
-  this.controls = new THREE.OrbitControls(this.camera);
+  var domElementForKeyBindings = document.getElementsByTagName('body')[0];
+  this.controls = new THREE.FirstPersonControls(this.camera, this.container, domElementForKeyBindings);
+  if (this.controls instanceof THREE.FirstPersonControls) {
+    this.controls.movementSpeed = 0.2;
+    this.controls.lookSpeed = 0.005;
+  }
   if (this.controls.targetIndicator !== undefined) {
     this.scene.add(this.controls.targetIndicator);
   }
@@ -469,7 +499,7 @@ GZ3D.Scene.prototype.onPointerDown = function(event)
  */
 GZ3D.Scene.prototype.onPointerUp = function(event)
 {
-  event.preventDefault();
+  //event.preventDefault();
 
   // Clicks (<150ms) outside any models trigger view mode
   var millisecs = new Date().getTime();
@@ -673,7 +703,7 @@ GZ3D.Scene.prototype.getRayCastModel = function(pos, intersect)
  */
 GZ3D.Scene.prototype.getDomElement = function()
 {
-  return this.renderer.domElement;
+  return this.container;
 };
 
 /**
@@ -703,17 +733,7 @@ GZ3D.Scene.prototype.render = function()
   this.modelManipulator.update();
   this.radialMenu.update();
 
-  if (this.effectsEnabled)
-  {
-    this.scene.overrideMaterial = this.depthMaterial;
-    this.renderer.render(this.scene, this.camera, this.depthTarget);
-    this.scene.overrideMaterial = null;
-    this.composer.render();
-  }
-  else
-  {
-    this.renderer.render(this.scene, this.camera);
-  }
+  this.viewManager.renderViews();
 };
 
 /**
@@ -727,6 +747,11 @@ GZ3D.Scene.prototype.setWindowSize = function(width, height)
   this.camera.updateProjectionMatrix();
 
   this.renderer.setSize( width, height);
+  this.renderer.context.canvas.width = width;
+  this.renderer.context.canvas.height = height;
+
+  this.viewManager.setWindowSize(width, height);
+
   this.render();
 };
 
@@ -915,6 +940,8 @@ GZ3D.Scene.prototype.createBox = function(width, height, depth)
  * @param {} attenuation_constant
  * @param {} attenuation_linear
  * @param {} attenuation_quadratic
+ * @param {} spot_angle
+ * @param {} spot_falloff
  * @returns {THREE.Object3D}
  */
 GZ3D.Scene.prototype.createLight = function(type, diffuse, intensity, pose,
@@ -943,6 +970,10 @@ GZ3D.Scene.prototype.createLight = function(type, diffuse, intensity, pose,
     specular = color.clone();
   }
 
+  if (typeof(specular) === 'undefined') {
+    specular = 0xffffff;
+  }
+
   var matrixWorld;
 
   if (pose)
@@ -966,17 +997,17 @@ GZ3D.Scene.prototype.createLight = function(type, diffuse, intensity, pose,
   }
 
   var elements;
-  if (type === 1)
+  if (type === this.LIGHT_POINT)
   {
     elements = this.createPointLight(obj, diffuse, intensity,
         distance, cast_shadows);
   }
-  else if (type === 2)
+  else if (type === this.LIGHT_SPOT)
   {
     elements = this.createSpotLight(obj, diffuse, intensity,
         distance, cast_shadows, spot_angle, spot_falloff);
   }
-  else if (type === 3)
+  else if (type === this.LIGHT_DIRECTIONAL)
   {
     elements = this.createDirectionalLight(obj, diffuse, intensity,
         cast_shadows);
@@ -1011,13 +1042,16 @@ GZ3D.Scene.prototype.createLight = function(type, diffuse, intensity, pose,
   obj.serverProperties.attenuation_constant = attenuation_constant;
   obj.serverProperties.attenuation_linear = attenuation_linear;
   obj.serverProperties.attenuation_quadratic = attenuation_quadratic;
+  obj.serverProperties.initial = {};
+  obj.serverProperties.initial.diffuse = diffuse;
 
-  obj.add(lightObj);
-  obj.add(helper);
-
+  helper.visible = false;
   lightObj.up = new THREE.Vector3(0,0,1);
   lightObj.shadowBias = -0.0005;
 
+  obj.add(lightObj);
+  obj.add(helper);
+
   return obj;
 };
 
@@ -1484,7 +1518,7 @@ GZ3D.Scene.prototype.loadHeightmap = function(heights, width, height,
  * @param {function} callback
  */
 GZ3D.Scene.prototype.loadMesh = function(uri, submesh, centerSubmesh,
-    callback)
+    callback, progressCallback)
 {
   var uriPath = uri.substring(0, uri.lastIndexOf('/'));
   var uriFile = uri.substring(uri.lastIndexOf('/') + 1);
@@ -1492,7 +1526,7 @@ GZ3D.Scene.prototype.loadMesh = function(uri, submesh, centerSubmesh,
   // load urdf model
   if (uriFile.substr(-4).toLowerCase() === '.dae')
   {
-    return this.loadCollada(uri, submesh, centerSubmesh, callback);
+    return this.loadCollada(uri, submesh, centerSubmesh, callback, progressCallback);
   }
   else if (uriFile.substr(-5).toLowerCase() === '.urdf')
   {
@@ -1538,7 +1572,7 @@ GZ3D.Scene.prototype.loadMesh = function(uri, submesh, centerSubmesh,
  * @param {function} callback
  */
 GZ3D.Scene.prototype.loadCollada = function(uri, submesh, centerSubmesh,
-    callback)
+    callback, progressCallback)
 {
   var dae;
   var mesh = null;
@@ -1561,6 +1595,7 @@ GZ3D.Scene.prototype.loadCollada = function(uri, submesh, centerSubmesh,
   var thatSubmesh = submesh;
   var thatCenterSubmesh = centerSubmesh;
 
+  var that = this;
   loader.load(uri, function(collada)
   {
     // check for a scale factor
@@ -1572,13 +1607,22 @@ GZ3D.Scene.prototype.loadCollada = function(uri, submesh, centerSubmesh,
 
     dae = collada.scene;
     dae.updateMatrix();
-    this.scene.prepareColladaMesh(dae);
-    this.scene.meshes[thatURI] = dae;
+    that.prepareColladaMesh(dae);
+    that.meshes[thatURI] = dae;
     dae = dae.clone();
-    this.scene.useColladaSubMesh(dae, thatSubmesh, centerSubmesh);
+    that.useColladaSubMesh(dae, thatSubmesh, centerSubmesh);
 
     dae.name = uri;
     callback(dae);
+  },function(progress) {
+    if (progressCallback !== undefined) {
+      progress.error = false;
+      progressCallback(progress);
+    }
+  },function(){
+    if (progressCallback !== undefined) {
+      progressCallback({ total: 0, loaded: 0, error: true });
+    }
   });
 };
 
@@ -1791,7 +1835,7 @@ GZ3D.Scene.prototype.setManipulationMode = function(mode)
   }
   else
   {
-    // Toggle manipulaion space (world / local)
+    // Toggle manipulation space (world / local)
     if (this.modelManipulator.mode === this.manipulationMode)
     {
       this.modelManipulator.space =
@@ -2384,7 +2428,7 @@ GZ3D.Scene.prototype.updateLight = function(entity, msg)
     this.setPose(entity, msg.pose.position, msg.pose.orientation);
     entity.matrixWorldNeedsUpdate = true;
 
-    if (entity.direction)
+    if (entity.direction && lightObj.target)
     {
       dir = new THREE.Vector3(entity.direction.x, entity.direction.y,
           entity.direction.z);
@@ -2426,6 +2470,18 @@ GZ3D.Scene.prototype.updateLight = function(entity, msg)
     entity.serverProperties.attenuation_quadratic = msg.attenuation_quadratic;
     lightObj.intensity = lightObj.intensity/(1+msg.attenuation_quadratic);
   }
+  if (msg.attenuation_linear && msg.attenuation_quadratic)
+  {
+    // equation taken from
+    // http://wiki.blender.org/index.php/Doc:2.6/Manual/Lighting/Lights/Light_Attenuation
+    var E = 1;
+    var D = 1;
+    var r = 1;
+    var L = msg.attenuation_linear;
+    var Q = msg.attenuation_quadratic;
+    lightObj.intensity = E*(D/(D+L*r))*(Math.pow(D,2)/(Math.pow(D,2)+Q*Math.pow(r,2)));
+  }
+
 
   if (lightObj instanceof THREE.SpotLight) {
     if (msg.spot_outer_angle) {
@@ -2437,7 +2493,7 @@ GZ3D.Scene.prototype.updateLight = function(entity, msg)
     }
   }
 
-  if (msg.direction)
+  if (msg.direction && lightObj.target)
   {
     dir = new THREE.Vector3(msg.direction.x, msg.direction.y,
         msg.direction.z);
@@ -2449,3 +2505,27 @@ GZ3D.Scene.prototype.updateLight = function(entity, msg)
     lightObj.target.position.copy(dir);
   }
 };
+
+GZ3D.Scene.prototype.setShadowMaps = function(enabled) {
+  this.renderer.shadowMapEnabled = enabled;
+
+  var that = this;
+  this.scene.traverse(function(node) {
+    if (enabled) {
+      if (node.material) {
+        node.material.needsUpdate = true;
+        if (node.material.materials) {
+          for (var i = 0; i < node.material.materials.length; i = i+1) {
+            node.material.materials[i].needsUpdate = true;
+          }
+        }
+      }
+    } else {
+      if (node instanceof THREE.Light) {
+        if (node.shadowMap) {
+          that.renderer.clearTarget( node.shadowMap );
+        }
+      }
+    }
+  });
+};
\ No newline at end of file
diff --git a/gz3d/src/gzsdfparser.js b/gz3d/src/gzsdfparser.js
index 9ff775e7ce7b0d8d30ba694a51bc7cdb57884aeb..96d8ed71a8b584943263013faf3bcb5fb64d90d4 100644
--- a/gz3d/src/gzsdfparser.js
+++ b/gz3d/src/gzsdfparser.js
@@ -9,7 +9,7 @@ GZ3D.SdfParser = function(scene, gui, gziface)
 {
   // set the sdf version
   this.SDF_VERSION = 1.5;
-  this.MATERIAL_ROOT = 'assets/';
+  this.MATERIAL_ROOT = GZ3D.assetsPath + '/';
 
   // set the xml parser function
   this.parseXML = function(xmlStr) {
@@ -539,7 +539,7 @@ GZ3D.SdfParser.prototype.createGeom = function(geom, mat, parent)
           allChildren[c].castShadow = false;
           allChildren[c].receiveShadow = false;
 
-          allChildren[c].visible = this.scene.showCollisions;
+          allChildren[c].visible = that.scene.showCollisions;
         }
         break;
       }