/// <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 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> /// 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 HandleMessage(CreateMessage msg, PacketBuffer packet, BinaryReader reader) { GameObject obj = null; if (msg.ObjectID == 0) { // Cannot support transient objects: we need to get point data through additional // messages which requires a valid object id. return(new Error(ErrorCode.InvalidObjectID, 0)); } else { obj = CreateObject(msg.ObjectID); if (!obj) { // Object already exists. return(new Error(ErrorCode.DuplicateShape, msg.ObjectID)); } } ShapeComponent shapeComp = obj.GetComponent <ShapeComponent>(); if (shapeComp) { shapeComp.Category = msg.Category; shapeComp.ObjectFlags = msg.Flags; shapeComp.Colour = ShapeComponent.ConvertColour(msg.Attributes.Colour); } obj.transform.SetParent(Root.transform, false); DecodeTransform(msg.Attributes, obj.transform); // Read additional attributes. PointsComponent pointsComp = obj.AddComponent <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(); pointsComp.PointSize = reader.ReadByte(); if (pointsComp.IndexCount == 0) { // Not expecting any index data messages. // Register the mesh now. RegisterForMesh(pointsComp); } return(new Error()); }
/// <summary> /// Creates a point cloud shape for serialising <paramref name="shapeComponent"/> and its point data. /// </summary> /// <param name="shapeComponent">The component to create a shape for.</param> /// <returns>A shape instance suitable for configuring to generate serialisation messages.</returns> protected override Shapes.Shape CreateSerialisationShape(ShapeCache cache, int shapeIndex, CreateMessage shapeData) { PointsComponent pointsComp = cache.GetShapeDataByIndex <PointsComponent>(shapeIndex); Shapes.PointCloudShape points = new Shapes.PointCloudShape(new MeshResourcePlaceholder(pointsComp.MeshID), shapeData.ObjectID, shapeData.Category, pointsComp.PointScale); points.SetAttributes(shapeData.Attributes); if (pointsComp.IndexCount > 0) { points.SetIndices(pointsComp.Indices); } return(points); }
/// <summary> /// Post destroy message: destroy sub-objects. /// </summary> /// <param name="obj"></param> /// <param name="msg"></param> /// <param name="packet"></param> /// <param name="reader"></param> /// <returns></returns> protected override Error PostHandleMessage(GameObject obj, DestroyMessage msg, PacketBuffer packet, BinaryReader reader) { PointsComponent points = obj.GetComponent <PointsComponent>(); if (points != null) { // Remove the registered parts. List <PointsComponent> parts = null; if (_registeredParts.TryGetValue(points.MeshID, out parts)) { // Remove from parts. parts.Remove(points); _awaitingFinalisation.RemoveAll((PointsComponent cmp) => { return(cmp == points); }); //parts.RemoveAll((PointsComponent cmp) => { return cmp == part; })); } } return(new Error()); }
/// <summary> /// Post destroy message: destroy sub-objects. /// </summary> /// <param name="obj"></param> /// <param name="msg"></param> /// <param name="packet"></param> /// <param name="reader"></param> /// <returns></returns> protected override Error PostHandleMessage(DestroyMessage msg, ShapeCache cache, int shapeIndex) { PointsComponent pointsComp = cache.GetShapeDataByIndex <PointsComponent>(shapeIndex); if (pointsComp != null) { // Remove from the registered mesh list. List <PointsComponent> parts; if (_registeredParts.TryGetValue(pointsComp.MeshID, out parts)) { // Remove from the list. parts.RemoveAll((PointsComponent cmp) => { return(cmp == pointsComp); }); } pointsComp.Release(); } return(new Error()); }
/// <summary> /// Creates a point cloud shape for serialising <paramref name="shapeComponent"/> and its point data. /// </summary> /// <param name="shapeComponent">The component to create a shape for.</param> /// <returns>A shape instance suitable for configuring to generate serialisation messages.</returns> protected override Shapes.Shape CreateSerialisationShape(ShapeComponent shapeComponent) { PointsComponent pointsComp = shapeComponent.GetComponent <PointsComponent>(); if (pointsComp != null) { ObjectAttributes attr = new ObjectAttributes(); EncodeAttributes(ref attr, shapeComponent.gameObject, shapeComponent); Shapes.PointCloudShape points = new Shapes.PointCloudShape(new MeshResourcePlaceholder(pointsComp.MeshID), shapeComponent.ObjectID, shapeComponent.Category, (byte)pointsComp.PointSize); points.SetAttributes(attr); return(points); } return(null); }
/// <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) { GameObject obj = null; if (msg.ObjectID != 0) { obj = FindObject(msg.ObjectID); } else { obj = _transientCache.LastObject; } if (obj == null) { return(new Error(ErrorCode.InvalidObjectID, msg.ObjectID)); } // Read index offset and count. uint offset = reader.ReadUInt32(); uint count = reader.ReadUInt32(); PointsComponent pointsComp = obj.GetComponent <PointsComponent>(); int[] indices = pointsComp.Indices; for (uint i = 0; i < count; ++i) { indices[i + offset] = reader.ReadInt32(); } if (pointsComp.IndexCount != 0 && offset + count >= pointsComp.IndexCount) { // Done. Register for the mesh. RegisterForMesh(pointsComp); } return(new Error()); }
protected virtual void BindMaterial(PointsComponent points) { // Select the material by topology. Material material = new Material(_pointsMaterial); if (points.Mesh.Mesh.HasColours) { material.EnableKeyword("WITH_COLOURS_UINT"); } if (points.Mesh.Mesh.HasNormals) { material.EnableKeyword("WITH_NORMALS"); } if (material.HasProperty("_LeftHanded")) { material.SetInt("_LeftHanded", ServerInfo.IsLeftHanded ? 1 : 0); } if (material.HasProperty("_PointSize")) { float pointScale = (points.PointScale > 0) ? points.PointScale : 1.0f; material.SetFloat("_PointSize", GlobalSettings.PointSize * pointScale); } if (material.HasProperty("_Color")) { material.SetColor("_Color", points.Colour); } if (material.HasProperty("_Tint")) { material.SetColor("_Tint", points.Mesh.Tint); } points.Material = material; }
/// <summary> /// Register <paramref name="points"/> for mesh resolving. /// </summary> /// <remarks> /// The mesh is either resolved now, or on mesh finalisation. /// Note that any limited indexing must be completed before calling /// this function. That is <see cref="PointsComponent.Indices"/> /// must have been resolved. /// </remarks> /// <param name="points">The points component.</param> protected void RegisterForMesh(PointsComponent points) { List <PointsComponent> parts = null; if (!_registeredParts.TryGetValue(points.MeshID, out parts)) { // Add new list. parts = new List <PointsComponent>(); _registeredParts.Add(points.MeshID, parts); } parts.Add(points); // Try resolve the mesh from the cache now. if (_meshCache != null) { MeshCache.MeshDetails meshDetails = _meshCache.GetEntry(points.MeshID); if (meshDetails != null && meshDetails.Finalised) { points.MeshDirty = true; _awaitingFinalisation.Add(points); } } }
/// <summary> /// Set the visuals of <pararef name="points"/> to use <paramref name="meshDetails"/>. /// </summary> /// <param name="points">The points object</param> /// <param name="meshDetails">The mesh details.</param> /// <remarks> /// Adds multiple children to <paramref name="points"/> when <paramref name="meshDetails"/> /// contains multiple mesh objects. /// </remarks> protected virtual void SetMesh(PointsComponent points, MeshCache.MeshDetails meshDetails) { ShapeComponent shape = points.GetComponent <ShapeComponent>(); // Clear all children as a hard reset. foreach (Transform child in points.GetComponentsInChildren <Transform>()) { if (child.gameObject != points.gameObject) { child.parent = null; GameObject.Destroy(child.gameObject); } } // Use shared resources if we have no limited indexing. if (points.IndexCount == 0) { // Add children for each mesh sub-sub-part. int partNumber = 0; foreach (Mesh mesh in meshDetails.FinalMeshes) { GameObject partMesh = new GameObject(string.Format("cloud{0}", partNumber)); partMesh.transform.localPosition = meshDetails.LocalPosition; partMesh.transform.localRotation = meshDetails.LocalRotation; partMesh.transform.localScale = meshDetails.LocalScale; partMesh.AddComponent <MeshFilter>().sharedMesh = mesh; MeshRenderer renderer = partMesh.AddComponent <MeshRenderer>(); if (meshDetails.Topology == MeshTopology.Points) { // Use mesh material as is. renderer.material = meshDetails.Material; } else { // Rendering a mesh with non-point topology. Set tha points based material. renderer.material = mesh.normals.Length > 0 ? _litMaterial : _unlitMaterial; } int pointSize = points.PointSize; if (pointSize == 0 && Materials != null) { pointSize = Materials.DefaultPointSize; } renderer.material.SetInt("_PointSize", pointSize); renderer.material.SetInt("_LeftHanded", ServerInfo.IsLeftHanded ? 1 : 0); renderer.material.color = (shape != null) ? shape.Colour : new Color32(255, 255, 255, 255); partMesh.transform.SetParent(points.transform, false); ++partNumber; } } else { // We are going to need to remap the indexing. For this we'll // copy the required vertices from the MeshDetails and create new // meshes with new indices. We could potentially share vertex data, // but this is difficult with the Unity vertex count limit. Mesh[] meshes = meshDetails.Builder.GetReindexedMeshes(points.Indices, MeshTopology.Points); for (int i = 0; i < meshes.Length; ++i) { // Create this mesh piece. bool defaultMaterial = meshDetails.Topology == MeshTopology.Points; Material material = (defaultMaterial) ? meshDetails.Material : _unlitMaterial; if (!defaultMaterial && meshDetails.Builder.Normals.Length != 0) { material = _litMaterial; } GameObject child = new GameObject(); child.AddComponent <MeshFilter>().mesh = meshes[i]; material.SetInt("_PointSize", points.PointSize); material.SetInt("_LeftHanded", ServerInfo.IsLeftHanded ? 1 : 0); child.AddComponent <MeshRenderer>().material = material; child.transform.SetParent(points.transform, false); } } }
void RenderPoints(CameraContext cameraContext, ShapeCache cache, int shapeIndex) { CreateMessage shape = cache.GetShapeByIndex(shapeIndex); if (CategoriesState != null && !CategoriesState.IsActive(shape.Category)) { return; } Matrix4x4 modelWorld = cameraContext.TesSceneToWorldTransform * cache.GetShapeTransformByIndex(shapeIndex); PointsComponent points = cache.GetShapeDataByIndex <PointsComponent>(shapeIndex); CommandBuffer renderQueue = cameraContext.OpaqueBuffer; RenderMesh mesh = points.Mesh != null ? points.Mesh.Mesh : null; if (mesh == null) { // No mesh. Debug.LogWarning($"Point cloud shape {shape.ObjectID} missing mesh with ID {points.MeshID}"); return; } if (points.Material == null) { // No mesh. Debug.LogWarning($"Point cloud shape {shape.ObjectID} missing material"); return; } // Check rendering with index buffer? GraphicsBuffer indexBuffer = null; int indexCount = 0; if (mesh.IndexCount > 0 || points.IndexCount > 0) { if ((int)points.IndexCount > 0) { indexBuffer = points.IndexBuffer; indexCount = (int)points.IndexCount; } // We only use the mesh index buffer if the mesh has points topology. // Otherwise we convert to points using vertices as is. else if (mesh.Topology == MeshTopology.Points) { indexBuffer = mesh.IndexBuffer; indexCount = mesh.IndexCount; } } if (mesh.HasColours) { points.Material.SetBuffer("_Colours", mesh.ColoursBuffer); } if (mesh.HasNormals) { points.Material.SetBuffer("_Normals", mesh.NormalsBuffer); } points.Material.SetBuffer("_Vertices", mesh.VertexBuffer); // Set min/max shader values. if (points.Material.HasProperty("_BoundsMin")) { points.Material.SetVector("_BoundsMin", points.Mesh.Mesh.MinBounds); } if (points.Material.HasProperty("_BoundsMax")) { points.Material.SetVector("_BoundsMax", points.Mesh.Mesh.MaxBounds); } float pointScale = (points.PointScale > 0) ? points.PointScale : 1.0f; points.Material.SetFloat("_PointSize", GlobalSettings.PointSize * pointScale); // Colour by height if we have a zero colour value. if (shape.Attributes.Colour == 0) { points.Material.SetColor("_Color", Color.white); points.Material.SetColor("_BackColour", Color.white); switch (CoordinateFrameUtil.AxisIndex(ServerInfo.CoordinateFrame, 2)) { case 0: points.Material.EnableKeyword("WITH_COLOURS_RANGE_X"); break; case 1: points.Material.EnableKeyword("WITH_COLOURS_RANGE_Y"); break; default: case 2: points.Material.EnableKeyword("WITH_COLOURS_RANGE_Z"); break; } } if (mesh.IndexBuffer != null) { renderQueue.DrawProcedural(mesh.IndexBuffer, modelWorld, points.Material, 0, mesh.Topology, mesh.IndexCount); } else { renderQueue.DrawProcedural(modelWorld, points.Material, 0, mesh.Topology, mesh.VertexCount); } }