import { SphereGeometry, Color, TextureLoader, SRGBColorSpace, MeshBasicMaterial, BackSide, Vector2, WebGLRenderer, Mesh, Clock, PerspectiveCamera, Scene, Raycaster, Vector3, Box3 } from 'three';
import { WebGPURenderer } from 'three/webgpu';
import { TrackballControls } from 'three/examples/jsm/controls/TrackballControls.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { FlyControls } from 'three/examples/jsm/controls/FlyControls.js';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { parseToRgb, opacify } from 'polished';
import { Group, Tween, Easing } from '@tweenjs/tween.js';
import accessorFn from 'accessor-fn';
import Kapsule from 'kapsule';
import Tooltip from 'float-tooltip';

function styleInject(css, ref) {
  if (ref === void 0) ref = {};
  var insertAt = ref.insertAt;
  if (typeof document === 'undefined') {
    return;
  }
  var head = document.head || document.getElementsByTagName('head')[0];
  var style = document.createElement('style');
  style.type = 'text/css';
  if (insertAt === 'top') {
    if (head.firstChild) {
      head.insertBefore(style, head.firstChild);
    } else {
      head.appendChild(style);
    }
  } else {
    head.appendChild(style);
  }
  if (style.styleSheet) {
    style.styleSheet.cssText = css;
  } else {
    style.appendChild(document.createTextNode(css));
  }
}

var css_248z = ".scene-nav-info {\n  position: absolute;\n  bottom: 5px;\n  width: 100%;\n  text-align: center;\n  color: slategrey;\n  opacity: 0.7;\n  font-size: 10px;\n  font-family: sans-serif;\n  pointer-events: none;\n  user-select: none;\n}\n\n.scene-container canvas:focus {\n  outline: none;\n}";
styleInject(css_248z);

