bool GetCityUnderMouse(int countryIndex, Vector3 localPoint, out int cityIndex) { cityIndex = -1; if (visibleCities == null) { return(false); } float hitPrecision = CITY_HIT_PRECISION * _cityIconSize * 5.0f; float hitPrecisionSqr = hitPrecision * hitPrecision; for (int c = 0; c < visibleCities.Length; c++) { City city = visibleCities [c]; if (city.countryIndex == countryIndex && city.isShown) { // if ((city.unitySphereLocation - localPoint).sqrMagnitude < hitPrecisionSqr) { if (FastVector.SqrDistance(ref city.unitySphereLocation, ref localPoint) < hitPrecisionSqr) { cityIndex = GetCityIndex(city, false); return(true); } } } return(false); }
/// <summary> /// Returns the nearest city to a point specified in sphere coordinates. /// </summary> /// <returns>The city near point.</returns> /// <param name="localPoint">Local point in sphere coordinates.</param> /// <param name="cityIndexToExclude">Optional city index which will be excluded. Useful for getting the nearest city to a given one.</param> public int GetCityIndex(Vector2 latlon, int cityIndexToExclude = -1) { if (visibleCities == null) { return(-1); } int nearest = -1; float minDist = float.MaxValue; int cityCount = cities.Count; for (int c = 0; c < cityCount; c++) { if (c == cityIndexToExclude) { continue; } City city = cities [c]; if (!city.isShown) { continue; } Vector2 cityLoc = city.latlon; float dist = FastVector.SqrDistance(ref cityLoc, ref latlon); if (dist < minDist) { minDist = dist; nearest = c; } } return(nearest); }
/// <summary> /// Returns any city near the point specified in sphere coordinates. Must be closer than CITY_HIT_PRECISION constant, usually when pointer clicks over a city icon. /// Optimized method for mouse hitting. /// </summary> /// <returns>The city near point.</returns> /// <param name="localPoint">Local point in sphere coordinates.</param> public int GetCityNearPointFast(Vector3 localPoint) { if (visibleCities == null) { return(-1); } float hitPrecision = CITY_HIT_PRECISION * _cityIconSize * 5.0f; float hitPrecisionSqr = hitPrecision * hitPrecision; Vector2 latlon = Conversion.GetLatLonFromSpherePoint(localPoint); for (int c = 0; c < countries.Length; c++) { Country country = countries [c]; if (country.regionsRect2D.Contains(latlon)) { for (int t = 0; t < visibleCities.Length; t++) { City city = visibleCities [t]; if (city.countryIndex != c) { continue; } float dist = FastVector.SqrDistance(ref city.unitySphereLocation, ref localPoint); // (city.unitySphereLocation - localPoint).sqrMagnitude; if (dist < hitPrecisionSqr) { return(GetCityIndex(city, false)); } } } } return(-1); }
bool GetProvinceUnderMouse(int countryIndex, Vector3 spherePoint, out int provinceIndex, out int regionIndex) { float startingDistance = 0; provinceIndex = regionIndex = -1; Country country = countries [countryIndex]; if (country.provinces == null && _provinces == null) { ReadProvincesPackedString(); } if (country.provinces == null) { return(false); } int provincesCount = country.provinces.Length; if (provincesCount == 0) { return(false); } Vector2 mousePos; Conversion.GetLatLonFromSpherePoint(spherePoint, out mousePos); float maxArea = float.MaxValue; // Is this the same province currently selected? if (_provinceHighlightedIndex >= 0 && _provinceRegionHighlightedIndex >= 0 && _provinceHighlighted.countryIndex == countryIndex && !_provinceHighlighted.hidden) { if (_provinceRegionHighlighted.Contains(mousePos)) { maxArea = _provinceHighlighted.mainRegionArea; // cannot return yet - need to check if any other province (smaller than this) could be highlighted } } // Check other provinces for (int tries = 0; tries < 75; tries++) { float minDist = float.MaxValue; for (int p = 0; p < provincesCount; p++) { Province province = country.provinces [p]; if (province.regions == null || province.mainRegionArea > maxArea || province.hidden) { continue; } for (int pr = 0; pr < province.regions.Count; pr++) { Vector3 regionCenter = province.regions [pr].sphereCenter; float dist = FastVector.SqrDistance(ref regionCenter, ref spherePoint); // (regionCenter - spherePoint).sqrMagnitude; if (dist > startingDistance && dist < minDist) { minDist = dist; provinceIndex = GetProvinceIndex(province); regionIndex = pr; } } } // Check if this region is visible and the mouse is inside if (provinceIndex >= 0) { Region region = provinces [provinceIndex].regions [regionIndex]; if (region.Contains(mousePos)) { return(true); } } // Continue searching but farther centers startingDistance = minDist; } return(false); }
/// <summary> /// Used internally by the Map Editor. It will recalculate de boundaries and optimize frontiers based on new data of provinces array /// </summary> public void RefreshProvinceGeometry(int provinceIndex) { if (provinceIndex < 0 || provinceIndex >= provinces.Length) { return; } lastProvinceLookupCount = -1; float maxVol = 0; Province province = provinces [provinceIndex]; if (province.regions == null) { ReadProvincePackedString(province); } int regionCount = province.regions.Count; Vector2 minProvince = Misc.Vector2one * 1000; Vector2 maxProvince = -minProvince; for (int r = 0; r < regionCount; r++) { Region provinceRegion = province.regions [r]; provinceRegion.entity = province; // just in case one country has been deleted provinceRegion.regionIndex = r; // just in case a region has been deleted int coorCount = provinceRegion.latlon.Length; Vector2 min = Misc.Vector2one * 1000; Vector2 max = -min; for (int c = 0; c < coorCount; c++) { float x = provinceRegion.latlon [c].x; float y = provinceRegion.latlon [c].y; if (x < min.x) { min.x = x; } if (x > max.x) { max.x = x; } if (y < min.y) { min.y = y; } if (y > max.y) { max.y = y; } } Vector3 normRegionCenter = (min + max) * 0.5f; provinceRegion.latlonCenter = normRegionCenter; if (min.x < minProvince.x) { minProvince.x = min.x; } if (min.y < minProvince.y) { minProvince.y = min.y; } if (max.x > maxProvince.x) { maxProvince.x = max.x; } if (max.y > maxProvince.y) { maxProvince.y = max.y; } provinceRegion.latlonRect2D = new Rect(min.x, min.y, Math.Abs(max.x - min.x), Mathf.Abs(max.y - min.y)); provinceRegion.rect2DArea = provinceRegion.latlonRect2D.width * provinceRegion.latlonRect2D.height; float vol = FastVector.SqrDistance(ref min, ref max); // (max - min).sqrMagnitude; if (vol > maxVol) { maxVol = vol; province.mainRegionIndex = r; province.latlonCenter = provinceRegion.latlonCenter; } } province.regionsRect2D = new Rect(minProvince.x, minProvince.y, Math.Abs(maxProvince.x - minProvince.x), Mathf.Abs(maxProvince.y - minProvince.y)); }
public void ReadProvincePackedString(Province province) { string[] regions = province.packedRegions.Split(new char[] { '*' }, StringSplitOptions.RemoveEmptyEntries); int regionCount = regions.Length; province.regions = new List <Region> (regionCount); float maxVol = float.MinValue; Vector2 minProvince = Misc.Vector2one * 1000; Vector2 maxProvince = -minProvince; Vector2 min = Misc.Vector2one * 1000; Vector2 max = -min; Vector2 latlonCenter = new Vector2(); for (int r = 0; r < regionCount; r++) { string[] coordinates = regions [r].Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); int coorCount = coordinates.Length; min.x = min.y = 1000; max.x = max.y = -1000; Region provinceRegion = new Region(province, province.regions.Count); Vector2[] latlon = new Vector2[coorCount]; for (int c = 0; c < coorCount; c++) { float lat, lon; GetPointFromPackedString(coordinates [c], out lat, out lon); if (lat < min.x) { min.x = lat; } if (lat > max.x) { max.x = lat; } if (lon < min.y) { min.y = lon; } if (lon > max.y) { max.y = lon; } latlon [c] = new Vector2(lat, lon); } provinceRegion.latlon = latlon; FastVector.Average(ref min, ref max, ref latlonCenter); // (min + max) * 0.5f; provinceRegion.latlonCenter = latlonCenter; province.regions.Add(provinceRegion); // Calculate province bounding rect if (min.x < minProvince.x) { minProvince.x = min.x; } if (min.y < minProvince.y) { minProvince.y = min.y; } if (max.x > maxProvince.x) { maxProvince.x = max.x; } if (max.y > maxProvince.y) { maxProvince.y = max.y; } provinceRegion.latlonRect2D = new Rect(min.x, min.y, max.x - min.x, max.y - min.y); provinceRegion.rect2DArea = provinceRegion.latlonRect2D.width * provinceRegion.latlonRect2D.height; float vol = FastVector.SqrDistance(ref min, ref max); // (max - min).sqrMagnitude; if (vol > maxVol) { maxVol = vol; province.mainRegionIndex = r; province.latlonCenter = provinceRegion.latlonCenter; } } province.regionsRect2D = new Rect(minProvince.x, minProvince.y, maxProvince.x - minProvince.x, maxProvince.y - minProvince.y); }
void CheckTiles(TileInfo parent, int currentZoomLevel, int xTile, int yTile, int zoomLevel, int subquadIndex) { // Is this tile visible? TileInfo ti; int tileCode = GetTileHashCode(xTile, yTile, zoomLevel); if (!cachedTiles.TryGetValue(tileCode, out ti)) { ti = new TileInfo(xTile, yTile, zoomLevel, subquadIndex, currentEarthTexture); ti.parent = parent; if (parent != null) { if (parent.children == null) { parent.children = new List <TileInfo> (); } parent.children.Add(ti); } for (int k = 0; k < 4; k++) { Vector2 latlon = Conversion.GetLatLonFromTile(xTile + offsets [k].x, yTile + offsets [k].y, zoomLevel); ti.latlons [k] = latlon; Vector3 spherePos = Conversion.GetSpherePointFromLatLon(latlon); ti.spherePos [k] = spherePos; } cachedTiles [tileCode] = ti; } // Check if any tile corner is visible // Phase I #if DEBUG_TILES if (ti.gameObject != null && ti.gameObject.GetComponent <TileInfoEx> ().debug) { Debug.Log("this"); } #endif bool cornersOccluded = true; Vector3 minWorldPos = Misc.Vector3Max; Vector3 maxWorldPos = Misc.Vector3Min; Vector3 tmp = Misc.Vector3zero; for (int c = 0; c < 4; c++) { Vector3 wpos = transform.TransformPoint(ti.spherePos [c]); ti.cornerWorldPos [c] = wpos; if (wpos.x < minWorldPos.x) { minWorldPos.x = wpos.x; } if (wpos.y < minWorldPos.y) { minWorldPos.y = wpos.y; } if (wpos.z < minWorldPos.z) { minWorldPos.z = wpos.z; } if (wpos.x > maxWorldPos.x) { maxWorldPos.x = wpos.x; } if (wpos.y > maxWorldPos.y) { maxWorldPos.y = wpos.y; } if (wpos.z > maxWorldPos.z) { maxWorldPos.z = wpos.z; } if (cornersOccluded) { float radiusSqr = (wpos.x - globePos.x) * (wpos.x - globePos.x) + (wpos.y - globePos.y) * (wpos.y - globePos.y) + (wpos.z - globePos.z) * (wpos.z - globePos.z); // Vector3.SqrMagnitude (wpos - globePos); // Vector3 camDir = (currentCameraPosition - wpos).normalized; FastVector.NormalizedDirection(ref wpos, ref currentCameraPosition, ref tmp); // Vector3 st = wpos + ndir * (0.01f * transform.localScale.x); Vector3 st = wpos; FastVector.Add(ref st, ref tmp, localScaleFactor); float mag = (st.x - globePos.x) * (st.x - globePos.x) + (st.y - globePos.y) * (st.y - globePos.y) + (st.z - globePos.z) * (st.z - globePos.z); if (mag > radiusSqr) { cornersOccluded = false; } } } // Bounds bounds = new Bounds ((minWorldPos + maxWorldPos) * 0.5f, maxWorldPos - minWorldPos); FastVector.Average(ref minWorldPos, ref maxWorldPos, ref tmp); Bounds bounds = new Bounds(tmp, maxWorldPos - minWorldPos); Vector3 tileMidPoint = bounds.center; // Check center of quad if (cornersOccluded) { float radiusSqr = (tileMidPoint.x - globePos.x) * (tileMidPoint.x - globePos.x) + (tileMidPoint.y - globePos.y) * (tileMidPoint.y - globePos.y) + (tileMidPoint.z - globePos.z) * (tileMidPoint.z - globePos.z); // Vector3.SqrMagnitude (tileMidPoint - globePos); // Vector3 camDir = (currentCameraPosition - tileMidPoint).normalized; FastVector.NormalizedDirection(ref tileMidPoint, ref currentCameraPosition, ref tmp); // Vector3 st = tileMidPoint + tmp * (0.01f * transform.localScale.x); Vector3 st = tileMidPoint; FastVector.Add(ref st, ref tmp, localScaleFactor); float mag = (st.x - globePos.x) * (st.x - globePos.x) + (st.y - globePos.y) * (st.y - globePos.y) + (st.z - globePos.z) * (st.z - globePos.z); if (mag > radiusSqr) { cornersOccluded = false; } } #if DEBUG_TILES if (root == null) { root = new GameObject(); root.transform.SetParent(transform); root.transform.localPosition = Vector3.zero; root.transform.localRotation = Misc.QuaternionZero; //Quaternion.Euler (0, 0, 0); } #endif bool insideViewport = false; float minX = currentCamera.pixelWidth * 2f, minY = currentCamera.pixelHeight * 2f; float maxX = -minX, maxY = -minY; if (!cornersOccluded) { // Phase II for (int c = 0; c < 4; c++) { Vector3 scrPos = currentCamera.WorldToScreenPoint(ti.cornerWorldPos [c]); insideViewport = insideViewport || (scrPos.z > 0 && scrPos.x >= 0 && scrPos.x < currentCamera.pixelWidth && scrPos.y >= 0 && scrPos.y < currentCamera.pixelHeight); if (scrPos.x < minX) { minX = scrPos.x; } if (scrPos.x > maxX) { maxX = scrPos.x; } if (scrPos.y < minY) { minY = scrPos.y; } if (scrPos.y > maxY) { maxY = scrPos.y; } } if (!insideViewport) { insideViewport = GeometryUtility.TestPlanesAABB(cameraPlanes, bounds); } } ti.insideViewport = insideViewport; ti.visible = false; if (insideViewport) { if (!ti.created) { CreateTile(ti); } if (!ti.gameObject.activeSelf) { ti.gameObject.SetActive(true); } // Manage hierarchy of tiles bool tileIsBig = false; FastVector.NormalizedDirection(ref globePos, ref tileMidPoint, ref tmp); // float dd = Vector3.Dot (currentCameraForward, (tileMidPoint - globePos).normalized); float dd = Vector3.Dot(currentCameraForward, tmp); if (dd > -0.8f || currentZoomLevel > 9) // prevents big seams on initial zooms { float aparentSize = Mathf.Max(maxX - minX, maxY - minY); tileIsBig = aparentSize > currentTileSize; } else { tileIsBig = ti.zoomLevel < currentZoomLevel; } #if DEBUG_TILES if (ti.gameObject != null) { ti.gameObject.GetComponent <TileInfoEx> ().bigTile = tileIsBig; ti.gameObject.GetComponent <TileInfoEx> ().zoomLevel = ti.zoomLevel; } #endif if ((tileIsBig || zoomLevel < TILE_MIN_ZOOM_LEVEL) && zoomLevel < _tileMaxZoomLevel) { // Load nested tiles CheckTiles(ti, currentZoomLevel, xTile * 2, yTile * 2, zoomLevel + 1, 0); CheckTiles(ti, currentZoomLevel, xTile * 2 + 1, yTile * 2, zoomLevel + 1, 1); CheckTiles(ti, currentZoomLevel, xTile * 2, yTile * 2 + 1, zoomLevel + 1, 2); CheckTiles(ti, currentZoomLevel, xTile * 2 + 1, yTile * 2 + 1, zoomLevel + 1, 3); ti.renderer.enabled = false; } else { ti.visible = true; // Show tile renderer if (!ti.renderer.enabled) { ti.renderer.enabled = true; } // If parent tile is loaded then use that as placeholder texture if (ti.zoomLevel > TILE_MIN_ZOOM_LEVEL && ti.parent.loadStatus == TILE_LOAD_STATUS.Loaded && !ti.placeholderImageSet) { ti.placeholderImageSet = true; ti.parentTextureCoords = placeHolderUV [ti.subquadIndex]; ti.SetPlaceholderImage(ti.parent.texture); } if (ti.loadStatus == TILE_LOAD_STATUS.Loaded) { if (!ti.hasAnimated) { ti.hasAnimated = true; ti.Animate(1f, AnimationEnded); } } else if (ti.loadStatus == TILE_LOAD_STATUS.Inactive) { ti.distToCamera = FastVector.SqrDistance(ref ti.cornerWorldPos [0], ref currentCameraPosition) * ti.zoomLevel; ti.loadStatus = TILE_LOAD_STATUS.InQueue; ti.queueTime = Time.time; loadQueue.Add(ti); } if (ti.children != null) { for (int k = 0; k < 4; k++) { TileInfo tiChild = ti.children [k]; HideTile(tiChild); } } } } else { HideTile(ti); } }