public void CalculateMesh() { #if WDEBUG if (!ThreadWorker.multithreading) { Profiler.BeginSample("Calculate Mesh"); } #endif //reading data if (meshWorker.stop) { return; } int margin = voxeland.meshMargin; //top and bottom points int topPoint; int bottomPoint; voxeland.data.GetTopBottomPoints(rect.Expanded(margin), out topPoint, out bottomPoint, ignoreEmptyColumns: true); //empty mesh check if (topPoint == 0) { hiWrapper = null; loWrapper = null; grassWrapper = null; indexToCoord = null; return; //exit to apply without stopping } //creating and filling matrix Matrix3 <byte> matrix = new Matrix3 <byte>(rect.offset.x - margin, bottomPoint - 1, rect.offset.z - margin, rect.size.x + margin * 2, topPoint - bottomPoint + 2, rect.size.z + margin * 2); voxeland.data.FillMatrix(matrix); //calculating verts if (meshWorker.stop) { return; } ChunkMesh.Face[] faces; Vector3[] verts; ChunkMesh.CalculateFaces(out faces, out verts, matrix, margin: margin); if (faces == null || verts == null || faces.Length == 0) { hiWrapper = null; loWrapper = null; return; } ChunkMesh.RelaxMesh(faces, verts, iterations: voxeland.relaxIterations, strength: voxeland.relaxStrength); hiWrapper = ChunkMesh.CalculateMesh(faces, verts, hipoly: true); loWrapper = ChunkMesh.CalculateMesh(faces, verts, hipoly: false); //normals if (meshWorker.stop) { return; } hiWrapper.normals = ChunkMesh.CalculateNormals(faces, verts, smoothIterations: voxeland.normalsSmooth); loWrapper.normals = hiWrapper.normals.Truncated(loWrapper.verts.Length); //types if (meshWorker.stop) { return; } int maxChannelsCount; switch (voxeland.channelEncoding) { case Voxeland.ChannelEncoding.MegaSplat: maxChannelsCount = 256; break; case Voxeland.ChannelEncoding.RTP: maxChannelsCount = 4; break; default: maxChannelsCount = 24; break; } maxChannelsCount = Mathf.Min(maxChannelsCount, voxeland.landTypes.array.Length); if (voxeland.channelEncoding == Voxeland.ChannelEncoding.Voxeland) { float[] chWeights = new float[verts.Length]; hiWrapper.tangents = new Vector4[verts.Length]; for (int ch = 0; ch < maxChannelsCount; ch++) { if (!ChunkMesh.IsChannelUsed(faces, ch)) { continue; } ChunkMesh.FillChWeights(faces, verts, ch, ref chWeights); ChunkMesh.EncodeChWeights(chWeights, ch, ref hiWrapper.tangents); } loWrapper.tangents = hiWrapper.tangents.Truncated(loWrapper.verts.Length); } else if (voxeland.channelEncoding == Voxeland.ChannelEncoding.MegaSplat) { float[] chWeights = new float[verts.Length]; hiWrapper.colors = new Color[verts.Length]; Vector4[] uvs2 = new Vector4[verts.Length]; byte[] topTypes = new byte[verts.Length]; byte[] secTypes = new byte[verts.Length]; float[] topBlends = new float[verts.Length]; //blend value of the top type float[] secBlends = new float[verts.Length]; ChunkMesh.EncodeMegaSplatFilters(faces, ref hiWrapper.colors); for (int ch = 0; ch < maxChannelsCount; ch++) { if (!ChunkMesh.IsChannelUsed(faces, ch)) { continue; } ChunkMesh.FillChWeights(faces, verts, ch, ref chWeights); ChunkMesh.FillMegaSplatChTopTypes(chWeights, ch, topTypes, secTypes, topBlends, secBlends); } ChunkMesh.EncodeMegaSplatChWeights(topTypes, secTypes, topBlends, secBlends, ref hiWrapper.colors, ref uvs2); hiWrapper.uvs4 = new List <Vector4> [4]; hiWrapper.uvs4[3] = new List <Vector4>(); //3rd channel hiWrapper.uvs4[3].AddRange(uvs2); hiWrapper.uv = new Vector2[hiWrapper.verts.Length]; for (int i = 0; i < hiWrapper.verts.Length; i++) { hiWrapper.uv[i] = new Vector2(-hiWrapper.verts[i].x, hiWrapper.verts[i].z); } hiWrapper.tangents = new Vector4[hiWrapper.verts.Length]; for (int i = 0; i < hiWrapper.verts.Length; i++) { hiWrapper.tangents[i] = new Vector4(0, 1, 0, 1); } } else if (voxeland.channelEncoding == Voxeland.ChannelEncoding.RTP) { float[] chWeights = new float[verts.Length]; hiWrapper.colors = new Color[verts.Length]; for (int ch = 0; ch < maxChannelsCount; ch++) { if (!ChunkMesh.IsChannelUsed(faces, ch)) { continue; } ChunkMesh.FillChWeights(faces, verts, ch, ref chWeights); ChunkMesh.EncodeColorChWeights(chWeights, ch, ref hiWrapper.colors); } loWrapper.colors = hiWrapper.colors.Truncated(loWrapper.verts.Length); } //index to coordinate array if (meshWorker.stop) { return; } indexToCoord = ChunkMesh.CalculateIndexToCoord(faces); //calculating grass if (meshWorker.stop) { return; } Matrix2 <byte> grassMatrix = new Matrix2 <byte>(rect); voxeland.data.FillGrass(grassMatrix); Matrix2 <ushort> topLevels = new Matrix2 <ushort>(rect); voxeland.data.FillHeightmap(topLevels); //offset lo border verts to prevent seams ChunkMesh.OffsetBorderVerts(loWrapper.verts, loWrapper.normals, rect.size.x, -0.15f); if (meshWorker.stop) { return; } grassWrapper = ChunkMesh.CalculateGrassMesh(grassMatrix, topLevels, loWrapper.verts, loWrapper.normals, loWrapper.tris, indexToCoord, voxeland.grassTypes); #if WDEBUG if (!ThreadWorker.multithreading) { Profiler.EndSample(); } #endif }
public void ApplyMesh() { #if WDEBUG Profiler.BeginSample("Apply Mesh"); #endif if (meshWorker.stop) { return; } //resetting collider if it has empty mesh before applying loMesh - otherwise it will not be refreshed (seems to be Unity bug) if (meshCollider.sharedMesh != null && meshCollider.sharedMesh.vertexCount == 0 && loWrapper != null && loWrapper.verts.Length != 0) { meshCollider.sharedMesh = null; } //apply meshes if (loWrapper == null || hiWrapper == null) { hiMesh.Clear(); loMesh.Clear(); grassMesh.Clear(); } else { //TODO: create new meshes instead re-using hiWrapper.ApplyTo(hiMesh); loWrapper.ApplyTo(loMesh); } //apply collider (in thread) if (colliderRequired) { colliderApplier.Start(); } else { Clear(collider: true); } //apply grass if (grassWrapper == null || grassWrapper.verts.Length == 0) { if (grassMesh != null) { grassMesh.Clear(); } } else { if (grassMesh == null) { grassMesh = new Mesh(); } //grassWrapper.uv3 = new Vector2[grassWrapper.verts.Length]; //for (int c=0; c<grassWrapper.uv3.Length; c++) grassWrapper.uv3[c] = new Vector2(1,1); grassWrapper.ApplyTo(grassMesh); grassFilter.sharedMesh = grassMesh; } //renaming meshes to provide highlight change int meshVer = 0; if (hiMesh.name.Contains("VoxelandTerrainHi")) { System.Int32.TryParse(hiMesh.name.Replace("VoxelandTerrainHi", ""), out meshVer); } if (meshVer > 100000000) { meshVer = 0; } hiMesh.name = "VoxelandTerrainHi" + (meshVer + 1); loMesh.name = "VoxelandTerrainLo" + (meshVer + 1); //purging wrappers loWrapper = null; hiWrapper = null; grassWrapper = null; //making ambient to apply this frame to prevent flickering if (!ambientWorker.calculated) { Debug.LogError("Ambient is not calculated"); //this should not happen } ambientWorker.FinalizeNow(); //forcing ambient to apply this frame (without condition!) /* Disabling seams flashes. Do not forget to use new meshes and disable mesh assigning in lod switch * foreach (Chunk chunk in voxeland.chunks.All()) * { * if (chunk.meshFilter.sharedMesh == null) * chunk.meshFilter.sharedMesh = chunk.loMesh; * * else if (chunk.meshRenderer.enabled && chunk.meshFilter.sharedMesh != chunk.hiMesh && chunk.meshFilter.sharedMesh != chunk.loMesh) //if nmew mesh not assigned * { * bool allReady = true; * foreach(Coord neigCoord in coord.Neightbours()) * { * if (voxeland.chunks[neigCoord] == null || voxeland.chunks[neigCoord].meshWorker == null) continue; * if (!voxeland.chunks[neigCoord].meshWorker.ready) allReady = false; * } * * if (allReady) * { * if (chunk.meshFilter.sharedMesh.name.Contains("TerrainHi")) * chunk.meshFilter.sharedMesh = chunk.hiMesh; * else chunk.meshFilter.sharedMesh = chunk.loMesh; * } * } * } * }*/ //enabling renderer on apply if (meshRequired && !meshRenderer.enabled) { meshRenderer.enabled = true; voxeland.CallOnChunkVisibilityChanged(this, true); } #if WDEBUG Profiler.EndSample(); #endif }
public void Append(MeshWrapper addMesh, Vector3 offset = new Vector3(), float size = 1, float height = 1) //custom verts length can be larger than addMesh vert count { /*for (int v=0; v<addMesh.verts.Length; v++) //TODO test iteration inside "if" * { * if (customVerts==null) verts[vertCounter+v] = addMesh.verts[v];// + offset; * else verts[vertCounter+v] = customVerts[v]; * * if (normals!=null && addMesh.normals!=null) * { * if (customNormals==null) normals[vertCounter+v] = addMesh.normals[v]; * else normals[vertCounter+v] = customNormals[v]; * } * * if (uv!=null && addMesh.uv!=null) uv[vertCounter+v] = addMesh.uv[v]; * if (uv2!=null && addMesh.uv2!=null) uv2[vertCounter+v] = addMesh.uv2[v]; * if (uv3!=null && addMesh.uv3!=null) uv3[vertCounter+v] = addMesh.uv3[v]; * if (uv4!=null && addMesh.uv4!=null) uv4[vertCounter+v] = addMesh.uv4[v]; * if (colors!=null && addMesh.colors!=null) colors[vertCounter+v] = addMesh.colors[v]; * if (tangents!=null && addMesh.tangents!=null) tangents[vertCounter+v] = addMesh.tangents[v]; * }*/ //this definitely works faster: //standard case (verts+normals+uv) if (normals != null && uv != null && addMesh.normals != null && normals.Length != 0 && addMesh.normals.Length != 0 && addMesh.uv != null && uv.Length != 0 && addMesh.uv.Length != 0) { for (int v = 0; v < addMesh.verts.Length; v++) { int v2 = vertCounter + v; verts[v2] = new Vector3(addMesh.verts[v].x * size + offset.x, addMesh.verts[v].y * size * height + offset.y, addMesh.verts[v].z * size + offset.z); normals[v2] = addMesh.normals[v]; uv[v2] = addMesh.uv[v]; } } //special cases when there's no normals or uvs else { for (int v = 0; v < addMesh.verts.Length; v++) { verts[vertCounter + v] = new Vector3(addMesh.verts[v].x * size + offset.x, addMesh.verts[v].y * size * height + offset.y, addMesh.verts[v].z * size + offset.z); } if (normals != null && addMesh.normals != null && normals.Length != 0 && addMesh.normals.Length != 0) { for (int v = 0; v < addMesh.normals.Length; v++) { normals[vertCounter + v] = addMesh.normals[v]; } } if (uv != null && addMesh.uv != null && uv.Length != 0 && addMesh.uv.Length != 0) { for (int v = 0; v < addMesh.verts.Length; v++) { uv[vertCounter + v] = addMesh.uv[v]; } } } //additional cases if (uv2 != null && addMesh.uv2 != null && uv2.Length != 0 && addMesh.uv2.Length != 0) { for (int v = 0; v < addMesh.verts.Length; v++) { uv2[vertCounter + v] = addMesh.uv2[v]; } } if (uv3 != null && addMesh.uv3 != null && uv3.Length != 0 && addMesh.uv3.Length != 0) { for (int v = 0; v < addMesh.verts.Length; v++) { uv3[vertCounter + v] = addMesh.uv3[v]; } } if (uv4 != null && addMesh.uv4 != null && uv4.Length != 0 && addMesh.uv4.Length != 0) { for (int v = 0; v < addMesh.verts.Length; v++) { uv4[vertCounter + v] = addMesh.uv4[v]; } } if (uv != null && addMesh.uv != null && uv.Length != 0 && addMesh.uv.Length != 0) { for (int v = 0; v < addMesh.verts.Length; v++) { uv[vertCounter + v] = addMesh.uv[v]; } } if (colors != null && addMesh.colors != null && colors.Length != 0 && addMesh.colors.Length != 0) { for (int v = 0; v < addMesh.verts.Length; v++) { colors[vertCounter + v] = addMesh.colors[v]; } } if (tangents != null && addMesh.tangents != null && tangents.Length != 0 && addMesh.tangents.Length != 0) { for (int v = 0; v < addMesh.verts.Length; v++) { tangents[vertCounter + v] = addMesh.tangents[v]; } } //tris for (int t = 0; t < addMesh.tris.Length; t++) { tris[triCounter + t] = addMesh.tris[t] + vertCounter; } //counters vertCounter += addMesh.verts.Length; triCounter += addMesh.tris.Length; }