/// <summary>
        /// Overridden to read optional point cloud indices.
        /// </summary>
        /// <param name="msg">Message header.</param>
        /// <param name="packet">Data packet.</param>
        /// <param name="reader">Data packet reader.</param>
        /// <returns></returns>
        protected override Error HandleMessage(DataMessage msg, PacketBuffer packet, BinaryReader reader)
        {
            ShapeCache cache      = (msg.ObjectID == 0) ? _transientCache : _shapeCache;
            int        shapeIndex = (msg.ObjectID == 0) ? _lastTransientIndex : cache.GetShapeIndex(msg.ObjectID);

            if (shapeIndex < 0)
            {
                return(new Error(ErrorCode.InvalidObjectID, msg.ObjectID));
            }

            PointsComponent pointsComp = cache.GetShapeDataByIndex <PointsComponent>(shapeIndex);

            // Read index offset and count.
            uint offset = reader.ReadUInt32();
            uint count  = reader.ReadUInt32();

            int[] indices = pointsComp.Indices;
            for (uint i = 0; i < count; ++i)
            {
                indices[i + offset] = reader.ReadInt32();
            }

            pointsComp.IndicesDirty = true;

            if (pointsComp.IndexCount != 0 && offset + count >= pointsComp.IndexCount)
            {
                // Done. Register for the mesh.
                RegisterForMesh(pointsComp);
            }

            return(new Error());
        }
        /// <summary>
        /// Overridden to handle triangle data in the <paramref name="msg"/>
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="packet"></param>
        /// <param name="reader"></param>
        /// <returns></returns>
        protected override Error HandleMessage(DataMessage msg, PacketBuffer packet, BinaryReader reader)
        {
            ShapeCache cache      = (msg.ObjectID == 0) ? _transientCache : _shapeCache;
            int        shapeIndex = (msg.ObjectID == 0) ? _lastTransientIndex : cache.GetShapeIndex(msg.ObjectID);

            if (shapeIndex < 0)
            {
                return(new Error(ErrorCode.InvalidObjectID, msg.ObjectID));
            }

            // Naive support for multiple packets. Assume:
            // - In order.
            MeshEntry meshEntry = cache.GetShapeDataByIndex <MeshEntry>(shapeIndex);

            // Well, this is confusing indirection...
            int readComponent = MeshShape.ReadDataComponentDeferred(
                reader, (uint)meshEntry.Mesh.VertexCount, (uint)meshEntry.Mesh.IndexCount,
                // Vertex handler.
                new MeshShape.ComponentBlockReader((MeshShape.SendDataType dataType, BinaryReader reader2, uint offset, uint count) =>
            {
                return(ReadMeshVector3Data(reader2, offset, count, (Vector3[] buffer, int writeOffset, int writeCount) =>
                {
                    meshEntry.Mesh.SetVertices(buffer, 0, writeOffset, writeCount, true);
                }));
            }),
                // Index handler
                new MeshShape.ComponentBlockReader((MeshShape.SendDataType dataType, BinaryReader reader2, uint offset, uint count) =>
            {
                return(ReadIndexComponent(reader2, offset, count, meshEntry.Mesh));
            }),
                // Normals handler.
                new MeshShape.ComponentBlockReader((MeshShape.SendDataType dataType, BinaryReader reader2, uint offset, uint count) =>
            {
                return(ReadMeshVector3Data(reader2, offset, count, (Vector3[] buffer, int writeOffset, int writeCount) =>
                {
                    if (dataType == MeshShape.SendDataType.UniformNormal)
                    {
                        // Only one normal for the whole mesh.
                        // Fill the buffer and write in chunks.
                        for (int i = 1; i < buffer.Length; ++i)
                        {
                            buffer[i] = buffer[0];
                        }
                        int localOffset = 0;
                        for (int i = 0; i < meshEntry.Mesh.VertexCount; i += buffer.Length)
                        {
                            int blockCount = Math.Min(buffer.Length, meshEntry.Mesh.VertexCount - localOffset);
                            meshEntry.Mesh.SetNormals(buffer, 0, localOffset, blockCount);
                            writeOffset += blockCount;
                        }
                    }
                    else
                    {
                        meshEntry.Mesh.SetNormals(buffer, 0, writeOffset, writeCount);
                    }
                }));
            }),
                // Colours handler.
                new MeshShape.ComponentBlockReader((MeshShape.SendDataType dataType, BinaryReader reader2, uint offset, uint count) =>
            {
                return(ReadColourComponent(reader2, offset, count, meshEntry.Mesh));
            })
                );

            if (readComponent == -1)
            {
                return(new Error(ErrorCode.MalformedMessage, DataMessage.MessageID));
            }

            if (readComponent == (int)(MeshShape.SendDataType.Vertices | MeshShape.SendDataType.End))
            {
                // Finalise the material.
                meshEntry.Material = CreateMaterial(cache.GetShapeByIndex(shapeIndex), meshEntry);
                cache.SetShapeDataByIndex(shapeIndex, meshEntry);
            }

            return(new Error());
        }