view Orchestland/Assets/LeapMotion/Scripts/LeapImageRetriever.cs @ 1:f7675884f2a1

Add Orchestland project
author Daiki OYAKAWA <e135764@ie.u-ryukyu.ac.jp>
date Fri, 17 Jul 2015 23:09:20 +0900
parents
children
line wrap: on
line source

/******************************************************************************\
* Copyright (C) Leap Motion, Inc. 2011-2014.                                   *
* Leap Motion proprietary. Licensed under Apache 2.0                           *
* Available at http://www.apache.org/licenses/LICENSE-2.0.html                 *
\******************************************************************************/

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Leap;

// To use the LeapImageRetriever you must be on version 2.1+
// and enable "Allow Images" in the Leap Motion settings.
public class LeapImageRetriever : MonoBehaviour {
    public const string IR_SHADER_VARIANT_NAME = "LEAP_FORMAT_IR";
    public const string RGB_SHADER_VARIANT_NAME = "LEAP_FORMAT_RGB";
    public const string DEPTH_TEXTURE_VARIANT_NAME = "USE_DEPTH_TEXTURE";
    public const int IMAGE_WARNING_WAIT = 10;

    public enum EYE {
        LEFT = 0,
        RIGHT = 1
    }

    public enum SYNC_MODE {
        SYNC_WITH_HANDS,
        LOW_LATENCY
    }

    public EYE eye = (EYE)(-1);
    [Tooltip ("Should the image match the tracked hand, or should it be displayed as fast as possible")]
    public SYNC_MODE syncMode = SYNC_MODE.LOW_LATENCY;
    public float gammaCorrection = 1.0f;


    private int _missedImages = 0;
    private Controller _controller;

    //Information about the current format the retriever is configured for.  Used to detect changes in format
    private int _currentWidth = 0;
    private int _currentHeight = 0;
    private Image.FormatType _currentFormat = (Image.FormatType)(-1);

    //ImageList to use during rendering.  Can either be updated in OnPreRender or in Update
    private ImageList _imageList;

    //Holders for Image Based Materials
    private static List<LeapImageBasedMaterial> _registeredImageBasedMaterials = new List<LeapImageBasedMaterial>();
    private static List<LeapImageBasedMaterial> _imageBasedMaterialsToInit = new List<LeapImageBasedMaterial>();

    // Main texture.
    private Texture2D _mainTexture = null;
    private byte[] _mainTextureData = null;

    //Used to recalculate the distortion every time a hand enters the frame.  Used because there is no way to tell if the device has flipped (which changes the distortion)
    private bool _requestDistortionRecalc = false;
    private bool _forceDistortionRecalc = false;

    // Distortion textures.
    private Texture2D _distortion = null;
    private Color32[] _distortionPixels = null;

    public static void registerImageBasedMaterial(LeapImageBasedMaterial imageBasedMaterial) {
        _registeredImageBasedMaterials.Add(imageBasedMaterial);
        _imageBasedMaterialsToInit.Add(imageBasedMaterial);
    }

    public static void unregisterImageBasedMaterial(LeapImageBasedMaterial imageBasedMaterial) {
        _registeredImageBasedMaterials.Remove(imageBasedMaterial);
    }

