public void EditorTessEnd() { if (mTessellationJob == null || LOD == null) { return; } //save data MTQuadTreeHeader header = new MTQuadTreeHeader(DataName); header.QuadTreeDepth = QuadTreeDepth; header.BoundMin = VolumnBound.min; header.BoundMax = VolumnBound.max; header.LOD = LOD.Length; foreach (var m in mTessellationJob.mesh) { MTMeshHeader mh = new MTMeshHeader(m.meshId, m.center); header.Meshes.Add(m.meshId, mh); MTFileUtils.SaveMesh(DataName, m); } MTLog.Log("mesh saved!"); MTFileUtils.SaveQuadTreeHeader(DataName, header, Terrain.activeTerrain.terrainData.alphamapTextureCount); MTLog.Log("header saved!"); string matPath = "Assets/MightyTerrainMesh/Resources"; MTMatUtils.SaveMaterials(matPath, DataName, Terrain.activeTerrain); MTLog.Log("material saved!"); }
public MTRuntimeMesh(int meshid, int lod, string dataName) { MeshID = meshid; m_LODMeshs = new Mesh[lod]; //加载所有LOD mesh 存在数组里 MTFileUtils.LoadMesh(m_LODMeshs, dataName, meshid); }
public void EditorCreatePreview() { if (DataName == "") { Debug.LogError("data should have a name"); return; } try { Transform[] lodParent = new Transform[LOD.Length]; for (int i = 0; i < LOD.Length; ++i) { GameObject lodGo = new GameObject("LOD" + i); lodGo.transform.parent = transform; lodParent[i] = lodGo.transform; } MTQuadTreeHeader header = MTFileUtils.LoadQuadTreeHeader(DataName); Debug.LogError("RuntimeMats:" + header.RuntimeMats.Length); for (int i = 0; i < header.RuntimeMats.Length; i++) { Debug.LogError("name:" + header.RuntimeMats[i].name); } foreach (var m in header.Meshes.Values) { Mesh[] lods = new Mesh[LOD.Length]; MTFileUtils.LoadMesh(lods, DataName, m.MeshID); for (int i = 0; i < LOD.Length; ++i) { MeshFilter meshF; MeshRenderer meshR; GameObject meshGo = new GameObject("meshObj" + i); meshGo.transform.parent = lodParent[i]; meshF = meshGo.AddComponent <MeshFilter>(); meshR = meshGo.AddComponent <MeshRenderer>(); meshR.materials = header.RuntimeMats; meshF.sharedMesh = lods[i]; } } } catch { Debug.LogError("failed to load datas"); } }
private void Awake() { if (DataName == "") { return; } m_Go = new GameObject("Chunk"); m_Go.Reset(); try { m_Header = MTFileUtils.LoadQuadTreeHeader(DataName); m_Root = new MTQuadTreeNode(m_Header.QuadTreeDepth, m_Header.BoundMin, m_Header.BoundMax, Vector3.zero); foreach (var mh in m_Header.Meshes.Values) { m_Root.AddMesh(mh); } int gridMax = 1 << m_Header.QuadTreeDepth; //1<< X 相当于 2的X次方 mVisiblePatches = new MTArray <uint>(gridMax * gridMax); //(int)Mathf.Pow(2, 2 * m_Header.QuadTreeDepth) if (lodPolicy.Length < m_Header.LOD) { float[] policy = new float[m_Header.LOD]; for (int i = 0; i < lodPolicy.Length; ++i) { policy[i] = lodPolicy[i]; } lodPolicy = policy; } lodPolicy[0] = Mathf.Clamp(lodPolicy[0], 0.5f * m_Root.Bound.size.x / gridMax, lodPolicy[0]); lodPolicy[lodPolicy.Length - 1] = float.MaxValue; } catch { m_Header = null; m_Root = null; Debug.LogError("MTLoader load quadtree header failed"); } m_Camera = GetComponent <Camera>(); }
private ushort SpawnOnePixel(DetailPrototype detailLayer, int layer, float start_x, float start_z, float pixelSize, int subGridSize, int maxDen, float maxH, MemoryStream ms) { ushort spawnedCount = 0; float stride = pixelSize / subGridSize; int layerMask = 1 << layer; RaycastHit hit = new RaycastHit(); for (int sub_u = 0; sub_u < subGridSize; ++sub_u) { for (int sub_v = 0; sub_v < subGridSize; ++sub_v) { if (spawnedCount >= maxDen) { break; } Vector2 localuv = new Vector2(start_x + stride * sub_u, start_z + stride * sub_v); float noise = Mathf.PerlinNoise(localuv.x, localuv.y) * pixelSize; float xOffset = stride * sub_u + noise; float zOffset = stride * sub_v + noise; Vector3 top = new Vector3(start_x + xOffset, maxH, start_z + zOffset); if (Physics.Raycast(top, Vector3.down, out hit, 2 * maxH, layerMask)) { Vector2 noisexy = detailLayer.noiseSpread * localuv; float spread_noise = Mathf.PerlinNoise(noisexy.x, noisexy.y) * pixelSize; float width = detailLayer.minWidth + (detailLayer.maxWidth - detailLayer.minWidth) * spread_noise; float height = detailLayer.minHeight + (detailLayer.maxHeight - detailLayer.minHeight) * spread_noise; Color c = detailLayer.dryColor + (detailLayer.healthyColor - detailLayer.dryColor) * spread_noise; MTFileUtils.WriteVector3(ms, hit.point); MTFileUtils.WriteVector3(ms, new Vector3(width, height, width)); MTFileUtils.WriteColor(ms, c); ++spawnedCount; } else { UnityEngine.Debug.LogWarning("SpawnOnePixel hits no terrain mesh"); } } } return(spawnedCount); }
// Start is called before the first frame update void Start() { MemoryStream ms = new MemoryStream(Data.bytes); int totalCount = MTFileUtils.ReadInt(ms); positions = new Vector3[totalCount]; colors = new Color[totalCount]; int spawned = 0; while (ms.Position < ms.Length && spawned < totalCount) { ushort spawnedCount = MTFileUtils.ReadUShort(ms); for (int i = 0; i < spawnedCount; ++i) { positions[spawned] = MTFileUtils.ReadVector3(ms); var scale = MTFileUtils.ReadVector3(ms); colors[spawned] = MTFileUtils.ReadColor(ms); ++spawned; } } ms.Close(); }
private void Awake() { if (DataName == "") { return; } try { mHeader = MTFileUtils.LoadQuadTreeHeader(DataName); mRoot = new MTQuadTreeNode(mHeader.QuadTreeDepth, mHeader.BoundMin, mHeader.BoundMax); foreach (var mh in mHeader.Meshes.Values) { mRoot.AddMesh(mh); } int gridMax = 1 << mHeader.QuadTreeDepth; mVisiblePatches = new MTArray <uint>(gridMax * gridMax); if (lodPolicy.Length < mHeader.LOD) { float[] policy = new float[mHeader.LOD]; for (int i = 0; i < lodPolicy.Length; ++i) { policy[i] = lodPolicy[i]; } lodPolicy = policy; } lodPolicy[0] = Mathf.Clamp(lodPolicy[0], 0.5f * mRoot.Bound.size.x / gridMax, lodPolicy[0]); lodPolicy[lodPolicy.Length - 1] = float.MaxValue; } catch { mHeader = null; mRoot = null; MTLog.LogError("MTLoader load quadtree header failed"); } mCamera = GetComponent <Camera>(); }
public MTRuntimeMesh(int meshid, int lod, string dataName) { MeshID = meshid; mLOD = new Mesh[lod]; MTFileUtils.LoadMesh(mLOD, dataName, meshid); }
private void EditorGrabDetails(string folderName, Terrain terrainTarget, int layer) { if (terrainTarget == null) { MTLog.LogError("no active terrain"); return; } TerrainData data = terrainTarget.terrainData; int detailLayerCnt = data.detailPrototypes.Length; int dataLen = data.detailResolution * data.detailResolution; string[] savedAssets = new string[detailLayerCnt]; Bounds bound = data.bounds; float pixelSize = bound.size.x / data.detailResolution; for (int i = 0; i < detailLayerCnt; ++i) { MemoryStream ms = new MemoryStream(); int totalCnt = 0; MTFileUtils.WriteInt(ms, totalCnt); DetailPrototype proto = data.detailPrototypes[i]; //prototype info //raw data int[,] dlayer = data.GetDetailLayer(0, 0, data.detailResolution, data.detailResolution, i); for (int u = 0; u < data.detailResolution; ++u) { for (int v = 0; v < data.detailResolution; ++v) { ushort pixelDen = (ushort)dlayer[v, u]; //gen data inside if (pixelDen <= 0) { continue; } //data count long countOffset = ms.Position; MTFileUtils.WriteUShort(ms, pixelDen); int subGridSize = Mathf.CeilToInt(Mathf.Sqrt(pixelDen)); float start_x = bound.min.x + u * pixelSize; float start_z = bound.min.z + v * pixelSize; ushort spawnedCount = SpawnOnePixel(proto, layer, start_x, start_z, pixelSize, subGridSize, pixelDen, bound.max.y, ms); if (countOffset != pixelDen) { long posCache = ms.Position; ms.Position = countOffset; MTFileUtils.WriteUShort(ms, spawnedCount); ms.Position = posCache; } totalCnt += spawnedCount; EditorUtility.DisplayProgressBar("bake details", "processing", (float)(u * data.detailResolution + v) / (data.detailResolution * data.detailResolution)); } } ms.Position = 0; MTFileUtils.WriteInt(ms, totalCnt); savedAssets[i] = ExportDensity2Bytes(folderName, i, ms); ms.Close(); } EditorUtility.ClearProgressBar(); AssetDatabase.Refresh(); //add componet and parameter GameObject prefabObj = new GameObject("detail_layers"); prefabObj.transform.position = terrainTarget.transform.position; MTDetailBatchRenderer dr = prefabObj.AddComponent <MTDetailBatchRenderer>(); dr.layers = new MTDetailBatchRenderer.ProtoLayerInfo[detailLayerCnt]; for (int i = 0; i < detailLayerCnt; ++i) { DetailPrototype proto = data.detailPrototypes[i]; if (savedAssets[i] == null || proto.prototype == null) { continue; } MeshFilter mf = proto.prototype.GetComponent <MeshFilter>(); if (mf == null) { continue; } MeshRenderer mr = proto.prototype.GetComponent <MeshRenderer>(); if (mr == null) { continue; } TextAsset asset = AssetDatabase.LoadAssetAtPath <TextAsset>(savedAssets[i]); dr.layers[i] = new MTDetailBatchRenderer.ProtoLayerInfo(); dr.layers[i].detailData = asset; dr.layers[i].mesh = mf.sharedMesh; dr.layers[i].mat = mr.sharedMaterial; } dr.bnds = bound; //dr.detailShader = AssetDatabase.LoadAssetAtPath<ComputeShader>("Assets/MightyTerrainMesh/Shaders/TerrainDetailMask"); dr.detailDistance = Terrain.activeTerrain.detailObjectDistance; dr.detailDensity = Terrain.activeTerrain.detailObjectDensity; dr.detailResolution = data.detailResolution; dr.detailResolutionPerPatch = data.detailResolutionPerPatch; string prefabPath = Path.Combine(folderName, "detail_layers.prefab"); PrefabUtility.SaveAsPrefabAsset(prefabObj, prefabPath); DestroyImmediate(prefabObj); AssetDatabase.Refresh(); }
public BatchSplitor(byte[] data, Bounds bnd, Mesh mesh) { MemoryStream ms = new MemoryStream(data); TotalCount = MTFileUtils.ReadInt(ms); matrices = new NativeArray <float4x4>(TotalCount, Allocator.Persistent); batchIds = new NativeArray <int>(TotalCount, Allocator.Persistent); colors = new Vector4[TotalCount]; int spawned = 0; while (ms.Position < ms.Length && spawned < matrices.Length) { ushort spawnedCount = MTFileUtils.ReadUShort(ms); for (int i = 0; i < spawnedCount; ++i) { var pos = MTFileUtils.ReadVector3(ms); var scale = MTFileUtils.ReadVector3(ms); var color = MTFileUtils.ReadColor(ms); matrices[spawned] = float4x4.TRS(pos, Quaternion.identity, scale); colors[spawned] = color; batchIds[spawned] = 0; ++spawned; } } ms.Close(); if (spawned != TotalCount) { Debug.LogError("terrain detail layer total count is different with spawned count"); } //使用空间4叉树分batch if (TotalCount >= count_per_batch) { job = new MTDetailBatchSeparateJob(); job.batchIds = batchIds; job.spawnMatrix = matrices; NativeArray <SeparateBound> bounds = new NativeArray <SeparateBound>(4, Allocator.TempJob); int offset = 0; SplitBounds(TotalCount, bnd.min, bnd.max, bounds, ref offset); job.bounds = bounds; separateJobs = job.Schedule(TotalCount, 16); IsComplete = false; } else { SplitedBatches = new BatchParameters[1]; var param = new BatchParameters() { InstanceCount = TotalCount, Bnd = new Bounds(bnd.center, bnd.size) }; for (int i = 0; i < matrices.Length; ++i) { param.Matrices.Add(matrices[i]); param.Colors.Add(colors[i]); } SplitedBatches[0] = param; LogResults(); batchIds.Dispose(); matrices.Dispose(); IsComplete = true; } }