private void LateUpdate() { if (!m_isRunning) { return; } if (!IsReady) { RequestUpdate(); } bool nullHit = false; for (int i = 0; i < m_sdfObjects.Count; i++) { SDFObject sdfObject = m_sdfObjects[i]; bool isNull = !sdfObject; nullHit |= isNull; if (!isNull) { m_isLocalDataDirty |= sdfObject.IsDirty; m_isLocalDataOrderDirty |= sdfObject.IsOrderDirty; } } if (nullHit) { ClearNulls(m_sdfObjects); } bool changed = false; if (m_isLocalDataOrderDirty) { ReorderObjects(); changed = true; } if (changed || m_forceUpdateNextFrame || m_isGlobalMeshDataDirty || m_isLocalDataDirty || transform.hasChanged) { changed = true; RebuildData(); } m_forceUpdateNextFrame = false; transform.hasChanged = false; if (changed && !IsEmpty) { for (int i = 0; i < m_sdfComponents.Count; i++) { m_sdfComponents[i].Run(); } } }
public void Register(SDFObject sdfObject) { if (m_sdfObjects.Contains(sdfObject)) { return; } if (sdfObject is SDFMesh sdfMesh) { // check if this is a totally new mesh that no group has seen if (!m_globalSDFMeshes.Contains(sdfMesh)) { m_globalSDFMeshes.Add(sdfMesh); m_isGlobalMeshDataDirty = true; } // keep track of how many groups contain a local reference to this sdfmesh if (!m_meshCounts.ContainsKey(sdfMesh.ID)) { m_meshCounts.Add(sdfMesh.ID, 0); } m_meshCounts[sdfMesh.ID]++; } bool wasEmpty = IsEmpty; m_sdfObjects.Add(sdfObject); m_isLocalDataDirty = true; m_isLocalDataOrderDirty = true; // this is almost certainly overkill, but i like the kind of guaranteed stability ClearNulls(m_sdfObjects); if (wasEmpty && !IsEmpty) { for (int i = 0; i < m_sdfComponents.Count; i++) { m_sdfComponents[i].OnNotEmpty(); } } RequestUpdate(); }
public void Deregister(SDFObject sdfObject) { bool wasEmpty = IsEmpty; if (!m_sdfObjects.Remove(sdfObject)) { return; } if (sdfObject is SDFMesh sdfMesh) { // if this was the only group referencing this sdfmesh, we can remove it from the global buffer too if (m_meshCounts.ContainsKey(sdfMesh.ID)) { m_meshCounts[sdfMesh.ID]--; if (m_meshCounts[sdfMesh.ID] <= 0 && m_globalSDFMeshes.Remove(sdfMesh)) { m_isGlobalMeshDataDirty = true; } } } m_isLocalDataDirty = true; m_isLocalDataOrderDirty = true; // this is almost certainly overkill ClearNulls(m_sdfObjects); if (!wasEmpty && IsEmpty) { for (int i = 0; i < m_sdfComponents.Count; i++) { m_sdfComponents[i].OnEmpty(); } } RequestUpdate(); }
/// <summary> /// Repopulate the data relating to SDF primitives (spheres, toruses, cuboids etc) and SDF meshes (which point to where in the list of sample and uv data they begin, and how large they are) /// </summary> /// <param name="onlySendBufferOnChange">Whether to invoke the components and inform them the buffer has changed. This is only really necessary when the size changes.</param> private void RebuildData(bool onlySendBufferOnChange = true) { m_isLocalDataDirty = false; // should we rebuild the buffers which contain mesh sample + uv data? bool globalBuffersChanged = false; if (m_meshSamplesBuffer == null || !m_meshSamplesBuffer.IsValid() || m_meshPackedUVsBuffer == null || !m_meshPackedUVsBuffer.IsValid() || m_isGlobalMeshDataDirty) { globalBuffersChanged = RebuildGlobalMeshData(m_sdfObjects, onlySendBufferOnChange); } // memorize the size of the array before clearing it, for later comparison int previousCount = m_data.Count; m_data.Clear(); // add all the sdf objects for (int i = 0; i < m_sdfObjects.Count; i++) { SDFObject sdfObject = m_sdfObjects[i]; if (!sdfObject) { continue; } sdfObject.SetClean(); int meshStartIndex = -1; int uvStartIndex = -1; if (sdfObject is SDFMesh mesh) { // get the index in the global samples buffer where this particular mesh's samples begin if (!m_meshSdfSampleStartIndices.TryGetValue(mesh.ID, out meshStartIndex)) { globalBuffersChanged = RebuildGlobalMeshData(m_sdfObjects, onlySendBufferOnChange); // we don't recognize this mesh so we may need to rebuild the entire global list of mesh samples and UVs } // likewise, if this mesh has UVs, get the index where they begin too if (mesh.Asset.HasUVs) { m_meshSdfUVStartIndices.TryGetValue(mesh.ID, out uvStartIndex); } } m_data.Add(sdfObject.GetSDFGPUData(meshStartIndex, uvStartIndex)); } bool sendBuffer = !onlySendBufferOnChange; // check whether we need to create a new buffer. buffers are fixed sizes so the most common occasion for this is simply a change of size if (m_dataBuffer == null || !m_dataBuffer.IsValid() || previousCount != m_data.Count) { sendBuffer = true; m_dataBuffer?.Dispose(); m_dataBuffer = new ComputeBuffer(Mathf.Max(1, m_data.Count), SDFGPUData.Stride, ComputeBufferType.Structured); } // if the buffer is new, the size has changed, or if it's forced, we resend the buffer to the sdf group component classes if (sendBuffer) { for (int i = 0; i < m_sdfComponents.Count; i++) { m_sdfComponents[i].UpdateDataBuffer(m_dataBuffer, m_data.Count); } } if (m_data.Count > 0) { m_dataBuffer.SetData(m_data); } // if we also changed the global mesh data buffer in this method, we need to send that as well if (!onlySendBufferOnChange || globalBuffersChanged) { Shader.SetGlobalBuffer(GlobalProperties.MeshSamples_StructuredBuffer, m_meshSamplesBuffer); Shader.SetGlobalBuffer(GlobalProperties.MeshPackedUVs_StructuredBuffer, m_meshPackedUVsBuffer); } }
public bool IsRegistered(SDFObject sdfObject) => !m_sdfObjects.IsNullOrEmpty() && m_sdfObjects.Contains(sdfObject);