    private void initImageBasedMaterial(LeapImageBasedMaterial imageBasedMaterial) {
        Material material = imageBasedMaterial.GetComponent<Renderer>().material;

        switch (_currentFormat) {
            case Image.FormatType.INFRARED:
                material.EnableKeyword(IR_SHADER_VARIANT_NAME);
                material.DisableKeyword(RGB_SHADER_VARIANT_NAME);
                break;
            case (Image.FormatType)4:
                material.EnableKeyword(RGB_SHADER_VARIANT_NAME);
                material.DisableKeyword(IR_SHADER_VARIANT_NAME);
                break;
            default:
                Debug.LogWarning("Unexpected format type " + _currentFormat);
                break;
        }

        if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.Depth)) {
            material.EnableKeyword(DEPTH_TEXTURE_VARIANT_NAME);
        } else {
            material.DisableKeyword(DEPTH_TEXTURE_VARIANT_NAME);
        }

        imageBasedMaterial.GetComponent<Renderer>().material.SetFloat("_LeapGammaCorrectionExponent", 1.0f / gammaCorrection);
    }

    private void updateImageBasedMaterial(LeapImageBasedMaterial imageBasedMaterial, ref Image image) {
        imageBasedMaterial.GetComponent<Renderer>().material.SetTexture("_LeapTexture", _mainTexture);

        Vector4 projection = new Vector4();
        projection.x = GetComponent<Camera>().projectionMatrix[0, 2];
        projection.z = GetComponent<Camera>().projectionMatrix[0, 0];
        projection.w = GetComponent<Camera>().projectionMatrix[1, 1];
        imageBasedMaterial.GetComponent<Renderer>().material.SetVector("_LeapProjection", projection);

        if (_distortion == null) {
            initDistortion(image);
            loadDistortion(image);
            _forceDistortionRecalc = false;
        }

        if (_forceDistortionRecalc || (_requestDistortionRecalc && _controller.Frame().Hands.Count != 0)) {
            loadDistortion(image);
            _requestDistortionRecalc = false;
            _forceDistortionRecalc = false;
        }

        imageBasedMaterial.GetComponent<Renderer>().material.SetTexture("_LeapDistortion", _distortion);
    }

    private TextureFormat getTextureFormat(Image image) {
        switch (image.Format) {
            case Image.FormatType.INFRARED:
                return TextureFormat.Alpha8;
            case (Image.FormatType)4:
                return TextureFormat.RGBA32;
            default:
                throw new System.Exception("Unexpected image format!");
        }
    }

    private int bytesPerPixel(TextureFormat format) {
        switch (format) {
            case TextureFormat.Alpha8: return 1;
            case TextureFormat.RGBA32:
            case TextureFormat.BGRA32:
            case TextureFormat.ARGB32: return 4;
            default: throw new System.Exception("Unexpected texture format " + format);
        }
    }

    private int totalBytes(Texture2D texture) {
        return texture.width * texture.height * bytesPerPixel(texture.format);
    }

    private void initMainTexture(Image image) {
        TextureFormat format = getTextureFormat(image);

        if (_mainTexture != null) {
            DestroyImmediate(_mainTexture);
        }

        _mainTexture = new Texture2D(image.Width, image.Height, format, false, true);
        _mainTexture.wrapMode = TextureWrapMode.Clamp;
        _mainTexture.filterMode = FilterMode.Bilinear;
        _mainTextureData = new byte[_mainTexture.width * _mainTexture.height * bytesPerPixel(format)];
    }

    private void loadMainTexture(Image sourceImage) {
        Marshal.Copy(sourceImage.DataPointer(), _mainTextureData, 0, _mainTextureData.Length);
        _mainTexture.LoadRawTextureData(_mainTextureData);
        _mainTexture.Apply();
    }

    private void initDistortion(Image image) {
        int width = image.DistortionWidth / 2;
        int height = image.DistortionHeight;

        _distortionPixels = new Color32[width * height];
        if (_distortion != null) {
            DestroyImmediate(_distortion);
        }
        _distortion = new Texture2D(width, height, TextureFormat.RGBA32, false, true);
        _distortion.wrapMode = TextureWrapMode.Clamp;
    }

    private void encodeFloat(float value, out byte byte0, out byte byte1) {
        // The distortion range is -0.6 to +1.7. Normalize to range [0..1).
        value = (value + 0.6f) / 2.3f;
        float enc_0 = value;
        float enc_1 = value * 255.0f;

        enc_0 = enc_0 - (int)enc_0;
        enc_1 = enc_1 - (int)enc_1;

        enc_0 -= 1.0f / 255.0f * enc_1;

        byte0 = (byte)(enc_0 * 256.0f);
        byte1 = (byte)(enc_1 * 256.0f);
    }

    private void loadDistortion(Image image) {
        float[] distortionData = image.Distortion;

        // Move distortion data to distortion texture
        for (int i = 0; i < distortionData.Length; i += 2) {
            byte b0, b1, b2, b3;
            encodeFloat(distortionData[i], out b0, out b1);
            encodeFloat(distortionData[i + 1], out b2, out b3);
            _distortionPixels[i / 2] = new Color32(b0, b1, b2, b3);
        }

        _distortion.SetPixels32(_distortionPixels);
        _distortion.Apply();
    }

    void Start() {
        HandController handController = FindObjectOfType<HandController>();
        if (handController == null) {
            Debug.LogWarning("Cannot use LeapImageRetriever if there is no HandController in the scene!");
            enabled = false;
            return;
        }

        _controller = handController.GetLeapController();
        _controller.SetPolicy(Controller.PolicyFlag.POLICY_IMAGES);
    }

    void Update() {
        Frame frame = _controller.Frame();

        if (frame.Hands.Count == 0) {
            _requestDistortionRecalc = true;
        }

        if (syncMode == SYNC_MODE.SYNC_WITH_HANDS) {
            _imageList = frame.Images;
        }
    }

    void OnPreRender() {
        if (syncMode == SYNC_MODE.LOW_LATENCY) {
            _imageList = _controller.Images;
        }

        Image referenceImage = _imageList[(int)eye];

        if (referenceImage.Width == 0 || referenceImage.Height == 0) {
            _missedImages++;
            if (_missedImages == IMAGE_WARNING_WAIT) {
                Debug.LogWarning("Can't find any images. " +
                                  "Make sure you enabled 'Allow Images' in the Leap Motion Settings, " +
                                  "you are on tracking version 2.1+ and " +
                                  "your Leap Motion device is plugged in.");
            }
            return;
        }

        if (referenceImage.Height != _currentHeight || referenceImage.Width != _currentWidth || referenceImage.Format != _currentFormat) {
            initMainTexture(referenceImage);

            _currentHeight = referenceImage.Height;
            _currentWidth = referenceImage.Width;
            _currentFormat = referenceImage.Format;

            _imageBasedMaterialsToInit.Clear();
            _imageBasedMaterialsToInit.AddRange(_registeredImageBasedMaterials);

            _forceDistortionRecalc = true;
        }

        loadMainTexture(referenceImage);

        for (int i = _imageBasedMaterialsToInit.Count - 1; i >= 0; i--) {
            LeapImageBasedMaterial material = _imageBasedMaterialsToInit[i];
            initImageBasedMaterial(material);
            _imageBasedMaterialsToInit.RemoveAt(i);
        }

        foreach (LeapImageBasedMaterial material in _registeredImageBasedMaterials) {
            if (material.imageMode == LeapImageBasedMaterial.ImageMode.STEREO ||
               (material.imageMode == LeapImageBasedMaterial.ImageMode.LEFT_ONLY && eye == EYE.LEFT) ||
               (material.imageMode == LeapImageBasedMaterial.ImageMode.RIGHT_ONLY && eye == EYE.RIGHT)) {
                updateImageBasedMaterial(material, ref referenceImage);
            }
        }
    }
}