private IEnumerator BuildTerrain(ElevationData elevation, Texture2D[] textures) { GameObject tc = GameObject.Find("terrain"); if (tc != null) { GameObject.Destroy(tc); } yield return(null); // Center position of terrain. var position = this.transform.position; position -= new Vector3(SIZE / 2, 0, SIZE / 2); // Create terrain game object. GameObject terrainObject = new GameObject("terrain"); terrainObject.transform.position = position; terrainObject.transform.parent = this.transform; yield return(null); // Create terrain data. TerrainData terrainData = new TerrainData(); terrainData.heightmapResolution = 33; terrainData.size = new Vector3(SIZE, HEIGHT, SIZE); terrainData.alphamapResolution = 32; terrainData.baseMapResolution = 1024; terrainData.SetDetailResolution(1024, 8); yield return(null); // Tiles per side. var dimension = (int)Math.Sqrt(textures.Length); // Splat maps. var splats = new List <SplatPrototype>(); foreach (var texture in textures) { splats.Add(new SplatPrototype() { tileOffset = new Vector2(0, 0), tileSize = new Vector2( SIZE / dimension, SIZE / dimension ), texture = texture }); yield return(null); } terrainData.splatPrototypes = splats.ToArray(); terrainData.RefreshPrototypes(); yield return(null); // Get tile var tile = this._place.Location.ToTile(this._place.Level); // Construct height map. float[,] data = new float[ terrainData.heightmapWidth, terrainData.heightmapHeight ]; for (int x = 0; x < terrainData.heightmapWidth; x++) { for (int y = 0; y < terrainData.heightmapHeight; y++) { // Scale elevation from 257x257 to 33x33 var x2 = Convert.ToInt32((double)x * 256 / (terrainData.heightmapWidth - 1)); var y2 = Convert.ToInt32((double)y * 256 / (terrainData.heightmapHeight - 1)); // Find index in Esri elevation array var id = y2 * 257 + x2; // Absolute height in map units. var h1 = elevation.Heights[id]; // Height in model units. var h2 = SIZE * (h1 - elevation.Min) / tile.Size; // Apply exaggeration. var h3 = h2 * VERTICAL_EXAGGERATION; // Apply base offset. var h4 = h3 + TERRAIN_BASE_HEIGHT; // Final height. data[terrainData.heightmapHeight - 1 - y, x] = h4; } } terrainData.SetHeights(0, 0, data); yield return(null); // Add alpha mapping //float[,,] maps = terrainData.GetAlphamaps(0, 0, terrainData.alphamapWidth, terrainData.alphamapHeight); var maps = new float[ terrainData.alphamapWidth, terrainData.alphamapHeight, textures.Length ]; for (int y = 0; y < terrainData.alphamapHeight; y++) { for (int x = 0; x < terrainData.alphamapWidth; x++) { // Convert alpha coordinates into tile index. Left to right, bottom to top. var tilex = x / (terrainData.alphamapWidth / dimension); var tiley = y / (terrainData.alphamapHeight / dimension); var index = (dimension - tiley - 1) * dimension + tilex; for (int t = 0; t < textures.Length; t++) { maps[y, x, t] = index == t ? 1f : 0f; } } } terrainData.SetAlphamaps(0, 0, maps); yield return(null); // Create terrain collider. TerrainCollider terrainCollider = terrainObject.AddComponent <TerrainCollider>(); terrainCollider.terrainData = terrainData; yield return(null); // Add terrain component. Terrain terrain = terrainObject.AddComponent <Terrain>(); terrain.terrainData = terrainData; yield return(null); // Calculate mesh vertices and triangles. List <Vector3> vertices = new List <Vector3>(); List <int> triangles = new List <int>(); // Distance between vertices var step = SIZE / 32f; // Front for (int x = 0; x < terrainData.heightmapWidth; x++) { vertices.Add(new Vector3(x * step, 0f, 0f)); vertices.Add(new Vector3(x * step, data[0, x], 0f)); } yield return(null); // Right for (int z = 0; z < terrainData.heightmapHeight; z++) { vertices.Add(new Vector3(SIZE, 0f, z * step)); vertices.Add(new Vector3(SIZE, data[z, terrainData.heightmapWidth - 1], z * step)); } yield return(null); // Back for (int x = 0; x < terrainData.heightmapWidth; x++) { var xr = terrainData.heightmapWidth - 1 - x; vertices.Add(new Vector3(xr * step, 0f, SIZE)); vertices.Add(new Vector3(xr * step, data[terrainData.heightmapHeight - 1, xr], SIZE)); } yield return(null); // Left for (int z = 0; z < terrainData.heightmapHeight; z++) { var zr = terrainData.heightmapHeight - 1 - z; vertices.Add(new Vector3(0f, 0f, zr * step)); vertices.Add(new Vector3(0f, data[zr, 0], zr * step)); } yield return(null); // Quads for (int i = 0; i < vertices.Count / 2 - 1; i++) { triangles.AddRange(new int[] { 2 * i + 0, 2 * i + 1, 2 * i + 2, 2 * i + 2, 2 * i + 1, 2 * i + 3 }); } // Create single mesh for all four sides GameObject side = new GameObject("side"); side.transform.position = position; side.transform.parent = terrainObject.transform; yield return(null); // Create mesh Mesh mesh = new Mesh() { vertices = vertices.ToArray(), triangles = triangles.ToArray() }; mesh.RecalculateNormals(); mesh.RecalculateBounds(); yield return(null); MeshFilter meshFilter = side.AddComponent <MeshFilter>(); meshFilter.mesh = mesh; yield return(null); MeshRenderer meshRenderer = side.AddComponent <MeshRenderer>(); meshRenderer.material = new Material(Shader.Find("Standard")) { color = new Color32(0, 128, 128, 100) }; yield return(null); MeshCollider meshCollider = side.AddComponent <MeshCollider>(); yield return(null); buttonObject = Instantiate(buttonZoomPrefab, terrainObject.transform.position + new Vector3(0, 0.2f, 0), Quaternion.Euler(0, -90, 0)) as StableMenu; buttonObject.transform.parent = terrain.transform; yield return(null); }
public List <GameObjectRayHit> RaycastAll(Ray ray, SceneRaycastPrecision raycastPresicion) { var nodeHits = _objectTree.RaycastAll(ray); if (nodeHits.Count == 0) { return(new List <GameObjectRayHit>()); } var boundsQConfig = new ObjectBounds.QueryConfig(); boundsQConfig.ObjectTypes = GameObjectTypeHelper.AllCombined; boundsQConfig.NoVolumeSize = EditorScene.Get.NoVolumeObjectSize; if (raycastPresicion == SceneRaycastPrecision.BestFit) { var hitList = new List <GameObjectRayHit>(10); foreach (var nodeHit in nodeHits) { GameObject sceneObject = nodeHit.HitNode.Data; if (sceneObject == null || !sceneObject.activeInHierarchy) { continue; } Renderer renderer = sceneObject.GetComponent <Renderer>(); if (renderer != null && !renderer.isVisible) { continue; } GameObjectType objectType = sceneObject.GetGameObjectType(); if (objectType == GameObjectType.Mesh) { GameObjectRayHit objectHit = RaycastMeshObject(ray, sceneObject); if (objectHit != null) { hitList.Add(objectHit); } } else if (objectType == GameObjectType.Terrain) { TerrainCollider terrainCollider = sceneObject.GetComponent <TerrainCollider>(); if (terrainCollider != null) { RaycastHit hitInfo; if (terrainCollider.Raycast(ray, out hitInfo, float.MaxValue)) { hitList.Add(new GameObjectRayHit(ray, hitInfo)); } } } else if (objectType == GameObjectType.Sprite) { GameObjectRayHit objectHit = RaycastSpriteObject(ray, sceneObject); if (objectHit != null) { hitList.Add(objectHit); } } else { OOBB worldOOBB = ObjectBounds.CalcWorldOOBB(sceneObject, boundsQConfig); if (worldOOBB.IsValid) { float t; if (BoxMath.Raycast(ray, out t, worldOOBB.Center, worldOOBB.Size, worldOOBB.Rotation)) { var faceDesc = BoxMath.GetFaceClosestToPoint(ray.GetPoint(t), worldOOBB.Center, worldOOBB.Size, worldOOBB.Rotation); var hit = new GameObjectRayHit(ray, sceneObject, faceDesc.Plane.normal, t); hitList.Add(hit); } } } } return(hitList); } else if (raycastPresicion == SceneRaycastPrecision.Box) { var hitList = new List <GameObjectRayHit>(10); foreach (var nodeHit in nodeHits) { GameObject sceneObject = nodeHit.HitNode.Data; if (sceneObject == null || !sceneObject.activeInHierarchy) { continue; } Renderer renderer = sceneObject.GetComponent <Renderer>(); if (renderer != null && !renderer.isVisible) { continue; } OOBB worldOOBB = ObjectBounds.CalcWorldOOBB(sceneObject, boundsQConfig); if (worldOOBB.IsValid) { float t; if (BoxMath.Raycast(ray, out t, worldOOBB.Center, worldOOBB.Size, worldOOBB.Rotation)) { var faceDesc = BoxMath.GetFaceClosestToPoint(ray.GetPoint(t), worldOOBB.Center, worldOOBB.Size, worldOOBB.Rotation); var hit = new GameObjectRayHit(ray, sceneObject, faceDesc.Plane.normal, t); hitList.Add(hit); } } } return(hitList); } return(new List <GameObjectRayHit>()); }
public RasterTerrainMesh(TerrainCollider collider) : base(collider.transform) { SetpUpTerrainMesh(collider.terrainData, out _vertices, out _triangles); RecalcBounds(); }
private void GenerateTerrain(List <HEU_LoadBufferVolume> terrainBuffers) { Transform parent = this.gameObject.transform; // Directory to store generated terrain files. string outputTerrainpath = GetOutputCacheDirectory(); outputTerrainpath = HEU_Platform.BuildPath(outputTerrainpath, "Terrain"); int numVolumes = terrainBuffers.Count; for (int t = 0; t < numVolumes; ++t) { if (terrainBuffers[t]._heightMap != null) { GameObject newGameObject = new GameObject("heightfield_" + terrainBuffers[t]._tileIndex); Transform newTransform = newGameObject.transform; newTransform.parent = parent; HEU_GeneratedOutput generatedOutput = new HEU_GeneratedOutput(); generatedOutput._outputData._gameObject = newGameObject; Terrain terrain = HEU_GeneralUtility.GetOrCreateComponent <Terrain>(newGameObject); #if !HEU_TERRAIN_COLLIDER_DISABLED TerrainCollider collider = HEU_GeneralUtility.GetOrCreateComponent <TerrainCollider>(newGameObject); #endif // The TerrainData and TerrainLayer files needs to be saved out if we create them. // Try user specified path, otherwise use the cache folder string exportTerrainDataPath = terrainBuffers[t]._terrainDataExportPath; if (string.IsNullOrEmpty(exportTerrainDataPath)) { // This creates the relative folder path from the Asset's cache folder: {assetCache}/{geo name}/Terrain/Tile{tileIndex}/... exportTerrainDataPath = HEU_Platform.BuildPath(outputTerrainpath, HEU_Defines.HEU_FOLDER_TERRAIN, HEU_Defines.HEU_FOLDER_TILE + terrainBuffers[t]._tileIndex); } bool bFullExportTerrainDataPath = HEU_Platform.DoesFileExist(exportTerrainDataPath); if (!string.IsNullOrEmpty(terrainBuffers[t]._terrainDataPath)) { // Load the source TerrainData, then make a unique copy of it in the cache folder TerrainData sourceTerrainData = HEU_AssetDatabase.LoadAssetAtPath(terrainBuffers[t]._terrainDataPath, typeof(TerrainData)) as TerrainData; if (sourceTerrainData == null) { Debug.LogWarningFormat("TerrainData, set via attribute, not found at: {0}", terrainBuffers[t]._terrainDataPath); } if (bFullExportTerrainDataPath) { terrain.terrainData = HEU_AssetDatabase.CopyAndLoadAssetAtGivenPath(sourceTerrainData, exportTerrainDataPath, typeof(TerrainData)) as TerrainData; } else { terrain.terrainData = HEU_AssetDatabase.CopyUniqueAndLoadAssetAtAnyPath(sourceTerrainData, exportTerrainDataPath, typeof(TerrainData)) as TerrainData; } if (terrain.terrainData != null) { // Store path so that it can be deleted on clean up AddGeneratedOutputFilePath(HEU_AssetDatabase.GetAssetPath(terrain.terrainData)); } } if (terrain.terrainData == null) { terrain.terrainData = new TerrainData(); if (bFullExportTerrainDataPath) { string folderPath = HEU_Platform.GetFolderPath(exportTerrainDataPath, true); HEU_AssetDatabase.CreatePathWithFolders(folderPath); HEU_AssetDatabase.CreateAsset(terrain.terrainData, exportTerrainDataPath); } else { string assetPathName = "TerrainData" + HEU_Defines.HEU_EXT_ASSET; HEU_AssetDatabase.CreateObjectInAssetCacheFolder(terrain.terrainData, exportTerrainDataPath, null, assetPathName, typeof(TerrainData)); } } TerrainData terrainData = terrain.terrainData; #if !HEU_TERRAIN_COLLIDER_DISABLED collider.terrainData = terrainData; #endif HEU_TerrainUtility.SetTerrainMaterial(terrain, terrainBuffers[t]._specifiedTerrainMaterialName); #if UNITY_2018_3_OR_NEWER terrain.allowAutoConnect = true; // This has to be set after setting material terrain.drawInstanced = true; #endif int heightMapSize = terrainBuffers[t]._heightMapWidth; terrainData.heightmapResolution = heightMapSize; if (terrainData.heightmapResolution != heightMapSize) { Debug.LogErrorFormat("Unsupported terrain size: {0}", heightMapSize); continue; } // The terrainData.baseMapResolution is not set here, but rather left to whatever default Unity uses // The terrainData.alphamapResolution is set later when setting the alphamaps. // 32 is the default for resolutionPerPatch const int detailResolution = 1024; const int resolutionPerPatch = 32; terrainData.SetDetailResolution(detailResolution, resolutionPerPatch); terrainData.SetHeights(0, 0, terrainBuffers[t]._heightMap); // Note that Unity uses a default height range of 600 when a flat terrain is created. // Without a non-zero value for the height range, user isn't able to draw heights. // Therefore, set 600 as the value if height range is currently 0 (due to flat heightfield). float heightRange = terrainBuffers[t]._heightRange; if (heightRange == 0) { heightRange = 600; } terrainData.size = new Vector3(terrainBuffers[t]._terrainSizeX, heightRange, terrainBuffers[t]._terrainSizeY); terrain.Flush(); // Set position HAPI_Transform hapiTransformVolume = new HAPI_Transform(true); hapiTransformVolume.position[0] += terrainBuffers[t]._position[0]; hapiTransformVolume.position[1] += terrainBuffers[t]._position[1]; hapiTransformVolume.position[2] += terrainBuffers[t]._position[2]; HEU_HAPIUtility.ApplyLocalTransfromFromHoudiniToUnity(ref hapiTransformVolume, newTransform); // Set layers Texture2D defaultTexture = HEU_VolumeCache.LoadDefaultSplatTexture(); int numLayers = terrainBuffers[t]._splatLayers.Count; #if UNITY_2018_3_OR_NEWER // Create TerrainLayer for each heightfield layer. // Note that height and mask layers are ignored (i.e. not created as TerrainLayers). // Since height layer is first, only process layers from 2nd index onwards. if (numLayers > 1) { // Keep existing TerrainLayers, and either update or append to them TerrainLayer[] existingTerrainLayers = terrainData.terrainLayers; // Total layers are existing layers + new alpha maps List <TerrainLayer> finalTerrainLayers = new List <TerrainLayer>(existingTerrainLayers); for (int m = 1; m < numLayers; ++m) { TerrainLayer terrainlayer = null; int terrainLayerIndex = -1; bool bSetTerrainLayerProperties = true; HEU_LoadBufferVolumeLayer layer = terrainBuffers[t]._splatLayers[m]; // Look up TerrainLayer file via attribute if user has set it if (!string.IsNullOrEmpty(layer._layerPath)) { terrainlayer = HEU_AssetDatabase.LoadAssetAtPath(layer._layerPath, typeof(TerrainLayer)) as TerrainLayer; if (terrainlayer == null) { Debug.LogWarningFormat("TerrainLayer, set via attribute, not found at: {0}", layer._layerPath); continue; } else { // Always check if its part of existing list so as not to add it again terrainLayerIndex = HEU_TerrainUtility.GetTerrainLayerIndex(terrainlayer, existingTerrainLayers); } } if (terrainlayer == null) { terrainlayer = new TerrainLayer(); terrainLayerIndex = finalTerrainLayers.Count; finalTerrainLayers.Add(terrainlayer); } else { // For existing TerrainLayer, make a copy of it if it has custom layer attributes // because we don't want to change the original TerrainLayer. if (layer._hasLayerAttributes) { // Copy the TerrainLayer file TerrainLayer prevTerrainLayer = terrainlayer; terrainlayer = HEU_AssetDatabase.CopyAndLoadAssetAtAnyPath(terrainlayer, outputTerrainpath, typeof(TerrainLayer), true) as TerrainLayer; if (terrainlayer != null) { if (terrainLayerIndex >= 0) { // Update the TerrainLayer reference in the list with this copy finalTerrainLayers[terrainLayerIndex] = terrainlayer; } else { // Newly added terrainLayerIndex = finalTerrainLayers.Count; finalTerrainLayers.Add(terrainlayer); } // Store path for clean up later AddGeneratedOutputFilePath(HEU_AssetDatabase.GetAssetPath(terrainlayer)); } else { Debug.LogErrorFormat("Unable to copy TerrainLayer '{0}' for generating Terrain. " + "Using original TerrainLayer. Will not be able to set any TerrainLayer properties.", layer._layerName); terrainlayer = prevTerrainLayer; bSetTerrainLayerProperties = false; // Again, continuing on to keep proper indexing. } } else { // Could be a layer in Assets/ but not part of existing layers in TerrainData terrainLayerIndex = finalTerrainLayers.Count; finalTerrainLayers.Add(terrainlayer); bSetTerrainLayerProperties = false; } } if (bSetTerrainLayerProperties) { if (!string.IsNullOrEmpty(layer._diffuseTexturePath)) { terrainlayer.diffuseTexture = HEU_MaterialFactory.LoadTexture(layer._diffuseTexturePath); } if (terrainlayer.diffuseTexture == null) { terrainlayer.diffuseTexture = defaultTexture; } terrainlayer.diffuseRemapMin = Vector4.zero; terrainlayer.diffuseRemapMax = Vector4.one; if (!string.IsNullOrEmpty(layer._maskTexturePath)) { terrainlayer.maskMapTexture = HEU_MaterialFactory.LoadTexture(layer._maskTexturePath); } terrainlayer.maskMapRemapMin = Vector4.zero; terrainlayer.maskMapRemapMax = Vector4.one; terrainlayer.metallic = layer._metallic; if (!string.IsNullOrEmpty(layer._normalTexturePath)) { terrainlayer.normalMapTexture = HEU_MaterialFactory.LoadTexture(layer._normalTexturePath); } terrainlayer.normalScale = layer._normalScale; terrainlayer.smoothness = layer._smoothness; terrainlayer.specular = layer._specularColor; terrainlayer.tileOffset = layer._tileOffset; if (layer._tileSize.magnitude == 0f && terrainlayer.diffuseTexture != null) { // Use texture size if tile size is 0 layer._tileSize = new Vector2(terrainlayer.diffuseTexture.width, terrainlayer.diffuseTexture.height); } terrainlayer.tileSize = layer._tileSize; } } terrainData.terrainLayers = finalTerrainLayers.ToArray(); } #else // Need to create SplatPrototype for each layer in heightfield, representing the textures. SplatPrototype[] splatPrototypes = new SplatPrototype[numLayers]; for (int m = 0; m < numLayers; ++m) { splatPrototypes[m] = new SplatPrototype(); HEU_LoadBufferVolumeLayer layer = terrainBuffers[t]._splatLayers[m]; Texture2D diffuseTexture = null; if (!string.IsNullOrEmpty(layer._diffuseTexturePath)) { diffuseTexture = HEU_MaterialFactory.LoadTexture(layer._diffuseTexturePath); } if (diffuseTexture == null) { diffuseTexture = defaultTexture; } splatPrototypes[m].texture = diffuseTexture; splatPrototypes[m].tileOffset = layer._tileOffset; if (layer._tileSize.magnitude == 0f && diffuseTexture != null) { // Use texture size if tile size is 0 layer._tileSize = new Vector2(diffuseTexture.width, diffuseTexture.height); } splatPrototypes[m].tileSize = layer._tileSize; splatPrototypes[m].metallic = layer._metallic; splatPrototypes[m].smoothness = layer._smoothness; if (!string.IsNullOrEmpty(layer._normalTexturePath)) { splatPrototypes[m].normalMap = HEU_MaterialFactory.LoadTexture(layer._normalTexturePath); } } terrainData.splatPrototypes = splatPrototypes; #endif // Set the splatmaps if (terrainBuffers[t]._splatMaps != null) { // Set the alphamap size before setting the alphamaps to get correct scaling // The alphamap size comes from the first alphamap layer int alphamapResolution = terrainBuffers[t]._heightMapWidth; if (numLayers > 1) { alphamapResolution = terrainBuffers[t]._splatLayers[1]._heightMapWidth; } terrainData.alphamapResolution = alphamapResolution; terrainData.SetAlphamaps(0, 0, terrainBuffers[t]._splatMaps); } // Set the tree scattering if (terrainBuffers[t]._scatterTrees != null) { HEU_TerrainUtility.ApplyScatterTrees(terrainData, terrainBuffers[t]._scatterTrees); } // Set the detail layers if (terrainBuffers[t]._detailPrototypes != null) { HEU_TerrainUtility.ApplyDetailLayers(terrain, terrainData, terrainBuffers[t]._detailProperties, terrainBuffers[t]._detailPrototypes, terrainBuffers[t]._detailMaps); } terrainBuffers[t]._generatedOutput = generatedOutput; _generatedOutputs.Add(generatedOutput); SetOutputVisiblity(terrainBuffers[t]); } } }
public static void GenerateTerrainMesh(GameObject terrainGO, string path, int sizeorg, int height, int fullsize, int terrainCount, int id) { int size = sizeorg + 1; byte[] orgBuffer = new byte[(size * size) * 2]; byte[] buffer = new byte[(size * size) * 2]; using (BinaryReader reader = new BinaryReader(File.Open(path, FileMode.Open, FileAccess.Read))) { int headerLength = Util.GetTerrainHeaderLength(path); reader.ReadBytes(headerLength); int lineoffset = 0; int terrainOffset = 0; while (lineoffset < sizeorg) // Resize the image to (power of 2) + 1 { int fileOffset = 0; while (fileOffset < sizeorg * 2) // read one line { orgBuffer [terrainOffset] = reader.ReadByte(); terrainOffset++; fileOffset++; } orgBuffer [fileOffset + 1] = buffer [fileOffset - 1]; orgBuffer [fileOffset + 2] = buffer [fileOffset]; terrainOffset += 2; lineoffset++; } int lastLine = 0; while (lastLine < (size * 2)) { int offset = (((size * size) * 2) - (size * 2)); orgBuffer [offset + lastLine] = orgBuffer [((offset - (size * 2)) + lastLine)]; lastLine++; } for (int y = 0; y < (size * 2); y++) { for (int x = 0; x < (size * 2); x++) { int newOffset = (x * size) + y; int orgOffset = (y * size) + x; buffer [newOffset] = orgBuffer [orgOffset]; buffer [newOffset + 1] = orgBuffer [orgOffset + 1]; x++; } y++; } for (int x = 0; x < size * 2; x++) { int offset = ((size * size) * 2) - (size * 2); buffer [offset + x] = buffer [offset - (size * 2) + x]; buffer [offset + x + 1] = buffer [offset - (size * 2) + x + 1]; x++; } reader.Close(); } Terrain terrain = terrainGO.AddComponent <Terrain>(); TerrainCollider tc = terrainGO.AddComponent <TerrainCollider>(); TerrainData terrainData = new TerrainData(); tc.terrainData = terrainData; terrainData.heightmapResolution = size; if (size < 512) { int otSize = (fullsize) / 2; if (terrainCount == 48 && !(id == 10 || id == 11 || id == 12 || id == 19 || id == 20 || id == 21 || id == 28 || id == 29 || id == 30 || id == 37 || id == 38 || id == 39)) { otSize = otSize / 2; } terrainData.size = new Vector3(otSize, height, otSize); } else { terrainData.size = new Vector3(size - 1, height, size - 1); } terrain.terrainData = terrainData; int heightmapWidth = terrain.terrainData.heightmapWidth; int heightmapHeight = terrain.terrainData.heightmapHeight; float[,] heights = new float[heightmapHeight, heightmapWidth]; float num3 = 1.525879E-05f; for (int i = 0; i < heightmapHeight; i++) { for (int j = 0; j < heightmapWidth; j++) { int num6 = Mathf.Clamp(j, 0, size - 1) + (Mathf.Clamp(i, 0, size - 1) * size); // byte num7 = buffer[num6 * 2]; // buffer[num6 * 2] = buffer[(num6 * 2) + 1]; // buffer[(num6 * 2) + 1] = num7; float num9 = System.BitConverter.ToUInt16(buffer, num6 * 2) * num3; heights[i, j] = num9; } } terrain.terrainData.SetHeights(0, 0, heights); terrain.heightmapPixelError = 1; }
private void Start() { rend = GetComponent <ParentBuilding>().MeshRenderer; boxCollider = GetComponent <BoxCollider>(); terrainCollider = Terrain.activeTerrain.GetComponent <TerrainCollider>(); }
protected override void Start() { base.init(); // Make heightfield data NativeArray <float> heights; int2 size; float3 scale; bool simple = false; #if UNITY_ANDROID || UNITY_IOS simple = true; #endif bool flat = false; bool mountain = false; if (simple) { size = new int2(2, 2); scale = new float3(25, 0.1f, 25); heights = new NativeArray <float>(size.x * size.y * UnsafeUtility.SizeOf <float>(), Allocator.Temp); heights[0] = 1; heights[1] = 0; heights[2] = 0; heights[3] = 1; } else { size = new int2(SizeX, SizeZ); scale = new float3(ScaleX, ScaleY, ScaleZ); float period = 50.0f; heights = new NativeArray <float>(size.x * size.y * UnsafeUtility.SizeOf <float>(), Allocator.Temp); for (int j = 0; j < size.y; j++) { for (int i = 0; i < size.x; i++) { float a = (i + j) * 2.0f * (float)math.PI / period; heights[i + j * size.x] = flat ? 0.0f : math.sin(a); if (mountain) { float fractionFromCenter = 1.0f - math.min(math.length(new float2(i - size.x / 2, j - size.y / 2)) / (math.min(size.x, size.y) / 2), 1.0f); float mountainHeight = math.smoothstep(0.0f, 1, fractionFromCenter) * 25.0f; heights[i + j * size.x] += mountainHeight; } } } } // static terrain Entity staticEntity; { bool createMesh = false; var collider = createMesh ? CreateMeshTerrain(heights, new int2(SizeX, SizeZ), new float3(ScaleX, ScaleY, ScaleZ)) : TerrainCollider.Create(heights, size, scale, Method); bool compound = false; if (compound) { var instances = new NativeArray <CompoundCollider.ColliderBlobInstance>(4, Allocator.Temp); for (int i = 0; i < 4; i++) { instances[i] = new CompoundCollider.ColliderBlobInstance { Collider = collider, CompoundFromChild = new RigidTransform { pos = new float3((i % 2) * scale.x * (size.x - 1), 0.0f, (i / 2) * scale.z * (size.y - 1)), rot = quaternion.identity } }; } collider = Unity.Physics.CompoundCollider.Create(instances); instances.Dispose(); } float3 position = new float3(size.x - 1, 0.0f, size.y - 1) * scale * -0.5f; staticEntity = CreateStaticBody(position, quaternion.identity, collider); } }
public HeightData(TerrainCollider source) { this.dataHandle = new GCHandle(); this.source = source; UpdateHeightData(); }
public void UpdateBounds() { if (setBoundsManually) { //mBounds = new Bounds(customBounds * 0.5f, customBounds); mBounds.size = customBounds; mBounds.center = customBoundsCenter; mBounds.Expand(new Vector3(boundsOffset, 0, boundsOffset)); bounds = mBounds; return; } bool flag = false; int i, imax = 0; mTerrains = FindObjectsOfType(typeof(Terrain)) as Terrain[]; bool multiTerrain = mTerrains != null; if (multiTerrain) { multiTerrain = mTerrains.Length > 1; } // First lets see if there is more than one terrain. Multi-terrain handling if (multiTerrain) { #if UNITY_EDITOR // Debug.Log("NJGMap: Calculating bounds for multiple terrains ("+mTerrains.Length+")"); #endif for (i = 0, imax = mTerrains.Length; i < imax; i++) { Terrain t = mTerrains[i]; MeshRenderer mMeshRenderer = t.GetComponent <MeshRenderer>(); if (!flag) { //t.transform.position, new Vector3(1f, 1f, 1f) mBounds = new Bounds(); flag = true; } if (mMeshRenderer != null) { //Debug.Log("Terrain Mesh Renderer " + i + " : " + mMeshRenderer.bounds.size + " / " + t.name); mBounds.Encapsulate(mMeshRenderer.bounds); } else { TerrainCollider mTerrainCollider = t.GetComponent <TerrainCollider>(); if (mTerrainCollider != null) { //Debug.Log("Terrain Collider " + i + " : " + mTerrainCollider.bounds.size+" / "+t.name); mBounds.Encapsulate(mTerrainCollider.bounds); } else { Debug.LogError("Could not get measure bounds of terrain.", this); return; } } } } // If not then check if there is one activeTerrain else if (Terrain.activeTerrain != null) { #if UNITY_EDITOR //Debug.Log("NJGMap: Calculating bounds for active terrain"); #endif Terrain t = Terrain.activeTerrain; MeshRenderer mMeshRenderer = t.GetComponent <MeshRenderer>(); if (!flag) { //t.transform.position, new Vector3(1f, 1f, 1f) mBounds = new Bounds(); flag = true; } if (mMeshRenderer != null) { //Debug.Log("Terrain Mesh Renderer " + i + " : " + mMeshRenderer.bounds.size + " / " + t.name); mBounds.Encapsulate(mMeshRenderer.bounds); } else { TerrainCollider mTerrainCollider = t.GetComponent <TerrainCollider>(); if (mTerrainCollider != null) { //Debug.Log("Terrain Collider " + i + " : " + mTerrainCollider.bounds.size+" / "+t.name); mBounds.Encapsulate(mTerrainCollider.bounds); } else { Debug.LogError("Could not get measure bounds of terrain.", this); return; } } } GameObject[] mGameObjects = UnityEngine.Object.FindObjectsOfType(typeof(GameObject)) as GameObject[]; if (mGameObjects != null) { #if UNITY_EDITOR //Debug.Log("NJGMap: Calculating bounds for multiple gameObjects (" + mGameObjects.Length + ")"); #endif for (i = 0, imax = mGameObjects.Length; i < imax; i++) { GameObject go = mGameObjects[i]; // Dont consider this game object if (go.layer == gameObject.layer) { continue; } // Only use objects from the layer mask if (!IsInRenderLayers(go, boundLayers)) { continue; } if (!flag) { mBounds = new Bounds(go.transform.position, new Vector3(1f, 1f, 1f)); flag = true; } Renderer renderer = go.renderer; if (renderer != null) { mBounds.Encapsulate(renderer.bounds); } else { Collider collider = go.collider; if (collider != null) { mBounds.Encapsulate(collider.bounds); } } } } if (!flag) { Debug.Log("Could not find terrain nor any other bounds in scene", this); mBounds = new Bounds(gameObject.transform.position, new Vector3(1f, 1f, 1f)); } mBounds.Expand(new Vector3(boundsOffset, 0, boundsOffset)); if (mapResolution == Resolution.Double) { //mBounds.Expand(new Vector3(-mBounds.extents.x, 0f, -mBounds.extents.z)); } // Set bounds bounds = mBounds; }
private void UpdateColliders() { for (int i = 0; i < colliders.Count; i++) { Collider source = colliders[i]; if (source == null || !source.enabled || !source.gameObject.activeInHierarchy) { continue; } Rigidbody rb = source.GetComponentInParent <Rigidbody>(); ObiCollider oc = source.GetComponent <ObiCollider>(); // Get the adequate rigidBodyIndex. If several colliders share a rigidbody, they'll get the same rigidBodyIndex. int rigidBodyIndex = -1; if (rb != null) { if (!rigidbodyIDs.TryGetValue(rb.GetInstanceID(), out rigidBodyIndex)) { rigidBodyIndex = Oni.GetRigidbodyCount(oniColliderGroup); Oni.SetRigidbodies(oniColliderGroup, new Oni.Rigidbody[] { new Oni.Rigidbody(rb) }, 1, rigidBodyIndex); rigidbodyIDs[rb.GetInstanceID()] = rigidBodyIndex; } } float thickness = (oc != null)?oc.thickness:0; if (source is SphereCollider) { Oni.SetColliders(oniColliderGroup, new Oni.Collider[] { new Oni.Collider(source, Oni.ShapeType.Sphere, thickness, Oni.GetShapeCount(oniColliderGroup, Oni.ShapeType.Sphere), rigidBodyIndex, (oc != null)?oc.materialIndex:0) }, 1, Oni.GetColliderCount(oniColliderGroup)); Oni.SetSphereShapes(oniColliderGroup, new Oni.SphereShape[] { new Oni.SphereShape(source as SphereCollider) }, 1, Oni.GetShapeCount(oniColliderGroup, Oni.ShapeType.Sphere)); } else if (source is BoxCollider) { Oni.SetColliders(oniColliderGroup, new Oni.Collider[] { new Oni.Collider(source, Oni.ShapeType.Box, thickness, Oni.GetShapeCount(oniColliderGroup, Oni.ShapeType.Box), rigidBodyIndex, (oc != null)?oc.materialIndex:0) }, 1, Oni.GetColliderCount(oniColliderGroup)); Oni.SetBoxShapes(oniColliderGroup, new Oni.BoxShape[] { new Oni.BoxShape(source as BoxCollider) }, 1, Oni.GetShapeCount(oniColliderGroup, Oni.ShapeType.Box)); } else if (source is CapsuleCollider) { Oni.SetColliders(oniColliderGroup, new Oni.Collider[] { new Oni.Collider(source, Oni.ShapeType.Capsule, thickness, Oni.GetShapeCount(oniColliderGroup, Oni.ShapeType.Capsule), rigidBodyIndex, (oc != null)?oc.materialIndex:0) }, 1, Oni.GetColliderCount(oniColliderGroup)); Oni.SetCapsuleShapes(oniColliderGroup, new Oni.CapsuleShape[] { new Oni.CapsuleShape(source as CapsuleCollider) }, 1, Oni.GetShapeCount(oniColliderGroup, Oni.ShapeType.Capsule)); } else if (source is CharacterController) { Oni.SetColliders(oniColliderGroup, new Oni.Collider[] { new Oni.Collider(source, Oni.ShapeType.Capsule, thickness, Oni.GetShapeCount(oniColliderGroup, Oni.ShapeType.Capsule), rigidBodyIndex, (oc != null)?oc.materialIndex:0) }, 1, Oni.GetColliderCount(oniColliderGroup)); Oni.SetCapsuleShapes(oniColliderGroup, new Oni.CapsuleShape[] { new Oni.CapsuleShape(source as CharacterController) }, 1, Oni.GetShapeCount(oniColliderGroup, Oni.ShapeType.Capsule)); } else if (source is TerrainCollider) { TerrainCollider tc = source as TerrainCollider; Oni.HeightData data; if (!heightData.TryGetValue(tc, out data)) { data = heightData[tc] = new Oni.HeightData(tc); } Oni.SetColliders(oniColliderGroup, new Oni.Collider[] { new Oni.Collider(source, Oni.ShapeType.Heightmap, thickness, Oni.GetShapeCount(oniColliderGroup, Oni.ShapeType.Heightmap), rigidBodyIndex, (oc != null)?oc.materialIndex:0) }, 1, Oni.GetColliderCount(oniColliderGroup)); Oni.SetHeightmapShapes(oniColliderGroup, new Oni.HeightmapShape[] { new Oni.HeightmapShape(tc, data.AddrOfHeightData()) }, 1, Oni.GetShapeCount(oniColliderGroup, Oni.ShapeType.Heightmap)); } else if (source is MeshCollider) { MeshCollider mc = source as MeshCollider; MeshColliderShapeAndData shapeAndData; if (mc.sharedMesh != null) { // We can share the same triangle data across several instances of the same MeshCollider: if (!meshColliderData.TryGetValue(mc.sharedMesh, out shapeAndData)) { // Get current amount of triangle mesh shapes: int shapeIndex = Oni.GetShapeCount(oniColliderGroup, Oni.ShapeType.TriangleMesh); // Generate mesh collider triangle data and shape: Oni.TriangleMeshData data = new Oni.TriangleMeshData(mc); Oni.TriangleMeshShape shape = new Oni.TriangleMeshShape(mc, (oc != null)?oc.meshColliderType:Oni.TriangleMeshShape.MeshColliderType.ThinTwoSided, thickness, data.AddrOfVertexData(), data.AddrOfTriangleData()); // Pack both in a small wrapper: shapeAndData = new MeshColliderShapeAndData(data, shape, shapeIndex); // Tell Oni we want to define a new triangle mesh: Oni.SetTriangleMeshShapes(oniColliderGroup, new Oni.TriangleMeshShape[] { shapeAndData.meshShape }, 1, shapeIndex); Oni.UpdateTriangleMeshShapes(oniColliderGroup, 1, shapeIndex); // Store mesh collider data: meshColliderData[mc.sharedMesh] = shapeAndData; } Oni.SetColliders(oniColliderGroup, new Oni.Collider[] { new Oni.Collider(source, Oni.ShapeType.TriangleMesh, thickness, shapeAndData.shapeIndex, rigidBodyIndex, (oc != null)?oc.materialIndex:0) }, 1, Oni.GetColliderCount(oniColliderGroup)); } } else { Debug.LogWarning("Collider type " + source.GetType() + " not supported by Obi. Ignoring it."); } } }
public void SetSettings () { MapMagic magic = MapMagic.instance; terrain.heightmapPixelError = magic.pixelError; if (magic.showBaseMap) terrain.basemapDistance = magic.baseMapDist; else terrain.basemapDistance = 999999; terrain.castShadows = magic.castShadows; #if UNITY_2017_4_OR_NEWER terrain.reflectionProbeUsage = magic.reflectionProbeUsage; #endif if (terrainCollider==null) terrainCollider = terrain.GetComponent<TerrainCollider>(); if (terrainCollider!=null) terrainCollider.enabled = MapMagic.instance.applyColliders; #if UNITY_2018_3_OR_NEWER terrain.drawInstanced = magic.drawInstanced; terrain.allowAutoConnect = magic.autoConnect; #endif //material if (!Preview.enabled) { #if UNITY_2019_2_OR_NEWER if (MapMagic.instance.customTerrainMaterial != null) { //checking if customTerrainMaterial assigned as a terrain mat if (terrain.materialTemplate == MapMagic.instance.customTerrainMaterial) { Debug.LogError("Terrain material template == MM.customTerrainMaterial (" + terrain.materialTemplate + "," + terrain.materialTemplate.shader + ")"); terrain.materialTemplate = null; } //remove previous material if the shader doesn't match if (terrain.materialTemplate != null && terrain.materialTemplate.shader != MapMagic.instance.customTerrainMaterial.shader) { GameObject.DestroyImmediate(terrain.materialTemplate); //removes custom shader textures as well. Unity does not remove them! Resources.UnloadUnusedAssets(); terrain.materialTemplate = null; //need to reset material template to prevent unity crash } //duplicating material to terrain if (terrain.materialTemplate == null) { terrain.materialTemplate = new Material(MapMagic.instance.customTerrainMaterial); terrain.materialTemplate.name += " (Copy)"; } } #else terrain.materialType = MapMagic.instance.terrainMaterialType; if (MapMagic.instance.terrainMaterialType == Terrain.MaterialType.Custom && MapMagic.instance.assignCustomTerrainMaterial) terrain.materialTemplate = MapMagic.instance.customTerrainMaterial; #endif } terrain.drawTreesAndFoliage = magic.detailDraw; terrain.detailObjectDistance = magic.detailDistance; terrain.detailObjectDensity = magic.detailDensity; terrain.treeDistance = magic.treeDistance; terrain.treeBillboardDistance = magic.treeBillboardStart; terrain.treeCrossFadeLength = magic.treeFadeLength; terrain.treeMaximumFullLODCount = magic.treeFullLod; #if UNITY_EDITOR terrain.bakeLightProbesForTrees = magic.bakeLightProbesForTrees; #endif terrain.terrainData.wavingGrassSpeed = magic.windSpeed; terrain.terrainData.wavingGrassAmount = magic.windSize; terrain.terrainData.wavingGrassStrength = magic.windBending; terrain.terrainData.wavingGrassTint = magic.grassTint; //copy layer, tag, scripts from mm to terrains if (MapMagic.instance.copyLayersTags) { GameObject go = terrain.gameObject; go.layer = MapMagic.instance.gameObject.layer; go.isStatic = MapMagic.instance.gameObject.isStatic; try { go.tag = MapMagic.instance.gameObject.tag; } catch { Debug.LogError("MapMagic: could not copy object tag"); } } if (MapMagic.instance.copyComponents) { GameObject go = terrain.gameObject; MonoBehaviour[] components = MapMagic.instance.GetComponents<MonoBehaviour>(); for (int i=0; i<components.Length; i++) { if (components[i] is MapMagic || components[i] == null) continue; //if MapMagic itself or script not assigned if (terrain.gameObject.GetComponent(components[i].GetType()) == null) Extensions.CopyComponent(components[i], go); } } }
void DoClick(object sender, ClickedEventArgs e) { if (teleportOnClick) { // First get the current Transform of the the reference space (i.e. the Play Area, e.g. CameraRig prefab) var t = reference; if (t == null) { return; } // Get the current Y position of the reference space float refY = t.position.y; // Create a plane at the Y position of the Play Area // Then create a Ray from the origin of the controller in the direction that the controller is pointing Plane plane = new Plane(Vector3.up, -refY); Ray ray = new Ray(this.transform.position, transform.forward); // Set defaults bool hasGroundTarget = false; float dist = 0f; if (teleportType == TeleportType.TeleportTypeUseTerrain) // If we picked to use the terrain { RaycastHit hitInfo; TerrainCollider tc = Terrain.activeTerrain.GetComponent <TerrainCollider>(); hasGroundTarget = tc.Raycast(ray, out hitInfo, 1000f); dist = hitInfo.distance; } else if (teleportType == TeleportType.TeleportTypeUseCollider) // If we picked to use the collider { RaycastHit hitInfo; hasGroundTarget = Physics.Raycast(ray, out hitInfo); dist = hitInfo.distance; } else // If we're just staying flat on the current Y axis { // Intersect a ray with the plane that was created earlier // and output the distance along the ray that it intersects hasGroundTarget = plane.Raycast(ray, out dist); } if (hasGroundTarget) { // Get the current Camera (head) position on the ground relative to the world //Vector3 headPosOnGround = new Vector3(SteamVR_Render.Top().head.position.x, refY, SteamVR_Render.Top().head.position.z); // We need to translate the reference space along the same vector // that is between the head's position on the ground and the intersection point on the ground // i.e. intersectionPoint - headPosOnGround = translateVector // currentReferencePosition + translateVector = finalPosition /* * Vector3 createPoint = new Vector3(); * createPoint = t.position + (ray.origin + (ray.direction * dist)) + new Vector3(0,0,0); * Instantiate(createObj, createPoint, Quaternion.identity); //[createObjSelected] */ if (selectingStep == selectingStepEnum.units) { slpoint1 = t.position + (ray.origin + (ray.direction * dist)); selectingStep = selectingStepEnum.target; Debug.Log("SelectingStep set to target"); } else if (selectingStep == selectingStepEnum.target) { targetPoint = t.position + (ray.origin + (ray.direction * dist)); selectingStep = selectingStepEnum.none; Debug.Log("SelectingStep set to none"); GameObject[] list = GameObject.FindGameObjectsWithTag("Player's Unit"); for (int i = 0; i < list.Length; i++) { list[i].BroadcastMessage("Target", targetPoint); Debug.Log("TARGETED"); slpoint1 = new Vector3(0, 0, 0); slpoint2 = new Vector3(0, 0, 0); } } //teleport //t.position = t.position + (ray.origin + (ray.direction * dist)) - headPosOnGround; } } }
private void Update() { var trackedController = GetComponent <SteamVR_TrackedController>(); if (trackedController.triggerPressed) { // First get the current Transform of the the reference space (i.e. the Play Area, e.g. CameraRig prefab) var t = reference; if (t == null) { return; } // Get the current Y position of the reference space float refY = t.position.y; // Create a plane at the Y position of the Play Area // Then create a Ray from the origin of the controller in the direction that the controller is pointing Plane plane = new Plane(Vector3.up, -refY); Ray ray = new Ray(this.transform.position, transform.forward); // Set defaults bool hasGroundTarget = false; float dist = 0f; if (teleportType == TeleportType.TeleportTypeUseTerrain) // If we picked to use the terrain { RaycastHit hitInfo; TerrainCollider tc = Terrain.activeTerrain.GetComponent <TerrainCollider>(); hasGroundTarget = tc.Raycast(ray, out hitInfo, 1000f); dist = hitInfo.distance; } else if (teleportType == TeleportType.TeleportTypeUseCollider) // If we picked to use the collider { RaycastHit hitInfo; hasGroundTarget = Physics.Raycast(ray, out hitInfo); dist = hitInfo.distance; } else // If we're just staying flat on the current Y axis { // Intersect a ray with the plane that was created earlier // and output the distance along the ray that it intersects hasGroundTarget = plane.Raycast(ray, out dist); } if (hasGroundTarget && selectingStep == selectingStepEnum.target) { slpoint2 = t.position + (ray.origin + (ray.direction * dist)); } } else { if (selectingStep == selectingStepEnum.none) { selectingStep = selectingStepEnum.units; Debug.Log("SelectingStep set to units"); } } if (selectingStep == selectingStepEnum.target) { //x1 to x2 on z1 DrawLine(new Vector3(slpoint1.x, slpoint1.y + .1f, slpoint1.z), new Vector3(slpoint2.x, slpoint1.y + .1f, slpoint1.z), Color.blue); //x1 to x2 on z2 DrawLine(new Vector3(slpoint1.x, slpoint1.y + .1f, slpoint2.z), new Vector3(slpoint2.x, slpoint1.y + .1f, slpoint2.z), Color.blue); //z1 to z2 on x1 DrawLine(new Vector3(slpoint1.x, slpoint1.y + .1f, slpoint1.z), new Vector3(slpoint1.x, slpoint1.y + .1f, slpoint2.z), Color.blue); //z1 to z2 on x2 DrawLine(new Vector3(slpoint2.x, slpoint1.y + .1f, slpoint1.z), new Vector3(slpoint2.x, slpoint1.y + .1f, slpoint2.z), Color.blue); } }
public TerrainSurfaceRaycaster(GameObject surfaceObject, bool raycastReverse) : base(surfaceObject, raycastReverse) { _terrainCollider = surfaceObject.GetComponent <TerrainCollider>(); }
void DoClick(object sender, ClickedEventArgs e) { if (teleportOnClick) { // First get the current Transform of the the reference space (i.e. the Play Area, e.g. CameraRig prefab) var t = reference; if (t == null) { return; } // Get the current Y position of the reference space float refY = t.position.y; Debug.Log(refY.ToString()); // Create a plane at the Y position of the Play Area // Then create a Ray from the origin of the controller in the direction that the controller is pointing Plane plane = new Plane(Vector3.up, -refY); Ray ray = new Ray(this.transform.position, transform.forward); // Set defaults bool hasGroundTarget = false; float dist = 0f; if (teleportType == TeleportType.TeleportTypeUseTerrain) // If we picked to use the terrain { RaycastHit hitInfo; TerrainCollider tc = Terrain.activeTerrain.GetComponent <TerrainCollider>(); hasGroundTarget = tc.Raycast(ray, out hitInfo, 1000f); dist = hitInfo.distance; } else if (teleportType == TeleportType.TeleportTypeUseCollider) // If we picked to use the collider { RaycastHit hitInfo; hasGroundTarget = Physics.Raycast(ray, out hitInfo); dist = hitInfo.distance; } else // If we're just staying flat on the current Y axis { // Intersect a ray with the plane that was created earlier // and output the distance along the ray that it intersects hasGroundTarget = plane.Raycast(ray, out dist); } if (hasGroundTarget) { // Get the current Camera (head) position on the ground relative to the world Vector3 headPosOnGround = new Vector3(SteamVR_Render.Top().head.position.x, refY, SteamVR_Render.Top().head.position.z); // We need to translate the reference space along the same vector // that is between the head's position on the ground and the intersection point on the ground // i.e. intersectionPoint - headPosOnGround = translateVector // currentReferencePosition + translateVector = finalPosition //Teleport a maxdistance t.position = t.position + (ray.origin + (ray.direction * maxDist)) - headPosOnGround; //Make sure the player doesn't fly t.position = new Vector3(t.position.x, 0, t.position.z); //Make sure the player can't teleport again teleportOnClick = false; } } }
public void UpdateBodiesInfo() { oniColliders.Clear(); oniRigidbodies.Clear(); oniSpheres.Clear(); oniBoxes.Clear(); oniCapsules.Clear(); oniHeightmaps.Clear(); rigidbodyIDs.Clear(); for (int i = 0; i < colliders.Count; i++) { Collider source = colliders[i]; if (source == null) { continue; } Rigidbody rb = colliders[i].GetComponentInParent <Rigidbody>(); ObiCollider oc = colliders[i].GetComponent <ObiCollider>(); // Get the adequate rigidBodyIndex. If several colliders share a rigidbody, they'll get the same rigidBodyIndex. int rigidBodyIndex = -1; if (rb != null) { if (!rigidbodyIDs.TryGetValue(rb.GetInstanceID(), out rigidBodyIndex)) { rigidBodyIndex = oniRigidbodies.Count; oniRigidbodies.Add(new Oni.Rigidbody(rb)); rigidbodyIDs[rb.GetInstanceID()] = rigidBodyIndex; } } if (source is SphereCollider) { oniColliders.Add(new Oni.Collider(source, Oni.ShapeType.Sphere, oniSpheres.Count, rigidBodyIndex, (oc != null)?oc.materialIndex:0)); oniSpheres.Add(new Oni.SphereShape(source as SphereCollider)); } else if (source is BoxCollider) { oniColliders.Add(new Oni.Collider(source, Oni.ShapeType.Box, oniBoxes.Count, rigidBodyIndex, (oc != null)?oc.materialIndex:0)); oniBoxes.Add(new Oni.BoxShape(source as BoxCollider)); } else if (source is CapsuleCollider) { oniColliders.Add(new Oni.Collider(source, Oni.ShapeType.Capsule, oniCapsules.Count, rigidBodyIndex, (oc != null)?oc.materialIndex:0)); oniCapsules.Add(new Oni.CapsuleShape(source as CapsuleCollider)); } else if (source is CharacterController) { oniColliders.Add(new Oni.Collider(source, Oni.ShapeType.Capsule, oniCapsules.Count, rigidBodyIndex, (oc != null)?oc.materialIndex:0)); oniCapsules.Add(new Oni.CapsuleShape(source as CharacterController)); } else if (source is TerrainCollider) { TerrainCollider tc = source as TerrainCollider; if (!heightData.ContainsKey(tc)) { heightData[tc] = new Oni.HeightData(source as TerrainCollider); } oniColliders.Add(new Oni.Collider(source, Oni.ShapeType.Heightmap, oniHeightmaps.Count, rigidBodyIndex, (oc != null)?oc.materialIndex:0)); oniHeightmaps.Add(new Oni.HeightmapShape(source as TerrainCollider, heightData[tc].AddrOfHeightData())); } else { Debug.LogWarning(source.GetType() + " not supported by Obi. Ignoring it."); } } UpdateColliders(); UpdateRigidbodies(); UpdateSpheres(); UpdateBoxes(); UpdateCapsules(); UpdateHeightmaps(); }
private void Update() { if (Input.GetKeyDown(KeyCode.C)) { // Remove all projectors. while (m_DecalProjectors.Count > 0) { m_DecalsMesh.ClearAll(); m_DecalProjectors.Clear(); // Clearing of the decals mesh means we need to initialize it again. m_DecalsMesh.Initialize(m_DecalsInstance); } m_DecalsInstance.UpdateDecalsMeshes(m_DecalsMesh); } if (Input.GetButtonDown("Fire1")) { Ray l_Ray = Camera.main.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0.0f)); RaycastHit l_RaycastHit; if (Physics.Raycast(l_Ray, out l_RaycastHit, Mathf.Infinity)) { // Collider hit. // Make sure there are not too many projectors. if (m_DecalProjectors.Count >= m_MaximumNumberOfProjectors) { // If there are more than the maximum number of projectors, we delete // the oldest one. DecalProjector l_DecalProjector = m_DecalProjectors [0]; m_DecalProjectors.RemoveAt(0); m_DecalsMesh.RemoveProjector(l_DecalProjector); } // Calculate the position and rotation for the new decal projector. Vector3 l_ProjectorPosition = l_RaycastHit.point - (m_DecalProjectorOffset * l_Ray.direction.normalized); Quaternion l_ProjectorRotation = ProjectorRotationUtility.ProjectorRotation(Camera.main.transform.forward, Vector3.up); // Randomize the rotation. Quaternion l_RandomRotation = Quaternion.Euler(0.0f, Random.Range(0.0f, 360.0f), 0.0f); l_ProjectorRotation = l_ProjectorRotation * l_RandomRotation; TerrainCollider l_TerrainCollider = l_RaycastHit.collider as TerrainCollider; if (l_TerrainCollider != null) { // Terrain collider hit. Terrain l_Terrain = l_TerrainCollider.GetComponent <Terrain> (); if (l_Terrain != null) { // Create the decal projector with all the required information. DecalProjector l_DecalProjector = new DecalProjector(l_ProjectorPosition, l_ProjectorRotation, m_DecalProjectorScale, m_CullingAngle, m_MeshOffset, m_UVRectangleIndex, m_UVRectangleIndex, CurrentColor, 0.0f); // Add the projector to our list and the decals mesh, such that both are // synchronized. All the mesh data that is now added to the decals mesh // will belong to this projector. m_DecalProjectors.Add(l_DecalProjector); m_DecalsMesh.AddProjector(l_DecalProjector); // The terrain data has to be converted to the decals instance's space. Matrix4x4 l_TerrainToDecalsMatrix = m_WorldToDecalsMatrix * Matrix4x4.TRS(l_Terrain.transform.position, Quaternion.identity, Vector3.one); // Pass the terrain data with the corresponding conversion to the decals mesh. m_DecalsMesh.Add(l_Terrain, l_TerrainToDecalsMatrix); // Cut and offset the decals mesh. m_DecalsMeshCutter.CutDecalsPlanes(m_DecalsMesh); m_DecalsMesh.OffsetActiveProjectorVertices(); // The changes are only present in the decals mesh at the moment. We have // to pass them to the decals instance to visualize them. m_DecalsInstance.UpdateDecalsMeshes(m_DecalsMesh); // For the next hit, use a new uv rectangle. Usually, you would select the uv rectangle // based on the surface you have hit. NextUVRectangleIndex(); } else { Debug.LogError("Terrain is null!"); } } else { // We hit a collider. Next we have to find the mesh that belongs to the collider. // That step depends on how you set up your mesh filters and collider relative to // each other in the game objects. It is important to have a consistent way in order // to have a simpler implementation. MeshCollider l_MeshCollider = l_RaycastHit.collider.GetComponent <MeshCollider> (); MeshFilter l_MeshFilter = l_RaycastHit.collider.GetComponent <MeshFilter> (); if (l_MeshCollider != null || l_MeshFilter != null) { Mesh l_Mesh = null; if (l_MeshCollider != null) { // Mesh collider was hit. Just use the mesh data from that one. l_Mesh = l_MeshCollider.sharedMesh; } else if (l_MeshFilter != null) { // Otherwise take the data from the shared mesh. l_Mesh = l_MeshFilter.sharedMesh; } if (l_Mesh != null) { // Create the decal projector. DecalProjector l_DecalProjector = new DecalProjector(l_ProjectorPosition, l_ProjectorRotation, m_DecalProjectorScale, m_CullingAngle, m_MeshOffset, m_UVRectangleIndex, m_UVRectangleIndex, CurrentColor, 0.0f); // Add the projector to our list and the decals mesh, such that both are // synchronized. All the mesh data that is now added to the decals mesh // will belong to this projector. m_DecalProjectors.Add(l_DecalProjector); m_DecalsMesh.AddProjector(l_DecalProjector); // Get the required matrices. Matrix4x4 l_WorldToMeshMatrix = l_RaycastHit.collider.renderer.transform.worldToLocalMatrix; Matrix4x4 l_MeshToWorldMatrix = l_RaycastHit.collider.renderer.transform.localToWorldMatrix; // Add the mesh data to the decals mesh, cut and offset it. m_DecalsMesh.Add(l_Mesh, l_WorldToMeshMatrix, l_MeshToWorldMatrix); m_DecalsMeshCutter.CutDecalsPlanes(m_DecalsMesh); m_DecalsMesh.OffsetActiveProjectorVertices(); // The changes are only present in the decals mesh at the moment. We have // to pass them to the decals instance to visualize them. m_DecalsInstance.UpdateDecalsMeshes(m_DecalsMesh); // For the next hit, use a new uv rectangle. Usually, you would select the uv rectangle // based on the surface you have hit. NextUVRectangleIndex(); } } } } } }
public static ReadOnlyTerrainCollider AsReadOnly(this TerrainCollider self) => self.IsTrulyNull() ? null : new ReadOnlyTerrainCollider(self);
/// <summary> /// Create a terrain tile based on these settings /// </summary> /// <param name="tx">X location</param> /// <param name="tz">Z location</param> /// <param name="world">The array managing it</param> private void CreateTile(int tx, int tz, ref Terrain[,] world, GaiaResource resources) { if (tx < 0 || tx >= m_tilesX) { Debug.LogError("X value out of bounds"); return; } if (tz < 0 || tz >= m_tilesZ) { Debug.LogError("Z value out of bounds"); return; } //Look for issues in the terrain settings and fix them GetAndFixDefaults(); //this will center terrain at origin Vector2 m_offset = new Vector2(-m_terrainSize * m_tilesX * 0.5f, -m_terrainSize * m_tilesZ * 0.5f); //create the terrains if they dont already exist if (world.Length < m_tilesX) { world = new Terrain[m_tilesX, m_tilesZ]; } //Create the terrain Terrain terrain; TerrainData terrainData = new TerrainData(); terrainData.name = string.Format("Terrain_{0}_{1}-{2:yyyyMMdd-HHmmss}", tx, tz, DateTime.Now); terrainData.alphamapResolution = m_controlTextureResolution; terrainData.baseMapResolution = m_baseMapSize; terrainData.SetDetailResolution(m_detailResolution, m_detailResolutionPerPatch); terrainData.heightmapResolution = m_heightmapResolution; //terrainData.physicsMaterial = m_physicsMaterial; terrainData.wavingGrassAmount = m_bending; terrainData.wavingGrassSpeed = m_size; terrainData.wavingGrassStrength = m_speed; terrainData.wavingGrassTint = m_grassTint; terrainData.size = new Vector3(m_terrainSize, m_terrainHeight, m_terrainSize); #if UNITY_EDITOR AssetDatabase.CreateAsset(terrainData, string.Format("Assets/{0}.asset", terrainData.name)); #endif terrain = Terrain.CreateTerrainGameObject(terrainData).GetComponent <Terrain>(); terrain.name = terrainData.name; terrain.transform.position = new Vector3(m_terrainSize * tx + m_offset.x, 0, m_terrainSize * tz + m_offset.y); terrain.basemapDistance = m_baseMapDist; terrain.castShadows = m_castShadows; terrain.detailObjectDensity = m_detailDensity; terrain.detailObjectDistance = m_detailDistance; terrain.heightmapPixelError = m_pixelError; terrain.treeBillboardDistance = m_billboardStart; terrain.treeCrossFadeLength = m_fadeLength; terrain.treeDistance = m_treeDistance; terrain.treeMaximumFullLODCount = m_maxMeshTrees; #if UNITY_EDITOR terrain.bakeLightProbesForTrees = false; #endif if (m_material != null) { terrain.materialType = Terrain.MaterialType.Custom; terrain.materialTemplate = m_material; } if (m_physicsMaterial != null) { TerrainCollider collider = terrain.GetComponent <TerrainCollider>(); if (collider != null) { collider.material = m_physicsMaterial; } else { Debug.LogWarning("Unable to assign physics material to terrain!"); } } //Assign prototypes if (resources != null) { resources.ApplyPrototypesToTerrain(terrain); } else { terrain.Flush(); } //Save the new tile world[tx, tz] = terrain; //Parent it to the environment GameObject gaiaObj = GameObject.Find("Gaia Environment"); if (gaiaObj == null) { gaiaObj = new GameObject("Gaia Environment"); } terrain.transform.parent = gaiaObj.transform; }
public void SetSettings () { MapMagic magic = MapMagic.instance; terrain.heightmapPixelError = magic.pixelError; if (magic.showBaseMap) terrain.basemapDistance = magic.baseMapDist; else terrain.basemapDistance = 999999; terrain.castShadows = magic.castShadows; if (terrainCollider==null) terrainCollider = terrain.GetComponent<TerrainCollider>(); if (terrainCollider!=null) terrainCollider.enabled = MapMagic.instance.applyColliders; //material if (!Preview.enabled) { terrain.materialType = MapMagic.instance.terrainMaterialType; if (MapMagic.instance.terrainMaterialType == Terrain.MaterialType.Custom && MapMagic.instance.assignCustomTerrainMaterial) terrain.materialTemplate = MapMagic.instance.customTerrainMaterial; /*if (MapMagic.instance.terrainMaterialType == Terrain.MaterialType.Custom) { if (MapMagic.instance.materialTemplateMode) { //assigning new mat only it has different name or shader in template mode if (terrain.materialTemplate == null || terrain.materialTemplate.shader != MapMagic.instance.customTerrainMaterial.shader || terrain.materialTemplate.name != MapMagic.instance.customTerrainMaterial.name) terrain.materialTemplate = MapMagic.instance.customTerrainMaterial; } else terrain.materialTemplate = MapMagic.instance.customTerrainMaterial; // terrain.materialTemplate = MapMagic.instance.customTerrainMaterial; }*/ } terrain.drawTreesAndFoliage = magic.detailDraw; terrain.detailObjectDistance = magic.detailDistance; terrain.detailObjectDensity = magic.detailDensity; terrain.treeDistance = magic.treeDistance; terrain.treeBillboardDistance = magic.treeBillboardStart; terrain.treeCrossFadeLength = magic.treeFadeLength; terrain.treeMaximumFullLODCount = magic.treeFullLod; #if UNITY_EDITOR terrain.bakeLightProbesForTrees = magic.bakeLightProbesForTrees; #endif terrain.terrainData.wavingGrassSpeed = magic.windSpeed; terrain.terrainData.wavingGrassAmount = magic.windSize; terrain.terrainData.wavingGrassStrength = magic.windBending; terrain.terrainData.wavingGrassTint = magic.grassTint; //copy layer, tag, scripts from mm to terrains if (MapMagic.instance.copyLayersTags) { GameObject go = terrain.gameObject; go.layer = MapMagic.instance.gameObject.layer; go.isStatic = MapMagic.instance.gameObject.isStatic; try { go.tag = MapMagic.instance.gameObject.tag; } catch { Debug.LogError("MapMagic: could not copy object tag"); } } if (MapMagic.instance.copyComponents) { GameObject go = terrain.gameObject; MonoBehaviour[] components = MapMagic.instance.GetComponents<MonoBehaviour>(); for (int i=0; i<components.Length; i++) { if (components[i] is MapMagic || components[i] == null) continue; //if MapMagic itself or script not assigned if (terrain.gameObject.GetComponent(components[i].GetType()) == null) Extensions.CopyComponent(components[i], go); } } }
public void Initialize() { //this just creates a basic chunk object if (mInitialized) { return; } mChunkName = ChunkName(State); mChunkDataDirectory = ChunkDataDirectory(mChunkName); gameObject.name = mChunkName; //initialize assumes that the chunk state has been loaded transform.position = State.TileOffset; ChunkGroup = WIGroups.GetOrAdd(gameObject, mChunkName, WIGroups.Get.World, null); ChunkGroup.Props.IgnoreOnSave = true; Transforms.WorldItems.gameObject.SetActive(true); Transforms.AboveGroundWorldItems.gameObject.SetActive(true); Transforms.BelowGroundWorldItems.gameObject.SetActive(true); Transforms.AboveGroundStaticDistant.gameObject.SetActive(true); Mods.Get.Runtime.LoadMod <ChunkTriggerData> (ref TriggerData, mChunkDataDirectory, "Triggers"); Mods.Get.Runtime.LoadMod <ChunkNodeData> (ref NodeData, mChunkDataDirectory, "Nodes"); //Mods.Get.Runtime.LoadMod <ChunkSceneryData> (ref SceneryData, mChunkDataDirectory, "Scenery"); Mods.Get.Runtime.LoadMod <ChunkTerrainData> (ref TerrainData, mChunkDataDirectory, "Terrain"); /*for (int i = 0; i < SceneryData.AboveGround.RiverNames.Count; i++) { * //Debug.Log("Loading river " + SceneryData.AboveGround.RiverNames[i]); * River river = null; * if (Mods.Get.Runtime.LoadMod <River> (ref river, "River", SceneryData.AboveGround.RiverNames [i])) { * Rivers.Add (river); * } * }*/ CalculateBounds(); mChunkScale.Set(State.SizeX, Globals.ChunkMaximumYBounds, State.SizeZ); //load tree data if (Mods.Get.Runtime.LoadMod <ChunkTreeData> (ref TreeData, mChunkDataDirectory, "Trees")) { //update our tree instances with our offset and create our quad tree //make sure not to use the TreeInstances convenience property for (int i = 0; i < TreeData.TreeInstances.Length; i++) { TreeInstanceTemplate tit = TreeData.TreeInstances [i]; tit.ParentChunk = this; tit.ChunkOffset = ChunkOffset; tit.ChunkScale = ChunkScale; } TreeInstanceQuad = new QuadTree <TreeInstanceTemplate> ( ChunkBounds, Math.Max(TreeInstances.Length / QuadTreeMaxContentScaler, QuadTreeMaxContentMinimum), TreeData.TreeInstances); } //load plant data //make sure not to use the PlantInstances convenience property //it will return an empty array if (Mods.Get.Runtime.LoadMod <ChunkPlantData> (ref PlantData, mChunkDataDirectory, "Plants")) { for (int i = 0; i < PlantData.PlantInstances.Length; i++) { PlantInstanceTemplate pit = PlantData.PlantInstances [i]; pit.HasInstance = false; pit.ChunkOffset = ChunkOffset; pit.ChunkScale = ChunkScale; pit.ParentChunk = this; } PlantInstanceQuad = new QuadTree <PlantInstanceTemplate> ( ChunkBounds, Math.Max(PlantData.PlantInstances.Length / QuadTreeMaxContentScaler, QuadTreeMaxContentMinimum), PlantData.PlantInstances); } //Dictionary <string,Texture2D> matChunkMaps = new Dictionary <string, Texture2D> (); for (int groundIndex = 0; groundIndex < TerrainData.TextureTemplates.Count; groundIndex++) { TerrainTextureTemplate ttt = TerrainData.TextureTemplates [groundIndex]; Texture2D Diffuse = null; if (Mats.Get.GetTerrainGroundTexture(ttt.DiffuseName, out Diffuse)) { ChunkDataMaps.Add("Ground" + groundIndex.ToString(), Diffuse); } } ChunkDataMaps.Add("ColorOverlay", PrimaryTerrain.materialTemplate.GetTexture("_CustomColorMap") as Texture2D); ChunkDataMaps.Add("Splat1", PrimaryTerrain.materialTemplate.GetTexture("_Splat2") as Texture2D); ChunkDataMaps.Add("Splat2", PrimaryTerrain.materialTemplate.GetTexture("_Splat2") as Texture2D); Texture2D chunkMap = null; //Debug.Log ("Getting terrain color overlay in " + Name); /*if (Mods.Get.Runtime.ChunkMap (ref chunkMap, Name, "ColorOverlay")) { * ChunkDataMaps.Add ("ColorOverlay", chunkMap); * }*/ if (GameWorld.Get.ChunkMap(ref chunkMap, Name, "AboveGroundTerrainType")) { ChunkDataMaps.Add("AboveGroundTerrainType", chunkMap); } if (GameWorld.Get.ChunkMap(ref chunkMap, Name, "BelowGroundTerrainType")) { ChunkDataMaps.Add("BelowGroundTerrainType", chunkMap); } if (GameWorld.Get.ChunkMap(ref chunkMap, Name, "RegionData")) { ChunkDataMaps.Add("RegionData", chunkMap); } /*if (Mods.Get.Runtime.ChunkMap (ref chunkMap, Name, "Splat1")) { * ChunkDataMaps.Add ("Splat1", chunkMap); * } * if (Mods.Get.Runtime.ChunkMap (ref chunkMap, Name, "Splat2")) { * ChunkDataMaps.Add ("Splat2", chunkMap); * }*/ //now start coroutines that load the nodes CreateNodesAndTriggers(); //activate the main terrain PrimaryTerrain.gameObject.layer = Globals.LayerNumSolidTerrain; PrimaryTerrain.enabled = true; PrimaryCollider = PrimaryTerrain.GetComponent <TerrainCollider>(); //set the static objects DetailPrototype[] details = PrimaryTerrain.terrainData.detailPrototypes; for (int i = 0; i < details.Length; i++) { if (details[i].usePrototypeMesh) { if (details[i].renderMode == DetailRenderMode.VertexLit) { details[i].renderMode = DetailRenderMode.Grass; } if (details[i].prototype == null) { Debug.Log("DETAIL " + i + " WAS NULL IN CHUNK " + name); } else if (details[i].prototype.name.Contains("Static")) { details[i].dryColor = Colors.Alpha(details[i].dryColor, 0f); details[i].healthyColor = Colors.Alpha(details[i].healthyColor, 0f); } } } PrimaryTerrain.terrainData.detailPrototypes = details; //remove plant instance prefab, replace it with an empty one TreePrototype[] treePrototypes = PrimaryTerrain.terrainData.treePrototypes; for (int i = 0; i < treePrototypes.Length; i++) { if (treePrototypes[i].prefab == Plants.Get.PlantInstancePrefab) { treePrototypes[i].prefab = Plants.Get.RuntimePlantInstancePrefab; } } PrimaryTerrain.terrainData.treePrototypes = treePrototypes; if (ColliderTemplates != null) { Array.Clear(ColliderTemplates, 0, ColliderTemplates.Length); ColliderTemplates = null; Plants.Get.GetTerrainPlantPrototypes(treePrototypes, ref ColliderTemplates); } /*if (!GameManager.Get.NoTreesMode) { * TreePrototype[] treePrototypes = null; * if (ColliderTemplates != null) { * Array.Clear(ColliderTemplates, 0, ColliderTemplates.Length); * ColliderTemplates = null; * } * //Debug.Log("Getting tree prototypes for " + Name); * Plants.Get.GetTerrainPlantPrototypes(TerrainData.TreeTemplates, TreeData.TreeInstances, ref treePrototypes, ref ColliderTemplates); * //PrimaryTerrain.terrainData.treePrototypes = treePrototypes; * }*/ //turn everything off initially Transforms.AboveGroundStaticImmediate.gameObject.SetActive(false); Transforms.AboveGroundStaticAdjascent.gameObject.SetActive(false); Transforms.AboveGroundStaticDistant.gameObject.SetActive(false); Transforms.BelowGroundStatic.gameObject.SetActive(false); mInitialized = true; }
private void ExportTerrain(XmlWriter writer, TerrainData terrainData, TerrainCollider terrainCollider, string subPrefix, bool enabled, PrefabContext prefabContext) { if (terrainData == null) { return; } var subSubPrefix = subPrefix + "\t"; var terrainSize = terrainData.size; StartNode(writer, subPrefix); _engine.ScheduleAssetExport(terrainData, prefabContext); var(min, max, size) = GetTerrainSize(terrainData); var offset = new Vector3(terrainSize.x * 0.5f, terrainSize.y * min, terrainSize.z * 0.5f); WriteAttribute(writer, subPrefix, "Position", offset); StartComponent(writer, subPrefix, "Terrain", enabled); WriteAttribute(writer, subSubPrefix, "Height Map", "Image;" + _engine.EvaluateTerrainHeightMap(terrainData)); WriteAttribute(writer, subSubPrefix, "Material", "Material;" + _engine.EvaluateTerrainMaterial(terrainData)); //WriteTerrainMaterial(terrainData, materialFileName, "Textures/Terrains/" + folderAndName + ".Weights.tga"); var vertexSpacing = new Vector3(terrainSize.x / size.x, terrainSize.y * (max - min) / 255.0f, terrainSize.z / size.y); WriteAttribute(writer, subSubPrefix, "Vertex Spacing", vertexSpacing); EndElement(writer, subPrefix); if (terrainCollider != null) { StartComponent(writer, subPrefix, "CollisionShape", enabled); WriteCommonCollisionAttributes(writer, subSubPrefix, terrainCollider); WriteAttribute(writer, subSubPrefix, "Shape Type", "Terrain"); EndElement(writer, subPrefix); StartComponent(writer, subPrefix, "RigidBody", enabled); var localToWorldMatrix = terrainCollider.transform.localToWorldMatrix; var pos = localToWorldMatrix.MultiplyPoint(offset); WriteAttribute(writer, subPrefix, "Physics Position", pos); EndElement(writer, subPrefix); } if (terrainData.detailPrototypes.Length > 0) { StartNode(writer, subPrefix); var detailOffset = subPrefix + "\t"; StartComponent(writer, detailOffset, "StaticModel", enabled); EndElement(writer, detailOffset); EndElement(writer, subPrefix); } EndElement(writer, subPrefix); var numTrees = Math.Min(terrainData.treeInstances.Length, terrainData.treeInstanceCount); //numTrees = Math.Min(numTrees, 20); for (var index = 0; index < numTrees; index++) { var treeInstance = terrainData.treeInstances[index]; ExportTree(writer, subPrefix, treeInstance, terrainData, enabled, prefabContext); } }
/// <summary> /// Creates terrain from given volumeInfo for the given gameObject. /// If gameObject has a valid Terrain component, then it is reused. /// Similarly, if the Terrain component has a valid TerrainData, or if the given terrainData is valid, then it is used. /// Otherwise a new TerrainData is created and set to the Terrain. /// Populates the volumePositionOffset with the heightfield offset position. /// Returns true if successfully created the terrain, otherwise false. /// </summary> /// <param name="session">Houdini Engine session to query heightfield data from</param> /// <param name="volumeInfo">Volume info pertaining to the heightfield to generate the Terrain from</param> /// <param name="geoID">The geometry ID</param> /// <param name="partID">The part ID (height layer)</param> /// <param name="gameObject">The target GameObject containing the Terrain component</param> /// <param name="terrainData">A valid TerrainData to use, or if empty, a new one is created and populated</param> /// <param name="volumePositionOffset">Heightfield offset</param> /// <returns>True if successfully popupated the terrain</returns> public static bool GenerateTerrainFromVolume(HEU_SessionBase session, ref HAPI_VolumeInfo volumeInfo, HAPI_NodeId geoID, HAPI_PartId partID, GameObject gameObject, ref TerrainData terrainData, out Vector3 volumePositionOffset, ref Terrain terrain) { volumePositionOffset = Vector3.zero; if (volumeInfo.zLength == 1 && volumeInfo.tupleSize == 1) { // Heightfields will be converted to terrain in Unity. // Unity requires terrainData.heightmapResolution to be square power of two plus 1 (eg. 513, 257, 129, 65). // Houdini gives volumeInfo.xLength and volumeInfo.yLength which are the number of height values per dimension. // Note that volumeInfo.xLength and volumeInfo.yLength is equal to Houdini heightfield size / grid spacing. // The heightfield grid spacing is given as volumeTransformMatrix.scale but divided by 2 (grid spacing / 2 = volumeTransformMatrix.scale). // It is recommended to use grid spacing of 2. // Use the volumeInfo.transform to get the actual heightfield position and size. Matrix4x4 volumeTransformMatrix = HEU_HAPIUtility.GetMatrixFromHAPITransform(ref volumeInfo.transform, false); Vector3 position = HEU_HAPIUtility.GetPosition(ref volumeTransformMatrix); Vector3 scale = HEU_HAPIUtility.GetScale(ref volumeTransformMatrix); // Calculate real terrain size in both Houdini and Unity. // The height values will be mapped over this terrain size. float gridSpacingX = scale.x * 2f; float gridSpacingY = scale.y * 2f; float terrainSizeX = Mathf.Round((volumeInfo.xLength - 1) * gridSpacingX); float terrainSizeY = Mathf.Round((volumeInfo.yLength - 1) * gridSpacingY); //Debug.LogFormat("GS = {0},{1},{2}. SX = {1}. SY = {2}", gridSpacingX, gridSpacingY, terrainSizeX, terrainSizeY); //Debug.LogFormat("HeightField Pos:{0}, Scale:{1}", position, scale.ToString("{0.00}")); //Debug.LogFormat("HeightField tileSize:{0}, xLength:{1}, yLength:{2}", volumeInfo.tileSize.ToString("{0.00}"), volumeInfo.xLength.ToString("{0.00}"), volumeInfo.yLength.ToString("{0.00}")); //Debug.LogFormat("HeightField Terrain Size x:{0}, y:{1}", terrainSizeX.ToString("{0.00}"), terrainSizeY.ToString("{0.00}")); //Debug.LogFormat("HeightField minX={0}, minY={1}, minZ={2}", volumeInfo.minX.ToString("{0.00}"), volumeInfo.minY.ToString("{0.00}"), volumeInfo.minZ.ToString("{0.00}")); const int UNITY_MINIMUM_HEIGHTMAP_RESOLUTION = 33; if (terrainSizeX < UNITY_MINIMUM_HEIGHTMAP_RESOLUTION || terrainSizeY < UNITY_MINIMUM_HEIGHTMAP_RESOLUTION) { Debug.LogWarningFormat("Unity Terrain has a minimum heightmap resolution of {0}. This HDA heightmap size is {1}x{2}." + "\nPlease resize the terrain to a value higher than this.", UNITY_MINIMUM_HEIGHTMAP_RESOLUTION, terrainSizeX, terrainSizeY); return false; } bool bNewTerrain = false; bool bNewTerrainData = false; terrain = gameObject.GetComponent<Terrain>(); if (terrain == null) { terrain = gameObject.AddComponent<Terrain>(); bNewTerrain = true; } TerrainCollider collider = HEU_GeneralUtility.GetOrCreateComponent<TerrainCollider>(gameObject); // This ensures to reuse existing terraindata, and only creates new if none exist or none provided if (terrain.terrainData == null) { if (terrainData == null) { terrainData = new TerrainData(); bNewTerrainData = true; } terrain.terrainData = terrainData; SetTerrainMaterial(terrain); } terrainData = terrain.terrainData; collider.terrainData = terrainData; if (bNewTerrain) { #if UNITY_2018_3_OR_NEWER terrain.allowAutoConnect = true; // This has to be set after setting material terrain.drawInstanced = true; #endif } // Heightmap resolution must be square power-of-two plus 1. // Unity will automatically resize terrainData.heightmapResolution so need to handle the changed size (if Unity changed it). int heightMapResolution = volumeInfo.xLength; terrainData.heightmapResolution = heightMapResolution; int terrainResizedDelta = terrainData.heightmapResolution - heightMapResolution; if (terrainResizedDelta < 0) { Debug.LogWarningFormat("Note that Unity automatically resized terrain resolution to {0} from {1}. Use terrain size of power of two plus 1, and grid spacing of 2.", heightMapResolution, terrainData.heightmapResolution); heightMapResolution = terrainData.heightmapResolution; } else if (terrainResizedDelta > 0) { Debug.LogErrorFormat("Unsupported terrain size. Use terrain size of power of two plus 1, and grid spacing of 2. Given size is {0} but Unity resized it to {1}.", heightMapResolution, terrainData.heightmapResolution); return false; } int mapWidth = volumeInfo.xLength; int mapHeight = volumeInfo.yLength; // Get the converted height values from Houdini and find the min and max height range. float minHeight = 0; float maxHeight = 0; float heightRange = 0; float[] normalizedHeights = GetNormalizedHeightmapFromPartWithMinMax(session, geoID, partID, volumeInfo.xLength, volumeInfo.yLength, ref minHeight, ref maxHeight, ref heightRange); float[,] unityHeights = ConvertHeightMapHoudiniToUnity(heightMapResolution, heightMapResolution, normalizedHeights); // The terrainData.baseMapResolution is not set here, but rather left to whatever default Unity uses // The terrainData.alphamapResolution is set later when setting the alphamaps. if (bNewTerrainData) { // 32 is the default for resolutionPerPatch const int detailResolution = 1024; const int resolutionPerPatch = 32; terrainData.SetDetailResolution(detailResolution, resolutionPerPatch); } // Note SetHeights must be called before setting size in next line, as otherwise // the internal terrain size will not change after setting the size. terrainData.SetHeights(0, 0, unityHeights); // Note that Unity uses a default height range of 600 when a flat terrain is created. // Without a non-zero value for the height range, user isn't able to draw heights. // Therefore, set 600 as the value if height range is currently 0 (due to flat heightfield). if (heightRange == 0) { heightRange = terrainData.size.y > 1 ? terrainData.size.y : 600; } terrainData.size = new Vector3(terrainSizeX, heightRange, terrainSizeY); terrain.Flush(); // Unity Terrain has origin at bottom left, whereas Houdini uses centre of terrain. // Use volume bounds to set position offset when using split tiles float xmin, xmax, zmin, zmax, ymin, ymax, xcenter, ycenter, zcenter; session.GetVolumeBounds(geoID, partID, out xmin, out ymin, out zmin, out xmax, out ymax, out zmax, out xcenter, out ycenter, out zcenter); //Debug.LogFormat("xmin: {0}, xmax: {1}, ymin: {2}, ymax: {3}, zmin: {4}, zmax: {5}, xc: {6}, yc: {7}, zc: {8}", // xmin, xmax, ymin, ymax, zmin, zmax, xcenter, ycenter, zcenter); // Offset position is based on size of heightfield float offsetX = (float)heightMapResolution / (float)mapWidth; float offsetZ = (float)heightMapResolution / (float)mapHeight; //Debug.LogFormat("offsetX: {0}, offsetZ: {1}", offsetX, offsetZ); volumePositionOffset = new Vector3((terrainSizeX + xmin) * offsetX, minHeight + position.y, zmin * offsetZ); return true; } else { Debug.LogWarning("Non-heightfield volume type not supported!"); } return false; }
public override void Setup() { this.ignoredColliders = new ListDictionary <Collider, List <Collider> >(8); this.terrainCollider = (TerrainCollider)((Component)this.terrain).GetComponent <TerrainCollider>(); }
public static Vector3 ClosestPointOnSurface(TerrainCollider collider, Vector3 to, float radius) { var terrainData = collider.terrainData; var local = collider.transform.InverseTransformPoint(to); // Calculate the size of each tile on the terrain horizontally and vertically float pixelSizeX = terrainData.size.x / (terrainData.heightmapResolution - 1); float pixelSizeZ = terrainData.size.z / (terrainData.heightmapResolution - 1); var percentZ = Mathf.Clamp01(local.z / terrainData.size.z); var percentX = Mathf.Clamp01(local.x / terrainData.size.x); float positionX = percentX * (terrainData.heightmapResolution - 1); float positionZ = percentZ * (terrainData.heightmapResolution - 1); // Calculate our position, in tiles, on the terrain int pixelX = Mathf.FloorToInt(positionX); int pixelZ = Mathf.FloorToInt(positionZ); // Calculate the distance from our point to the edge of the tile we are in float distanceX = (positionX - pixelX) * pixelSizeX; float distanceZ = (positionZ - pixelZ) * pixelSizeZ; // Find out how many tiles we are overlapping on the X plane float radiusExtentsLeftX = radius - distanceX; float radiusExtentsRightX = radius - (pixelSizeX - distanceX); int overlappedTilesXLeft = radiusExtentsLeftX > 0 ? Mathf.FloorToInt(radiusExtentsLeftX / pixelSizeX) + 1 : 0; int overlappedTilesXRight = radiusExtentsRightX > 0 ? Mathf.FloorToInt(radiusExtentsRightX / pixelSizeX) + 1 : 0; // Find out how many tiles we are overlapping on the Z plane float radiusExtentsLeftZ = radius - distanceZ; float radiusExtentsRightZ = radius - (pixelSizeZ - distanceZ); int overlappedTilesZLeft = radiusExtentsLeftZ > 0 ? Mathf.FloorToInt(radiusExtentsLeftZ / pixelSizeZ) + 1 : 0; int overlappedTilesZRight = radiusExtentsRightZ > 0 ? Mathf.FloorToInt(radiusExtentsRightZ / pixelSizeZ) + 1 : 0; // Retrieve the heights of the pixels we are testing against int startPositionX = pixelX - overlappedTilesXLeft; int startPositionZ = pixelZ - overlappedTilesZLeft; int numberOfXPixels = overlappedTilesXRight + overlappedTilesXLeft + 1; int numberOfZPixels = overlappedTilesZRight + overlappedTilesZLeft + 1; // Account for if we are off the terrain if (startPositionX < 0) { numberOfXPixels -= Mathf.Abs(startPositionX); startPositionX = 0; } if (startPositionZ < 0) { numberOfZPixels -= Mathf.Abs(startPositionZ); startPositionZ = 0; } if (startPositionX + numberOfXPixels + 1 > terrainData.heightmapResolution) { numberOfXPixels = terrainData.heightmapResolution - startPositionX - 1; } if (startPositionZ + numberOfZPixels + 1 > terrainData.heightmapResolution) { numberOfZPixels = terrainData.heightmapResolution - startPositionZ - 1; } // Retrieve the heights of the tile we are in and all overlapped tiles var heights = terrainData.GetHeights(startPositionX, startPositionZ, numberOfXPixels + 1, numberOfZPixels + 1); // Pre-scale the heights data to be world-scale instead of 0...1 for (int i = 0; i < numberOfXPixels + 1; i++) { for (int j = 0; j < numberOfZPixels + 1; j++) { heights[j, i] *= terrainData.size.y; } } // Find the shortest distance to any triangle in the set gathered float shortestDistance = float.MaxValue; Vector3 shortestPoint = Vector3.zero; for (int x = 0; x < numberOfXPixels; x++) { for (int z = 0; z < numberOfZPixels; z++) { // Build the set of points that creates the two triangles that form this tile Vector3 a = new Vector3((startPositionX + x) * pixelSizeX, heights[z, x], (startPositionZ + z) * pixelSizeZ); Vector3 b = new Vector3((startPositionX + x + 1) * pixelSizeX, heights[z, x + 1], (startPositionZ + z) * pixelSizeZ); Vector3 c = new Vector3((startPositionX + x) * pixelSizeX, heights[z + 1, x], (startPositionZ + z + 1) * pixelSizeZ); Vector3 d = new Vector3((startPositionX + x + 1) * pixelSizeX, heights[z + 1, x + 1], (startPositionZ + z + 1) * pixelSizeZ); Vector3 nearest; BSPTree.ClosestPointOnTriangleToPoint(ref a, ref d, ref c, ref local, out nearest); float distance = (local - nearest).sqrMagnitude; if (distance <= shortestDistance) { shortestDistance = distance; shortestPoint = nearest; } BSPTree.ClosestPointOnTriangleToPoint(ref a, ref b, ref d, ref local, out nearest); distance = (local - nearest).sqrMagnitude; if (distance <= shortestDistance) { shortestDistance = distance; shortestPoint = nearest; } } } return(collider.transform.TransformPoint(shortestPoint)); }
public static bool GenerateTerrainFromVolume(HEU_SessionBase session, ref HAPI_VolumeInfo volumeInfo, HAPI_NodeId geoID, HAPI_PartId partID, GameObject gameObject, out TerrainData terrainData, out Vector3 volumePositionOffset) { terrainData = null; volumePositionOffset = Vector3.zero; if (volumeInfo.zLength == 1 && volumeInfo.tupleSize == 1) { // Heightfields will be converted to terrain in Unity. // Unity requires terrainData.heightmapResolution to be square power of two plus 1 (eg. 513, 257, 129, 65). // Houdini gives volumeInfo.xLength and volumeInfo.yLength which are the number of height values per dimension. // Note that volumeInfo.xLength and volumeInfo.yLength is equal to Houdini heightfield size / grid spacing. // The heightfield grid spacing is given as volumeTransformMatrix.scale but divided by 2 (grid spacing / 2 = volumeTransformMatrix.scale). // It is recommended to use grid spacing of 2. // Use the volumeInfo.transform to get the actual heightfield position and size. Matrix4x4 volumeTransformMatrix = HEU_HAPIUtility.GetMatrixFromHAPITransform(ref volumeInfo.transform, false); Vector3 position = HEU_HAPIUtility.GetPosition(ref volumeTransformMatrix); Vector3 scale = HEU_HAPIUtility.GetScale(ref volumeTransformMatrix); // Calculate real terrain size in both Houdini and Unity. // The height values will be mapped over this terrain size. float gridSpacingX = scale.x * 2f; float gridSpacingY = scale.y * 2f; //float gridSpacingZ = scale.z * 2f; float multiplierOffsetX = Mathf.Round(scale.x); float multiplierOffsetY = Mathf.Round(scale.y); //float multiplierOffsetZ = Mathf.Round(scale.z); float terrainSizeX = Mathf.Round(volumeInfo.xLength * gridSpacingX - multiplierOffsetX); float terrainSizeY = Mathf.Round(volumeInfo.yLength * gridSpacingY - multiplierOffsetY); //Debug.LogFormat("GS = {0},{1},{2}. SX = {1}. SY = {2}", gridSpacingX, gridSpacingY, gridSpacingZ, terrainSizeX, terrainSizeY); //Debug.LogFormat("HeightField Pos:{0}, Scale:{1}", position, scale.ToString("{0.00}")); //Debug.LogFormat("HeightField tileSize:{0}, xLength:{1}, yLength:{2}", volumeInfo.tileSize.ToString("{0.00}"), volumeInfo.xLength.ToString("{0.00}"), volumeInfo.yLength.ToString("{0.00}")); //Debug.LogFormat("HeightField Terrain Size x:{0}, y:{1}", terrainSizeX.ToString("{0.00}"), terrainSizeY.ToString("{0.00}")); //Debug.LogFormat("HeightField minX={0}, minY={1}, minZ={2}", volumeInfo.minX.ToString("{0.00}"), volumeInfo.minY.ToString("{0.00}"), volumeInfo.minZ.ToString("{0.00}")); const int UNITY_MINIMUM_HEIGHTMAP_RESOLUTION = 33; if (terrainSizeX < UNITY_MINIMUM_HEIGHTMAP_RESOLUTION || terrainSizeY < UNITY_MINIMUM_HEIGHTMAP_RESOLUTION) { Debug.LogWarningFormat("Unity Terrain has a minimum heightmap resolution of {0}. This HDA heightmap size is {1}x{2}." + "\nPlease resize the terrain to a value higher than this.", UNITY_MINIMUM_HEIGHTMAP_RESOLUTION, terrainSizeX, terrainSizeY); return false; } Terrain terrain = HEU_GeneralUtility.GetOrCreateComponent<Terrain>(gameObject); TerrainCollider collider = HEU_GeneralUtility.GetOrCreateComponent<TerrainCollider>(gameObject); if (terrain.terrainData == null) { terrain.terrainData = new TerrainData(); } terrainData = terrain.terrainData; collider.terrainData = terrainData; // Heightmap resolution must be square power-of-two plus 1. // Unity will automatically resize terrainData.heightmapResolution so need to handle the changed size (if Unity changed it). int heightMapResolution = volumeInfo.xLength; terrainData.heightmapResolution = heightMapResolution; int terrainResizedDelta = terrainData.heightmapResolution - heightMapResolution; if (terrainResizedDelta < 0) { Debug.LogWarningFormat("Note that Unity automatically resized terrain resolution to {0} from {1}. Use terrain size of power of two plus 1, and grid spacing of 2.", heightMapResolution, terrainData.heightmapResolution); heightMapResolution = terrainData.heightmapResolution; } else if(terrainResizedDelta > 0) { Debug.LogErrorFormat("Unsupported terrain size. Use terrain size of power of two plus 1, and grid spacing of 2. (delta = {0})", terrainResizedDelta); return false; } // Get the height values from Houdini and find the min and max height range. int totalHeightValues = volumeInfo.xLength * volumeInfo.yLength; float[] heightValues = new float[totalHeightValues]; bool bResult = HEU_GeneralUtility.GetArray2Arg(geoID, partID, session.GetHeightFieldData, heightValues, 0, totalHeightValues); if (!bResult) { return false; } float minHeight = heightValues[0]; float maxHeight = minHeight; for (int i = 0; i < totalHeightValues; ++i) { float f = heightValues[i]; if (f > maxHeight) { maxHeight = f; } else if (f < minHeight) { minHeight = f; } } const int UNITY_MAX_HEIGHT_RANGE = 65536; float heightRange = (maxHeight - minHeight); if (Mathf.RoundToInt(heightRange) > UNITY_MAX_HEIGHT_RANGE) { Debug.LogWarningFormat("Unity Terrain has maximum height range of {0}. This HDA height range is {1}, so it will be maxed out at {0}.\nPlease resize to within valid range!", UNITY_MAX_HEIGHT_RANGE, Mathf.RoundToInt(heightRange)); heightRange = UNITY_MAX_HEIGHT_RANGE; } int mapWidth = volumeInfo.xLength; int mapHeight = volumeInfo.yLength; int paddingWidth = heightMapResolution - mapWidth; int paddingLeft = Mathf.CeilToInt(paddingWidth * 0.5f); int paddingRight = heightMapResolution - paddingLeft; //Debug.LogFormat("Padding: Width={0}, Left={1}, Right={2}", paddingWidth, paddingLeft, paddingRight); int paddingHeight = heightMapResolution - mapHeight; int paddingTop = Mathf.CeilToInt(paddingHeight * 0.5f); int paddingBottom = heightMapResolution - paddingTop; //Debug.LogFormat("Padding: Height={0}, Top={1}, Bottom={2}", paddingHeight, paddingTop, paddingBottom); // Set height values at centre of the terrain, with padding on the sides if we resized float[,] unityHeights = new float[heightMapResolution, heightMapResolution]; for (int y = 0; y < heightMapResolution; ++y) { for (int x = 0; x < heightMapResolution; ++x) { if (y >= paddingTop && y < (paddingBottom) && x >= paddingLeft && x < (paddingRight)) { int ay = x - paddingLeft; int ax = y - paddingTop; // Unity expects normalized height values float h = heightValues[ay + ax * mapWidth] - minHeight; float f = h / heightRange; // Flip for right-hand to left-handed coordinate system int ix = x; int iy = heightMapResolution - (y + 1); // Unity expects height array indexing to be [y, x]. unityHeights[ix, iy] = f; } } } terrainData.baseMapResolution = heightMapResolution; terrainData.alphamapResolution = heightMapResolution; //int detailResolution = heightMapResolution; // 128 is the maximum for resolutionPerPatch const int resolutionPerPatch = 128; terrainData.SetDetailResolution(resolutionPerPatch, resolutionPerPatch); // Note SetHeights must be called before setting size in next line, as otherwise // the internal terrain size will not change after setting the size. terrainData.SetHeights(0, 0, unityHeights); terrainData.size = new Vector3(terrainSizeX, heightRange, terrainSizeY); terrain.Flush(); // Unity Terrain has origin at bottom left, whereas Houdini uses centre of terrain. // Use volume bounds to set position offset when using split tiles float xmin, xmax, zmin, zmax, ymin, ymax, xcenter, ycenter, zcenter; session.GetVolumeBounds(geoID, partID, out xmin, out ymin, out zmin, out xmax, out ymax, out zmax, out xcenter, out ycenter, out zcenter); //Debug.LogFormat("xmin: {0}, xmax: {1}, ymin: {2}, ymax: {3}, zmin: {4}, zmax: {5}, xc: {6}, yc: {7}, zc: {8}", // xmin, xmax, ymin, ymax, zmin, zmax, xcenter, ycenter, zcenter); // Offset position is based on size of heightfield float offsetX = (float)heightMapResolution / (float)mapWidth; float offsetZ = (float)heightMapResolution / (float)mapHeight; //Debug.LogFormat("offsetX: {0}, offsetZ: {1}", offsetX, offsetZ); //Debug.LogFormat("position.x: {0}, position.z: {1}", position.x, position.z); //volumePositionOffset = new Vector3(-position.x * offsetX, minHeight + position.y, position.z * offsetZ); volumePositionOffset = new Vector3((terrainSizeX + xmin) * offsetX, minHeight + position.y, zmin * offsetZ); return true; } else { Debug.LogWarning("Non-heightfield volume type not supported!"); } return false; }
// Use this for initialization void Start() { terrainCollider = GetComponent <TerrainCollider>(); }
public static void SectorizeTerrain(Terrain terrain, int sectorsWidth, int sectorsLength, int sectorsHeight, bool splitTerrain, bool createPortalGeo, bool includeStatic, bool includeDynamic) { if (!terrain) { Debug.LogWarning("Cannot sectorize null terrain."); return; } if (terrain.transform.root.GetComponentsInChildren <SECTR_Sector>().Length > 0) { Debug.LogWarning("Cannot sectorize terrain that is already part of a Sector."); } string undoString = "Sectorized " + terrain.name; if (sectorsWidth == 1 && sectorsLength == 1) { SECTR_Sector newSector = terrain.gameObject.AddComponent <SECTR_Sector>(); SECTR_Undo.Created(newSector, undoString); newSector.ForceUpdate(true); return; } if (splitTerrain && (!Mathf.IsPowerOfTwo(sectorsWidth) || !Mathf.IsPowerOfTwo(sectorsLength))) { Debug.LogWarning("Splitting terrain requires power of two sectors in width and length."); splitTerrain = false; } else if (splitTerrain && sectorsWidth != sectorsLength) { Debug.LogWarning("Splitting terrain requires same number of sectors in width and length."); splitTerrain = false; } int terrainLayer = terrain.gameObject.layer; Vector3 terrainSize = terrain.terrainData.size; float sectorWidth = terrainSize.x / sectorsWidth; float sectorHeight = terrainSize.y / sectorsHeight; float sectorLength = terrainSize.z / sectorsLength; int heightmapWidth = (terrain.terrainData.heightmapWidth / sectorsWidth); int heightmapLength = (terrain.terrainData.heightmapHeight / sectorsLength); int alphaWidth = terrain.terrainData.alphamapWidth / sectorsWidth; int alphaLength = terrain.terrainData.alphamapHeight / sectorsLength; int detailWidth = terrain.terrainData.detailWidth / sectorsWidth; int detailLength = terrain.terrainData.detailHeight / sectorsLength; string sceneDir = ""; string sceneName = ""; string exportFolder = splitTerrain ? SECTR_Asset.MakeExportFolder("TerrainSplits", false, out sceneDir, out sceneName) : ""; Transform baseTransform = null; if (splitTerrain) { GameObject baseObject = new GameObject(terrain.name); baseTransform = baseObject.transform; SECTR_Undo.Created(baseObject, undoString); } List <Transform> rootTransforms = new List <Transform>(); List <Bounds> rootBounds = new List <Bounds>(); _GetRoots(includeStatic, includeDynamic, rootTransforms, rootBounds); // Create Sectors string progressTitle = "Sectorizing Terrain"; int progressCounter = 0; EditorUtility.DisplayProgressBar(progressTitle, "Preparing", 0); SECTR_Sector[,,] newSectors = new SECTR_Sector[sectorsWidth, sectorsLength, sectorsHeight]; Terrain[,] newTerrains = splitTerrain ? new Terrain[sectorsWidth, sectorsLength] : null; for (int widthIndex = 0; widthIndex < sectorsWidth; ++widthIndex) { for (int lengthIndex = 0; lengthIndex < sectorsLength; ++lengthIndex) { for (int heightIndex = 0; heightIndex < sectorsHeight; ++heightIndex) { string newName = terrain.name + " " + widthIndex + "-" + lengthIndex + "-" + heightIndex; EditorUtility.DisplayProgressBar(progressTitle, "Creating sector " + newName, progressCounter++ / (float)(sectorsWidth * sectorsLength * sectorsHeight)); GameObject newSectorObject = new GameObject("SECTR " + newName + " Sector"); newSectorObject.transform.parent = baseTransform; Vector3 sectorCorner = new Vector3(widthIndex * sectorWidth, heightIndex * sectorHeight, lengthIndex * sectorLength) + terrain.transform.position; newSectorObject.transform.position = sectorCorner; newSectorObject.isStatic = true; SECTR_Sector newSector = newSectorObject.AddComponent <SECTR_Sector>(); newSector.OverrideBounds = !splitTerrain && (sectorsWidth > 1 || sectorsLength > 1); newSector.BoundsOverride = new Bounds(sectorCorner + new Vector3(sectorWidth * 0.5f, sectorHeight * 0.5f, sectorLength * 0.5f), new Vector3(sectorWidth, sectorHeight, sectorLength)); newSectors[widthIndex, lengthIndex, heightIndex] = newSector; if (splitTerrain && heightIndex == 0) { GameObject newTerrainObject = new GameObject(newName + " Terrain"); newTerrainObject.layer = terrainLayer; newTerrainObject.transform.parent = newSectorObject.transform; newTerrainObject.transform.localPosition = Vector3.zero; newTerrainObject.transform.localRotation = Quaternion.identity; newTerrainObject.transform.localScale = Vector3.one; newTerrainObject.isStatic = true; Terrain newTerrain = newTerrainObject.AddComponent <Terrain>(); newTerrain.terrainData = SECTR_Asset.Create <TerrainData>(exportFolder, newName, new TerrainData()); EditorUtility.SetDirty(newTerrain.terrainData); SECTR_VC.WaitForVC(); // Copy properties // Basic terrain properties newTerrain.editorRenderFlags = terrain.editorRenderFlags; newTerrain.castShadows = terrain.castShadows; newTerrain.heightmapMaximumLOD = terrain.heightmapMaximumLOD; newTerrain.heightmapPixelError = terrain.heightmapPixelError; newTerrain.lightmapIndex = -1; // Can't set lightmap UVs on terrain. newTerrain.materialTemplate = terrain.materialTemplate; // Copy geometric data int heightmapBaseX = widthIndex * heightmapWidth; int heightmapBaseY = lengthIndex * heightmapLength; int heightmapWidthX = heightmapWidth + (sectorsWidth > 1 ? 1 : 0); int heightmapWidthY = heightmapLength + (sectorsLength > 1 ? 1 : 0); newTerrain.terrainData.heightmapResolution = terrain.terrainData.heightmapResolution / sectorsWidth; newTerrain.terrainData.size = new Vector3(sectorWidth, terrainSize.y, sectorLength); newTerrain.terrainData.SetHeights(0, 0, terrain.terrainData.GetHeights(heightmapBaseX, heightmapBaseY, heightmapWidthX, heightmapWidthY)); // Copy alpha maps int alphaBaseX = alphaWidth * widthIndex; int alphaBaseY = alphaLength * lengthIndex; newTerrain.terrainData.splatPrototypes = terrain.terrainData.splatPrototypes; newTerrain.basemapDistance = terrain.basemapDistance; newTerrain.terrainData.baseMapResolution = terrain.terrainData.baseMapResolution / sectorsWidth; newTerrain.terrainData.alphamapResolution = terrain.terrainData.alphamapResolution / sectorsWidth; newTerrain.terrainData.SetAlphamaps(0, 0, terrain.terrainData.GetAlphamaps(alphaBaseX, alphaBaseY, alphaWidth, alphaLength)); // Copy detail info newTerrain.detailObjectDensity = terrain.detailObjectDensity; newTerrain.detailObjectDistance = terrain.detailObjectDistance; newTerrain.terrainData.detailPrototypes = terrain.terrainData.detailPrototypes; newTerrain.terrainData.SetDetailResolution(terrain.terrainData.detailResolution / sectorsWidth, 8); // TODO: extract detailResolutionPerPatch int detailBaseX = detailWidth * widthIndex; int detailBaseY = detailLength * lengthIndex; int numLayers = terrain.terrainData.detailPrototypes.Length; for (int layer = 0; layer < numLayers; ++layer) { newTerrain.terrainData.SetDetailLayer(0, 0, layer, terrain.terrainData.GetDetailLayer(detailBaseX, detailBaseY, detailWidth, detailLength, layer)); } // Copy grass and trees newTerrain.terrainData.wavingGrassAmount = terrain.terrainData.wavingGrassAmount; newTerrain.terrainData.wavingGrassSpeed = terrain.terrainData.wavingGrassSpeed; newTerrain.terrainData.wavingGrassStrength = terrain.terrainData.wavingGrassStrength; newTerrain.terrainData.wavingGrassTint = terrain.terrainData.wavingGrassTint; newTerrain.treeBillboardDistance = terrain.treeBillboardDistance; newTerrain.treeCrossFadeLength = terrain.treeCrossFadeLength; newTerrain.treeDistance = terrain.treeDistance; newTerrain.treeMaximumFullLODCount = terrain.treeMaximumFullLODCount; newTerrain.terrainData.treePrototypes = terrain.terrainData.treePrototypes; newTerrain.terrainData.RefreshPrototypes(); foreach (TreeInstance treeInstace in terrain.terrainData.treeInstances) { if (treeInstace.prototypeIndex >= 0 && treeInstace.prototypeIndex < newTerrain.terrainData.treePrototypes.Length && newTerrain.terrainData.treePrototypes[treeInstace.prototypeIndex].prefab) { Vector3 worldSpaceTreePos = Vector3.Scale(treeInstace.position, terrainSize) + terrain.transform.position; if (newSector.BoundsOverride.Contains(worldSpaceTreePos)) { Vector3 localSpaceTreePos = new Vector3((worldSpaceTreePos.x - newTerrain.transform.position.x) / sectorWidth, treeInstace.position.y, (worldSpaceTreePos.z - newTerrain.transform.position.z) / sectorLength); TreeInstance newInstance = treeInstace; newInstance.position = localSpaceTreePos; newTerrain.AddTreeInstance(newInstance); } } } // Copy physics #if UNITY_4_LATE newTerrain.terrainData.physicMaterial = terrain.terrainData.physicMaterial; #endif // Force terrain to rebuild newTerrain.Flush(); UnityEditor.EditorUtility.SetDirty(newTerrain.terrainData); SECTR_VC.WaitForVC(); newTerrain.enabled = false; newTerrain.enabled = true; TerrainCollider terrainCollider = terrain.GetComponent <TerrainCollider>(); if (terrainCollider) { TerrainCollider newCollider = newTerrainObject.AddComponent <TerrainCollider>(); #if !UNITY_4_LATE newCollider.sharedMaterial = terrainCollider.sharedMaterial; #endif newCollider.terrainData = newTerrain.terrainData; } newTerrains[widthIndex, lengthIndex] = newTerrain; SECTR_Undo.Created(newTerrainObject, undoString); } newSector.ForceUpdate(true); SECTR_Undo.Created(newSectorObject, undoString); _Encapsulate(newSector, rootTransforms, rootBounds, undoString); } } } // Create portals and neighbors progressCounter = 0; for (int widthIndex = 0; widthIndex < sectorsWidth; ++widthIndex) { for (int lengthIndex = 0; lengthIndex < sectorsLength; ++lengthIndex) { for (int heightIndex = 0; heightIndex < sectorsHeight; ++heightIndex) { EditorUtility.DisplayProgressBar(progressTitle, "Creating portals...", progressCounter++ / (float)(sectorsWidth * sectorsLength * sectorsHeight)); if (widthIndex < sectorsWidth - 1) { _CreatePortal(createPortalGeo, newSectors[widthIndex + 1, lengthIndex, heightIndex], newSectors[widthIndex, lengthIndex, heightIndex], baseTransform, undoString); } if (lengthIndex < sectorsLength - 1) { _CreatePortal(createPortalGeo, newSectors[widthIndex, lengthIndex + 1, heightIndex], newSectors[widthIndex, lengthIndex, heightIndex], baseTransform, undoString); } if (heightIndex > 0) { _CreatePortal(createPortalGeo, newSectors[widthIndex, lengthIndex, heightIndex], newSectors[widthIndex, lengthIndex, heightIndex - 1], baseTransform, undoString); } else if (splitTerrain) { SECTR_Sector terrainSector = newSectors[widthIndex, lengthIndex, 0]; terrainSector.LeftTerrain = widthIndex > 0 ? newSectors[widthIndex - 1, lengthIndex, 0] : null; terrainSector.RightTerrain = widthIndex < sectorsWidth - 1 ? newSectors[widthIndex + 1, lengthIndex, 0] : null; terrainSector.BottomTerrain = lengthIndex > 0 ? newSectors[widthIndex, lengthIndex - 1, 0] : null; terrainSector.TopTerrain = lengthIndex < sectorsLength - 1 ? newSectors[widthIndex, lengthIndex + 1, 0] : null; terrainSector.ConnectTerrainNeighbors(); // Blend together the seams of the alpha maps, which requires // going through all of the mip maps of all of the layer textures. // We have to blend here rather than when we set the alpha data (above) // because Unity computes mips and we need to blend all of the mips. Terrain newTerrain = newTerrains[widthIndex, lengthIndex]; // Use reflection trickery to get at the raw texture values. System.Reflection.PropertyInfo alphamapProperty = newTerrain.terrainData.GetType().GetProperty("alphamapTextures", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Static); // Get the texture we'll write into Texture2D[] alphaTextures = (Texture2D[])alphamapProperty.GetValue(newTerrain.terrainData, null); int numTextures = alphaTextures.Length; // Get the textures we'll read from Texture2D[] leftNeighborTextures = terrainSector.LeftTerrain != null ? (Texture2D[])alphamapProperty.GetValue(newTerrains[widthIndex - 1, lengthIndex].terrainData, null) : null; Texture2D[] rightNeighborTextures = terrainSector.RightTerrain != null ? (Texture2D[])alphamapProperty.GetValue(newTerrains[widthIndex + 1, lengthIndex].terrainData, null) : null; Texture2D[] topNeighborTextures = terrainSector.TopTerrain != null ? (Texture2D[])alphamapProperty.GetValue(newTerrains[widthIndex, lengthIndex + 1].terrainData, null) : null; Texture2D[] bottomNeighborTextures = terrainSector.BottomTerrain != null ? (Texture2D[])alphamapProperty.GetValue(newTerrains[widthIndex, lengthIndex - 1].terrainData, null) : null; for (int textureIndex = 0; textureIndex < numTextures; ++textureIndex) { Texture2D alphaTexture = alphaTextures[textureIndex]; Texture2D leftTexture = leftNeighborTextures != null ? leftNeighborTextures[textureIndex] : null; Texture2D rightTexture = rightNeighborTextures != null ? rightNeighborTextures[textureIndex] : null; Texture2D topTexture = topNeighborTextures != null ? topNeighborTextures[textureIndex] : null; Texture2D bottomTexture = bottomNeighborTextures != null ? bottomNeighborTextures[textureIndex] : null; int numMips = alphaTexture.mipmapCount; for (int mipIndex = 0; mipIndex < numMips; ++mipIndex) { Color[] alphaTexels = alphaTexture.GetPixels(mipIndex); int width = (int)Mathf.Sqrt(alphaTexels.Length); int height = width; for (int texelWidthIndex = 0; texelWidthIndex < width; ++texelWidthIndex) { for (int texelHeightIndex = 0; texelHeightIndex < height; ++texelHeightIndex) { // We can take advantage of the build order to average on the leading edges (right and top) // and then copy form the trailing edges (left and bottom) if (texelWidthIndex == 0 && leftTexture) { Color[] neighborTexels = leftTexture.GetPixels(mipIndex); alphaTexels[texelWidthIndex + texelHeightIndex * width] = neighborTexels[(width - 1) + (texelHeightIndex * width)]; } else if (texelWidthIndex == width - 1 && rightTexture) { Color[] neighborTexels = rightTexture.GetPixels(mipIndex); alphaTexels[texelWidthIndex + texelHeightIndex * width] += neighborTexels[0 + (texelHeightIndex * width)]; alphaTexels[texelWidthIndex + texelHeightIndex * width] *= 0.5f; } else if (texelHeightIndex == 0 && bottomTexture) { Color[] neighborTexels = bottomTexture.GetPixels(mipIndex); alphaTexels[texelWidthIndex + texelHeightIndex * width] = neighborTexels[texelWidthIndex + ((height - 1) * width)]; } else if (texelHeightIndex == height - 1 && topTexture) { Color[] neighborTexels = topTexture.GetPixels(mipIndex); alphaTexels[texelWidthIndex + texelHeightIndex * width] += neighborTexels[texelWidthIndex + (0 * width)]; alphaTexels[texelWidthIndex + texelHeightIndex * width] *= 0.5f; } } } alphaTexture.SetPixels(alphaTexels, mipIndex); } alphaTexture.Apply(false); } } } } } EditorUtility.ClearProgressBar(); // destroy original terrain if (splitTerrain) { SECTR_Undo.Destroy(terrain.gameObject, undoString); } }
private void Update() { if (Input.GetButtonDown("Fire1")) { Ray l_Ray = Camera.main.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0.0f)); RaycastHit l_RaycastHit; if (Physics.Raycast(l_Ray, out l_RaycastHit, Mathf.Infinity)) { // Collider hit. // Make sure there are not too many projectors. if (m_DecalProjectors.Count >= 50) { // If there are more than 50 projectors, we remove the first one from // our list and certainly from the decals mesh (the intermediate mesh // format). All the mesh data that belongs to this projector will // be removed. DecalProjector l_DecalProjector = m_DecalProjectors [0]; // The vertex color list has to be updated as well. m_VertexColors.RemoveRange(0, l_DecalProjector.DecalsMeshUpperVertexIndex + 1); m_DecalProjectors.RemoveAt(0); m_DecalsMesh.RemoveProjector(l_DecalProjector); } // Calculate the position and rotation for the new decal projector. Vector3 l_ProjectorPosition = l_RaycastHit.point - (decalProjectorOffset * l_Ray.direction.normalized); Vector3 l_ForwardDirection = Camera.main.transform.up; Vector3 l_UpDirection = -Camera.main.transform.forward; Quaternion l_ProjectorRotation = Quaternion.LookRotation(l_ForwardDirection, l_UpDirection); // Randomize the rotation. Quaternion l_RandomRotation = Quaternion.Euler(0.0f, Random.Range(0.0f, 360.0f), 0.0f); l_ProjectorRotation = l_ProjectorRotation * l_RandomRotation; TerrainCollider l_TerrainCollider = l_RaycastHit.collider as TerrainCollider; if (l_TerrainCollider != null) { // Terrain collider hit. Terrain l_Terrain = l_TerrainCollider.GetComponent <Terrain> (); if (l_Terrain != null) { // Create the decal projector with all the required information. DecalProjector l_DecalProjector = new DecalProjector(l_ProjectorPosition, l_ProjectorRotation, decalProjectorScale, cullingAngle, meshOffset, m_UVRectangleIndex, m_UVRectangleIndex); // Add the projector to our list and the decals mesh, such that both are // synchronized. All the mesh data that is now added to the decals mesh // will belong to this projector. m_DecalProjectors.Add(l_DecalProjector); m_DecalsMesh.AddProjector(l_DecalProjector); // The terrain data has to be converted to the decals instance's space. Matrix4x4 l_TerrainToDecalsMatrix = Matrix4x4.TRS(l_Terrain.transform.position, Quaternion.identity, Vector3.one) * m_WorldToDecalsMatrix; // Pass the terrain data with the corresponding conversion to the decals mesh. m_DecalsMesh.Add(l_Terrain, l_TerrainToDecalsMatrix); // Cut the data in the decals mesh accoring to the size and position of the decal projector. Offset the // vertices afterwards and pass the newly computed mesh to the decals instance, such that it becomes // visible. m_DecalsMeshCutter.CutDecalsPlanes(m_DecalsMesh); m_DecalsMesh.OffsetActiveProjectorVertices(); m_Decals.UpdateDecalsMeshes(m_DecalsMesh); // Update the vertex colors too. Color l_VertexColor = CurrentColor; int l_VertexCount = l_DecalProjector.DecalsMeshUpperVertexIndex - l_DecalProjector.DecalsMeshLowerVertexIndex + 1; for (int i = 0; i < l_VertexCount; i = i + 1) { m_VertexColors.Add(l_VertexColor); } m_Decals.DecalsMeshRenderers [0].MeshFilter.mesh.colors = m_VertexColors.ToArray(); // For the next hit, use a new uv rectangle. Usually, you would select the uv rectangle // based on the surface you have hit. NextUVRectangleIndex(); NextColorIndex(); } else { Debug.Log("Terrain is null!"); } } else { // We hit a collider. Next we have to find the mesh that belongs to the collider. // That step depends on how you set up your mesh filters and collider relative to // each other in the game objects. It is important to have a consistent way in order // to have a simpler implementation. MeshCollider l_MeshCollider = l_RaycastHit.collider.GetComponent <MeshCollider> (); MeshFilter l_MeshFilter = l_RaycastHit.collider.GetComponent <MeshFilter> (); if (l_MeshCollider != null || l_MeshFilter != null) { Mesh l_Mesh = null; if (l_MeshCollider != null) { // Mesh collider was hit. Just use the mesh data from that one. l_Mesh = l_MeshCollider.sharedMesh; } else if (l_MeshFilter != null) { // Otherwise take the data from the shared mesh. l_Mesh = l_MeshFilter.sharedMesh; } if (l_Mesh != null) { // Create the decal projector. DecalProjector l_DecalProjector = new DecalProjector(l_ProjectorPosition, l_ProjectorRotation, decalProjectorScale, cullingAngle, meshOffset, m_UVRectangleIndex, m_UVRectangleIndex); // Add the projector to our list and the decals mesh, such that both are // synchronized. All the mesh data that is now added to the decals mesh // will belong to this projector. m_DecalProjectors.Add(l_DecalProjector); m_DecalsMesh.AddProjector(l_DecalProjector); // Get the required matrices. Matrix4x4 l_WorldToMeshMatrix = l_RaycastHit.collider.renderer.transform.worldToLocalMatrix; Matrix4x4 l_MeshToWorldMatrix = l_RaycastHit.collider.renderer.transform.localToWorldMatrix; // Add the mesh data to the decals mesh, cut and offset it before we pass it // to the decals instance to be displayed. m_DecalsMesh.Add(l_Mesh, l_WorldToMeshMatrix, l_MeshToWorldMatrix); m_DecalsMeshCutter.CutDecalsPlanes(m_DecalsMesh); m_DecalsMesh.OffsetActiveProjectorVertices(); m_Decals.UpdateDecalsMeshes(m_DecalsMesh); // Update the vertex colors too. Color l_VertexColor = CurrentColor; int l_VertexCount = l_DecalProjector.DecalsMeshUpperVertexIndex - l_DecalProjector.DecalsMeshLowerVertexIndex + 1; for (int i = 0; i < l_VertexCount; i = i + 1) { m_VertexColors.Add(l_VertexColor); } m_Decals.DecalsMeshRenderers [0].MeshFilter.mesh.colors = m_VertexColors.ToArray(); // For the next hit, use a new uv rectangle. Usually, you would select the uv rectangle // based on the surface you have hit. NextUVRectangleIndex(); NextColorIndex(); } } } } } }
public Octave3DTerrainCollider(TerrainCollider terrainCollider) { _terrainCollider = terrainCollider; }