Home > database >  Why does Destroy(GameObject) not work on the second item of a List of Tuples of GameObjects?
Why does Destroy(GameObject) not work on the second item of a List of Tuples of GameObjects?

Time:01-04

In my application the user can place objects into a scene. Each objects gets a background mesh, which will be calculated and added by the program. Therefore placed objects are stored as a List of Tuples:

public List<Tuple<GameObject, GameObject>> PlacedObjects;

The user also gets a "clear" button to delete the object, which looks as follows:

foreach (var placedObject in PlacedObjects)
{
    Destroy(placedObject.Item1);
    Destroy(placedObject.Item2);
}
PlacedObjects.Clear();

For some reason, Item1 gets deleted, but the Item2 is still visible in the scene. I guess, there must be some leak or reference that prevents the data from being destroyed?

Anyhow to get the full picture, here is how the data is generated. First the user can place an item (which are clones from a dropdown selection) which will be done as follows:

public void OnPlaceHereButtonPressed()
{
    var clone = Instantiate(currentlySelectedObject);

    clone.GetComponentInChildren<MeshFilter>().mesh = Instantiate(currentlySelectedObject.GetComponentInChildren<MeshFilter>().mesh);
    // Clone the material for each item
    Material cloneMat = new Material(currentlySelectedObject.GetComponentInChildren<Renderer>().material);

    if (currentlySelectedObject.tag.Equals(paramSphere)) // Spheres get random colors after placing
        cloneMat.SetColor("_BaseColor", Random.ColorHSV());
    clone.GetComponentInChildren<Renderer>().material = cloneMat;

    PlacedObjects.Add(new Tuple<GameObject, GameObject>(clone, null));
    SendVirtualObjectsToServer(clone);

    SetCameraBackgroundShader(true);
}

Item2 is a bit more complicated, since it comes as resulting data from a server. Most of it is just converting the incoming data stream to mesh data:

// this is done for each of the placed objects after getting the result
if (hasReconstrucedData)
{
    var reconstructed = new GameObject("Reconstructed Mesh "   i.ToString(), typeof(MeshFilter), typeof(MeshRenderer));
    reconstructed.transform.position = new Vector3(0, 0, 0);
    reconstructed.transform.localScale = new Vector3(1, -1, 1);
    reconstructed.isStatic = true;
    Mesh mesh = new Mesh();

    // vertices
    int vertexBufferSize = BitConverter.ToInt32(data, bufferOffset);
    bufferOffset  = sizeof(int);
    mesh.vertices = nativeBuffer.GetSubArray(bufferOffset, vertexBufferSize).Reinterpret<Vector3>(vertexBufferSize / 12).ToArray();
    bufferOffset  = vertexBufferSize;

    // normals
    int vertexNormalsBufferSize = BitConverter.ToInt32(data, bufferOffset);
    bufferOffset  = sizeof(int);
    mesh.normals = nativeBuffer.GetSubArray(bufferOffset, vertexNormalsBufferSize).Reinterpret<Vector3>(vertexNormalsBufferSize / 12).ToArray();
    bufferOffset  = vertexNormalsBufferSize;

    // colors
    int vertexColorsBufferSize = BitConverter.ToInt32(data, bufferOffset);
    bufferOffset  = sizeof(int);
    mesh.colors = nativeBuffer.GetSubArray(bufferOffset, vertexColorsBufferSize).Reinterpret<Color>(vertexNormalsBufferSize / 16).ToArray();
    bufferOffset  = vertexColorsBufferSize;

    // triangles
    int trianglesBufferSize = BitConverter.ToInt32(data, bufferOffset);
    bufferOffset  = sizeof(int);
    mesh.triangles = nativeBuffer.GetSubArray(bufferOffset, trianglesBufferSize).Reinterpret<int>(trianglesBufferSize / 4).ToArray();
    bufferOffset  = trianglesBufferSize;

    // sh probes
    int reconstructedProbeBufferSize = BitConverter.ToInt32(data, bufferOffset);
    bufferOffset  = sizeof(int);

    // set material and probe texture for reconstructed mesh
    Material reconstructedMat = new Material(Shader.Find("Custom/Differential"));

    rows = (int)Math.Ceiling((float)reconstructedProbeBufferSize / (float)cols);
    recbuffer = new NativeArray<float>(cols * rows, Allocator.TempJob);
    // only needed to fill up the complete texture
    recbuffer.CopyFrom(nativeBuffer.GetSubArray(bufferOffset, reconstructedProbeBufferSize).Reinterpret<float>(reconstructedProbeBufferSize / 4));
    bufferOffset  = reconstructedProbeBufferSize;
    Texture2D shEnvTexture = new Texture2D(cols, rows, TextureFormat.RFloat, false);
    shEnvTexture.LoadRawTextureData(recbuffer);
    shEnvTexture.filterMode = FilterMode.Point;
    shEnvTexture.wrapMode = TextureWrapMode.Clamp;
    shEnvTexture.Apply(updateMipmaps: false);
    reconstructedMat.SetTexture("_SHTexture", shEnvTexture);


    float[] objectPos = new float[3]
    {
        PlacedObjects[i].Item1.transform.position.x,
        PlacedObjects[i].Item1.transform.position.y,
        PlacedObjects[i].Item1.transform.position.z
    };
    reconstructedMat.SetFloatArray("_ObjectPosition", objectPos);
    reconstructedMat.SetFloat("_ObjectScale",
        (PlacedObjects[i].Item1.transform.localScale * differentialShaderScaleFactor).magnitude);

    reconstructed.GetComponentInChildren<Renderer>().material = reconstructedMat;
    reconstructed.GetComponent<MeshFilter>().mesh = mesh;
    PlacedObjects[i] = Tuple.Create(PlacedObjects[i].Item1, reconstructed);
}

