//======= Copyright (c) Stereolabs Corporation, All rights reserved. ===============
using UnityEngine;
///
/// Displays the point cloud of the real world in front of the camera.
/// Can be attached to any GameObject in a scene, but requires a ZEDManager component to exist somewhere.
///
public class ZEDPointCloudManager : MonoBehaviour
{
///
/// Set to a camera if you do not want that camera to see the point cloud.
///
[Tooltip("Set to a camera if you do not want that camera to see the point cloud. ")]
public Camera hiddenObjectFromCamera;
///
/// Number of points displayed. Usually equal to the width * height of the ZED's resolution (eg. 1280 * 720).
///
private int numberPoints = 0;
///
/// Instance of the ZEDManager interface
///
public ZEDManager zedManager = null;
///
/// zed Camera controller by zedManager
///
private sl.ZEDCamera zed = null;
///
/// Texture that holds the 3D position of the points.
///
private Texture2D XYZTexture;
///
/// Texture that holds the colors of each point.
///
private Texture2D colorTexture;
///
/// Temporary copy/buffer of the XYZTexture to stop the point cloud in a defined moment.
///
private RenderTexture XYZTextureCopy = null;
///
/// Temporary copy/buffer of the ColorTexture to stop the point cloud in a defined moment.
///
private RenderTexture ColorTextureCopy = null;
///
/// Material used to display the point cloud. Usually Mat_ZED_PointCloud.
///
public Material mat;
///
/// Cached property index of _Position shader property, so we only look it up once. Do not use.
///
private static int? _positionid;
///
/// Returns the property index of the _Position property, and looks it up if it hasn't been looked up yet.
///
private static int positionID
{
get
{
if (_positionid == null)
{
_positionid = Shader.PropertyToID("_Position");
}
return (int)_positionid;
}
}
///
/// Cached property index of the _ColorTex shader property, so we only look it up once. Use colorTexID instead.
///
private static int? _colortexid;
///
/// Returns the property index of the _ColorTex property, which is the RGB color texture from the ZED.
///
private static int colorTexID
{
get
{
if (_colortexid == null)
{
_colortexid = Shader.PropertyToID("_ColorTex");
}
return (int)_colortexid;
}
}
///
/// Cached property index of _XYZTex shader property, so we only look it up once. Use xyzTexID instead.
///
private static int? _xyztexid;
///
/// Returns the property index of the _XYZTex property, which is the XYZ position of each pixel relative to the ZED.
///
private static int xyzTexID
{
get
{
if (_xyztexid == null)
{
_xyztexid = Shader.PropertyToID("_XYZTex");
}
return (int)_xyztexid;
}
}
///
/// Whether the point cloud should be visible or not.
///
[Tooltip("Whether the point cloud should be visible or not. ")]
public bool display = true;
///
/// Whether to update the point cloud.
/// If false, the point cloud will display the content of the temp textures from the last update.
///
[Tooltip("Whether to update the point cloud. If false, the point cloud will display the content of the temp textures from the last update. ")]
public bool update = true;
///
/// Flag to check if the update has changed state.
///
private bool previousUpdate = true;
void Start()
{
if(zedManager == null)
{
zedManager = FindObjectOfType();
if(ZEDManager.GetInstances().Count > 1) //We chose a ZED arbitrarily, but there are multiple cams present. Warn the user.
{
Debug.Log("Warning: " + gameObject.name + "'s zedManager was not specified, so the first available ZEDManager instance was " +
"assigned. However, there are multiple ZEDManager's in the scene. It's recommended to specify which ZEDManager you want to " +
"use to display a point cloud.");
}
}
if (zedManager != null)
zed = zedManager.zedCamera;
}
// Update is called once per frame
void Update()
{
if (zed.IsCameraReady) //Don't do anything unless the ZED has been initialized.
{
if (numberPoints == 0)
{
//Create the textures. These will be updated automatically by the ZED.
//We'll copy them each frame into XYZTextureCopy and ColorTextureCopy, which will be the ones actually displayed.
XYZTexture = zed.CreateTextureMeasureType(sl.MEASURE.XYZ);
colorTexture = zed.CreateTextureImageType(sl.VIEW.LEFT);
numberPoints = zed.ImageWidth * zed.ImageHeight;
//Load and set the material properties.
if (mat == null)
{
mat = new Material(Resources.Load("Materials/PointCloud/Mat_ZED_PointCloud") as Material);
}
if (mat != null)
{
//mat.SetTexture("_XYZTex", XYZTexture);
mat.SetTexture(xyzTexID, XYZTexture);
//mat.SetTexture("_ColorTex", colorTexture);
mat.SetTexture(colorTexID, colorTexture);
}
}
//If stop updated, create new render texture and fill them with the textures from the ZED.
// These textures will be displayed as they are not updated
if (!update && previousUpdate != update)
{
if (XYZTextureCopy == null)
{
XYZTextureCopy = new RenderTexture(XYZTexture.width, XYZTexture.height, 0, RenderTextureFormat.ARGBFloat);
}
if (ColorTextureCopy == null)
{
ColorTextureCopy = new RenderTexture(colorTexture.width, colorTexture.height, 0, RenderTextureFormat.ARGB32);
}
//Copy the new textures into the buffers.
Graphics.Blit(XYZTexture, XYZTextureCopy);
Graphics.Blit(colorTexture, ColorTextureCopy);
if (mat != null)
{
//mat.SetTexture("_XYZTex", XYZTextureCopy);
mat.SetTexture(xyzTexID, XYZTextureCopy);
//mat.SetTexture("_ColorTex", ColorTextureCopy);
mat.SetTexture(colorTexID, ColorTextureCopy);
}
}
//Send the textures to the material/shader.
if (update && previousUpdate != update && mat != null)
{
//mat.SetTexture("_XYZTex", XYZTexture);
mat.SetTexture(xyzTexID, XYZTexture);
//mat.SetTexture("_ColorTex", colorTexture);
mat.SetTexture(colorTexID, colorTexture);
}
previousUpdate = update;
}
}
void OnApplicationQuit()
{
//Free up memory.
mat = null;
if (XYZTextureCopy != null)
{
XYZTextureCopy.Release();
}
if (ColorTextureCopy != null)
{
ColorTextureCopy.Release();
}
}
void OnRenderObject()
{
if (mat != null)
{
if (hiddenObjectFromCamera == Camera.current) return;
if (!display) return; //Don't draw anything if the user doesn't want to.
//mat.SetMatrix("_Position", transform.localToWorldMatrix);
mat.SetMatrix(positionID, transform.localToWorldMatrix);
mat.SetPass(0);
#if UNITY_2019_1_OR_NEWER
Graphics.DrawProceduralNow(MeshTopology.Points, 1, numberPoints);
#else
Graphics.DrawProcedural(MeshTopology.Points, 1, numberPoints);
#endif
}
}
}