function _arrayLikeToArray(r, a) {
  (null == a || a > r.length) && (a = r.length);
  for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
  return n;
}
function _arrayWithHoles(r) {
  if (Array.isArray(r)) return r;
}
function _arrayWithoutHoles(r) {
  if (Array.isArray(r)) return _arrayLikeToArray(r);
}
function _defineProperty(e, r, t) {
  return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
    value: t,
    enumerable: true,
    configurable: true,
    writable: true
  }) : e[r] = t, e;
}
function _iterableToArray(r) {
  if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r);
}
function _iterableToArrayLimit(r, l) {
  var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
  if (null != t) {
    var e,
      n,
      i,
      u,
      a = [],
      f = true,
      o = false;
    try {
      if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
    } catch (r) {
      o = true, n = r;
    } finally {
      try {
        if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
      } finally {
        if (o) throw n;
      }
    }
    return a;
  }
}
function _nonIterableRest() {
  throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _nonIterableSpread() {
  throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _slicedToArray(r, e) {
  return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
}
function _toConsumableArray(r) {
  return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread();
}
function _toPrimitive(t, r) {
  if ("object" != typeof t || !t) return t;
  var e = t[Symbol.toPrimitive];
  if (void 0 !== e) {
    var i = e.call(t, r);
    if ("object" != typeof i) return i;
    throw new TypeError("@@toPrimitive must return a primitive value.");
  }
  return ("string" === r ? String : Number)(t);
}
function _toPropertyKey(t) {
  var i = _toPrimitive(t, "string");
  return "symbol" == typeof i ? i : i + "";
}
function _unsupportedIterableToArray(r, a) {
  if (r) {
    if ("string" == typeof r) return _arrayLikeToArray(r, a);
    var t = {}.toString.call(r).slice(8, -1);
    return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
  }
}

var three = window.THREE ? window.THREE // Prefer consumption from global THREE, if exists
: {
  WebGLRenderer: WebGLRenderer,
  Scene: Scene,
  PerspectiveCamera: PerspectiveCamera,
  Raycaster: Raycaster,
  SRGBColorSpace: SRGBColorSpace,
  TextureLoader: TextureLoader,
  Vector2: Vector2,
  Vector3: Vector3,
  Box3: Box3,
  Color: Color,
  Mesh: Mesh,
  SphereGeometry: SphereGeometry,
  MeshBasicMaterial: MeshBasicMaterial,
  BackSide: BackSide,
  Clock: Clock
};
var threeRenderObjects = Kapsule({
  props: {
    width: {
      "default": window.innerWidth,
      onChange: function onChange(width, state, prevWidth) {
        isNaN(width) && (state.width = prevWidth);
      }
    },
    height: {
      "default": window.innerHeight,
      onChange: function onChange(height, state, prevHeight) {
        isNaN(height) && (state.height = prevHeight);
      }
    },
    viewOffset: {
      "default": [0, 0]
    },
    backgroundColor: {
      "default": '#000011'
    },
    backgroundImageUrl: {},
    onBackgroundImageLoaded: {},
    showNavInfo: {
      "default": true
    },
    skyRadius: {
      "default": 50000
    },
    objects: {
      "default": []
    },
    lights: {
      "default": []
    },
    enablePointerInteraction: {
      "default": true,
      onChange: function onChange(_, state) {
        // Reset hover state
        state.hoverObj = null;
        state.tooltip && state.tooltip.content(null);
      },
      triggerUpdate: false
    },
    pointerRaycasterThrottleMs: {
      "default": 50,
      triggerUpdate: false
    },
    lineHoverPrecision: {
      "default": 1,
      triggerUpdate: false
    },
    pointsHoverPrecision: {
      "default": 1,
      triggerUpdate: false
    },
    hoverOrderComparator: {
      triggerUpdate: false
    },
    // keep existing order by default
    hoverFilter: {
      "default": function _default() {
        return true;
      },
      triggerUpdate: false
    },
    // exclude objects from interaction
    tooltipContent: {
      triggerUpdate: false
    },
    hoverDuringDrag: {
      "default": false,
      triggerUpdate: false
    },
    clickAfterDrag: {
      "default": false,
      triggerUpdate: false
    },
    onHover: {
      "default": function _default() {},
      triggerUpdate: false
    },
    onClick: {
      "default": function _default() {},
      triggerUpdate: false
    },
    onRightClick: {
      triggerUpdate: false
    }
  },
  methods: {
    tick: function tick(state) {
      if (state.initialised) {
        state.controls.enabled && state.controls.update && state.controls.update(Math.min(1, state.clock.getDelta())); // timedelta is required for fly controls

        state.postProcessingComposer ? state.postProcessingComposer.render() // if using postprocessing, switch the output to it
        : state.renderer.render(state.scene, state.camera);
        state.extraRenderers.forEach(function (r) {
          return r.render(state.scene, state.camera);
        });
        var now = +new Date();
        if (state.enablePointerInteraction && now - state.lastRaycasterCheck >= state.pointerRaycasterThrottleMs) {
          state.lastRaycasterCheck = now;
          // Update tooltip and trigger onHover events
          var topObject = null;
          if (state.hoverDuringDrag || !state.isPointerDragging) {
            var intersects = this.intersectingObjects(state.pointerPos.x, state.pointerPos.y);
            state.hoverOrderComparator && intersects.sort(function (a, b) {
              return state.hoverOrderComparator(a.object, b.object);
            });
            var topIntersect = intersects.find(function (d) {
              return state.hoverFilter(d.object);
            }) || null;
            topObject = topIntersect ? topIntersect.object : null;
            state.intersection = topIntersect || null;
          }
          if (topObject !== state.hoverObj) {
            state.onHover(topObject, state.hoverObj, state.intersection);
            state.tooltip.content(topObject ? accessorFn(state.tooltipContent)(topObject, state.intersection) || null : null);
            state.hoverObj = topObject;
          }
        }
        state.tweenGroup.update(); // update camera animation tweens
      }
      return this;
    },
    getPointerPos: function getPointerPos(state) {
      var _state$pointerPos = state.pointerPos,
        x = _state$pointerPos.x,
        y = _state$pointerPos.y;
      return {
        x: x,
        y: y
      };
    },
    cameraPosition: function cameraPosition(state, position, lookAt, transitionDuration) {
      var camera = state.camera;

      // Setter
      if (position && state.initialised) {
        var finalPos = position;
        var finalLookAt = lookAt || {
          x: 0,
          y: 0,
          z: 0
        };
        if (!transitionDuration) {
          // no animation
          setCameraPos(finalPos);
          setLookAt(finalLookAt);
        } else {
          var camPos = Object.assign({}, camera.position);
          var camLookAt = getLookAt();
          state.tweenGroup.add(new Tween(camPos).to(finalPos, transitionDuration).easing(Easing.Quadratic.Out).onUpdate(setCameraPos).start());

          // Face direction in 1/3rd of time
          state.tweenGroup.add(new Tween(camLookAt).to(finalLookAt, transitionDuration / 3).easing(Easing.Quadratic.Out).onUpdate(setLookAt).start());
        }
        return this;
      }

      // Getter
      return Object.assign({}, camera.position, {
        lookAt: getLookAt()
      });

      //

      function setCameraPos(pos) {
        var x = pos.x,
          y = pos.y,
          z = pos.z;
        if (x !== undefined) camera.position.x = x;
        if (y !== undefined) camera.position.y = y;
        if (z !== undefined) camera.position.z = z;
      }
      function setLookAt(lookAt) {
        var lookAtVect = new three.Vector3(lookAt.x, lookAt.y, lookAt.z);
        if (state.controls.target) {
          state.controls.target = lookAtVect;
        } else {
          // Fly controls doesn't have target attribute
          camera.lookAt(lookAtVect); // note: lookAt may be overridden by other controls in some cases
        }
      }
      function getLookAt() {
        return Object.assign(new three.Vector3(0, 0, -1e3).applyQuaternion(camera.quaternion).add(camera.position));
      }
    },
    zoomToFit: function zoomToFit(state) {
      var transitionDuration = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
      var padding = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 10;
      for (var _len = arguments.length, bboxArgs = new Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) {
        bboxArgs[_key - 3] = arguments[_key];
      }
      return this.fitToBbox(this.getBbox.apply(this, bboxArgs), transitionDuration, padding);
    },
    fitToBbox: function fitToBbox(state, bbox) {
      var transitionDuration = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
      var padding = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 10;
      // based on https://discourse.threejs.org/t/camera-zoom-to-fit-object/936/24
      var camera = state.camera;
      if (bbox) {
        var center = new three.Vector3(0, 0, 0); // reset camera aim to center
        var maxBoxSide = Math.max.apply(Math, _toConsumableArray(Object.entries(bbox).map(function (_ref) {
          var _ref2 = _slicedToArray(_ref, 2),
            coordType = _ref2[0],
            coords = _ref2[1];
          return Math.max.apply(Math, _toConsumableArray(coords.map(function (c) {
            return Math.abs(center[coordType] - c);
          })));
        }))) * 2;

        // find distance that fits whole bbox within padded fov
        var paddedFov = (1 - padding * 2 / state.height) * camera.fov;
        var fitHeightDistance = maxBoxSide / Math.atan(paddedFov * Math.PI / 180);
        var fitWidthDistance = fitHeightDistance / camera.aspect;
        var distance = Math.max(fitHeightDistance, fitWidthDistance);
        if (distance > 0) {
          var newCameraPosition = center.clone().sub(camera.position).normalize().multiplyScalar(-distance);
          this.cameraPosition(newCameraPosition, center, transitionDuration);
        }
      }
      return this;
    },
    getBbox: function getBbox(state) {
      var objFilter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {
        return true;
      };
      var box = new three.Box3(new three.Vector3(0, 0, 0), new three.Vector3(0, 0, 0));
      var objs = state.objects.filter(objFilter);
      if (!objs.length) return null;
      objs.forEach(function (obj) {
        return box.expandByObject(obj);
      });

      // extract global x,y,z min/max
      return Object.assign.apply(Object, _toConsumableArray(['x', 'y', 'z'].map(function (c) {
        return _defineProperty({}, c, [box.min[c], box.max[c]]);
      })));
    },
    getScreenCoords: function getScreenCoords(state, x, y, z) {
      var vec = new three.Vector3(x, y, z);
      vec.project(this.camera()); // project to the camera plane
      return {
        // align relative pos to canvas dimensions
        x: (vec.x + 1) * state.width / 2,
        y: -(vec.y - 1) * state.height / 2
      };
    },
    getSceneCoords: function getSceneCoords(state, screenX, screenY) {
      var distance = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
      var relCoords = new three.Vector2(screenX / state.width * 2 - 1, -(screenY / state.height) * 2 + 1);
      var raycaster = new three.Raycaster();
      raycaster.setFromCamera(relCoords, state.camera);
      return Object.assign({}, raycaster.ray.at(distance, new three.Vector3()));
    },
    intersectingObjects: function intersectingObjects(state, x, y) {
      var relCoords = new three.Vector2(x / state.width * 2 - 1, -(y / state.height) * 2 + 1);
      var raycaster = new three.Raycaster();
      raycaster.params.Line.threshold = state.lineHoverPrecision; // set linePrecision
      raycaster.params.Points.threshold = state.pointsHoverPrecision; // set pointsPrecision
      raycaster.setFromCamera(relCoords, state.camera);
      return raycaster.intersectObjects(state.objects, true);
    },
    renderer: function renderer(state) {
      return state.renderer;
    },
    scene: function scene(state) {
      return state.scene;
    },
    camera: function camera(state) {
      return state.camera;
    },
    postProcessingComposer: function postProcessingComposer(state) {
      return state.postProcessingComposer;
    },
    controls: function controls(state) {
      return state.controls;
    },
    tbControls: function tbControls(state) {
      return state.controls;
    } // to be deprecated
  },
  stateInit: function stateInit() {
    return {
      scene: new three.Scene(),
      camera: new three.PerspectiveCamera(),
      clock: new three.Clock(),
      tweenGroup: new Group(),
      lastRaycasterCheck: 0
    };
  },
  init: function init(domNode, state) {
    var _ref4 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {},
      _ref4$controlType = _ref4.controlType,
      controlType = _ref4$controlType === void 0 ? 'trackball' : _ref4$controlType,
      _ref4$useWebGPU = _ref4.useWebGPU,
      useWebGPU = _ref4$useWebGPU === void 0 ? false : _ref4$useWebGPU,
      _ref4$rendererConfig = _ref4.rendererConfig,
      rendererConfig = _ref4$rendererConfig === void 0 ? {} : _ref4$rendererConfig,
      _ref4$extraRenderers = _ref4.extraRenderers,
      extraRenderers = _ref4$extraRenderers === void 0 ? [] : _ref4$extraRenderers,
      _ref4$waitForLoadComp = _ref4.waitForLoadComplete,
      waitForLoadComplete = _ref4$waitForLoadComp === void 0 ? true : _ref4$waitForLoadComp;
    // Wipe DOM
    domNode.innerHTML = '';

    // Add relative container
    domNode.appendChild(state.container = document.createElement('div'));
    state.container.className = 'scene-container';
    state.container.style.position = 'relative';

    // Add nav info section
    state.container.appendChild(state.navInfo = document.createElement('div'));
    state.navInfo.className = 'scene-nav-info';
    state.navInfo.textContent = {
      orbit: 'Left-click: rotate, Mouse-wheel/middle-click: zoom, Right-click: pan',
      trackball: 'Left-click: rotate, Mouse-wheel/middle-click: zoom, Right-click: pan',
      fly: 'WASD: move, R|F: up | down, Q|E: roll, up|down: pitch, left|right: yaw'
    }[controlType] || '';
    state.navInfo.style.display = state.showNavInfo ? null : 'none';

    // Setup tooltip
    state.tooltip = new Tooltip(state.container);

    // Capture pointer coords on move or touchstart
    state.pointerPos = new three.Vector2();
    state.pointerPos.x = -2; // Initialize off canvas
    state.pointerPos.y = -2;
    ['pointermove', 'pointerdown'].forEach(function (evType) {
      return state.container.addEventListener(evType, function (ev) {
        // track click state
        evType === 'pointerdown' && (state.isPointerPressed = true);

        // detect point drag
        !state.isPointerDragging && ev.type === 'pointermove' && (ev.pressure > 0 || state.isPointerPressed) // ev.pressure always 0 on Safari, so we used the isPointerPressed tracker
        && (ev.pointerType !== 'touch' || ev.movementX === undefined || [ev.movementX, ev.movementY].some(function (m) {
          return Math.abs(m) > 1;
        })) // relax drag trigger sensitivity on touch events
        && (state.isPointerDragging = true);
        if (state.enablePointerInteraction) {
          // update the pointer pos
          var offset = getOffset(state.container);
          state.pointerPos.x = ev.pageX - offset.left;
          state.pointerPos.y = ev.pageY - offset.top;
        }
        function getOffset(el) {
          var rect = el.getBoundingClientRect(),
            scrollLeft = window.pageXOffset || document.documentElement.scrollLeft,
            scrollTop = window.pageYOffset || document.documentElement.scrollTop;
          return {
            top: rect.top + scrollTop,
            left: rect.left + scrollLeft
          };
        }
      }, {
        passive: true
      });
    });

    // Handle click events on objs
    state.container.addEventListener('pointerup', function (ev) {
      state.isPointerPressed = false;
      if (state.isPointerDragging) {
        state.isPointerDragging = false;
        if (!state.clickAfterDrag) return; // don't trigger onClick after pointer drag (camera motion via controls)
      }
      requestAnimationFrame(function () {
        // trigger click events asynchronously, to allow hoverObj to be set (on frame)
        if (ev.button === 0) {
          // left-click
          state.onClick(state.hoverObj || null, ev, state.intersection); // trigger background clicks with null
        }
        if (ev.button === 2 && state.onRightClick) {
          // right-click
          state.onRightClick(state.hoverObj || null, ev, state.intersection);
        }
      });
    }, {
      passive: true,
      capture: true
    }); // use capture phase to prevent propagation blocking from controls (specifically for fly)

    state.container.addEventListener('contextmenu', function (ev) {
      if (state.onRightClick) ev.preventDefault(); // prevent default contextmenu behavior and allow pointerup to fire instead
    });

    // Setup renderer, camera and controls
    state.renderer = new (useWebGPU ? WebGPURenderer : three.WebGLRenderer)(Object.assign({
      antialias: true,
      alpha: true
    }, rendererConfig));
    state.renderer.setPixelRatio(Math.min(2, window.devicePixelRatio)); // clamp device pixel ratio
    state.container.appendChild(state.renderer.domElement);

    // Setup extra renderers
    state.extraRenderers = extraRenderers;
    state.extraRenderers.forEach(function (r) {
      // overlay them on top of main renderer
      r.domElement.style.position = 'absolute';
      r.domElement.style.top = '0px';
      r.domElement.style.pointerEvents = 'none';
      state.container.appendChild(r.domElement);
    });

    // configure post-processing composer
    state.postProcessingComposer = new EffectComposer(state.renderer);
    state.postProcessingComposer.addPass(new RenderPass(state.scene, state.camera)); // render scene as first pass

    // configure controls
    state.controls = new {
      trackball: TrackballControls,
      orbit: OrbitControls,
      fly: FlyControls
    }[controlType](state.camera, state.renderer.domElement);
    if (controlType === 'fly') {
      state.controls.movementSpeed = 300;
      state.controls.rollSpeed = Math.PI / 6;
      state.controls.dragToLook = true;
    }
    if (controlType === 'trackball' || controlType === 'orbit') {
      state.controls.minDistance = 0.1;
      state.controls.maxDistance = state.skyRadius;
      state.controls.addEventListener('start', function () {
        state.controlsEngaged = true;
      });
      state.controls.addEventListener('change', function () {
        if (state.controlsEngaged) {
          state.controlsDragging = true;
        }
      });
      state.controls.addEventListener('end', function () {
        state.controlsEngaged = false;
        state.controlsDragging = false;
      });
    }
    [state.renderer, state.postProcessingComposer].concat(_toConsumableArray(state.extraRenderers)).forEach(function (r) {
      return r.setSize(state.width, state.height);
    });
    state.camera.aspect = state.width / state.height;
    state.camera.updateProjectionMatrix();
    state.camera.position.z = 1000;

    // add sky
    state.scene.add(state.skysphere = new three.Mesh());
    state.skysphere.visible = false;
    state.loadComplete = state.scene.visible = !waitForLoadComplete;
    window.scene = state.scene;
  },
  update: function update(state, changedProps) {
    // resize canvas
    if (state.width && state.height && (changedProps.hasOwnProperty('width') || changedProps.hasOwnProperty('height'))) {
      var _state$camera;
      var w = state.width;
      var h = state.height;
      state.container.style.width = "".concat(w, "px");
      state.container.style.height = "".concat(h, "px");
      [state.renderer, state.postProcessingComposer].concat(_toConsumableArray(state.extraRenderers)).forEach(function (r) {
        return r.setSize(w, h);
      });
      state.camera.aspect = w / h;
      var o = state.viewOffset.slice(0, 2);
      o.some(function (n) {
        return n;
      }) && (_state$camera = state.camera).setViewOffset.apply(_state$camera, [w, h].concat(_toConsumableArray(o), [w, h]));
      state.camera.updateProjectionMatrix();
    }
    if (changedProps.hasOwnProperty('viewOffset')) {
      var _state$camera2;
      var _w = state.width;
      var _h = state.height;
      var _o = state.viewOffset.slice(0, 2);
      _o.some(function (n) {
        return n;
      }) ? (_state$camera2 = state.camera).setViewOffset.apply(_state$camera2, [_w, _h].concat(_toConsumableArray(_o), [_w, _h])) : state.camera.clearViewOffset();
    }
    if (changedProps.hasOwnProperty('skyRadius') && state.skyRadius) {
      state.controls.hasOwnProperty('maxDistance') && changedProps.skyRadius && (state.controls.maxDistance = Math.min(state.controls.maxDistance, state.skyRadius));
      state.camera.far = state.skyRadius * 2.5;
      state.camera.updateProjectionMatrix();
      state.skysphere.geometry = new three.SphereGeometry(state.skyRadius);
    }
    if (changedProps.hasOwnProperty('backgroundColor')) {
      var alpha = parseToRgb(state.backgroundColor).alpha;
      if (alpha === undefined) alpha = 1;
      state.renderer.setClearColor(new three.Color(opacify(1, state.backgroundColor)), alpha);
    }
    if (changedProps.hasOwnProperty('backgroundImageUrl')) {
      if (!state.backgroundImageUrl) {
        state.skysphere.visible = false;
        state.skysphere.material.map = null;
        !state.loadComplete && finishLoad();
      } else {
        new three.TextureLoader().load(state.backgroundImageUrl, function (texture) {
          texture.colorSpace = three.SRGBColorSpace;
          state.skysphere.material = new three.MeshBasicMaterial({
            map: texture,
            side: three.BackSide
          });
          state.skysphere.visible = true;

          // triggered when background image finishes loading (asynchronously to allow 1 frame to load texture)
          state.onBackgroundImageLoaded && setTimeout(state.onBackgroundImageLoaded);
          !state.loadComplete && finishLoad();
        });
      }
    }
    changedProps.hasOwnProperty('showNavInfo') && (state.navInfo.style.display = state.showNavInfo ? null : 'none');
    if (changedProps.hasOwnProperty('lights')) {
      (changedProps.lights || []).forEach(function (light) {
        return state.scene.remove(light);
      }); // Clear the place
      state.lights.forEach(function (light) {
        return state.scene.add(light);
      }); // Add to scene
    }
    if (changedProps.hasOwnProperty('objects')) {
      (changedProps.objects || []).forEach(function (obj) {
        return state.scene.remove(obj);
      }); // Clear the place
      state.objects.forEach(function (obj) {
        return state.scene.add(obj);
      }); // Add to scene
    }

    //

    function finishLoad() {
      state.loadComplete = state.scene.visible = true;
    }
  }
});

export { threeRenderObjects as default };
