/**************************************************************************** Copyright (c) 2013, Jonathan Cecil and UCLA Game Lab All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ using UnityEngine; using UnityEditor; using System; using System.IO; using System.Collections; using System.Collections.Generic; public class MeshCreator : UnityEngine.Object { public static float versionNumber = 0.7f; public static void UpdateMesh(GameObject gameObject) { MeshCreatorData mcd = gameObject.GetComponent(typeof(MeshCreatorData)) as MeshCreatorData; // unity should prevent this from happening to the inspector, but just in case..... if (mcd == null) { Debug.LogError("MeshCreator Error: selected object does not have a MeshCreatorData component. Select an object with a MeshCreatorData component to update."); return; } // add a TextureImporter object here to check whether texture is readable // set it to readable if necessary if (mcd.outlineTexture == null) { Debug.LogError("MeshCreator: no texture found. Make sure to have a texture selected before updating mesh."); return; } // if this is a new save, generate a unique idNumber if ( mcd.idNumber == "" ) { mcd.idNumber = MeshCreator.GenerateId(); // Debug.Log(mcd.gameObject.name + "MeshCreator: set new mesh id number to " + mcd.idNumber); } // check the id number, if it is used in another scene object // generate a new id number while (MeshCreator.IdExistsInScene(mcd)) { mcd.idNumber = MeshCreator.GenerateId(); } // check for scene folder string[] sceneNames = EditorApplication.currentScene.Split('/'); if (sceneNames.Length == 1 && sceneNames[0] == "") { Debug.LogError("MeshCreator Error: please save the scene before creating a mesh."); DestroyImmediate(mcd.gameObject); return; } string sceneName = sceneNames[sceneNames.Length-1]; string folderName = sceneName.Substring(0, sceneName.Length - 6); string folderPath = "Assets/UCLAGameLab/Meshes/" + folderName; // TODO: this should be a preference if (!Directory.Exists("Assets/UCLAGameLab/Meshes")) { if (!Directory.Exists("Assets/UCLAGameLab")) { Debug.LogError("MeshCreator: UCLAGameLab folder is missing from your project, please reinstall Mesh Creator."); return; } AssetDatabase.CreateFolder("Assets/UCLAGameLab", "Meshes"); Debug.Log("MeshCreator: making new Meshes folder at Assets/Meshes"); } if (!Directory.Exists(folderPath)) { Debug.Log("MeshCreator: making new folder in Meshes folder at " + folderPath); AssetDatabase.CreateFolder("Assets/UCLAGameLab/Meshes", folderName ); } string saveName = folderName + "/" + mcd.gameObject.name + "." + mcd.idNumber ; // stash the rotation value, set back to identity, then switch back later Quaternion oldRotation = mcd.gameObject.transform.rotation; mcd.gameObject.transform.rotation = Quaternion.identity; // stash the scale value, set back to one, then switch back later Vector3 oldScale = mcd.gameObject.transform.localScale; mcd.gameObject.transform.localScale = Vector3.one; // transform the object if needed to account for the new pivot if (mcd.pivotHeightOffset != mcd.lastPivotOffset.x || mcd.pivotWidthOffset != mcd.lastPivotOffset.y || mcd.pivotWidthOffset != mcd.lastPivotOffset.z ) { mcd.gameObject.transform.localPosition -= mcd.lastPivotOffset; mcd.lastPivotOffset = new Vector3(mcd.pivotWidthOffset, mcd.pivotHeightOffset, mcd.pivotDepthOffset); mcd.gameObject.transform.localPosition += mcd.lastPivotOffset; } // // start mesh renderer setup section // // mesh for rendering the object // will either be flat or full mesh Mesh msh = new Mesh(); // collider for mesh, if used Mesh collidermesh = new Mesh(); if (mcd.uvWrapMesh) { // Set up game object with mesh; AssignMesh(gameObject, ref msh); collidermesh = msh; } else { AssignPlaneMesh(gameObject, ref msh); // if needed, create the 3d mesh collider if (mcd.generateCollider && !mcd.usePrimitiveCollider && !mcd.useAABBCollider) AssignMesh(gameObject, ref collidermesh); } MeshRenderer mr = (MeshRenderer) mcd.gameObject.GetComponent("MeshRenderer"); if (mr == null) { //Debug.Log("MeshCreator Warning: no mesh renderer found on update object, adding one."); mcd.gameObject.AddComponent(typeof(MeshRenderer)); } // update the front material via renderer Material meshmat; string materialNameLocation = "Assets/UCLAGameLab/Materials/"+mcd.outlineTexture.name+".material.mat"; string transparentMaterialNameLocation = "Assets/UCLAGameLab/Materials/"+mcd.outlineTexture.name+".trans.material.mat"; string baseMaterialNameLocation = "Assets/UCLAGameLab/Materials/baseMaterial.mat"; string transparentBaseMaterialNameLocation = "Assets/UCLAGameLab/Materials/baseTransparentMaterial.mat"; if (mcd.useAutoGeneratedMaterial) { // if using uvWrapMesh, use regular material if (mcd.uvWrapMesh) { meshmat = (Material) AssetDatabase.LoadAssetAtPath(materialNameLocation, typeof(Material)); if (meshmat == null) { meshmat = CopyTexture(baseMaterialNameLocation, materialNameLocation, mcd.outlineTexture); } mcd.gameObject.GetComponent().sharedMaterial = meshmat; } else { // use a transparent material meshmat = (Material) AssetDatabase.LoadAssetAtPath(transparentMaterialNameLocation, typeof(Material)); if (meshmat == null) { meshmat = CopyTexture(transparentBaseMaterialNameLocation, transparentMaterialNameLocation, mcd.outlineTexture); } mcd.gameObject.GetComponent().sharedMaterial = meshmat; } } else { mcd.gameObject.GetComponent().sharedMaterial = mcd.frontMaterial; } MeshFilter mf = (MeshFilter) mcd.gameObject.GetComponent("MeshFilter"); if (mf == null) { //Debug.LogWarning("MeshCreator Warning: no mesh filter found on update object, adding one."); mf= mcd.gameObject.AddComponent(typeof(MeshFilter)) as MeshFilter; } mf.sharedMesh = msh; // save the main mesh string meshName = "Assets/UCLAGameLab/Meshes/" + saveName + ".asset"; AssetDatabase.CreateAsset(msh, meshName); // make the side edges if (!mcd.uvWrapMesh && mcd.createEdges) { Mesh edgemesh = new Mesh(); MeshCreator.AssignEdgeMesh(gameObject, ref edgemesh); // remove the old backside mesh game object string edgeName = mcd.gameObject.name + ".edge"; ArrayList destroyObject = new ArrayList(); foreach (Transform child in mcd.gameObject.transform) { if (child.name == edgeName) { MeshFilter emf = (MeshFilter) child.gameObject.GetComponent("MeshFilter"); if (emf != null) { Mesh ems = (Mesh) emf.sharedMesh; if (ems != null) { //DestroyImmediate(ems, true); } } destroyObject.Add(child); } } while (destroyObject.Count > 0) { Transform child = (Transform) destroyObject[0]; destroyObject.Remove(child); DestroyImmediate(child.gameObject); } // create a new game object to attach the backside plane GameObject edgeObject = new GameObject(); edgeObject.transform.parent = mcd.gameObject.transform; edgeObject.transform.localPosition = Vector3.zero; edgeObject.transform.rotation = Quaternion.identity; edgeObject.name = edgeName; MeshFilter edgemf = (MeshFilter) edgeObject.AddComponent(typeof(MeshFilter)) as MeshFilter; edgemf.sharedMesh = edgemesh; // save the mesh in the Assets folder string edgeMeshName = "Assets/UCLAGameLab/Meshes/" + saveName + ".Edge" + ".asset"; AssetDatabase.CreateAsset(edgemesh, edgeMeshName); MeshRenderer edgemr = edgeObject.AddComponent(typeof(MeshRenderer)) as MeshRenderer; // for side meshes use the opaque material Material edgematerial = (Material)AssetDatabase.LoadAssetAtPath(materialNameLocation, typeof(Material)); if (edgematerial == null) { edgematerial = CopyTexture(baseMaterialNameLocation, materialNameLocation, mcd.outlineTexture); } edgemr.GetComponent().sharedMaterial = edgematerial; } else // destroy the old edge objects because they're not needed { string edgeName = mcd.gameObject.name + ".edge"; ArrayList destroyObject = new ArrayList(); foreach (Transform child in mcd.gameObject.transform) { if (child.name == edgeName) { destroyObject.Add(child); MeshFilter emf = (MeshFilter) child.gameObject.GetComponent("MeshFilter"); if (emf != null) { Mesh ems = (Mesh) emf.sharedMesh; if (ems != null) { //DestroyImmediate(ems, true); } } } } while (destroyObject.Count > 0) { Transform child = (Transform) destroyObject[0]; destroyObject.Remove(child); DestroyImmediate(child.gameObject); } } // make the backside plane if (!mcd.uvWrapMesh && mcd.createBacksidePlane) { Mesh backmesh = new Mesh(); AssignPlaneMeshBackside(gameObject, ref backmesh); // remove the old backside mesh game object string backsideName = mcd.gameObject.name + ".backside"; ArrayList destroyObject = new ArrayList(); foreach (Transform child in mcd.gameObject.transform) { if (child.name == backsideName) { destroyObject.Add(child); MeshFilter emf = (MeshFilter) child.gameObject.GetComponent("MeshFilter"); if (emf != null) { Mesh ems = (Mesh) emf.sharedMesh; if (ems != null) { //DestroyImmediate(ems, true); } } } } while (destroyObject.Count > 0) { Transform child = (Transform) destroyObject[0]; destroyObject.Remove(child); DestroyImmediate(child.gameObject); } // create a new game object to attach the backside plane GameObject backsideObject = new GameObject(); backsideObject.transform.parent = mcd.gameObject.transform; backsideObject.transform.localPosition = Vector3.zero; backsideObject.transform.rotation = Quaternion.identity; backsideObject.name = backsideName; MeshFilter backmf = (MeshFilter) backsideObject.AddComponent(typeof(MeshFilter)) as MeshFilter; backmf.sharedMesh = backmesh; // save the mesh in the Assets folder string backMeshName = "Assets/UCLAGameLab/Meshes/" + saveName + ".Back" + ".asset"; AssetDatabase.CreateAsset(backmesh, backMeshName); MeshRenderer backmr = backsideObject.AddComponent(typeof(MeshRenderer)) as MeshRenderer; // for backside plane, use the transparent material Material backmaterial = (Material)AssetDatabase.LoadAssetAtPath(transparentMaterialNameLocation, typeof(Material)); if (backmaterial == null) { backmaterial = CopyTexture(transparentBaseMaterialNameLocation, transparentMaterialNameLocation, mcd.outlineTexture); } backmr.GetComponent().sharedMaterial = backmaterial; } else // remove the old backside mesh game object because it's not needed { string backsideName = mcd.gameObject.name + ".backside"; ArrayList destroyObject = new ArrayList(); foreach (Transform child in mcd.gameObject.transform) { if (child.name == backsideName) { destroyObject.Add(child); // get rid of the old mesh from the assets MeshFilter emf = (MeshFilter) child.gameObject.GetComponent("MeshFilter"); if (emf != null) { Mesh ems = (Mesh) emf.sharedMesh; if (ems != null) { //DestroyImmediate(ems, true); } } } } while (destroyObject.Count > 0) { Transform child = (Transform) destroyObject[0]; destroyObject.Remove(child); DestroyImmediate(child.gameObject); } } // end mesh renderer setup section // // start collider setup section // // generate a mesh collider if (mcd.generateCollider && !mcd.usePrimitiveCollider && !mcd.useAABBCollider) { // remove the old compound collider before assigning new string compoundColliderName = mcd.gameObject.name + "CompoundColliders"; foreach(Transform child in mcd.gameObject.transform) { if (child.name == compoundColliderName) { DestroyImmediate(child.gameObject); } } // if the current mesh on the mesh renderer is flat // and the object has a rigidbody, unity will give an // error trying to update the mass. // the fix is to stash the current mesh, switch to the // full 3d version, and switch back if ( !mcd.uvWrapMesh ) { mf.mesh = collidermesh; } Collider col = mcd.gameObject.GetComponent(); if (col == null) { mcd.gameObject.AddComponent(typeof(MeshCollider)); } else { DestroyImmediate(col); mcd.gameObject.AddComponent(typeof(MeshCollider)); } MeshCollider mcol = mcd.gameObject.GetComponent("MeshCollider") as MeshCollider; if (mcol == null) { Debug.LogWarning("MeshCreator: found a non-Mesh collider on object to update. If you really want a new collider generated, remove the old one and update the object with MeshCreator again."); } else { mcol.sharedMesh = collidermesh; // save the collider mesh if necessary if (!mcd.uvWrapMesh) // if uvWrapMesh, then mesh already saved { string colliderMeshName = "Assets/UCLAGameLab/Meshes/" + saveName + ".collider.asset"; AssetDatabase.CreateAsset(collidermesh, colliderMeshName); } } // switch mesh filter back if the flat one was // swapped out previously if (!mcd.uvWrapMesh) { mf.mesh = msh; } if (mcd.usePhysicMaterial) { mcol.material = mcd.physicMaterial; } // set triggers for the mesh collider? if (mcd.setTriggers) { mcol.isTrigger = true; } else { mcol.isTrigger = false; } } // end generate mesh collider // generate box colliders else if (mcd.generateCollider && mcd.usePrimitiveCollider && !mcd.useAABBCollider) { // remove the old collider if necessary Collider col = mcd.gameObject.GetComponent(); if (col != null) { if (col.GetType() == typeof(MeshCollider)) { //Debug.LogWarning("Mesh Creator: found a collider on game object " + gameObject.name +", please remove it."); MeshCollider mshcol = mcd.gameObject.GetComponent("MeshCollider") as MeshCollider; if (mshcol != null) { //Debug.LogWarning("Mesh Creator: found a mesh collider on game object " + gameObject.name + ", destroying it's mesh."); mshcol.sharedMesh = null; } } DestroyImmediate(col); } // all compound colliders are stored in a gameObject string compoundColliderName = mcd.gameObject.name + "CompoundColliders"; GameObject go = new GameObject(); // find old compound colliders and remove foreach (Transform child in mcd.gameObject.transform) { if (child.name == compoundColliderName) { DestroyImmediate(go); go = child.gameObject; ArrayList removeChildren = new ArrayList(); foreach (Transform childchild in child) { removeChildren.Add(childchild); } foreach (Transform childchild in removeChildren) { DestroyImmediate(childchild.gameObject); } } } go.name = compoundColliderName; go.transform.parent = mcd.gameObject.transform; go.transform.localPosition = Vector3.zero; go.transform.rotation = Quaternion.identity; ArrayList boxColliderCoordinates = GetBoxColliderCoordinates(gameObject); int count = 0; int imageHeight = mcd.outlineTexture.height; int imageWidth = mcd.outlineTexture.width; foreach (Vector4 bcc in boxColliderCoordinates) { Vector4 bc = bcc; // if using a uvWrapMesh, subtract half a pixel from each side if (mcd.uvWrapMesh && Math.Abs(bc.x - bc.z) > 1.0f && Math.Abs(bc.y - bc.w) > 1.0f) { bc.x += 0.5f; bc.y += 0.5f; bc.z -= 0.5f; bc.w -= 0.5f; } else if (mcd.uvWrapMesh) { // if here, height or width is only one continue; } { count++; GameObject colgo = new GameObject(); colgo.name = compoundColliderName+"."+count; colgo.transform.parent = go.transform; colgo.transform.localPosition = Vector3.zero; BoxCollider bxcol = colgo.AddComponent(typeof(BoxCollider)) as BoxCollider; float vertX = 1.0f - (bc.x/imageWidth) ; // get X point and normalize float vertY = bc.y/imageHeight ; // get Y point and normalize float vert2X = 1.0f - (bc.z/imageWidth); float vert2Y = bc.w/imageHeight; vertX = (vertX * mcd.meshWidth) - (mcd.meshWidth / 2.0f); // scale X and position centered vertY = (vertY * mcd.meshHeight) - (mcd.meshHeight / 2.0f); vert2X = (vert2X * mcd.meshWidth) - (mcd.meshWidth / 2.0f); // scale X and position centered vert2Y = (vert2Y * mcd.meshHeight) - (mcd.meshHeight / 2.0f); bxcol.center = new Vector3(vertX - ((vertX-vert2X)/2.0f)-mcd.pivotWidthOffset, vertY - ((vertY-vert2Y)/2.0f)-mcd.pivotHeightOffset, - mcd.pivotDepthOffset); bxcol.size = new Vector3(Math.Abs(vertX-vert2X), Math.Abs(vertY-vert2Y), mcd.meshDepth); // use physics material if (mcd.usePhysicMaterial) { bxcol.material = mcd.physicMaterial; } // set trigger for this box collider? if (mcd.setTriggers) { bxcol.isTrigger = true; } } } } // end generate box colliders // generate AABB collider else if (mcd.generateCollider && !mcd.usePrimitiveCollider && mcd.useAABBCollider) { // remove the old collider if necessary Collider col = mcd.gameObject.GetComponent(); if (col != null) { DestroyImmediate(col); } mcd.gameObject.AddComponent(typeof(BoxCollider)); // remove the old compound collider before assigning new string compoundColliderName = mcd.gameObject.name + "CompoundColliders"; foreach (Transform child in mcd.gameObject.transform) { if (child.name == compoundColliderName) { DestroyImmediate(child.gameObject); } } BoxCollider bxcol = mcd.gameObject.GetComponent("BoxCollider") as BoxCollider; Vector4 extents = GetTransparencyExtents(mcd.gameObject); int imageHeight = mcd.outlineTexture.height; int imageWidth = mcd.outlineTexture.width; float vertX = 1.0f - (extents.x / imageWidth); // get X point and normalize float vertY = extents.y / imageHeight; // get Y point and normalize float vert2X = 1.0f - (extents.z / imageWidth); float vert2Y = extents.w / imageHeight; vertX = (vertX * mcd.meshWidth) - (mcd.meshWidth / 2.0f); // scale X and position centered vertY = (vertY * mcd.meshHeight) - (mcd.meshHeight / 2.0f); vert2X = (vert2X * mcd.meshWidth) - (mcd.meshWidth / 2.0f); // scale X and position centered vert2Y = (vert2Y * mcd.meshHeight) - (mcd.meshHeight / 2.0f); bxcol.center = new Vector3(vertX - ((vertX - vert2X) / 2.0f) - mcd.pivotWidthOffset, vertY - ((vertY - vert2Y) / 2.0f) - mcd.pivotHeightOffset, -mcd.pivotDepthOffset); bxcol.size = new Vector3(Math.Abs(vertX - vert2X), Math.Abs(vertY - vert2Y), mcd.meshDepth); // use physics material if (mcd.usePhysicMaterial) { bxcol.material = mcd.physicMaterial; } // set trigger for this box collider? if (mcd.setTriggers) { bxcol.isTrigger = true; } } // end generate AABB collider else { // remove the old collider if necessary Collider col = mcd.gameObject.GetComponent(); if (col != null) { DestroyImmediate(col); } // remove the old compound collider before assigning new string compoundColliderName = mcd.gameObject.name + "CompoundColliders"; foreach (Transform child in mcd.gameObject.transform) { if (child.name == compoundColliderName) { DestroyImmediate(child.gameObject); } } } // end collider section mcd.gameObject.transform.rotation = oldRotation; mcd.gameObject.transform.localScale = oldScale; } // Vec4 returned is box coordinates // upperleft.x,upperleft.y, lowerright.x,lowerright.y // pixels in Unity are left to right, from bottom to top static Vector4 GetTransparencyExtents(GameObject gameObject) { MeshCreatorData mcd = gameObject.GetComponent(typeof(MeshCreatorData)) as MeshCreatorData; Vector4 extents = new Vector4(); string path = AssetDatabase.GetAssetPath(mcd.outlineTexture); TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter; textureImporter.isReadable = true; AssetDatabase.ImportAsset(path); Color[] pixels = mcd.outlineTexture.GetPixels(); // get the pixels to build the mesh from float pixelThreshold = mcd.pixelTransparencyThreshold / 255.0f; int imageHeight = mcd.outlineTexture.height; int imageWidth = mcd.outlineTexture.width; // set the extents to max mins extents.z = imageWidth - 1; extents.w = imageHeight - 1; extents.x = 0; extents.y = 0; for (int I = 0; I < imageWidth; I++) { for (int j = 0; j < imageHeight; j++) { if (pixels[I + (imageWidth * j)].a >= pixelThreshold) { if (I < extents.z) extents.z = I; if (I > extents.x) extents.x = I; if (j < extents.w) extents.w = j; if (j > extents.y) extents.y = j; } } } return extents; } static ArrayList GetBoxColliderCoordinates(GameObject gameObject) { MeshCreatorData mcd = gameObject.GetComponent(typeof(MeshCreatorData)) as MeshCreatorData; ArrayList boxCoordinates = new ArrayList(); string path = AssetDatabase.GetAssetPath(mcd.outlineTexture); TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter; textureImporter.isReadable = true; AssetDatabase.ImportAsset(path); Color[] pixels = mcd.outlineTexture.GetPixels(); // get the pixels to build the mesh from // possibly do some size checking // TODO: check for a square power of two int imageHeight = mcd.outlineTexture.height; int imageWidth = mcd.outlineTexture.width; if ( ((float)imageWidth)/((float)imageHeight) != mcd.meshWidth/mcd.meshHeight) { Debug.LogWarning("Mesh Creator: selected meshWidth and meshHeight is not the same proportion as source image width and height. Results may be distorted."); } // copy the pixels so they can be modified Color[] pix = new Color[pixels.Length]; for (int i = 0; i < pixels.Length; i++) { Color pixel = pixels[i]; pix[i] = new Color(pixel.r, pixel.g, pixel.b, pixel.a); } Vector4 boxCoord = GetLargestBox(ref pix, imageWidth, imageHeight, mcd.pixelTransparencyThreshold/255.0f); boxCoordinates.Add(boxCoord); while(boxCoordinates.Count < mcd.maxNumberBoxes) { boxCoord = GetLargestBox(ref pix, imageWidth, imageHeight, mcd.pixelTransparencyThreshold/255.0f); boxCoordinates.Add(boxCoord); } return boxCoordinates; } // based on algorithm from http://e-maxx.ru/algo/maximum_zero_submatrix static Vector4 GetLargestBox(ref Color[] pixs, int imageWidth, int imageHeight, float threshold) { Vector4 largestBox = new Vector4(-1.0f,-1.0f,-1.0f,-1.0f); int n = imageHeight; int m = imageWidth; List< List > a = new List< List > ( n ) ; for (int i = 0; i < n; i++) { a.Add(new List(m)); for (int j = 0; j < m; j++) { a[i].Add(0); } } for ( int I = 0 ; I < n ; I++ ) { for ( int j = 0 ; j < m ; j++ ) { if (pixs[j + (imageWidth * I )].a < threshold) a[ I ][ j ] = 1; // check if alpha is less than threshold } } int ans = 0 ; List < int > d = new List < int > ( m ); List < int > d1 = new List ( m ); List d2 = new List( m ) ; for (int i = 0; i < m; ++i) { d.Add(-1); d1.Add(-1); d2.Add(-1); } Stack < int > st = new Stack(); for (int i=0; i 0) st.Pop(); // empty the stack for (int j=0; j 0 && d[st.Peek()] <= d[j]) st.Pop(); d1[j] = st.Count == 0 ? -1 : st.Peek(); st.Push(j); } while (st.Count > 0) st.Pop(); for (int j=m-1; j>=0; --j) { while (st.Count>0 && d[st.Peek()] <= d[j]) st.Pop(); d2[j] = st.Count == 0 ? m : st.Peek(); st.Push (j); } for (int j=0; j