private void Update() { //Rate Limit it to doing a cull-calculation every 10 physics-second frames, which should be plenty. These are very heavy. counter++; if (counter > 500) { counter = 0; if (maxdistance == -1) { maxdistance = Kopernicus.RuntimeUtility.RuntimeUtility.KopernicusConfig.ScatterCullDistance; } //if 0 abort. if (maxdistance == 0) { return; } surfaceObjectQuad = GetComponentInParent <PQSMod_LandClassScatterQuad>(); kopSurfaceObjects = surfaceObjectQuad.GetComponentsInChildren <KopernicusSurfaceObject>(true); if (kopSurfaceObjects.Length == 0) { return; } int distance = 15000; if (HighLogic.LoadedSceneIsFlight && FlightGlobals.ActiveVessel) { distance = (int)Vector3.Distance(FlightGlobals.ActiveVessel.transform.position, surfaceObjectQuad.transform.position); } else { distance = 0; } //Optimization checks if ((distance > maxdistance) && (kopSurfaceObjects[0].GetComponent <MeshRenderer>().enabled == false)) { return; } if ((distance <= maxdistance) && (kopSurfaceObjects[0].GetComponent <MeshRenderer>().enabled == true)) { return; } for (int i = 0; i < kopSurfaceObjects.Length; i++) { MeshRenderer surfaceObject = kopSurfaceObjects[i].GetComponent <MeshRenderer>(); if (distance > maxdistance) { surfaceObject.enabled = false; } else { surfaceObject.enabled = true; } } } else { return; } }
private void Update() { //Rate Limit it to doing a cull-calculation every 120 frames, which should be plenty since we don't update more anyways. These are very heavy. counter++; if (counter > 120) { if (!init) { init = true; maxdistance = Kopernicus.RuntimeUtility.RuntimeUtility.KopernicusConfig.ScatterCullDistance; } surfaceObjectQuad = GetComponentInParent <PQSMod_LandClassScatterQuad>(); surfaceObjects = surfaceObjectQuad.GetComponentsInChildren <MeshRenderer>(true); counter = 0; int distance = 15000; if (HighLogic.LoadedSceneIsFlight) { distance = (int)Vector3.Distance(Camera.current.transform.position, surfaceObjectQuad.transform.position); } else { distance = 0; } for (int i = 0; i < surfaceObjects.Length; i++) { MeshRenderer surfaceObject = surfaceObjects[i]; if (distance > maxdistance) { surfaceObject.enabled = false; } else { surfaceObject.enabled = true; } } } else { return; } }
/// <summary> /// Recalculates the distance to a hit item, or -1f if nothing /// was hit by the laser. /// </summary> /// <returns></returns> private void castUpdate() { if (resetHitThisUpdate) { updateForcedResultAge = 0; } else { ++updateForcedResultAge; } float newDist = -1f; // The location of origin is different in LateUpdate than it is // in Update, so it has to be reset in both: origin = this.part.transform.TransformPoint(relLaserOrigin); pointing = this.part.transform.rotation * Vector3d.down; HitName = "<none>"; if (hasPower && Activated && origin != null && pointing != null) { // the points on the map-space corresponding to these points is different: mapOrigin = ScaledSpace.LocalToScaledSpace(origin); mapPointing = pointing; if (bestLateUpdateHit.distance >= 0) { Debug.Log(" using local raycast result."); UpdateAge = updateForcedResultAge; RaycastHit hit = bestLateUpdateHit; newDist = hit.distance; // Walk up the UnityGameObject tree trying to find an object that is // something the user will be familiar with: GameObject hitObject = hit.transform.gameObject; if (hitObject != null) { HitName = hitObject.name; // default if the checks below don't work. // Despite the name and what the Unity documentation says, // GetComponentInParent actually looks all the way up the // ancestor list, not just in Parents, so these following // checks are walking up the ancestors to find the one that // has a KSP component assigned to it: if (hitObject.layer == 15) { // Support Kopernicus scatter colliders // - no more crashing into boulders // - shoot them with lasers! (and then drive around them) PQSMod_LandClassScatterQuad scatter = hitObject.GetComponentInParent <PQSMod_LandClassScatterQuad>(); if (scatter != null) { HitName = scatter.transform.parent.name; // the name of the Scatter, eg. "Scatter boulder". } else { // Fallback to the body. CelestialBody body = hitObject.GetComponentInParent <CelestialBody>(); if (body != null) { HitName = body.name; } } } else { Part part = hitObject.GetComponentInParent <Part>(); if (part != null) { HitName = part.name; } } UpdateAge = 0; } } // If the hit is not found, or it is found but is far enough // away that it might be on the other side of the planet, seen // through the ocean (which has no collider so raycasts pass // through it), then try the more expensive pqs ray cast solver. if (newDist < 0 || newDist > 100000) { Debug.Log(" numeric solver starting:."); double pqsDist; CelestialBody pqsBody; bool success = pqsTool.RayCast(origin, pointing, out pqsBody, out pqsDist); if (pqsTool.UpdateAge == 0) { Debug.Log(" UpdateAge == 0."); if (success) { Debug.Log(" success."); // If it's a closer hit than we have already, then use it: if (pqsDist < newDist || newDist < 0) { HitName = pqsBody.name; newDist = (float)pqsDist; } } } else { Debug.Log(" UpdateAge != 0."); if (pqsTool.PrevSuccess) { Debug.Log(" prevsuccess."); // If it's a closer hit than we have already, then use it: if (pqsTool.PrevDist < newDist || newDist < 0) { Debug.Log(" prevsuccess."); HitName = pqsTool.PrevBodyName; newDist = (float)pqsTool.PrevDist; } } } UpdateAge = pqsTool.UpdateAge; } } Distance = newDist; Debug.Log("Distance = " + Distance); resetHitThisUpdate = false; }
/// <summary> /// By default, KSP merges all scatters that are on one quad into one gigantic mesh that is then rendered. /// Because Kopernicus wants to know where each scatter actually is, we have to create our own scatter objects /// </summary> private void CreateScatterMeshes(PQSMod_LandClassScatterQuad quad) { Random.InitState(quad.seed); // Redo the density calculation if (useBetterDensity) { Dictionary <String, Double> densities = quad.quad.GetComponent <DensityContainer>().densities; if (densities.ContainsKey(quad.scatter.scatterName)) { Double density = densities[quad.scatter.scatterName]; if (ignoreDensityGameSetting && PQS.Global_ScatterFactor > 0) { density /= PQS.Global_ScatterFactor; } Double scatterN = density * quad.scatter.densityFactor * (quad.quad.quadArea / quad.quad.sphereRoot.radius / 1000.0) * quad.scatter.maxScatter; scatterN += Random.Range(-0.5f, 0.5f); quad.count = Math.Min((Int32)Math.Round(scatterN), quad.scatter.maxScatter); } } for (Int32 i = 0; i < quad.count; i++) { if (useBetterDensity) { // Generate a random number between 0 and 1. If it is above the spawn chance, abort if (Random.value > spawnChance) { continue; } } Int32 num2 = -1; Int32 num3 = -1; while (num3 == num2) { Int32 num4 = Random.Range(1, PQS.cacheRes + 1); Int32 num5 = Random.Range(1, PQS.cacheRes + 1); Int32 x = num4 + Random.Range(-1, 1); Int32 z = num5 + Random.Range(-1, 1); num3 = PQS.vi(num4, num5); num2 = PQS.vi(x, z); } Vector3 scatterPos = Vector3.Lerp(quad.quad.verts[num3], quad.quad.verts[num2], Random.value); Vector3 scatterUp = quad.quad.sphereRoot.surfaceRelativeQuads ? (Vector3)(scatterPos + quad.quad.positionPlanet).normalized : scatterPos.normalized; scatterPos += scatterUp * quad.scatter.verticalOffset; Single scatterAngle = Random.Range(rotation[0], rotation[1]); Quaternion scatterRot = QuaternionD.AngleAxis(scatterAngle, scatterUp) * quad.quad.quadRotation; Single scatterScale = Random.Range(quad.scatter.minScale, quad.scatter.maxScale); // Create a new object for the scatter GameObject scatterObject = new GameObject("Scatter"); scatterObject.transform.parent = quad.obj.transform; scatterObject.transform.localPosition = scatterPos; scatterObject.transform.localRotation = scatterRot; scatterObject.transform.localScale = Vector3.one * scatterScale; scatterObject.AddComponent <KopernicusSurfaceObject>().objectName = quad.scatter.scatterName; MeshFilter filter = scatterObject.AddComponent <MeshFilter>(); filter.sharedMesh = meshes.Count > 0 ? meshes[Random.Range(0, meshes.Count)] : baseMesh; MeshRenderer renderer = scatterObject.AddComponent <MeshRenderer>(); renderer.sharedMaterial = quad.scatter.material; renderer.shadowCastingMode = quad.scatter.castShadows ? ShadowCastingMode.On : ShadowCastingMode.Off; renderer.receiveShadows = quad.scatter.recieveShadows; scatterObject.layer = GameLayers.LOCAL_SPACE; scatterObjects.Add(scatterObject); } quad.obj.name = "Kopernicus-" + quad.scatter.scatterName; }
/// <summary> /// Recalculates the distance to a hit item, or -1f if nothing /// was hit by the laser. /// </summary> /// <returns></returns> private void castUpdate() { if (resetHitThisUpdate) { updateForcedResultAge = 0; } else { ++updateForcedResultAge; } float newDist = -1f; // The location of origin is different in LateUpdate than it is // in Update, so it has to be reset in both: UpdatePointing(); HitName = "<none>"; HitLayer = "<none>"; if (hasPower && Activated && origin != null && pointing != null) { // the points on the map-space corresponding to these points is different: mapOrigin = ScaledSpace.LocalToScaledSpace(origin); mapPointing = pointing; if (bestLateUpdateHit.distance >= 0) { DebugMsg(" using local raycast result."); UpdateAge = updateForcedResultAge; RaycastHit hit = bestLateUpdateHit; newDist = hit.distance; // Walk up the UnityGameObject tree trying to find an object that is // something the user will be familiar with: GameObject hitObject = (hit.transform == null ? null : hit.transform.gameObject); if (hitObject != null) { HitLayer = LayerMask.LayerToName(hitObject.layer); // for debug reasons HitName = hitObject.name; // default if the checks below don't work. // Despite the name and what the Unity documentation says, // GetComponentInParent actually looks all the way up the // ancestor list, not just in Parents, so these following // checks are walking up the ancestors to find the one that // has a KSP component assigned to it: if (hitObject.layer == 15) { // Support Kopernicus scatter colliders // - no more crashing into boulders // - shoot them with lasers! (and then drive around them) PQSMod_LandClassScatterQuad scatter = hitObject.GetComponentInParent <PQSMod_LandClassScatterQuad>(); if (scatter != null) { HitName = scatter.transform.parent.name; // the name of the Scatter, eg. "Scatter boulder". } else { // Fallback to the body. CelestialBody body = hitObject.GetComponentInParent <CelestialBody>(); if (body != null) { HitName = body.name; } } } else { Part part = hitObject.GetComponentInParent <Part>(); if (part != null) { HitName = part.name; } } UpdateAge = 0; } } // If the hit is not found, or it is found but is far enough // away that it might be on the other side of the planet, seen // through the ocean (which has no collider so raycasts pass // through it), then try the more expensive pqs ray cast solver. if (newDist < 0 || newDist > 100000) { DebugMsg(" numeric solver starting:."); double pqsDist; CelestialBody pqsBody; bool success = pqsTool.RayCast(origin, pointing, out pqsBody, out pqsDist); if (pqsTool.UpdateAge == 0) { DebugMsg(" UpdateAge == 0."); if (success) { DebugMsg(" success."); // If it's a closer hit than we have already, then use it: if (pqsDist < newDist || newDist < 0) { HitName = pqsBody.name; // Ignore any hit closer than 2km as probably bogus "vessel below PQS" hit: // (it's possible for the actual terrain polygons to approximate the PQS curve // in a way where the vessel sits "under" the PQS predicted altitude despite // being above the polygon - that generates a bogus "hit terrain" false positive // as the line goes from "under" the terrain to "above" it. The PQS systen should // not need to be queried for nearby terrain, so if there isn't a nearby real raycast // hit, then don't believe it when PQS claims there is one: if (pqsDist >= 2000) { newDist = (float)pqsDist; } } } } else { DebugMsg(" UpdateAge != 0."); if (pqsTool.PrevSuccess) { DebugMsg(" prevsuccess."); // If it's a closer hit than we have already, then use it: if (pqsTool.PrevDist < newDist || newDist < 0) { DebugMsg(" prevsuccess."); HitName = pqsTool.PrevBodyName; // Ignore any hit closer than 2km as probably bogus "vessel below PQS" hit: // (see comment above in the "if" about this.) if (pqsTool.PrevDist >= 2000) { newDist = (float)pqsTool.PrevDist; } } } } UpdateAge = pqsTool.UpdateAge; } } Distance = newDist; DebugMsg("Distance = " + Distance); resetHitThisUpdate = false; }