/// <summary> /// Handle additional <see cref="CreateMessage"/> data. /// </summary> /// <param name="msg"></param> /// <param name="packet"></param> /// <param name="reader"></param> /// <param name="cache"></param> /// <param name="shapeIndex"></param> /// <returns></returns> protected override Error PostHandleMessage(CreateMessage msg, PacketBuffer packet, BinaryReader reader, ShapeCache cache, int shapeIndex) { if (shapeIndex >= 0) { // Read the text in the buffer. TextShapeData textData = new TextShapeData(); int textLength = reader.ReadUInt16(); textData.Mesh = null; textData.Text = string.Empty; if (textLength > 0) { byte[] textBytes = reader.ReadBytes(textLength); textData.Text = System.Text.Encoding.UTF8.GetString(textBytes); } textData.FontSize = (int)msg.Attributes.ScaleZ; if ((msg.Flags & (ushort)Text3DFlag.ScreenFacing) != 0) { // Need to use -forward for text. // ScreenFacing.AddToManager(obj, -Vector3.forward, Vector3.up); textData.ScreenFacing = true; } cache.SetShapeDataByIndex(shapeIndex, textData); } return(base.PostHandleMessage(msg, packet, reader, cache, shapeIndex)); }
/// <summary> /// Overridden to read information about mesh parts. /// </summary> /// <param name="msg"></param> /// <param name="packet"></param> /// <param name="reader"></param> /// <returns></returns> protected override Error PostHandleMessage(CreateMessage msg, PacketBuffer packet, BinaryReader reader, ShapeCache cache, int shapeIndex) { PartSet parts = new PartSet(); ushort meshPartCount = reader.ReadUInt16(); parts.MeshIDs = new uint[meshPartCount]; parts.Meshes = new MeshCache.MeshDetails[meshPartCount]; parts.Transforms = new Matrix4x4[meshPartCount]; parts.Tints = new Maths.Colour[meshPartCount]; parts.MaterialOverrides = new Material[meshPartCount]; parts.ObjectFlags = (ObjectFlag)msg.Flags; for (ushort i = 0; i < meshPartCount; ++i) { parts.MeshIDs[i] = reader.ReadUInt32(); ObjectAttributes attributes = new ObjectAttributes(); if (!attributes.Read(reader)) { return(new Error(ErrorCode.MalformedMessage, (int)ObjectMessageID.Create)); } DecodeTransform(attributes, out parts.Transforms[i]); parts.Tints[i] = new Maths.Colour(attributes.Colour); } cache.SetShapeDataByIndex(shapeIndex, parts); RegisterForMesh(msg, parts); return(new Error()); }
/// <summary> /// Handle triangle count. /// </summary> /// <param name="obj"></param> /// <param name="msg"></param> /// <param name="packet"></param> /// <param name="reader"></param> /// <returns></returns> protected override Error PostHandleMessage(CreateMessage msg, PacketBuffer packet, BinaryReader reader, ShapeCache cache, int shapeIndex) { int vertexCount = reader.ReadInt32(); int indexCount = reader.ReadInt32(); MeshDrawType drawType = (MeshDrawType)reader.ReadByte(); RenderMesh mesh = new RenderMesh(drawType, vertexCount, indexCount); MeshEntry meshEntry = new MeshEntry { Mesh = mesh, CalculateNormals = (msg.Flags & (ushort)MeshShapeFlag.CalculateNormals) != 0, DrawScale = 0.0f }; if (packet.Header.VersionMajor == 0 && packet.Header.VersionMinor == 1) { // Legacy handling. meshEntry.DrawScale = 0.0f; } else { meshEntry.DrawScale = reader.ReadSingle(); } cache.SetShapeDataByIndex(shapeIndex, meshEntry); if (meshEntry.CalculateNormals) { _toCalculateNormals.Add(mesh); } return(base.PostHandleMessage(msg, packet, reader, cache, shapeIndex)); }
/// <summary> /// Overridden to read information about mesh parts. /// </summary> /// <param name="msg"></param> /// <param name="packet"></param> /// <param name="reader"></param> /// <returns></returns> protected override Error PostHandleMessage(CreateMessage msg, PacketBuffer packet, BinaryReader reader, ShapeCache cache, int shapeIndex) { // Read additional attributes. PointsComponent pointsComp = new PointsComponent(); // Read the mesh ID to render points from. pointsComp.MeshID = reader.ReadUInt32(); // Read the number of indices (zero implies show entire mesh). pointsComp.IndexCount = reader.ReadUInt32(); if (packet.Header.VersionMajor == 0 && packet.Header.VersionMinor == 1) { // Legacy support. pointsComp.PointScale = (float)reader.ReadByte(); } else { pointsComp.PointScale = reader.ReadSingle(); } cache.SetShapeDataByIndex(shapeIndex, pointsComp); if (pointsComp.IndexCount == 0) { pointsComp.IndicesDirty = false; // Not expecting any index data messages. // Register the mesh now. RegisterForMesh(pointsComp); } return(new Error()); }
/// <summary> /// Handle additional <see cref="UpdateMessage"/> data. /// </summary> /// <param name="obj"></param> /// <param name="msg"></param> /// <param name="packet"></param> /// <param name="reader"></param> /// <returns></returns> protected override Error PostHandleMessage(UpdateMessage msg, PacketBuffer packet, BinaryReader reader, ShapeCache cache, int shapeIndex) { TextShapeData textData = cache.GetShapeDataByIndex <TextShapeData>(shapeIndex); // textData.Mesh.fontSize = (int)msg.Attributes.ScaleZ; // textData.Mesh.color = Maths.ColourExt.ToUnity32(new Maths.Colour(msg.Attributes.Colour)); textData.ScreenFacing = (msg.Flags & (ushort)Text3DFlag.ScreenFacing) != 0; cache.SetShapeDataByIndex(shapeIndex, textData); return(base.PostHandleMessage(msg, packet, reader, cache, shapeIndex)); }
protected void Render(CameraContext cameraContext, ShapeCache shapeCache) { // TODO: (KS) verify material setup. // TODO: (KS) incorporate the 3es scene transform. // TODO: (KS) handle multiple cameras (code only tailored to one). Vector3 cameraPosition = (Vector3)cameraContext.CameraToWorldTransform.GetColumn(3); CategoriesState categories = this.CategoriesState; // Walk the items in the shape cache. foreach (int shapeIndex in shapeCache.ShapeIndices) { // if (shapeCache.GetShapeDataByIndex<CreateMessage>(shapeIndex).Category) CreateMessage shape = shapeCache.GetShapeByIndex(shapeIndex); if (!categories.IsActive(shape.Category)) { // Category not enabled. continue; } // Get transform and text data. Matrix4x4 transform = shapeCache.GetShapeTransformByIndex(shapeIndex); TextShapeData textData = shapeCache.GetShapeDataByIndex <TextShapeData>(shapeIndex); if (textData.Mesh == null || textData.Material == null) { // No mesh/material. Try instantiate via the delegate. if (CreateTextMeshHandler != null) { CreateTextMeshHandler(textData.Text, textData.FontSize, Maths.ColourExt.ToUnity(new Maths.Colour(shape.Attributes.Colour)), ref textData.Mesh, ref textData.Material); } if (textData.Mesh == null || textData.Material == null) { continue; } // Newly creates mesh/material. Store the changes. shapeCache.SetShapeDataByIndex <TextShapeData>(shapeIndex, textData); } if (textData.Mesh == null || textData.Material == null) { continue; } if (textData.ScreenFacing) { Vector3 textPosition = cameraContext.TesSceneToWorldTransform * (Vector3)transform.GetColumn(3); Vector3 toCamera = cameraPosition - textPosition; // Remove any height component from the camera. Indexing using Unity's left handed, Y up system. toCamera[1] = 0; if (toCamera.sqrMagnitude > 1e-3f) { toCamera = toCamera.normalized; Vector3 side = Vector3.Cross(toCamera, Vector3.up); // Build new rotation axes using toCamera for forward and a new Up axis. transform.SetColumn(0, new Vector4(side.x, side.y, side.z)); transform.SetColumn(1, new Vector4(Vector3.up.x, Vector3.up.y, Vector3.up.z)); transform.SetColumn(2, new Vector4(toCamera.x, toCamera.y, toCamera.z)); transform.SetColumn(3, new Vector4(textPosition.x, textPosition.y, textPosition.z, 1.0f)); } // else too close to the camera to build a rotation. } else { // transform = cameraContext.TesSceneToWorldTransform * transform; // Just extract the text position. // TODO: (KS) will have to look at allowing users to orient the text from the server. Vector3 textPosition = cameraContext.TesSceneToWorldTransform * (Vector3)transform.GetColumn(3); transform = Matrix4x4.identity; transform.SetColumn(3, new Vector4(textPosition.x, textPosition.y, textPosition.z, 1.0f)); } cameraContext.TransparentBuffer.DrawMesh(textData.Mesh, transform, textData.Material); // TODO: (KS) resolve procedural rendering without a game object. Consider TextMeshPro. // TODO: (KS) select opaque layer. // Graphics.DrawMesh(textData.Mesh, transform, material, 0); } }
/// <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()); }