These are all the places where data is added to or deleted from the list. Why doesn't the Destroy() function not work on the second item?

CodePudding user response:

Finally found the issue, which was quite well hidden. The problem wasn't that the destroy function doesn't work, but that the creation of Item2 creates a new Object every time the server sends a new result without cleaning up existing objects.

After changing the respective code to the following:

if (hasReconstrucedData)
{
    if(PlacedObjects[i].Item2 == null)
        PlacedObjects[i] = Tuple.Create(PlacedObjects[i].Item1, new GameObject("Reconstructed Mesh "   i.ToString(), typeof(MeshFilter), typeof(MeshRenderer)));

    PlacedObjects[i].Item2.transform.position = new Vector3(0, 0, 0);
    PlacedObjects[i].Item2.transform.localScale = new Vector3(1, -1, 1);
    PlacedObjects[i].Item2.isStatic = true;
    Mesh mesh = new Mesh();

    // vertices
    int vertexBufferSize = BitConverter.ToInt32(data, bufferOffset);
    bufferOffset  = sizeof(int);
    mesh.vertices = nativeBuffer.GetSubArray(bufferOffset, vertexBufferSize).Reinterpret<Vector3>(vertexBufferSize / 12).ToArray();
    bufferOffset  = vertexBufferSize;

    // normals
    int vertexNormalsBufferSize = BitConverter.ToInt32(data, bufferOffset);
    bufferOffset  = sizeof(int);
    mesh.normals = nativeBuffer.GetSubArray(bufferOffset, vertexNormalsBufferSize).Reinterpret<Vector3>(vertexNormalsBufferSize / 12).ToArray();
    bufferOffset  = vertexNormalsBufferSize;

    // colors
    int vertexColorsBufferSize = BitConverter.ToInt32(data, bufferOffset);
    bufferOffset  = sizeof(int);
    mesh.colors = nativeBuffer.GetSubArray(bufferOffset, vertexColorsBufferSize).Reinterpret<Color>(vertexNormalsBufferSize / 16).ToArray();
    bufferOffset  = vertexColorsBufferSize;

    // triangles
    int trianglesBufferSize = BitConverter.ToInt32(data, bufferOffset);
    bufferOffset  = sizeof(int);
    mesh.triangles = nativeBuffer.GetSubArray(bufferOffset, trianglesBufferSize).Reinterpret<int>(trianglesBufferSize / 4).ToArray();
    bufferOffset  = trianglesBufferSize;

    // sh probes
    int reconstructedProbeBufferSize = BitConverter.ToInt32(data, bufferOffset);
    bufferOffset  = sizeof(int);

    // set material and probe texture for reconstructed mesh
    Material reconstructedMat = new Material(Shader.Find("Custom/Differential"));

    rows = (int)Math.Ceiling((float)reconstructedProbeBufferSize / (float)cols);
    recbuffer = new NativeArray<float>(cols * rows, Allocator.TempJob);
    // only needed to fill up the complete texture
    recbuffer.CopyFrom(nativeBuffer.GetSubArray(bufferOffset, reconstructedProbeBufferSize).Reinterpret<float>(reconstructedProbeBufferSize / 4));
    bufferOffset  = reconstructedProbeBufferSize;
    Texture2D shEnvTexture = new Texture2D(cols, rows, TextureFormat.RFloat, false);
    shEnvTexture.LoadRawTextureData(recbuffer);
    shEnvTexture.filterMode = FilterMode.Point;
    shEnvTexture.wrapMode = TextureWrapMode.Clamp;
    shEnvTexture.Apply(updateMipmaps: false);
    reconstructedMat.SetTexture("_SHTexture", shEnvTexture);
    reconstructedMat.SetFloatArray("_SHEnvironmentProbe", lightProbeValues);

    float[] objectPos = new float[3]
    {
        PlacedObjects[i].Item1.transform.position.x,
        PlacedObjects[i].Item1.transform.position.y,
        PlacedObjects[i].Item1.transform.position.z
    };
    reconstructedMat.SetFloatArray("_ObjectPosition", objectPos);
    reconstructedMat.SetFloat("_ObjectScale",
        (PlacedObjects[i].Item1.transform.localScale * differentialShaderScaleFactor).magnitude);

    PlacedObjects[i].Item2.GetComponentInChildren<Renderer>().material = reconstructedMat;
    PlacedObjects[i].Item2.GetComponent<MeshFilter>().mesh = mesh;
}

I guess, under normal circumstances one would see multiple objects easily since I guess there would be z-fighting going on. However, here these objects are for differential rendering of an AR system (i.e. rendering shadows of virtual objects onto real world scenery) so they are mostly transparent and for debugging I simply changed the output color to red in the shader.

  •  Tags:  
  • Related