/// <summary> /// Get or create a NGUIScreen responsible for drawing the widgets using the specified material. /// </summary> NGUIDrawCall GetDrawCall(Material mat, bool createIfMissing) { var dcCache = mDrawCalls; for (int i = 0, imax = dcCache.size; i < imax; ++i) { NGUIDrawCall dc = dcCache.buffer[i]; if (dc.material == mat) { return(dc); } } NGUIDrawCall sc = null; if (createIfMissing) { #if UNITY_EDITOR // If we're in the editor, create the game object with hide flags set right away GameObject go = UnityEditor.EditorUtility.CreateGameObjectWithHideFlags("_UIDrawCall [" + mat.name + "]", (mDebugInfo == DebugInfo.Geometry) ? HideFlags.DontSave | HideFlags.NotEditable : HideFlags.HideAndDontSave); #else GameObject go = new GameObject("_UIDrawCall [" + mat.name + "]"); //go.hideFlags = HideFlags.DontSave; DontDestroyOnLoad(go); #endif go.layer = gameObject.layer; sc = go.AddComponent <NGUIDrawCall>(); sc.material = mat; mDrawCalls.Add(sc); } return(sc); }
/// <summary> /// Destroy all draw calls we've created when this script gets disabled. /// </summary> void OnDisable() { for (int i = mDrawCalls.size; i > 0;) { NGUIDrawCall dc = mDrawCalls.buffer[--i]; if (dc != null) { NGUITools.DestroyImmediate(dc.gameObject); } } mDrawCalls.Clear(); mChanged.Clear(); mChildren.Clear(); }
/// <summary> /// Draw the inspector widget. /// </summary> public override void OnInspectorGUI() { if (Event.current.type == EventType.Repaint || Event.current.type == EventType.Layout) { NGUIDrawCall dc = target as NGUIDrawCall; NGUIPanel[] panels = (NGUIPanel[])Component.FindObjectsOfType(typeof(NGUIPanel)); foreach (NGUIPanel p in panels) { if (p.drawCalls.Contains(dc)) { EditorGUILayout.LabelField("Owner Panel", NGUITools.GetHierarchy(p.gameObject)); EditorGUILayout.LabelField("Triangles", dc.triangles.ToString()); return; } } if (Event.current.type == EventType.Repaint) { Debug.LogWarning("Orphaned NGUIDrawCall detected!\nUse [Selection -> Force Delete] to get rid of it."); } } }
/// <summary> /// Convenience function that figures out the panel's correct change flag by searching the parents. /// </summary> int GetChangeFlag(NGUINode start) { int flag = start.changeFlag; if (flag == -1) { Transform trans = start.trans.parent; NGUINode sub; // Keep going until we find a set flag for (;;) { // Check the parent's flag #if USE_SIMPLE_DICTIONARY if (trans != null && mChildren.TryGetValue(trans, out sub)) { #else if (trans != null && mChildren.Contains(trans)) { sub = (NGUINode)mChildren[trans]; #endif flag = sub.changeFlag; trans = trans.parent; // If the flag hasn't been set either, add this child to the hierarchy if (flag == -1) { mHierarchy.Add(sub); } else { break; } } else { flag = 0; break; } } // Update the parent flags for (int i = 0, imax = mHierarchy.size; i < imax; ++i) { NGUINode pc = mHierarchy.buffer[i]; pc.changeFlag = flag; } mHierarchy.Clear(); } return(flag); } /// <summary> /// Update the world-to-local transform matrix as well as clipping bounds. /// </summary> void UpdateTransformMatrix() { if (mUpdateTime == 0f || mMatrixTime != mUpdateTime) { mMatrixTime = mUpdateTime; mWorldToLocal = cachedTransform.worldToLocalMatrix; if (mClipping != NGUIDrawCall.Clipping.None) { Vector2 size = new Vector2(mClipRange.z, mClipRange.w); if (size.x == 0f) { size.x = (mCam == null) ? Screen.width : mCam.pixelWidth; } if (size.y == 0f) { size.y = (mCam == null) ? Screen.height : mCam.pixelHeight; } size *= 0.5f; mMin.x = mClipRange.x - size.x; mMin.y = mClipRange.y - size.y; mMax.x = mClipRange.x + size.x; mMax.y = mClipRange.y + size.y; } } } /// <summary> /// Run through all managed transforms and see if they've changed. /// </summary> void UpdateTransforms() { mChangedLastFrame = false; bool transformsChanged = false; bool shouldCull = false; #if UNITY_EDITOR shouldCull = (clipping != NGUIDrawCall.Clipping.None) && (!Application.isPlaying || mUpdateTime > mCullTime); if (!Application.isPlaying || !widgetsAreStatic || mWidgetsAdded || shouldCull != mCulled) #else shouldCull = (clipping != NGUIDrawCall.Clipping.None) && (mUpdateTime > mCullTime); if (!widgetsAreStatic || mWidgetsAdded || shouldCull != mCulled) #endif { #if USE_SIMPLE_DICTIONARY foreach (KeyValuePair <Transform, NGUINode> child in mChildren) { NGUINode node = child.Value; #else for (int i = 0, imax = mChildren.Count; i < imax; ++i) { NGUINode node = (NGUINode)mChildren[i]; #endif if (node.trans == null) { mRemoved.Add(node.trans); continue; } if (node.HasChanged()) { node.changeFlag = 1; transformsChanged = true; #if UNITY_EDITOR Vector3 s = node.trans.lossyScale; float min = Mathf.Abs(Mathf.Min(s.x, s.y)); if (min == 0f) { Debug.LogError("Scale of 0 is invalid! Zero cannot be divided by, which causes problems. Use a small value instead, such as 0.01\n" + node.trans.lossyScale, node.trans); } #endif } else { node.changeFlag = -1; } } // Clean up the deleted transforms for (int i = 0, imax = mRemoved.Count; i < imax; ++i) { mChildren.Remove(mRemoved[i]); } mRemoved.Clear(); } // If the children weren't culled but should be, check their visibility if (!mCulled && shouldCull) { mCheckVisibility = true; } // If something has changed, propagate the changes *down* the tree hierarchy (to children). // An alternative (but slower) approach would be to do a pc.trans.GetComponentsInChildren<NGUIWidget>() // in the loop above, and mark each one as dirty. if (mCheckVisibility || transformsChanged || mRebuildAll) { #if USE_SIMPLE_DICTIONARY foreach (KeyValuePair <Transform, NGUINode> child in mChildren) { NGUINode pc = child.Value; #else for (int i = 0, imax = mChildren.Count; i < imax; ++i) { NGUINode pc = (NGUINode)mChildren[i]; #endif if (pc.widget != null) { int visibleFlag = 1; // No sense in checking the visibility if we're not culling anything (as the visibility is always 'true') if (shouldCull || transformsChanged) { // If the change flag has not yet been determined... if (pc.changeFlag == -1) { pc.changeFlag = GetChangeFlag(pc); } // Is the widget visible? if (shouldCull) { visibleFlag = (mCheckVisibility || pc.changeFlag == 1) ? (IsVisible(pc.widget) ? 1 : 0) : pc.visibleFlag; } } // If visibility changed, mark the node as changed as well if (pc.visibleFlag != visibleFlag) { pc.changeFlag = 1; } // If the node has changed and the widget is visible (or was visible before) if (pc.changeFlag == 1 && (visibleFlag == 1 || pc.visibleFlag != 0)) { // Update the visibility flag pc.visibleFlag = visibleFlag; Material mat = pc.widget.material; // Add this material to the list of changed materials if (!mChanged.Contains(mat)) { mChanged.Add(mat); mChangedLastFrame = true; } } } } } mCulled = shouldCull; mCheckVisibility = false; mWidgetsAdded = false; } /// <summary> /// Update all widgets and rebuild their geometry if necessary. /// </summary> void UpdateWidgets() { #if USE_SIMPLE_DICTIONARY foreach (KeyValuePair <Transform, NGUINode> c in mChildren) { NGUINode pc = c.Value; #else for (int i = 0, imax = mChildren.Count; i < imax; ++i) { NGUINode pc = (NGUINode)mChildren[i]; #endif NGUIWidget w = pc.widget; // If the widget is visible, update it if (pc.visibleFlag == 1 && w != null && w.UpdateGeometry(this, ref mWorldToLocal, (pc.changeFlag == 1), generateNormals)) { // We will need to refill this buffer if (!mChanged.Contains(w.material)) { mChanged.Add(w.material); mChangedLastFrame = true; } } pc.changeFlag = 0; } } /// <summary> /// Update the clipping rect in the shaders and draw calls' positions. /// </summary> public void UpdateDrawcalls() { Vector4 range = Vector4.zero; if (mClipping != NGUIDrawCall.Clipping.None) { range = new Vector4(mClipRange.x, mClipRange.y, mClipRange.z * 0.5f, mClipRange.w * 0.5f); } if (range.z == 0f) { range.z = Screen.width * 0.5f; } if (range.w == 0f) { range.w = Screen.height * 0.5f; } RuntimePlatform platform = Application.platform; if (platform == RuntimePlatform.WindowsPlayer || platform == RuntimePlatform.WindowsWebPlayer || platform == RuntimePlatform.WindowsEditor) { range.x -= 0.5f; range.y += 0.5f; } Transform t = cachedTransform; for (int i = 0, imax = mDrawCalls.size; i < imax; ++i) { NGUIDrawCall dc = mDrawCalls.buffer[i]; dc.clipping = mClipping; dc.clipRange = range; dc.clipSoftness = mClipSoftness; dc.depthPass = depthPass && mClipping == NGUIDrawCall.Clipping.None; // Set the draw call's transform to match the panel's. // Note that parenting directly to the panel causes unity to crash as soon as you hit Play. Transform dt = dc.transform; dt.position = t.position; dt.rotation = t.rotation; dt.localScale = t.lossyScale; } } /// <summary> /// Set the draw call's geometry responsible for the specified material. /// </summary> void Fill(Material mat) { // Cleanup deleted widgets for (int i = mWidgets.size; i > 0;) { if (mWidgets[--i] == null) { mWidgets.RemoveAt(i); } } // Fill the buffers for the specified material for (int i = 0, imax = mWidgets.size; i < imax; ++i) { NGUIWidget w = mWidgets.buffer[i]; if (w.visibleFlag == 1 && w.material == mat) { NGUINode node = GetNode(w.cachedTransform); if (node != null) { if (generateNormals) { w.WriteToBuffers(mVerts, mUvs, mCols, mNorms, mTans); } else { w.WriteToBuffers(mVerts, mUvs, mCols, null, null); } } else { Debug.LogError("No transform found for " + NGUITools.GetHierarchy(w.gameObject), this); } } } if (mVerts.size > 0) { // Rebuild the draw call's mesh NGUIDrawCall dc = GetDrawCall(mat, true); dc.depthPass = depthPass && mClipping == NGUIDrawCall.Clipping.None; dc.Set(mVerts, generateNormals ? mNorms : null, generateNormals ? mTans : null, mUvs, mCols); } else { // There is nothing to draw for this material -- eliminate the draw call NGUIDrawCall dc = GetDrawCall(mat, false); if (dc != null) { mDrawCalls.Remove(dc); NGUITools.DestroyImmediate(dc.gameObject); } } // Cleanup mVerts.Clear(); mNorms.Clear(); mTans.Clear(); mUvs.Clear(); mCols.Clear(); } /// <summary> /// Main update function /// </summary> void LateUpdate() { mUpdateTime = Time.realtimeSinceStartup; UpdateTransformMatrix(); UpdateTransforms(); // Always move widgets to the panel's layer if (mLayer != gameObject.layer) { mLayer = gameObject.layer; NGUICamera uic = NGUICamera.FindCameraForLayer(mLayer); mCam = (uic != null) ? uic.cachedCamera : NGUITools.FindCameraForLayer(mLayer); SetChildLayer(cachedTransform, mLayer); var cache = drawCalls; for (int i = 0, imax = cache.size; i < imax; ++i) { mDrawCalls.buffer[i].gameObject.layer = mLayer; } } UpdateWidgets(); // If the depth has changed, we need to re-sort the widgets if (mDepthChanged) { mDepthChanged = false; mWidgets.Sort(NGUIWidget.CompareFunc); } var dcInit = drawCalls; // Fill the draw calls for all of the changed materials for (int i = 0, imax = mChanged.size; i < imax; ++i) { Fill(mChanged.buffer[i]); } // Update the clipping rects UpdateDrawcalls(); mChanged.Clear(); mRebuildAll = false; #if UNITY_EDITOR mScreenSize = new Vector2(Screen.width, Screen.height); #endif }