/////////////////////////////////////////////////////////////////////////////// // Overridable functions /////////////////////////////////////////////////////////////////////////////// // TODO: check border change if sliced // ------------------------------------------------------------------ // Desc: // ------------------------------------------------------------------ internal override exUpdateFlags UpdateBuffers(exList <Vector3> _vertices, exList <Vector2> _uvs, exList <Color32> _colors32, exList <int> _indices) { exDebug.Assert(textureInfo_ != null, "textureInfo_ == null"); switch (spriteType_) { case exSpriteType.Simple: SpriteBuilder.SimpleUpdateBuffers(this, textureInfo_, useTextureOffset_, Space.Self, _vertices, _uvs, _indices, 0, 0); break; case exSpriteType.Sliced: SpriteBuilder.SlicedUpdateBuffers(this, textureInfo_, useTextureOffset_, Space.Self, _vertices, _uvs, _indices, 0, 0); break; case exSpriteType.Tiled: SpriteBuilder.TiledUpdateBuffers(this, textureInfo_, useTextureOffset_, tiledSpacing_, Space.Self, _vertices, _uvs, _indices, 0, 0); break; case exSpriteType.Diced: SpriteBuilder.DicedUpdateBuffers(this, textureInfo_, useTextureOffset_, Space.Self, _vertices, _uvs, _indices, 0, 0); break; } if ((updateFlags & exUpdateFlags.Color) != 0 && _colors32 != null) { Color32 color32 = new Color(color_.r, color_.g, color_.b, color_.a); for (int i = 0; i < vertexCount_; ++i) { _colors32.buffer [i] = color32; } } exUpdateFlags applyedFlags = updateFlags; updateFlags = exUpdateFlags.None; return(applyedFlags); }
// ------------------------------------------------------------------ /// Get sprite's geometry data // ------------------------------------------------------------------ public void GetBuffers(exList <Vector3> _vertices, exList <Vector2> _uvs, exList <Color32> _colors, exList <int> _indices = null) { _vertices.Clear(); _uvs.Clear(); if (_indices != null) { _indices.Clear(); } if (visible) { UpdateTransform(); exUpdateFlags originalFlags = updateFlags; int originalVertexBufferIndex = vertexBufferIndex; int originalIndexBufferIndex = indexBufferIndex; FillBuffers(_vertices, _uvs, _colors); if (_indices != null) { _indices.AddRange(indexCount); } indexBufferIndex = 0; updateFlags |= exUpdateFlags.Index; UpdateBuffers(_vertices, _uvs, _colors, _indices); vertexBufferIndex = originalVertexBufferIndex; indexBufferIndex = originalIndexBufferIndex; updateFlags = originalFlags; } }
// ------------------------------------------------------------------ /// Apply all buffer changes // ------------------------------------------------------------------ public void Apply(exUpdateFlags _additionalUpdateFlags = exUpdateFlags.None) { updateFlags |= _additionalUpdateFlags; Mesh mesh; if (isDynamic && updateFlags != exUpdateFlags.None) { mesh = SwapMeshBuffer(); } else { mesh = GetMeshBuffer(); } FlushBuffers(mesh, updateFlags, vertices, indices, uvs, colors32); #if EX_DEBUG if (mesh.uv.Length != mesh.vertices.Length) { Debug.LogError("Shader wants texture coordinates... uv.Length != vertex.Length"); OutputDebugInfo(true); } #endif if ((updateFlags & exUpdateFlags.Vertex) != 0) { // 当fast hide sprite时,判断是否可以隐藏整个mesh,减少draw call bool visible = false; if (spriteList.Count > 10) { visible = true; // 如果有大量sprite,不遍历整个列表,强制可见 } else { for (int i = 0, count = spriteList.Count; i < count; ++i) { if (spriteList[i].visible) { visible = true; break; } } } if (gameObject.activeSelf != visible) { gameObject.SetActive(visible); } } else if ((updateFlags & exUpdateFlags.Index) != 0) { // 如果index改变了则也有可能改变显隐,这种情况不多只是基本简单检查下索引数量就够 bool visible = (indices.Count > 0); if (gameObject.activeSelf != visible) { gameObject.SetActive(visible); } } updateFlags = exUpdateFlags.None; }
// ------------------------------------------------------------------ /// Compact all reserved buffers // ------------------------------------------------------------------ public void Compact() { // 其实mscorlib在实现TrimExcess时还是会预留10%的buffer spriteList.TrimExcess(); vertices.TrimExcess(); indices.TrimExcess(); uvs.TrimExcess(); colors32.TrimExcess(); updateFlags |= (exUpdateFlags.Color | exUpdateFlags.UV | exUpdateFlags.Normal); //need to flush to mesh if defined exLayer.LAZY_UPDATE_BUFFER_TAIL }
// ------------------------------------------------------------------ /// If we are using dynamic layer, the mesh is double buffered so that we can get the best performance on iOS devices. /// http://forum.unity3d.com/threads/118723-Huge-performance-loss-in-Mesh.CreateVBO-for-dynamic-meshes-IOS // ------------------------------------------------------------------ private Mesh SwapMeshBuffer() { exDebug.Assert(isDynamic); if (enableDoubleBuffer) { isEvenMeshBuffer = !isEvenMeshBuffer; exUpdateFlags currentBufferUpdate = updateFlags; updateFlags |= lastUpdateFlags; // combine changes during two frame lastUpdateFlags = currentBufferUpdate; // for next buffer } return(GetMeshBuffer()); }
// ------------------------------------------------------------------ /// Actually apply all buffer changes to mesh. // ------------------------------------------------------------------ public static void FlushBuffers(Mesh _mesh, exUpdateFlags _updateFlags, exList <Vector3> _vertices, exList <int> _indices, exList <Vector2> _uvs, exList <Color32> _colors32) { if ((_updateFlags & exUpdateFlags.VertexAndIndex) == exUpdateFlags.VertexAndIndex) { // if we need to update vertices, we should also clear triangles first _mesh.triangles = null; // we dont use Mesh.Clear() since sometimes it goes wrong when the uv and color have not been updated } if ((_updateFlags & exUpdateFlags.Vertex) != 0 || (_updateFlags & exUpdateFlags.Index) != 0) // if we need to update triangles, we should also update vertices, or mesh will become invisible { _mesh.vertices = _vertices.FastToArray(); } if ((_updateFlags & exUpdateFlags.UV) != 0) { _mesh.uv = _uvs.FastToArray(); } if ((_updateFlags & exUpdateFlags.Color) != 0) { _mesh.colors32 = _colors32.FastToArray(); } if ((_updateFlags & exUpdateFlags.Index) != 0) { _mesh.triangles = _indices.FastToArray(); // we do update triangles here //string bufv = "Vertex Buffer: "; //string vertices = "Mesh.vertices[" + _mesh.vertices.Length + "]: "; //foreach (var v in _mesh.vertices) { // bufv += v; // bufv += ", "; //} //Debug.Log(bufv); //var buf = _indices.FastToArray(); //string triangles = "Mesh.indices[" + buf.Length + "]: "; //foreach (var index in buf) { // triangles += index; // triangles += ","; //} //Debug.Log(triangles); } if ((_updateFlags & exUpdateFlags.Index) != 0 || (_updateFlags & exUpdateFlags.Vertex) != 0) { _mesh.RecalculateBounds(); // Sometimes Unity will not automatically recalculate the bounding volume. } if ((_updateFlags & exUpdateFlags.Normal) != 0) { Vector3[] normals = new Vector3[_vertices.Count]; for (int i = 0; i < normals.Length; ++i) { normals[i] = new Vector3(0, 0, -1); } _mesh.normals = normals; } }
// ------------------------------------------------------------------ /// Update mesh // ------------------------------------------------------------------ protected new void LateUpdate() { if (updateFlags != exUpdateFlags.None && visible) { exUpdateFlags meshUpdateFlags = UpdateBuffers(vertices, uvs, colors32, indices); exMesh.FlushBuffers(mesh, meshUpdateFlags, vertices, indices, uvs, colors32); if ((meshUpdateFlags & exUpdateFlags.Index) != 0) { bool realVisible = (indices.Count > 0); if (cachedRenderer.enabled != realVisible) { cachedRenderer.enabled = realVisible; } } } }
/////////////////////////////////////////////////////////////////////////////// // Internal Functions /////////////////////////////////////////////////////////////////////////////// // ------------------------------------------------------------------ // Desc: // ------------------------------------------------------------------ public void Clear() { spriteList.Clear(); sortedSpriteList.Clear(); vertices.Clear(); indices.Clear(); uvs.Clear(); colors32.Clear(); if (mesh0 != null) { mesh0.Clear(); } if (mesh1 != null) { mesh1.Clear(); } updateFlags = exUpdateFlags.None; }
// ------------------------------------------------------------------ /// \param _dynamic if true, optimize mesh for frequent updates // ------------------------------------------------------------------ public void SetDynamic(bool _dynamic) { //Debug.Log(string.Format("[SetDynamic|exMesh] _dynamic: {0} isDynamic: " + isDynamic, _dynamic)); if (isDynamic == _dynamic) { return; } if (_dynamic) { // create all buffer exDebug.Assert(mesh0 != null); if (mesh0 == null) { mesh0 = CreateMesh(); } mesh0.MarkDynamic(); exDebug.Assert(mesh1 == null); if (mesh1 == null) { mesh1 = CreateMesh(); } mesh1.MarkDynamic(); lastUpdateFlags = exUpdateFlags.All; // init new created mesh buffer } else { if (isEvenMeshBuffer != true) { isEvenMeshBuffer = true; updateFlags |= lastUpdateFlags; } // destroy another buffer if (mesh1 != null) { mesh1.Destroy(); } mesh1 = null; } }
// ------------------------------------------------------------------ /// If we are using dynamic layer, the mesh is double buffered so that we can get the best performance on iOS devices. /// http://forum.unity3d.com/threads/118723-Huge-performance-loss-in-Mesh.CreateVBO-for-dynamic-meshes-IOS // ------------------------------------------------------------------ private Mesh SwapMeshBuffer() { exDebug.Assert(isDynamic); if (enableDoubleBuffer) { isEvenMeshBuffer = !isEvenMeshBuffer; exUpdateFlags currentBufferUpdate = updateFlags; updateFlags |= lastUpdateFlags; // combine changes during two frame lastUpdateFlags = currentBufferUpdate; // for next buffer } return GetMeshBuffer(); }
// ------------------------------------------------------------------ /// \param _dynamic if true, optimize mesh for frequent updates // ------------------------------------------------------------------ public void SetDynamic(bool _dynamic) { //Debug.Log(string.Format("[SetDynamic|exMesh] _dynamic: {0} isDynamic: " + isDynamic, _dynamic)); if (isDynamic == _dynamic) { return; } if (_dynamic) { // create all buffer exDebug.Assert(mesh0 != null); if (mesh0 == null) { mesh0 = CreateMesh(); } mesh0.MarkDynamic(); exDebug.Assert(mesh1 == null); if (mesh1 == null) { mesh1 = CreateMesh(); } mesh1.MarkDynamic(); lastUpdateFlags = exUpdateFlags.All; // init new created mesh buffer } else { if (isEvenMeshBuffer != true) { isEvenMeshBuffer = true; updateFlags |= lastUpdateFlags; } // destroy another buffer if (mesh1 != null) { mesh1.Destroy(); } mesh1 = null; } }
// ------------------------------------------------------------------ /// Compact all reserved buffers // ------------------------------------------------------------------ public void Compact() { // 其实mscorlib在实现TrimExcess时还是会预留10%的buffer spriteList.TrimExcess(); vertices.TrimExcess(); indices.TrimExcess(); uvs.TrimExcess(); colors32.TrimExcess(); updateFlags |= (exUpdateFlags.Color | exUpdateFlags.UV | exUpdateFlags.Normal); //need to flush to mesh if defined exLayer.LAZY_UPDATE_BUFFER_TAIL }
/////////////////////////////////////////////////////////////////////////////// // Internal Functions /////////////////////////////////////////////////////////////////////////////// // ------------------------------------------------------------------ // Desc: // ------------------------------------------------------------------ public void Clear() { spriteList.Clear(); sortedSpriteList.Clear(); vertices.Clear(); indices.Clear(); uvs.Clear(); colors32.Clear(); if (mesh0 != null) { mesh0.Clear(); } if (mesh1 != null) { mesh1.Clear(); } updateFlags = exUpdateFlags.None; }
// ------------------------------------------------------------------ /// Apply all buffer changes // ------------------------------------------------------------------ public void Apply(exUpdateFlags _additionalUpdateFlags = exUpdateFlags.None) { updateFlags |= _additionalUpdateFlags; Mesh mesh; if (isDynamic && updateFlags != exUpdateFlags.None) { mesh = SwapMeshBuffer(); } else { mesh = GetMeshBuffer(); } FlushBuffers (mesh, updateFlags, vertices, indices, uvs, colors32); #if EX_DEBUG if (mesh.uv.Length != mesh.vertices.Length) { Debug.LogError("Shader wants texture coordinates... uv.Length != vertex.Length"); OutputDebugInfo(true); } #endif if ((updateFlags & exUpdateFlags.Vertex) != 0) { // 当fast hide sprite时,判断是否可以隐藏整个mesh,减少draw call bool visible = false; if (spriteList.Count > 10) { visible = true; // 如果有大量sprite,不遍历整个列表,强制可见 } else { for (int i = 0, count = spriteList.Count; i < count; ++i) { if (spriteList[i].visible) { visible = true; break; } } } if (gameObject.activeSelf != visible) { gameObject.SetActive(visible); } } else if ((updateFlags & exUpdateFlags.Index) != 0) { // 如果index改变了则也有可能改变显隐,这种情况不多只是基本简单检查下索引数量就够 bool visible = (indices.Count > 0); if (gameObject.activeSelf != visible) { gameObject.SetActive(visible); } } updateFlags = exUpdateFlags.None; }
// ------------------------------------------------------------------ /// Actually apply all buffer changes to mesh. // ------------------------------------------------------------------ public static void FlushBuffers(Mesh _mesh, exUpdateFlags _updateFlags, exList<Vector3> _vertices, exList<int> _indices, exList<Vector2> _uvs, exList<Color32> _colors32) { if ((_updateFlags & exUpdateFlags.VertexAndIndex) == exUpdateFlags.VertexAndIndex) { // if we need to update vertices, we should also clear triangles first _mesh.triangles = null; // we dont use Mesh.Clear() since sometimes it goes wrong when the uv and color have not been updated } if ((_updateFlags & exUpdateFlags.Vertex) != 0 || (_updateFlags & exUpdateFlags.Index) != 0) { // if we need to update triangles, we should also update vertices, or mesh will become invisible _mesh.vertices = _vertices.FastToArray(); } if ((_updateFlags & exUpdateFlags.UV) != 0) { _mesh.uv = _uvs.FastToArray(); } if ((_updateFlags & exUpdateFlags.Color) != 0) { _mesh.colors32 = _colors32.FastToArray(); } if ((_updateFlags & exUpdateFlags.Index) != 0) { _mesh.triangles = _indices.FastToArray(); // we do update triangles here //string bufv = "Vertex Buffer: "; //string vertices = "Mesh.vertices[" + _mesh.vertices.Length + "]: "; //foreach (var v in _mesh.vertices) { // bufv += v; // bufv += ", "; //} //Debug.Log(bufv); //var buf = _indices.FastToArray(); //string triangles = "Mesh.indices[" + buf.Length + "]: "; //foreach (var index in buf) { // triangles += index; // triangles += ","; //} //Debug.Log(triangles); } if ((_updateFlags & exUpdateFlags.Index) != 0 || (_updateFlags & exUpdateFlags.Vertex) != 0) { _mesh.RecalculateBounds(); // Sometimes Unity will not automatically recalculate the bounding volume. } if ((_updateFlags & exUpdateFlags.Normal) != 0) { Vector3[] normals = new Vector3[_vertices.Count]; for (int i = 0; i < normals.Length; ++i) { normals[i] = new Vector3(0, 0, -1); } _mesh.normals = normals; } }
// ------------------------------------------------------------------ // Desc: // ------------------------------------------------------------------ public static exUpdateFlags UpdateBuffers(exISpriteFont _sprite, Space _space, float _alpha, exList <Vector3> _vertices, exList <Vector2> _uvs, exList <Color32> _colors32, exList <int> _indices, int _vbIndex, int _ibIndex) { #if UNITY_EDITOR if (_sprite.text == null || _sprite.vertexCount < _sprite.text.Length * exMesh.QUAD_VERTEX_COUNT) { Debug.LogError("顶点缓冲长度不够,是否绕开属性直接修改了text_?: " + _sprite.vertexCount, _sprite as Object); return(_sprite.updateFlags); } #endif bool colorUpdated = false; // 有渐变色时,每个字符单独计算顶点色,否则全局填充顶点色 // Debug.Log(string.Format("[UpdateBuffers|SpriteFontBuilder] _vbIndex: {0} _ibIndex: {1}", _vbIndex, _ibIndex)); if ((_sprite.updateFlags & exUpdateFlags.Text) != 0) { if (_alpha > 0.0f) { // 初始化渐变色要用到的全局参数 Color tmp = _sprite.color; tmp.a *= _alpha; topFinalColor = _sprite.topColor * tmp; botFinalColor = _sprite.botColor * tmp; if (_sprite.fontSize != 0) { botFinalColor = exMath.Lerp(topFinalColor, botFinalColor, 1.0f / _sprite.fontSize); // 预除 } else { botFinalColor = topFinalColor; Debug.LogWarning("Failed to use gradient font color due to invalid font size", _sprite as Object); } } else { topFinalColor = new Color(); botFinalColor = new Color(); } colorUpdated = topFinalColor != botFinalColor; if ((_sprite.updateFlags & exUpdateFlags.Text & ~exUpdateFlags.Color) != 0 || colorUpdated) { BuildText(_sprite, _space, _vertices, _vbIndex, _uvs, colorUpdated ? _colors32 : null); } } if ((_sprite.updateFlags & exUpdateFlags.Index) != 0 && _indices != null) { // update index buffer int indexBufferEnd = _ibIndex + _sprite.indexCount - 5; for (int i = _ibIndex, v = _vbIndex; i < indexBufferEnd; i += 6, v += 4) { _indices.buffer[i] = v; _indices.buffer[i + 1] = v + 1; _indices.buffer[i + 2] = v + 2; _indices.buffer[i + 3] = v + 2; _indices.buffer[i + 4] = v + 3; _indices.buffer[i + 5] = v; } } if ((_sprite.updateFlags & exUpdateFlags.Color) != 0 && _colors32 != null && colorUpdated == false) { // if not gradient we just need to fill same color here Color tmp = _sprite.topColor * _sprite.color; Color32 color32 = new Color(tmp.r, tmp.g, tmp.b, tmp.a * _alpha); int vertexBufferEnd = _vbIndex + _sprite.text.Length * 4; for (int i = _vbIndex; i < vertexBufferEnd; ++i) { _colors32.buffer[i] = color32; } } exUpdateFlags updatedFlags = _sprite.updateFlags; _sprite.updateFlags = exUpdateFlags.None; return(updatedFlags); }