chmodseven

StreetViewPlayerCamera.cs

Jul 12th, 2020 (edited)
304
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 24.39 KB | None | 0 0
  1. using System;
  2. using System.Collections;
  3. using CityGen3D;
  4. using UnityEngine;
  5. using UnityEngine.Networking;
  6.  
  7. #region MIT LICENSE
  8.  
  9. /*
  10.     License: The MIT License (MIT)
  11.     Copyright (C) 2020 Shannon Rowe
  12.    
  13.     Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
  14.     documentation files (the "Software"), to deal in the Software without restriction, including without limitation
  15.     the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
  16.     and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  17.  
  18.     The above copyright notice and this permission notice shall be included in all copies or substantial portions of
  19.     the Software.
  20.    
  21.     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  22.     TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  23.     THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  24.     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  25.     DEALINGS IN THE SOFTWARE.
  26. */
  27.  
  28. #endregion
  29.  
  30. #region INSTRUCTIONS
  31.  
  32. /*
  33.     Setup:
  34.    
  35.     1. Attach this script to an empty game object called Player (or similar) to be the player controller.
  36.     2. Drag or create the main camera as a child under Player, and attach it to "mainCamera" field in the inspector.
  37.        Set its Clear Flags to Solid Color and its Culling Mask to Everything, and reset all transform values to zero.
  38.     3. Create a second camera (remove the redundant audio listener) as a child, also under Player,
  39.        and attach it to "renderCamera" in the inspector for this script.
  40.        Set its Clear Flags to Skybox and its Culling Mask to Nothing, and again reset transform values to zero.
  41.     4. Attach the landscape GameObject created by CityGen3D to the "landscape" field in the inspector.
  42.     5. Add your Google StreetView API key to the relevant field in the inspector.
  43.        To get one, create a free Google Cloud Platform developer account and enable the Street View Static API.
  44.        On the free pricing tier, this should get you around 25,000 free tile fetches a month, however it is
  45.        advisable to regularly keep an eye on your usage using the developer console to make sure you
  46.        don't inadvertently tip over into the paid threshold. Each panorama used here consists of 6 tiles which
  47.        are downloaded and then folded up into a cubemap.
  48.  
  49.     Controller Usage:
  50.    
  51.     This script is a pretty standard flying camera controller that uses WASD/arrows to fly, shift to sprint,
  52.     E/PgUp to ascend or Q/PgDn to descend, and the mouse to turn the camera. Speeds are adjustable in the inspector.
  53.     There's a little red pulsing ball that serves as a crosshairs, but you can turn that off if you prefer.
  54.     If you left-click, it will teleport to where the crosshairs are pointing (and also triggers loading the
  55.     StreetView image - more on that below). If you right-click, it will return you to wherever the Player transform
  56.     was when you first entered play mode, which is useful for setting a starting point to return to.
  57.     The player starts in free movement mode, meaning no gravity and the ability to clip through colliders; if you
  58.     press P it will toggle physics on or off (creating the physics components if you don't have any on the player).    
  59.     Lastly, Escape will release the cursor so you can get back to the editor, and left-click will re-enter fly mode.
  60.    
  61.     StreetView Display usage:
  62.    
  63.     To open the window, look for it under the CityGen3D menu for "StreetView Display".
  64.     It's a basic editor window that starts out floating but can be docked or resized as per normal window behaviour.
  65.     The resizing defaults to keeping the image square to match the render aspect ratio, but you can change the
  66.     DefaultKeepImageSquare constant in StreetViewWindow if you prefer it to be unconstrained (albeit distorted).
  67.    
  68.     The magic happens when you enter play mode and fly around and then teleport somewhere with left-click.
  69.     The Google API will be polled and (assuming you have a StreetView Display window open) the panorama will
  70.     appear shortly afterwards in the window, and should rotate in sync with your camera movements.  Note that the
  71.     view is set to attempt to match the map's latitude/longitude position to the equivalent Unity position
  72.     corresponding to roughly where you clicked to teleport, so once you start moving around again, the illusion will
  73.     quickly be lost.  But while you stay in place, you can rotate around and enjoy the synchronised view.
  74.    
  75.     Technical notes:
  76.    
  77.     This tool uses the Unity scene skybox to stash the panorama and allow it to be panned around.  This will
  78.     therefore replace any skybox you normally have in your scene.  There may be some adjustments that can be made
  79.     to avoid this, but I haven't explored any of that as this tool isn't intended for use outside the editor anyway.
  80.     You will also notice seams in many of the panoramas - this occurs due to the fairly crude method used for
  81.     stitching the tiles together, which would take a better mathematician than myself to resolve.
  82.     Similarly, I haven't bothered with any of the zoom levels for the tiles that are available in the API, as the
  83.     results are good enough and Google have resolution limits for the free tier anyway, so while if you resize
  84.     the window the size of the image will increase, the level of detail will not.
  85.     Finally, when you left-click somewhere to teleport, you might notice that the player is not always placed
  86.     exactly at the point you clicked.  All Google StreetView panoramas cover a bounding box, so when you click,
  87.     the API call will first get the nearest panorama to where you clicked, and then take the location stored in
  88.     the panorama metadata to derive a revised Unity position that approximates the metadata location, and teleports
  89.     the player controller to that point rather than the point you clicked.  In practice, these are usually so
  90.     close together as to be unnoticeable, however in some sparsely covered regions, you might see larger jumps.
  91.     The reason for this repositioning is so that your view will more closely be in sync between Unity and StreetView.    
  92. */
  93.  
  94. #endregion
  95.  
  96. public class StreetViewPlayerCamera : MonoBehaviour
  97. {
  98.     // This is the approximate height of a Google StreetView car camera (8.2 feet)
  99.     private const float CameraHeight = 2.49936f;
  100.     private const PrimitiveType MarkerPrimitive = PrimitiveType.Sphere;
  101.     private const string MarkerObjectName = "Marker";
  102.     private const float MarkerScaleSpeed = 10f;
  103.     private const float MarkerScaleAmount = 0.015f;
  104.     private const float MarkerRotationSpeed = 100f;
  105.     private const float MarkerPositionOffset = 0.25f;
  106.     private const int NumberSides = 6;
  107.     private const int TileWidth = 400;
  108.     private const int TileHeight = 400;
  109.     private const int Fov = 90;
  110.     private const float WaitTimeout = 10f;
  111.     private const string DoublePrecision = "F6";
  112.     private const float DefaultCapsuleColliderHeight = 3f;
  113.     private const float DefaultCapsuleColliderRadius = 2f;
  114.     private const CollisionDetectionMode DefaultCollisionDetectionMode = CollisionDetectionMode.Discrete;
  115.  
  116.     [SerializeField] private string streetViewApiKey;
  117.     [SerializeField] private Camera mainCamera;
  118.     [SerializeField] private Camera renderCamera;
  119.     [SerializeField] private Landscape landscape;
  120.     [SerializeField] private float walkSpeed = 100f;
  121.     [SerializeField] private float runSpeed = 250f;
  122.     [SerializeField] private float rotationSpeed = 4f;
  123.     [SerializeField] private bool invertYAxis;
  124.     [SerializeField] private bool showMarker = true;
  125.     [SerializeField] private Color markerColor = Color.red;
  126.  
  127.     public delegate void ImageLoadedDelegate (LoadChain chain);
  128.  
  129.     public static ImageLoadedDelegate onImageLoaded;
  130.  
  131.     private Transform _trans;
  132.     private Transform _mainCameraTrans;
  133.     private Transform _renderCameraTrans;
  134.     private Transform _markerTrans;
  135.     private Collider _collider;
  136.     private Rigidbody _rigidbody;
  137.     private Vector2 _currentRotation;
  138.     private Vector2 _restartRotation;
  139.     private Vector3 _restartPosition;
  140.     private GameObject _marker;
  141.     private readonly int _frontTex = Shader.PropertyToID ("_FrontTex");
  142.     private readonly int _leftTex = Shader.PropertyToID ("_LeftTex");
  143.     private readonly int _backTex = Shader.PropertyToID ("_BackTex");
  144.     private readonly int _rightTex = Shader.PropertyToID ("_RightTex");
  145.     private readonly int _upTex = Shader.PropertyToID ("_UpTex");
  146.     private readonly int _downTex = Shader.PropertyToID ("_DownTex");
  147.     private bool _isWorkingOnMetadata;
  148.     private bool _isWorkingOnPanorama;
  149.     private int _fetchesDone;
  150.     private float _timeoutTimer;
  151.     private LoadChain _loadChain;
  152.     private Action _metadataCallbackAction;
  153.     private Action _imageCallbackAction;
  154.     private readonly object _lockObject = new object ();
  155.  
  156.     private void Awake ()
  157.     {
  158.         _trans = transform;
  159.         _mainCameraTrans = mainCamera.transform;
  160.         _renderCameraTrans = renderCamera.transform;
  161.         _collider = GetComponent<Collider> ();
  162.         if (_collider == null)
  163.         {
  164.             _collider = gameObject.AddComponent<CapsuleCollider> ();
  165.             ((CapsuleCollider) _collider).height = DefaultCapsuleColliderHeight;
  166.             ((CapsuleCollider) _collider).radius = DefaultCapsuleColliderRadius;
  167.         }
  168.  
  169.         _rigidbody = GetComponent<Rigidbody> ();
  170.         if (_rigidbody == null)
  171.         {
  172.             _rigidbody = gameObject.AddComponent<Rigidbody> ();
  173.             _rigidbody.freezeRotation = true;
  174.             _rigidbody.collisionDetectionMode = DefaultCollisionDetectionMode;
  175.         }
  176.  
  177.         _collider.enabled = false;
  178.         _rigidbody.isKinematic = true;
  179.  
  180.         _currentRotation = _mainCameraTrans.eulerAngles / rotationSpeed;
  181.         _restartRotation = _currentRotation;
  182.         _restartPosition = _trans.position;
  183.         Cursor.lockState = CursorLockMode.Locked;
  184.  
  185.         if (showMarker)
  186.         {
  187.             _marker = GameObject.CreatePrimitive (MarkerPrimitive);
  188.             _marker.name = MarkerObjectName;
  189.             _marker.GetComponent<Renderer> ().sharedMaterial.color = markerColor;
  190.             _marker.GetComponent<Collider> ().enabled = false;
  191.             _markerTrans = _marker.transform;
  192.         }
  193.  
  194.         renderCamera.targetTexture = new RenderTexture (TileWidth, TileHeight, 24);
  195.     }
  196.  
  197.     private void Update ()
  198.     {
  199.         if (Cursor.lockState == CursorLockMode.None)
  200.         {
  201.             if (Input.GetButtonDown ("Fire1"))
  202.             {
  203.                 Cursor.lockState = CursorLockMode.Locked;
  204.             }
  205.  
  206.             return;
  207.         }
  208.  
  209.         if (Input.GetKeyDown (KeyCode.Escape))
  210.         {
  211.             Cursor.lockState = CursorLockMode.None;
  212.             return;
  213.         }
  214.  
  215.         if (Input.GetButtonDown ("Fire2"))
  216.         {
  217.             _currentRotation = _restartRotation;
  218.             _mainCameraTrans.eulerAngles = _currentRotation * rotationSpeed;
  219.             _renderCameraTrans.eulerAngles = _currentRotation * rotationSpeed;
  220.             _trans.position = _restartPosition;
  221.             return;
  222.         }
  223.  
  224.         Ray ray = mainCamera.ScreenPointToRay (Input.mousePosition);
  225.         if (!Physics.Raycast (ray.origin, _mainCameraTrans.forward * mainCamera.farClipPlane, out RaycastHit hit))
  226.         {
  227.             if (showMarker)
  228.             {
  229.                 _marker.SetActive (false);
  230.             }
  231.         }
  232.         else
  233.         {
  234.             if (showMarker)
  235.             {
  236.                 _marker.SetActive (true);
  237.                 float currentDistance = Vector3.Distance (_trans.position, hit.point);
  238.                 float adjustScale = Mathf.Sin (Time.time * MarkerScaleSpeed) * MarkerScaleAmount;
  239.                 float finalScale = currentDistance * adjustScale;
  240.                 _markerTrans.localScale = new Vector3 (finalScale, finalScale, finalScale);
  241.                 _markerTrans.position =
  242.                     new Vector3 (hit.point.x, hit.point.y, hit.point.z) -
  243.                     _mainCameraTrans.forward * (Mathf.Abs (finalScale) * MarkerPositionOffset);
  244.                 _markerTrans.Rotate (Vector3.up, Time.deltaTime * MarkerRotationSpeed);
  245.             }
  246.  
  247.             if (Input.GetButtonDown ("Fire1"))
  248.             {
  249.                 // Jutting out perpendicularly a little bit by the normal helps avoid clipping
  250.                 _trans.position = hit.point + hit.normal * CameraHeight;
  251.                 _mainCameraTrans.LookAt (hit.point);
  252.                 _renderCameraTrans.LookAt (hit.point);
  253.                 TeleportToPosition (_trans.position);
  254.             }
  255.         }
  256.  
  257.         if (Input.GetKeyDown (KeyCode.P))
  258.         {
  259.             _collider.enabled = !_collider.enabled;
  260.             _rigidbody.isKinematic = !_rigidbody.isKinematic;
  261.         }
  262.  
  263.         bool isPressingPageUpKey = Input.GetKey (KeyCode.PageUp) || Input.GetKey (KeyCode.E);
  264.         bool isPressingPageDownKey = Input.GetKey (KeyCode.PageDown) || Input.GetKey (KeyCode.Q);
  265.         bool isPressingRunKey = Input.GetKey (KeyCode.LeftShift) || Input.GetKey (KeyCode.RightShift);
  266.  
  267.         float movementSpeed = Time.deltaTime * (isPressingRunKey ? runSpeed : walkSpeed);
  268.         float x = Input.GetAxisRaw ("Horizontal") * movementSpeed;
  269.         float z = Input.GetAxisRaw ("Vertical") * movementSpeed;
  270.         float y = isPressingPageUpKey ? movementSpeed : isPressingPageDownKey ? -movementSpeed : 0f;
  271.  
  272.         // Need to temporarily set rotation to the same as camera, so .forward is correct
  273.         Quaternion tempRot = _trans.rotation;
  274.         _trans.rotation = _mainCameraTrans.rotation;
  275.         _trans.Translate (x, y, z, Space.Self);
  276.         _trans.Translate (0f, y, 0f, Space.World);
  277.         // ReSharper disable once Unity.InefficientPropertyAccess
  278.         _trans.rotation = tempRot;
  279.  
  280.         _currentRotation.y += Input.GetAxis ("Mouse X");
  281.         _currentRotation.x -= Input.GetAxis ("Mouse Y") * (invertYAxis ? -1f : 1f);
  282.         _mainCameraTrans.eulerAngles = _currentRotation * rotationSpeed;
  283.         _renderCameraTrans.eulerAngles = _currentRotation * rotationSpeed;
  284.     }
  285.  
  286.     private void TeleportToPosition (Vector3 position)
  287.     {
  288.         if (_isWorkingOnMetadata || _isWorkingOnPanorama)
  289.         {
  290.             Debug.LogError ("Already working on another panorama.");
  291.             return;
  292.         }
  293.  
  294.         _loadChain = new LoadChain {location = landscape.origin.GetLocation (position.x, position.z)};
  295.  
  296.         lock (_lockObject)
  297.         {
  298.             _isWorkingOnMetadata = true;
  299.             _metadataCallbackAction = CallbackFetchedMetadata;
  300.  
  301.             string url = "https://maps.googleapis.com/maps/api/streetview/metadata?" +
  302.                          "key=" + streetViewApiKey +
  303.                          "&size=" + TileWidth + "x" + TileHeight +
  304.                          "&location=" + _loadChain.location.latitude.ToString (DoublePrecision) + "," +
  305.                          _loadChain.location.longitude.ToString (DoublePrecision) +
  306.                          "&heading=" + 0d +
  307.                          "&pitch=" + 0d +
  308.                          "&fov=" + Fov +
  309.                          "&sensor=false";
  310.  
  311.             StartCoroutine (FetchJsonClass<PanoObject> (url, CallbackFetchedMetadataJson));
  312.         }
  313.     }
  314.  
  315.     private void CallbackFetchedMetadataJson (bool success, PanoObject panorama, string message)
  316.     {
  317.         if (!success)
  318.         {
  319.             _isWorkingOnMetadata = false;
  320.             _loadChain.success = false;
  321.             _loadChain.panorama = panorama;
  322.             _loadChain.message = message;
  323.             _metadataCallbackAction ();
  324.             return;
  325.         }
  326.  
  327.         if (panorama.status != "OK")
  328.         {
  329.             _isWorkingOnMetadata = false;
  330.             _loadChain.success = false;
  331.             _loadChain.panorama = panorama;
  332.             _loadChain.message = panorama.status;
  333.             _metadataCallbackAction ();
  334.             return;
  335.         }
  336.  
  337.         _isWorkingOnMetadata = false;
  338.         _loadChain.panorama = panorama;
  339.         _metadataCallbackAction ();
  340.     }
  341.  
  342.     private void CallbackFetchedMetadata ()
  343.     {
  344.         if (!_loadChain.success)
  345.         {
  346.             onImageLoaded?.Invoke (_loadChain);
  347.             return;
  348.         }
  349.  
  350.         lock (_lockObject)
  351.         {
  352.             _isWorkingOnPanorama = true;
  353.             Shader skyboxShader = Shader.Find ("Skybox/6 Sided");
  354.             _loadChain.material = new Material (skyboxShader);
  355.             _imageCallbackAction = CallbackFetchedStreetView;
  356.             _timeoutTimer = Time.time + WaitTimeout;
  357.             _fetchesDone = 0;
  358.  
  359.             RequestPanoramaImage (_loadChain.location, 0d, 0d, CallbackPanoramaImageFront);
  360.             RequestPanoramaImage (_loadChain.location, 90d, 0d, CallbackPanoramaImageLeft);
  361.             RequestPanoramaImage (_loadChain.location, 180d, 0d, CallbackPanoramaImageBack);
  362.             RequestPanoramaImage (_loadChain.location, 270d, 0d, CallbackPanoramaImageRight);
  363.             RequestPanoramaImage (_loadChain.location, 0d, 90d, CallbackPanoramaImageUp);
  364.             RequestPanoramaImage (_loadChain.location, 0d, -90d, CallbackPanoramaImageDown);
  365.         }
  366.     }
  367.  
  368.     private void RequestPanoramaImage (
  369.         GeoCoord location, double heading, double pitch, Action<bool, Texture2D, string> callback)
  370.     {
  371.         string url = "https://maps.googleapis.com/maps/api/streetview?" +
  372.                      "key=" + streetViewApiKey +
  373.                      "&size=" + TileWidth + "x" + TileHeight +
  374.                      "&location=" + location.latitude + "," + location.longitude +
  375.                      "&heading=" + heading +
  376.                      "&pitch=" + pitch +
  377.                      "&fov=" + Fov +
  378.                      "&sensor=false";
  379.  
  380.         StartCoroutine (FetchImage (url, callback));
  381.     }
  382.  
  383.     private void CallbackPanoramaImage (int side, bool success, Texture result, string message)
  384.     {
  385.         lock (_lockObject)
  386.         {
  387.             if (!success)
  388.             {
  389.                 _isWorkingOnPanorama = false;
  390.                 _loadChain.success = false;
  391.                 _loadChain.message = message;
  392.                 return;
  393.             }
  394.  
  395.             result.wrapMode = TextureWrapMode.Clamp;
  396.             _loadChain.material.SetTexture (side, result);
  397.             _fetchesDone++;
  398.  
  399.             if (Time.time > _timeoutTimer)
  400.             {
  401.                 _isWorkingOnPanorama = false;
  402.                 _loadChain.success = false;
  403.                 _loadChain.message = "Attempt to fetch StreetView panorama timed out";
  404.             }
  405.  
  406.             if (!_loadChain.success)
  407.             {
  408.                 _imageCallbackAction ();
  409.                 return;
  410.             }
  411.  
  412.             if (_fetchesDone < NumberSides)
  413.             {
  414.                 return;
  415.             }
  416.  
  417.             _imageCallbackAction ();
  418.             _isWorkingOnPanorama = false;
  419.         }
  420.     }
  421.  
  422.     private void CallbackPanoramaImageFront (bool success, Texture2D texture, string message)
  423.     {
  424.         CallbackPanoramaImage (_frontTex, success, texture, message);
  425.     }
  426.  
  427.     private void CallbackPanoramaImageLeft (bool success, Texture2D texture, string message)
  428.     {
  429.         CallbackPanoramaImage (_leftTex, success, texture, message);
  430.     }
  431.  
  432.     private void CallbackPanoramaImageBack (bool success, Texture2D texture, string message)
  433.     {
  434.         CallbackPanoramaImage (_backTex, success, texture, message);
  435.     }
  436.  
  437.     private void CallbackPanoramaImageRight (bool success, Texture2D texture, string message)
  438.     {
  439.         CallbackPanoramaImage (_rightTex, success, texture, message);
  440.     }
  441.  
  442.     private void CallbackPanoramaImageUp (bool success, Texture2D texture, string message)
  443.     {
  444.         CallbackPanoramaImage (_upTex, success, texture, message);
  445.     }
  446.  
  447.     private void CallbackPanoramaImageDown (bool success, Texture2D texture, string message)
  448.     {
  449.         CallbackPanoramaImage (_downTex, success, texture, message);
  450.     }
  451.  
  452.     private void CallbackFetchedStreetView ()
  453.     {
  454.         if (!_loadChain.success)
  455.         {
  456.             onImageLoaded?.Invoke (_loadChain);
  457.             return;
  458.         }
  459.  
  460.         RenderSettings.skybox = _loadChain.material;
  461.  
  462.         GeoCoord revisedLocation = new GeoCoord (_loadChain.panorama.location.lat, _loadChain.panorama.location.lng);
  463.         Vector3 revisedPosition = revisedLocation.GetMapCoord (landscape.origin).GetPosition ();
  464.         Vector3 pointAboveTerrain = revisedPosition;
  465.         pointAboveTerrain.y -= 5000;
  466.         Vector3 pointBelowTerrain = new Vector3 (pointAboveTerrain.x, pointAboveTerrain.y + 10000, pointAboveTerrain.z);
  467.         Vector3 rayDirection = pointAboveTerrain - pointBelowTerrain;
  468.         Ray ray = new Ray (pointBelowTerrain, rayDirection);
  469.         bool didHitTerrain = Physics.Raycast (ray, out RaycastHit hit, Mathf.Infinity);
  470.         if (!didHitTerrain)
  471.         {
  472.             return;
  473.         }
  474.  
  475.         revisedPosition = hit.point;
  476.         revisedPosition.y += CameraHeight;
  477.         _trans.position = revisedPosition;
  478.  
  479.         _loadChain.success = true;
  480.         _loadChain.message = revisedLocation.ToString ();
  481.         _loadChain.renderTexture = renderCamera.targetTexture;
  482.         onImageLoaded?.Invoke (_loadChain);
  483.     }
  484.  
  485.     private static IEnumerator FetchJsonClass<T> (string url, Action<bool, T, string> callback)
  486.     {
  487.         bool success = true;
  488.         string message = null;
  489.  
  490.         UnityWebRequest www = UnityWebRequest.Get (url);
  491.         yield return www.SendWebRequest ();
  492.  
  493.         if (www.isHttpError || www.isNetworkError || !string.IsNullOrEmpty (www.error))
  494.         {
  495.             success = false;
  496.             message = "Network error " + www.error + " attempting to fetch URL " + url;
  497.         }
  498.  
  499.         T result = JsonUtility.FromJson<T> (www.downloadHandler.text);
  500.         if (result == null)
  501.         {
  502.             success = false;
  503.             message = "No JSON data could be parsed from URL " + url;
  504.         }
  505.  
  506.         callback?.Invoke (success, result, message);
  507.     }
  508.  
  509.     private static IEnumerator FetchImage (string url, Action<bool, Texture2D, string> callback)
  510.     {
  511.         bool success = true;
  512.         string message = null;
  513.  
  514.         UnityWebRequest www = UnityWebRequestTexture.GetTexture (url);
  515.         yield return www.SendWebRequest ();
  516.  
  517.         if (www.isHttpError || www.isNetworkError || !string.IsNullOrEmpty (www.error))
  518.         {
  519.             success = false;
  520.             message = "Network error " + www.error + " attempting to fetch URL " + url;
  521.         }
  522.  
  523.         Texture2D result = ((DownloadHandlerTexture) www.downloadHandler).texture;
  524.         if (result == null)
  525.         {
  526.             success = false;
  527.             message = "No texture data could be parsed from URL " + url;
  528.         }
  529.  
  530.         callback?.Invoke (success, result, message);
  531.     }
  532.  
  533.     // These names are required to be in these formats by the JSON conversion and can't be changed
  534.     // ReSharper disable IdentifierTypo
  535.     // ReSharper disable StringLiteralTypo
  536.     // ReSharper disable NotAccessedField.Global
  537.     // ReSharper disable InconsistentNaming
  538.     [Serializable]
  539.     public class PanoObject
  540.     {
  541.         public string copyright;
  542.         public string date;
  543.         public Location location;
  544.         public string pano_id;
  545.         public string status;
  546.     }
  547.     // ReSharper restore InconsistentNaming
  548.     // ReSharper restore NotAccessedField.Global
  549.     // ReSharper restore StringLiteralTypo
  550.     // ReSharper restore IdentifierTypo
  551.  
  552.     // These names are required to be in these formats by the JSON conversion and can't be changed
  553.     [Serializable]
  554.     public class Location
  555.     {
  556.         public double lat;
  557.         public double lng;
  558.  
  559.         public override string ToString ()
  560.         {
  561.             return lat + "," + lng;
  562.         }
  563.     }
  564.  
  565.     [Serializable]
  566.     public class LoadChain
  567.     {
  568.         public bool success;
  569.         public GeoCoord location;
  570.         public PanoObject panorama;
  571.         public RenderTexture renderTexture;
  572.         public Material material;
  573.         public string message;
  574.  
  575.         public LoadChain ()
  576.         {
  577.             success = true;
  578.         }
  579.     }
  580. }
Add Comment
Please, Sign In to add comment