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.