diff --git a/OpenMetaverse/Primitives/ParticleSystem.cs b/OpenMetaverse/Primitives/ParticleSystem.cs index d7d9490b..3f2c932a 100644 --- a/OpenMetaverse/Primitives/ParticleSystem.cs +++ b/OpenMetaverse/Primitives/ParticleSystem.cs @@ -89,7 +89,13 @@ namespace OpenMetaverse /// Particles emit a glow Emissive = 0x100, /// used for point/grab/touch - Beam = 0x200 + Beam = 0x200, + /// continuous ribbon particle + Ribbon = 0x400, + /// particle data contains glow + DataGlow = 0x10000, + /// particle data contains blend functions + DataBlend = 0x20000, } /// @@ -107,6 +113,19 @@ namespace OpenMetaverse UseNewAngle = 0x02 } + public enum BlendFunc : byte + { + One = 0, + Zero = 1, + DestColor = 2, + SourceColor = 3, + OneMinusDestColor = 4, + OneMinusSourceColor = 5, + DestAlpha = 6, + SourceAlpha = 7, + OneMinusDestAlpha = 8, + OneMinusSourceAlpha = 9, + } public uint CRC; /// Particle Flags @@ -170,6 +189,42 @@ namespace OpenMetaverse /// Minimum value is 0, maximum value is 4 public float PartEndScaleY; + /// A that represents the start glow value + /// Minimum value is 0, maximum value is 1 + public float PartStartGlow; + /// A that represents the end glow value + /// Minimum value is 0, maximum value is 1 + public float PartEndGlow; + + /// OpenGL blend function to use at particle source + public byte BlendFuncSource; + /// OpenGL blend function to use at particle destination + public byte BlendFuncDest; + + public const byte MaxDataBlockSize = 98; + public const byte LegacyDataBlockSize = 86; + public const byte SysDataSize = 68; + public const byte PartDataSize = 18; + + /// + /// Can this particle system be packed in a legacy compatible way + /// + /// True if the particle system doesn't use new particle system features + public bool IsLegacyCompatible() + { + return !HasGlow() && !HasBlendFunc(); + } + + public bool HasGlow() + { + return PartStartGlow > 0f || PartEndGlow > 0f; + } + + public bool HasBlendFunc() + { + return BlendFuncSource != (byte)BlendFunc.SourceAlpha || BlendFuncDest != (byte)BlendFunc.OneMinusSourceAlpha; + } + /// /// Decodes a byte[] array into a ParticleSystem Object /// @@ -177,66 +232,103 @@ namespace OpenMetaverse /// Start position for BitPacker public ParticleSystem(byte[] data, int pos) { - // TODO: Not sure exactly how many bytes we need here, so partial - // (truncated) data will cause an exception to be thrown - if (data.Length > 0) - { - BitPack pack = new BitPack(data, pos); + PartStartGlow = 0f; + PartEndGlow = 0f; + BlendFuncSource = (byte)BlendFunc.SourceAlpha; + BlendFuncDest = (byte)BlendFunc.OneMinusSourceAlpha; - CRC = pack.UnpackUBits(32); - PartFlags = pack.UnpackUBits(32); - Pattern = (SourcePattern)pack.UnpackByte(); - MaxAge = pack.UnpackFixed(false, 8, 8); - StartAge = pack.UnpackFixed(false, 8, 8); - InnerAngle = pack.UnpackFixed(false, 3, 5); - OuterAngle = pack.UnpackFixed(false, 3, 5); - BurstRate = pack.UnpackFixed(false, 8, 8); - BurstRadius = pack.UnpackFixed(false, 8, 8); - BurstSpeedMin = pack.UnpackFixed(false, 8, 8); - BurstSpeedMax = pack.UnpackFixed(false, 8, 8); - BurstPartCount = pack.UnpackByte(); - float x = pack.UnpackFixed(true, 8, 7); - float y = pack.UnpackFixed(true, 8, 7); - float z = pack.UnpackFixed(true, 8, 7); - AngularVelocity = new Vector3(x, y, z); - x = pack.UnpackFixed(true, 8, 7); - y = pack.UnpackFixed(true, 8, 7); - z = pack.UnpackFixed(true, 8, 7); - PartAcceleration = new Vector3(x, y, z); - Texture = pack.UnpackUUID(); - Target = pack.UnpackUUID(); + CRC = PartFlags = 0; + Pattern = SourcePattern.None; + MaxAge = StartAge = InnerAngle = OuterAngle = BurstRate = BurstRadius = BurstSpeedMin = + BurstSpeedMax = 0.0f; + BurstPartCount = 0; + AngularVelocity = PartAcceleration = Vector3.Zero; + Texture = Target = UUID.Zero; + PartDataFlags = ParticleDataFlags.None; + PartMaxAge = 0.0f; + PartStartColor = PartEndColor = Color4.Black; + PartStartScaleX = PartStartScaleY = PartEndScaleX = PartEndScaleY = 0.0f; - PartDataFlags = (ParticleDataFlags)pack.UnpackUBits(32); - PartMaxAge = pack.UnpackFixed(false, 8, 8); - byte r = pack.UnpackByte(); - byte g = pack.UnpackByte(); - byte b = pack.UnpackByte(); - byte a = pack.UnpackByte(); - PartStartColor = new Color4(r, g, b, a); - r = pack.UnpackByte(); - g = pack.UnpackByte(); - b = pack.UnpackByte(); - a = pack.UnpackByte(); - PartEndColor = new Color4(r, g, b, a); - PartStartScaleX = pack.UnpackFixed(false, 3, 5); - PartStartScaleY = pack.UnpackFixed(false, 3, 5); - PartEndScaleX = pack.UnpackFixed(false, 3, 5); - PartEndScaleY = pack.UnpackFixed(false, 3, 5); - } - else + int size = data.Length - pos; + BitPack pack = new BitPack(data, pos); + + if (size == LegacyDataBlockSize) { - CRC = PartFlags = 0; - Pattern = SourcePattern.None; - MaxAge = StartAge = InnerAngle = OuterAngle = BurstRate = BurstRadius = BurstSpeedMin = - BurstSpeedMax = 0.0f; - BurstPartCount = 0; - AngularVelocity = PartAcceleration = Vector3.Zero; - Texture = Target = UUID.Zero; - PartDataFlags = ParticleDataFlags.None; - PartMaxAge = 0.0f; - PartStartColor = PartEndColor = Color4.Black; - PartStartScaleX = PartStartScaleY = PartEndScaleX = PartEndScaleY = 0.0f; + UnpackSystem(ref pack); + UnpackLegacyData(ref pack); } + else if (size > LegacyDataBlockSize && size <= MaxDataBlockSize) + { + int sysSize = pack.UnpackBits(32); + if (sysSize != SysDataSize) return; // unkown particle system data size + UnpackSystem(ref pack); + int dataSize = pack.UnpackBits(32); + UnpackLegacyData(ref pack); + + + if ((PartDataFlags & ParticleDataFlags.DataGlow) == ParticleDataFlags.DataGlow) + { + if (pack.Data.Length - pack.BytePos < 2) return; + uint glow = pack.UnpackUBits(8); + PartStartGlow = glow / 255f; + glow = pack.UnpackUBits(8); + PartEndGlow = glow / 255f; + } + + if ((PartDataFlags & ParticleDataFlags.DataBlend) == ParticleDataFlags.DataBlend) + { + if (pack.Data.Length - pack.BytePos < 2) return; + BlendFuncSource = (byte)pack.UnpackUBits(8); + BlendFuncDest = (byte)pack.UnpackUBits(8); + } + + } + } + + void UnpackSystem(ref BitPack pack) + { + CRC = pack.UnpackUBits(32); + PartFlags = pack.UnpackUBits(32); + Pattern = (SourcePattern)pack.UnpackByte(); + MaxAge = pack.UnpackFixed(false, 8, 8); + StartAge = pack.UnpackFixed(false, 8, 8); + InnerAngle = pack.UnpackFixed(false, 3, 5); + OuterAngle = pack.UnpackFixed(false, 3, 5); + BurstRate = pack.UnpackFixed(false, 8, 8); + BurstRadius = pack.UnpackFixed(false, 8, 8); + BurstSpeedMin = pack.UnpackFixed(false, 8, 8); + BurstSpeedMax = pack.UnpackFixed(false, 8, 8); + BurstPartCount = pack.UnpackByte(); + float x = pack.UnpackFixed(true, 8, 7); + float y = pack.UnpackFixed(true, 8, 7); + float z = pack.UnpackFixed(true, 8, 7); + AngularVelocity = new Vector3(x, y, z); + x = pack.UnpackFixed(true, 8, 7); + y = pack.UnpackFixed(true, 8, 7); + z = pack.UnpackFixed(true, 8, 7); + PartAcceleration = new Vector3(x, y, z); + Texture = pack.UnpackUUID(); + Target = pack.UnpackUUID(); + } + + void UnpackLegacyData(ref BitPack pack) + { + PartDataFlags = (ParticleDataFlags)pack.UnpackUBits(32); + PartMaxAge = pack.UnpackFixed(false, 8, 8); + byte r = pack.UnpackByte(); + byte g = pack.UnpackByte(); + byte b = pack.UnpackByte(); + byte a = pack.UnpackByte(); + PartStartColor = new Color4(r, g, b, a); + r = pack.UnpackByte(); + g = pack.UnpackByte(); + b = pack.UnpackByte(); + a = pack.UnpackByte(); + PartEndColor = new Color4(r, g, b, a); + PartStartScaleX = pack.UnpackFixed(false, 3, 5); + PartStartScaleY = pack.UnpackFixed(false, 3, 5); + PartEndScaleX = pack.UnpackFixed(false, 3, 5); + PartEndScaleY = pack.UnpackFixed(false, 3, 5); } /// @@ -245,9 +337,47 @@ namespace OpenMetaverse /// Byte array public byte[] GetBytes() { - byte[] bytes = new byte[86]; + int size = LegacyDataBlockSize; + if (!IsLegacyCompatible()) size += 8; // two new ints for size + if (HasGlow()) size += 2; // two bytes for start and end glow + if (HasBlendFunc()) size += 2; // two bytes for start end end blend function + + byte[] bytes = new byte[size]; BitPack pack = new BitPack(bytes, 0); + if (IsLegacyCompatible()) + { + PackSystemBytes(ref pack); + PackLegacyData(ref pack); + } + else + { + pack.PackBits(SysDataSize, 32); + PackSystemBytes(ref pack); + int partSize = PartDataSize; + if (HasGlow()) partSize += 2; // two bytes for start and end glow + if (HasBlendFunc()) partSize += 2; // two bytes for start end end blend function + pack.PackBits(partSize, 32); + PackLegacyData(ref pack); + + if (HasGlow()) + { + pack.PackBits((byte)(PartStartGlow * 255f), 8); + pack.PackBits((byte)(PartEndGlow * 255f), 8); + } + + if (HasBlendFunc()) + { + pack.PackBits(BlendFuncSource, 8); + pack.PackBits(BlendFuncDest, 8); + } + } + + return bytes; + } + + void PackSystemBytes(ref BitPack pack) + { pack.PackBits(CRC, 32); pack.PackBits((uint)PartFlags, 32); pack.PackBits((uint)Pattern, 8); @@ -268,7 +398,10 @@ namespace OpenMetaverse pack.PackFixed(PartAcceleration.Z, true, 8, 7); pack.PackUUID(Texture); pack.PackUUID(Target); + } + void PackLegacyData(ref BitPack pack) + { pack.PackBits((uint)PartDataFlags, 32); pack.PackFixed(PartMaxAge, false, 8, 8); pack.PackColor(PartStartColor); @@ -277,8 +410,6 @@ namespace OpenMetaverse pack.PackFixed(PartStartScaleY, false, 3, 5); pack.PackFixed(PartEndScaleX, false, 3, 5); pack.PackFixed(PartEndScaleY, false, 3, 5); - - return bytes; } public OSD GetOSD() @@ -302,6 +433,25 @@ namespace OpenMetaverse map["texture"] = OSD.FromUUID(Texture); map["target"] = OSD.FromUUID(Target); + map["part_data_flags"] = (uint)PartDataFlags; + map["part_max_age"] = PartMaxAge; + map["part_start_color"] = PartStartColor; + map["part_end_color"] = PartEndColor; + map["part_start_scale"] = new Vector3(PartStartScaleX, PartStartScaleY, 0f); + map["part_end_scale"] = new Vector3(PartEndScaleX, PartEndScaleY, 0f); + + if (HasGlow()) + { + map["part_start_glow"] = PartStartGlow; + map["part_end_glow"] = PartEndGlow; + } + + if (HasBlendFunc()) + { + map["blendfunc_source"] = BlendFuncSource; + map["blendfunc_dest"] = BlendFuncDest; + } + return map; } @@ -328,6 +478,29 @@ namespace OpenMetaverse partSys.PartAcceleration = map["part_acceleration"].AsVector3(); partSys.Texture = map["texture"].AsUUID(); partSys.Target = map["target"].AsUUID(); + + partSys.PartDataFlags = (ParticleDataFlags)map["part_data_flags"].AsUInteger(); + partSys.PartMaxAge = map["part_max_age"]; + partSys.PartStartColor = map["part_start_color"]; + partSys.PartEndColor = map["part_end_color"]; + Vector3 ss = map["part_start_scale"]; + partSys.PartStartScaleX = ss.X; + partSys.PartStartScaleY = ss.Y; + Vector3 es = map["part_end_scale"]; + partSys.PartEndScaleX = es.X; + partSys.PartEndScaleY = es.Y; + + if (map.ContainsKey("part_start_glow")) + { + partSys.PartStartGlow = map["part_start_glow"]; + partSys.PartEndGlow = map["part_end_glow"]; + } + + if (map.ContainsKey("blendfunc_source")) + { + partSys.BlendFuncSource = (byte)map["blendfunc_source"].AsUInteger(); + partSys.BlendFuncDest = (byte)map["blendfunc_dest"].AsUInteger(); + } } return partSys;