//main thread public ColliderCollectorTerrainUnityWay(NavMeshTemplateRecast template, Collider[] colliders) : base(template, colliders) { var allTerrains = (from collider in validColliders select collider.GetComponent <Terrain>()); float fragmentSize = template.voxelSize; float halfFragment = fragmentSize * 0.5f; foreach (var curTerrain in allTerrains) { TerrainData data = curTerrain.terrainData; Vector3 position = curTerrain.transform.position; VectorInt.Vector3Int terrainStartInt = new VectorInt.Vector3Int((position / fragmentSize) + template.halfFragmentOffset); VectorInt.Vector3Int terrainEndInt = new VectorInt.Vector3Int((position + data.size) / fragmentSize + template.halfFragmentOffset); int startXClamp = Mathf.Clamp(terrainStartInt.x, template.startX_extra, template.endX_extra); int startZClamp = Mathf.Clamp(terrainStartInt.z, template.startZ_extra, template.endZ_extra); int endXClamp = Mathf.Clamp(terrainEndInt.x, template.startX_extra, template.endX_extra); int endZClamp = Mathf.Clamp(terrainEndInt.z, template.startZ_extra, template.endZ_extra); int terrainStartX = terrainStartInt.x; int terrainStartZ = terrainStartInt.z; int offset = terrainStartInt.y; float terrainSizeX = terrainEndInt.x - terrainStartX; float terrainSizeZ = terrainEndInt.z - terrainStartZ; float[][] heightMap = new float[template.lengthX_extra][]; int[][] passabilityMap = new int[template.lengthX_extra][]; //angle map for (int x = 0; x < template.lengthX_extra; x++) { heightMap[x] = new float[template.lengthZ_extra]; passabilityMap[x] = new int[template.lengthZ_extra]; } //some cashed values int startX_extra = template.startX_extra; int startZ_extra = template.startZ_extra; float maxSlope = template.maxSlope; //lets do bazzilion times Terrain.SampleHeight and TerrainData.GetSteepness! //it cant be done in thread cause terrain API is "threadsafe"! //actualy it can be done. but in order to do this we need to recreate this part of terrain mesh in thread but no. i dont want to do this. no. //maybe later for (int x = startXClamp; x < endXClamp; x++) { for (int z = startZClamp; z < endZClamp; z++) { heightMap[x - startX_extra][z - startZ_extra] = curTerrain.SampleHeight(new Vector3(x * fragmentSize + halfFragment, 0, z * fragmentSize + halfFragment)) + offset; passabilityMap[x - startX_extra][z - startZ_extra] = data.GetSteepness((x - terrainStartX) / terrainSizeX, (z - terrainStartZ) / terrainSizeZ) < maxSlope ? (int)Passability.Walkable : (int)Passability.Slope; } } TerrainColliderInfoPrecise info = new TerrainColliderInfoPrecise(curTerrain, heightMap, passabilityMap); if (template.profiler != null) { template.profiler.AddLog("start collecting tree bounds"); } info.treeData = GenerateTreeBounds(curTerrain); if (template.profiler != null) { template.profiler.AddLog("end collecting tree bounds. collected bounds: " + info.treeData.Count); } SetTerrainSettings(info, curTerrain, startXClamp, startZClamp, endXClamp, endZClamp, terrainStartX, terrainStartZ, terrainSizeX, terrainSizeZ, position); _terrainsInfo.Add(info); } }
public ColliderCollectorTerrainMeshAbstract(NavMeshTemplateRecast template, Collider[] colliders) : base(template, colliders) { List <Terrain> allTerrains = new List <Terrain>(); for (int i = 0; i < validColliders.Count; i++) { allTerrains.Add(validColliders[i].GetComponent <Terrain>()); } if (template.profiler != null) { template.profiler.AddLog("collecting terrain using fast collector. valid colliders:" + validColliders.Count); } float voxelSize = template.voxelSize; Bounds offsetedBounds = template.chunkOffsetedBounds; Vector3 boundsMin = offsetedBounds.min; Vector3 boundsMax = offsetedBounds.max; float minSize = PathFinder.settings.terrainFastMinimalSize; foreach (var curTerrain in allTerrains) { TerrainColliderInfoMesh info = new TerrainColliderInfoMesh(curTerrain); if (template.profiler != null) { template.profiler.AddLog("start collecting tree bounds"); } info.treeData = GenerateTreeBounds(curTerrain); if (template.profiler != null) { template.profiler.AddLog("end collecting tree bounds. collected bounds: " + info.treeData.Count); } TerrainData data = curTerrain.terrainData; Vector3 position = curTerrain.transform.position; Vector3 scale = data.size; //height map int resolution = 1; int heightMapSizeX = data.heightmapWidth; int heightMapSizeZ = data.heightmapHeight; float hScaleX = scale.x / (heightMapSizeX - 1); float hScaleZ = scale.z / (heightMapSizeZ - 1); for (int i = 0; i < 4; i++) { if (minSize > hScaleX * resolution) { resolution = (int)Mathf.Pow(2, i); } else { break; } } int hTargetMinX = Mathf.Clamp(((Mathf.RoundToInt((boundsMin.x - position.x) / hScaleX) / resolution) - 1) * resolution, 0, heightMapSizeX); int hTargetMinZ = Mathf.Clamp(((Mathf.RoundToInt((boundsMin.z - position.z) / hScaleZ) / resolution) - 1) * resolution, 0, heightMapSizeX); int hTargetMaxX = Mathf.Clamp(((Mathf.RoundToInt((boundsMax.x - position.x) / hScaleX) / resolution) + 1) * resolution + 1, 0, heightMapSizeX); int hTargetMaxZ = Mathf.Clamp(((Mathf.RoundToInt((boundsMax.z - position.z) / hScaleZ) / resolution) + 1) * resolution + 1, 0, heightMapSizeX); int hSizeX = hTargetMaxX - hTargetMinX; int hSizeZ = hTargetMaxZ - hTargetMinZ; int hTargetSizeX = hSizeX / resolution + 1; int hTargetSizeZ = hSizeZ / resolution + 1; if (resolution == 1) { hTargetSizeX = hSizeX; hTargetSizeZ = hSizeZ; } info.heightMap = data.GetHeights(hTargetMinX, hTargetMinZ, hSizeX, hSizeZ); info.heightMatrix = Matrix4x4.TRS( position + new Vector3(hScaleX * hTargetMinX, 0, hScaleZ * hTargetMinZ), Quaternion.identity, new Vector3(hScaleX * resolution, scale.y, hScaleZ * resolution)); info.hSizeX = hTargetSizeX; info.hSizeZ = hTargetSizeZ; info.resolution = resolution; //rest VectorInt.Vector3Int terrainStartInt = new VectorInt.Vector3Int((position / voxelSize) + template.halfFragmentOffset); VectorInt.Vector3Int terrainEndInt = new VectorInt.Vector3Int((position + data.size) / voxelSize + template.halfFragmentOffset); int startXClamp = Mathf.Clamp(terrainStartInt.x, template.startX_extra, template.endX_extra); int startZClamp = Mathf.Clamp(terrainStartInt.z, template.startZ_extra, template.endZ_extra); int endXClamp = Mathf.Clamp(terrainEndInt.x, template.startX_extra, template.endX_extra); int endZClamp = Mathf.Clamp(terrainEndInt.z, template.startZ_extra, template.endZ_extra); int terrainStartX = terrainStartInt.x; int terrainStartZ = terrainStartInt.z; float terrainSizeX = terrainEndInt.x - terrainStartX; float terrainSizeZ = terrainEndInt.z - terrainStartZ; SetTerrainSettings(info, curTerrain, startXClamp, startZClamp, endXClamp, endZClamp, terrainStartX, terrainStartZ, terrainSizeX, terrainSizeZ, position); terrainsInfo.Add(info); } }
private void AddColliderTerrain(Collider collider, TerrainCollectorType terrainCollectionType) { Terrain terrain = collider.GetComponent <Terrain>(); if (terrain == null | terrain.enabled == false) { return; } if (profiler != null) { profiler.AddLogFormat("collecting terrain {0}", terrain.gameObject.name); } TerrainColliderInfoMesh info = new TerrainColliderInfoMesh(terrain); info.trees = CollectTreeData(terrain); //general stuff float voxelSize = template.voxelSize; Bounds offsetedBounds = template.chunkOffsetedBounds; Vector3 boundsMin = offsetedBounds.min; Vector3 boundsMax = offsetedBounds.max; float minSize = PathFinder.settings.terrainFastMinimalSize; //terrain stuff TerrainData data = terrain.terrainData; Vector3 position = terrain.transform.position; Vector3 scale = data.size; //height map int resolution = 1; int heightMapSizeX = data.heightmapWidth; int heightMapSizeZ = data.heightmapHeight; float hScaleX = scale.x / (heightMapSizeX - 1); float hScaleZ = scale.z / (heightMapSizeZ - 1); for (int i = 0; i < 4; i++) { if (minSize > hScaleX * resolution) { resolution = (int)Mathf.Pow(2, i); } else { break; } } int hTargetMinX = Mathf.Clamp(((Mathf.RoundToInt((boundsMin.x - position.x) / hScaleX) / resolution) - 1) * resolution, 0, heightMapSizeX); int hTargetMinZ = Mathf.Clamp(((Mathf.RoundToInt((boundsMin.z - position.z) / hScaleZ) / resolution) - 1) * resolution, 0, heightMapSizeX); int hTargetMaxX = Mathf.Clamp(((Mathf.RoundToInt((boundsMax.x - position.x) / hScaleX) / resolution) + 1) * resolution + 1, 0, heightMapSizeX); int hTargetMaxZ = Mathf.Clamp(((Mathf.RoundToInt((boundsMax.z - position.z) / hScaleZ) / resolution) + 1) * resolution + 1, 0, heightMapSizeX); int hSizeX = hTargetMaxX - hTargetMinX; int hSizeZ = hTargetMaxZ - hTargetMinZ; int hTargetSizeX = hSizeX / resolution + 1; int hTargetSizeZ = hSizeZ / resolution + 1; if (resolution == 1) { hTargetSizeX = hSizeX; hTargetSizeZ = hSizeZ; } info.heightMap = data.GetHeights(hTargetMinX, hTargetMinZ, hSizeX, hSizeZ); info.heightMatrix = Matrix4x4.TRS( position + new Vector3(hScaleX * hTargetMinX, 0, hScaleZ * hTargetMinZ), Quaternion.identity, new Vector3(hScaleX * resolution, scale.y, hScaleZ * resolution)); info.hSizeX = hTargetSizeX; info.hSizeZ = hTargetSizeZ; info.resolution = resolution; //rest VectorInt.Vector3Int terrainStartInt = new VectorInt.Vector3Int((position / voxelSize) + template.halfVoxelOffset); VectorInt.Vector3Int terrainEndInt = new VectorInt.Vector3Int((position + data.size) / voxelSize + template.halfVoxelOffset); int startXClamp = Mathf.Clamp(terrainStartInt.x, template.startX_extra, template.endX_extra); int startZClamp = Mathf.Clamp(terrainStartInt.z, template.startZ_extra, template.endZ_extra); int endXClamp = Mathf.Clamp(terrainEndInt.x, template.startX_extra, template.endX_extra); int endZClamp = Mathf.Clamp(terrainEndInt.z, template.startZ_extra, template.endZ_extra); int terrainStartX = terrainStartInt.x; int terrainStartZ = terrainStartInt.z; float terrainSizeX = terrainEndInt.x - terrainStartX; float terrainSizeZ = terrainEndInt.z - terrainStartZ; var navmeshSettings = terrain.gameObject.GetComponent <TerrainNavmeshSettings>(); if (navmeshSettings != null && navmeshSettings.isActiveAndEnabled && navmeshSettings.data.Any(x => x != 0)) //0 is default so if there is settings full of deffault areas then dont need that { Vector3 size = data.size; info.settings = navmeshSettings; info.startXClamp = startXClamp; info.startZClamp = startZClamp; info.endXClamp = endXClamp; info.endZClamp = endZClamp; info.terrainStartX = terrainStartX; info.terrainStartZ = terrainStartZ; info.terrainSizeX = terrainSizeX; info.terrainSizeZ = terrainSizeZ; //all this values needed in 2 places. here to use data.GetAlphamaps in main thread and later in not main thread //so we just store it in terrain collider info info.alphaWidth = data.alphamapWidth; info.alphaHeight = data.alphamapHeight; //normalized start and end //dont needed later float terNormStartX = Mathf.Clamp01((template.chunkData.realX - template.properties.radius - position.x) / size.x); float terNormStartZ = Mathf.Clamp01((template.chunkData.realZ - template.properties.radius - position.z) / size.z); float terNormEndX = Mathf.Clamp01((template.chunkData.realX + PathFinder.gridSize + template.properties.radius - position.x) / size.x); float terNormEndZ = Mathf.Clamp01((template.chunkData.realZ + PathFinder.gridSize + template.properties.radius - position.z) / size.z); //alpha map position of chunk info.alphaStartX = Mathf.RoundToInt(terNormStartX * info.alphaWidth); info.alphaStartZ = Mathf.RoundToInt(terNormStartZ * info.alphaHeight); //alpha map size of chunk //size needed only now info.alphaSizeX = Mathf.Min(Mathf.RoundToInt((terNormEndX - terNormStartX) * info.alphaWidth) + 1, info.alphaWidth - info.alphaStartX); info.alphaSizeZ = Mathf.Min(Mathf.RoundToInt((terNormEndZ - terNormStartZ) * info.alphaHeight) + 1, info.alphaHeight - info.alphaStartZ); info.alphaMap = data.GetAlphamaps(info.alphaStartX, info.alphaStartZ, info.alphaSizeX, info.alphaSizeZ); info.possibleArea = (from areaID in navmeshSettings.data select PathFinder.GetArea(areaID)).ToArray(); //else if doCollectAreaInfo == false than later it became defaul area } switch (terrainCollectionType) { case TerrainCollectorType.CPU: terrainsInfoForCPU.Add(info); break; case TerrainCollectorType.ComputeShader: CollectTerrainOnGPU(info); break; } }