/// <summary> /// Sets the default value for the specified field. /// </summary> /// <typeparam name="T">The field type.</typeparam> /// <param name="particleField">The field.</param> /// <param name="defaultValue">The new field default value.</param> internal void SetDefaultValue <T>(ParticleField particleField, T defaultValue) where T : struct { if (particleDefaultValue == IntPtr.Zero) { throw new InvalidOperationException(); } // Write new default value for the field Utilities.Write(particleDefaultValue + particleField.Offset, ref defaultValue); }
/// <summary> /// Adds a new field to the particle system. /// </summary> /// <typeparam name="T">The field type.</typeparam> /// <param name="fieldDesc">The field description.</param> /// <returns>The field.</returns> /// <exception cref="System.ArgumentException">Particle field size must be a multiple of 4;size</exception> internal ParticleField AddField <T>(ParticleFieldDescription <T> fieldDesc) where T : struct { ParticleField existingField; if (fields.TryGetValue(fieldDesc, out existingField)) { return(existingField); } var size = Utilities.SizeOf <T>(); // Only accept 4-byte multiples. if (size % 4 != 0) { throw new ArgumentException("Particle field size must be a multiple of 4", "size"); } var newParticleSize = particleSize + size; // Update default value var newParticleDefaultValue = Utilities.AllocateMemory(newParticleSize); if (particleDefaultValue != IntPtr.Zero) { Utilities.CopyMemory(newParticleDefaultValue, particleDefaultValue, particleSize); Utilities.FreeMemory(particleDefaultValue); } particleDefaultValue = newParticleDefaultValue; // Write default value for new field var defaultValue = fieldDesc.DefaultValue; Utilities.Write(newParticleDefaultValue + particleSize, ref defaultValue); // Check if there is enough space to do the conversion in-place if (particleData != IntPtr.Zero && particleCapacity * particleSize < newParticleSize * particleCount) { // Copy in-place, particles from back to front (so that there is no memory overlap) // i.e. // AAABBBCCCDDD gets copied that way: // --------------D- // -------------DD- // ------------DDD- // ----------C-DDD- // ................ // AAA-BBB-CCC-DDD- var particleSizeInDword = particleSize / 4; unsafe { var particleEnd = (int *)(particleData + particleCount * particleSize); var newParticleEnd = (int *)(particleData + particleCount * newParticleSize - size); for (int i = particleCount - 1; i >= 0; --i) { // Copy one particle, from back to end (no overlap) for (int j = 0; j < particleSizeInDword; ++j) { *--newParticleEnd = *--particleEnd; } // Leave space for the newly added field. newParticleEnd -= size; // Write default value for new field Utilities.Write((IntPtr)newParticleEnd, ref defaultValue); } } // Update capacity particleCapacity = particleCapacity * particleSize / newParticleSize; } else if (particleCapacity > 0) { var newParticleData = Utilities.AllocateMemory(particleCapacity * newParticleSize); // Copy previous data to the new buffer, interleaved with space for the new field if (particleData != IntPtr.Zero) { var newParticleDataPtr = newParticleData; var particleDataPtr = particleData; for (int i = 0; i < particleCount; ++i) { // Copy particle values Utilities.CopyMemory(newParticleDataPtr, particleDataPtr, particleSize); // Write default value for new field Utilities.Write(newParticleDataPtr + particleSize, ref defaultValue); particleDataPtr += particleSize; newParticleDataPtr += newParticleSize; } Utilities.FreeMemory(particleData); } particleData = newParticleData; } // Add the field var field = new ParticleField { Offset = particleSize, Size = size }; fields.Add(fieldDesc, field); // Update particle size particleSize = newParticleSize; return(field); }