Appearance
LeapMotionHaptics
セットアップ
unityのプロジェクト作って、
エクスプローラーからHap-Eフォルダを入れる。
projectsettingsから
- Name: Ultraleap
- URL: https://package.openupm.com
- Scope(s): com.ultraleap
これらを入れる。
streamingAssetsフォルダ(Packages/Ultraleap Hap-E Haptics/StreamingAssets)をAssetsに動かす。
ScenesフォルダがあるとREADMEに書いてあったが、なかったのでエクスプローラーから探してそれを直接Assetsに入れました。
※TMPを入れること!
デモシーン
●JSONPlayer
”StreamingAssets/haptics”にはいってるJSONファイルを読み取って再生できるデモらしい。
シーンを動かしているコード
csharpusing System.Collections.Generic; using System.IO; using UnityEngine; using UnityEngine.UI; using TMPro; namespace HapE.Unity { public class JSONLoaderDropdown : MonoBehaviour { public TMP_Dropdown dropdown; public TMP_InputField jsonRootInput; public Button refreshButton; public Button playButton; public bool isPlaying = false; public Sprite playSprite; public Sprite pauseSprite; [SerializeField] public string hapticRootPath = ""; public string currentJSONPath = ""; public HapEDeviceManager hapticDevice; public List<string> jsonFiles = new(); // Start is called before the first frame update void Start() { hapticRootPath = Path.Combine(Application.streamingAssetsPath, "haptics"); Debug.Log("hapticRootPath: " + hapticRootPath); jsonRootInput.text = hapticRootPath; LoadJSONFilesFromRootPath(); jsonRootInput.onEndEdit.AddListener(RootInputFieldChanged); dropdown.onValueChanged.AddListener(DropdownChanged); dropdown.RefreshShownValue(); } public void RootInputFieldChanged(string value) { hapticRootPath = value; } public void DropdownChanged(int value) { currentJSONPath = jsonFiles[value]; } public void LoadJSONFilesFromRootPath() { jsonFiles.Clear(); dropdown.ClearOptions(); Debug.Log("hapticRootPath:" + hapticRootPath); DirectoryInfo dir = new DirectoryInfo(hapticRootPath); FileInfo[] info = dir.GetFiles("*.json"); foreach (FileInfo f in info) { // For each Row, instantiate a new HaptiRowPrefb and store its data dropdown.options.Add(new TMP_Dropdown.OptionData() { text = f.Name}); jsonFiles.Add(f.FullName); } dropdown.RefreshShownValue(); } public void PlayButtonPressed() { if (isPlaying) { hapticDevice.StopHaptics(); playButton.image.sprite = playSprite; } else { currentJSONPath = jsonFiles[dropdown.value]; Debug.Log("Playing :" + currentJSONPath); hapticDevice.PlayHapEJSON(currentJSONPath); playButton.image.sprite = pauseSprite; } isPlaying = !isPlaying; } // Update is called once per frame void Update() { if (Input.GetKeyUp(KeyCode.Escape)) { // TODO: raise dialog - are you sure you want to quit? Application.Quit(); } } } }
●抜粋
csharp
public string currentJSONPath = "";
public HapEDeviceManager hapticDevice;
//~~~~~~~~~~省略~~~~~~~~~~
//再生
hapticDevice.PlayHapEJSON(currentJSONPath);
//停止
hapticDevice.StopHaptics();hapticDeviceは、HapEDeviceManager型で定義されている。HapEDeviceManagerはUltraleap Hap-E Hapticsフォルダに含まれていました。(冒頭でエクスプローラからPackageに入れたファイル)
HapEDeviceMangerのコード(すごく長いです)
csharpusing System.Collections.Generic; using UnityEngine; using System; using System.Linq; using System.IO; namespace HapE.Unity { /// <summary> /// The Main interfce to the Hap-E Device. Handles connection and Playback. /// TODO: Split out the connection/playback logic /// </summary> /// using Ultraleap.Haptics; using Leap.Unity; public class HapEDeviceManager : MonoBehaviour { [Header("Hap-e Device")] [Tooltip("If enabled, we attempt to connect to a device on startup")] public bool autoConnect = true; public Context ctx; [SerializeField] private List<DeviceInfo> availableDevices; public Device hapticDevice; public bool useMockIfRequired = true; [SerializeField] private bool _muteHaptics; [SerializeField] private float _softStartStopValue = 0f; public float SoftStartStopValue { get { return _softStartStopValue; } set { _softStartStopValue = Mathf.Clamp(value, 0.0f, 10f); } } [Tooltip("If true, haptic rendering will still occur, but not output any haptics. Useful for Demo mode.")] public bool MuteHaptics { get { return _muteHaptics; } set { SetHapticsMuteEnabled(value); } } public HapEParticleRenderer hapticRenderer; public LeapServiceProvider leapServiceProvider; // A bool which determines whether haptics should be updated every frame // (should be set to 'true' for anything that tracking the hand. 'false' if Fixed. [SerializeField] private bool _updateHaptics; public bool UpdateHaptics { get { return _updateHaptics; } set { _updateHaptics = value; } } [Header("Transforms")] [Tooltip("The Unity world space transform of the Haptic Array")] public Transform arrayTransform; private float[] previousTransform = new float[16]; private float[] trackerTransform = new float[16]; private TrackingOriginTransform trackingOriginTransform; private Vector3 defaultTrackerPosition = new Vector3(0, 0, 0.2f); [Header("Tracking")] public TrackingFixation trackTransformObject; public Transform trackerOrigin; public bool loadTrackerOriginFromJSON = true; // Start is called before the first frame update void Start() { if (arrayTransform == null){ arrayTransform = transform; } if (loadTrackerOriginFromJSON) { SetTrackingOriginFromJSON(); } // Don't do anything if we don't want to auto-connect. Useful for multi-array configs. if (!autoConnect) { return; } ConnectToNewHapEDevice(); if (hapticDevice == null) { Debug.LogWarning("Could not connect to phyiscal Hap-E device or create a MockDevice! Check Hap-e Plugins are up to date."); } // Set up Leap Listener to push tracking frame events to update the HapE Tracker Transform. if (leapServiceProvider == null) { leapServiceProvider = FindObjectOfType<LeapServiceProvider>(); } leapServiceProvider.GetLeapController().FrameReady += FrameReady; } private void FrameReady(object sender, Leap.FrameEventArgs e) { if (UpdateHaptics) { //Debug.Log($"FrameReady at Time {Time.time} with {e.frame.CurrentFramesPerSecond}"); UpdateTrackerTransformFromNewLeapFrame(); } } private void OnDisable() { if (hapticDevice != null && hapticDevice.HapE.IsSupported) { StopHaptics(); } if (ctx != null) { //ctx.Dispose(); ctx = null; } } #region Tracking // Main update loop updates the Hap-e Tracker Transform // Main update loop updates the Hap-e Tracker Transform void UpdateTrackerTransformFromNewLeapFrame() { if (trackTransformObject != null) { Quaternion rotation = trackTransformObject.transform.localRotation; Vector3 translation = trackTransformObject.transform.localPosition; SetTrackerTransform(rotation, translation); } } public void SetTrackerTransform(Quaternion rotation, Vector3 translation) { var hapticUnityLocalMatrix = Matrix4x4.TRS(translation, rotation, Vector3.one); var ulMatrix = UnityToHapticSpace.inverse * (hapticUnityLocalMatrix * UnityToHapticSpace); trackerTransform = ToFloatArray(ulMatrix); // Only update the Tracker if the Transform has changed, no point sending updates for fixed point haptics, only for hand tracked ones. if (!Enumerable.SequenceEqual(previousTransform, trackerTransform)) { try { hapticDevice.HapE.V3SetTracker(trackerTransform); Array.Copy(trackerTransform, previousTransform, 16); } catch (Exception e) { Debug.LogWarning(e); Debug.LogWarning("Unable to update haptics."); UpdateHaptics = false; } } } readonly Matrix4x4 UnityToHapticSpace = new Matrix4x4(new Vector4(1f, 0f, 0f, 0f), new Vector4(0f, 0f, 1f, 0f), new Vector4(0f, 1f, 0f, 0f), new Vector4(0f, 0f, 0f, 1f)); float[] ToFloatArray(Matrix4x4 inMatrix) { return new float[16] { inMatrix.m00, inMatrix.m01, inMatrix.m02, inMatrix.m03, inMatrix.m10, inMatrix.m11, inMatrix.m12, inMatrix.m13, inMatrix.m20, inMatrix.m21, inMatrix.m22, inMatrix.m23, inMatrix.m30, inMatrix.m31, inMatrix.m32, inMatrix.m33 }; } /// <summary> /// If requested, set the HandTracker Origin to load from JSON at runtime. /// </summary> private void SetTrackingOriginFromJSON() { trackingOriginTransform = new(); string file = Path.Combine(Application.streamingAssetsPath, trackingOriginTransform.defaultTrackerOriginJSONFilename); if (!File.Exists(file)) { Debug.LogWarning("Unable to detect a TrackerOrigin. Assuming Leap Tracker position is x=0,y=0,z=121"); } else { string trackingOriginJSON = File.ReadAllText(file); trackingOriginTransform = JsonUtility.FromJson<TrackingOriginTransform>(trackingOriginJSON); } if (trackingOriginTransform.trackingPosition.Count == 3 && trackingOriginTransform.trackingRotation.Count == 3) { float xPos = trackingOriginTransform.trackingPosition[0]; float yPos = trackingOriginTransform.trackingPosition[1]; float zPos = trackingOriginTransform.trackingPosition[2]; float xRot = trackingOriginTransform.trackingRotation[0]; float yRot = trackingOriginTransform.trackingRotation[1]; float zRot = trackingOriginTransform.trackingRotation[2]; Vector3 pos = new Vector3(xPos, yPos, zPos); Quaternion rot = Quaternion.Euler(new Vector3(xRot, yRot, zRot)); trackerOrigin.SetPositionAndRotation(pos, rot); } if (trackingOriginTransform.arrayPosition.Count == 3 && trackingOriginTransform.arrayRotation.Count == 3) { float xPos = trackingOriginTransform.arrayPosition[0]; float yPos = trackingOriginTransform.arrayPosition[1]; float zPos = trackingOriginTransform.arrayPosition[2]; float xRot = trackingOriginTransform.arrayRotation[0]; float yRot = trackingOriginTransform.arrayRotation[1]; float zRot = trackingOriginTransform.arrayRotation[2]; Vector3 pos = new Vector3(xPos, yPos, zPos); Quaternion rot = Quaternion.Euler(new Vector3(xRot, yRot, zRot)); arrayTransform.SetPositionAndRotation(pos, rot); } } /// <summary> /// Set the default tracker transform (20 cm above array) /// </summary> public void SetDefaultTrackerTransform() { if (hapticDevice != null) { trackerTransform[0] = 1f; trackerTransform[3] = defaultTrackerPosition.x; trackerTransform[5] = 1f; trackerTransform[7] = defaultTrackerPosition.y; trackerTransform[10] = 1f; trackerTransform[11] = defaultTrackerPosition.z; trackerTransform[15] = 1f; hapticDevice.HapE.V3SetTracker(trackerTransform); } } public void SetHapEMasterVolume(float value) { if (hapticDevice != null) { Debug.Log("Setting Master volume to: " + value); hapticDevice.HapE.V3EnvelopeSetMasterVolume(value); } } #endregion #region Connection Handling /// <summary> /// Sets whether the device should FORCE use a Mock device, i.e. not output /// haptics, but still do evaluation, with a Mock device, (for visualisation purposes). /// </summary> /// <param name="muted"></param> public void SetHapticsMuteEnabled(bool muted) { _muteHaptics = muted; if (muted) { if (!UsingMockHapEDevice()) { StopHaptics(); hapticDevice = ctx.CreateMockDevice(); } } else { if (UsingMockHapEDevice()) { // Try and get a real device... ConnectToNewHapEDevice(); } } } public bool UsingMockHapEDevice() { if (hapticDevice == null) { return false; } return hapticDevice.GetType() == typeof(MockDevice); } public List<DeviceInfo> GetConnectedDevices() { if (ctx == null) { ctx = new Context(); } return ctx.GetConnectedDevices(); } /// <summary> /// Returns the number of currently connected Haptic Devices /// </summary> /// <returns></returns> public int GetDeviceCount() { return GetConnectedDevices().Count; } public Device ConnectToNewHapEDevice() { if (ctx == null) { ctx = new Context(); } availableDevices = GetConnectedDevices(); // Ensure haptics are not playing! StopHaptics(); if (availableDevices.Count == 0) { if (hapticDevice != null) { hapticDevice.Dispose(); hapticDevice = null; } if (useMockIfRequired && !UsingMockHapEDevice()) { hapticDevice = ctx.CreateMockDevice(); } // Return now, because no phyiscal devices were detected. return null; } // If we're here, we might have already created a MockDevice, so get rid of it first and stop if playing.. if (hapticDevice != null) { hapticDevice.Dispose(); hapticDevice = null; } foreach (DeviceInfo di in availableDevices) { try { // Get the first connected device... hapticDevice = di.Open(); string deviceInfo = GetInfoStringForDevice(hapticDevice); Debug.Log(deviceInfo); break; } catch (Exception e) { Debug.Log("Exception: " + e.ToString()); } } return hapticDevice; } public string HapEDeviceConnectionStatusString() { string connectionStatusString = "Connection unavailable"; if (hapticDevice != null) { string hardwareFirmware = GetFirmwareString(); connectionStatusString = $"Connected to Hap-E Device:\n {hapticDevice.Identifier} \n Firmware: {hardwareFirmware}"; } return connectionStatusString; } /// <summary> /// This is the Firmware String we use in the Sensation Designer array connection tooltip. /// </summary> /// <returns></returns> public string GetFirmwareString() { string firmwareString = "Unknown"; if (hapticDevice != null) { try { var firmwareVersions = hapticDevice.getFirmwareVersions(); firmwareString = $"{firmwareVersions.HapticsApp.Major}.{firmwareVersions.HapticsApp.Minor}.{firmwareVersions.HapticsApp.Patch}"; } catch (Exception e) { Debug.LogWarning($"Unable to obtain firmware version: {e}"); } } return firmwareString; } public void CopyAllHardwareInfoToClipboard() { if (hapticDevice != null) { string hardwareInfoString = GetInfoStringForDevice(hapticDevice); hardwareInfoString.CopyToClipboard(); } } /// <summary> /// Returns a string of all the available Hardware info (firmware etc.) about the Hap-e Device. /// </summary> /// <returns></returns> public string GetInfoStringForDevice(Device device) { string deviceInfo = "Ultraleap Hap-e Device Info\n" + $"Identifier: {hapticDevice.Identifier}\n"; if (device != null) { try { var firmwareVersions = device.getFirmwareVersions(); var hapticsApp = firmwareVersions.HapticsApp; var bootloader = firmwareVersions.Bootloader; var dfu = firmwareVersions.DFUFlashloader; var hardware = firmwareVersions.HardwareVersion; var fpgaProgram = firmwareVersions.FPGAProgrammer; var fpgaSolver = firmwareVersions.FPGASolver; deviceInfo += $"HapticsApp: {hapticsApp.Major}.{hapticsApp.Minor}.{hapticsApp.Patch} (#{hapticsApp.GitHash.HashAsHex()}, Dirty={hapticsApp.Dirty}, Valid={hapticsApp.Valid})\n" + $"Bootloader: {bootloader.Major}.{bootloader.Minor}.{bootloader.Patch} (#{bootloader.GitHash.HashAsHex()}, Dirty={bootloader.Dirty}, Valid={bootloader.Valid})\n" + $"DFUFlashloader: {dfu.Major}.{dfu.Minor}.{dfu.Patch} (#{dfu.GitHash.HashAsHex()}, Dirty={dfu.Dirty}, Valid={dfu.Valid})\n" + $"HardwareVersion: {hardware.Major}.{hardware.Minor}.{hardware.Patch} (#{hardware.GitHash.HashAsHex()}, Dirty={hardware.Dirty}, Valid={hardware.Valid})\n" + $"FPGAProgrammer: {fpgaProgram.Major}.{fpgaProgram.Minor}.{fpgaProgram.Patch} (#{fpgaProgram.GitHash.HashAsHex()}, Dirty={fpgaProgram.Dirty}, Valid={fpgaProgram.Valid})\n" + $"FPGASolver: {fpgaSolver.Major}.{fpgaSolver.Minor}.{fpgaSolver.Patch} (#{fpgaSolver.GitHash.HashAsHex()}, Dirty={fpgaSolver.Dirty}, Valid={fpgaSolver.Valid})\n"; } catch (Exception e) { Debug.LogWarning($"Unable to obtain firmware version: {e}"); } } return deviceInfo; } #endregion #region Playback /// <summary> /// Plays a Hap-E json file, as defined by the jsonPath /// </summary> /// <param name="jsonPath"></param> public void PlayHapEJSON(string jsonPath) { if (hapticDevice == null) { Debug.LogWarning("Unable to Play HapEJSON, no valid hapticDevice available!"); return; } if (File.Exists(jsonPath)) { UpdateHaptics = true; hapticDevice.HapE.V3LoadJSONFile(jsonPath); hapticDevice.HapE.Start(softValue: SoftStartStopValue); } else { Debug.LogWarning("JSON Path could not be found:" + jsonPath); } } public void PlayHapEJSONOnDevice(string jsonPath, Device hapeDevice) { if (hapeDevice == null || !hapeDevice.IsConnected || !hapeDevice.HapE.IsSupported) { Debug.LogWarning("Unable to Play HapEJSON, no valid hapticDevice available!"); return; } if (File.Exists(jsonPath)) { UpdateHaptics = true; hapeDevice.HapE.V3LoadJSONFile(jsonPath); hapeDevice.HapE.Start(softValue: SoftStartStopValue); } else { Debug.LogWarning("JSON Path could not be found:" + jsonPath); } } /// <summary> /// Plays a Hap-E haptic definition from a string containing the Hap-e JSON contents. /// </summary> /// <param name="jsonPath"></param> public void PlayHapEJSONString(string jsonString) { if (hapticDevice == null) { Debug.LogWarning("Unable to Play HapEJSON, no valid hapticDevice available!"); return; } UpdateHaptics = true; hapticDevice.HapE.V3LoadJSONString(jsonString); hapticDevice.HapE.Start(softValue: SoftStartStopValue); } public void SetHapEPlaybackMode(int playbackMode) { // 0=Forward, 1=Backward, 2=PingPong, 3=Random StopHaptics(); hapticDevice.HapE.V3PainterSetMode((HapE.V3PainterMode)(byte)playbackMode); hapticDevice.HapE.V3EnvelopeSetMode((HapE.V3EnvelopeMode)(byte)playbackMode); } public virtual void StopHaptics() { UpdateHaptics = false; hapticRenderer.ClearParticles(); // We have to wrap this in a try catch, because if the device gets disconnected, // there's no signal to allow us to null the hapticDevice. try { hapticDevice?.HapE.Stop(); } catch (Exception e) { Debug.LogWarning($"Error Stopping {hapticDevice}. Message = {e.Message}"); Debug.LogWarning("Unable to Stop Haptics... Was the device disconnected?"); } } public void PlayStaticPointFromHapEData(HapEData hapEData) { // Set Primitive Params SetPrimitivePropertiesFromHapEData(hapEData); hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnablePainter, 1); PainterNode point; if (hapEData.painter == null || hapEData.painter.nodes == null) { Debug.LogWarning("No Node data existed in hapEData! Assuming point at 0,0"); point.x = 0f; point.y = 0f; point.x_scale = 1f; point.y_scale = 1f; point.z_rotation = 0; } else { point = hapEData.painter.nodes[0]; } hapticDevice.HapE.V3PainterUpdateNode(0, point.x, point.y, point.z_rotation, point.x_scale, point.y_scale, 1f); hapticDevice.HapE.V3PainterUpdateNode(1, point.x, point.y, point.z_rotation, point.x_scale, point.y_scale, 1f); hapticDevice.HapE.V3PainterSetLength(1); hapticDevice.HapE.V3PainterSetRepeatCount(0); SetEnvelopeNodesFromHapEData(hapEData); if (hapEData.animator != null && hapEData.animator.enabled) { SetAnimatorTransformsFromHapEData(hapEData); hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnableAnimator, 1); } else { hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnableAnimator, 0); } if (!UpdateHaptics) { hapticDevice.HapE.Start(softValue: SoftStartStopValue); } UpdateHaptics = true; return; } public void SetPlaybackRepeatCount(int repeatCount) { if (hapticDevice == null) { return; } hapticDevice.HapE.V3PainterSetRepeatCount((uint)repeatCount); hapticDevice.HapE.V3EnvelopeSetRepeatCount((uint)repeatCount); } #endregion #region HapE Data Handling public void ResetHapEState() { hapticDevice?.HapE.V3ResetToDefault(); } public bool HapEDeviceAvailable() { return (hapticDevice != null);// (hapticDevice != null && ((GetConnectedDevices().Count) > 0 || UsingMockHapEDevice())); } /// <summary> /// Sets all Primitive Properties, stored in hapEData /// </summary> /// <param name="hapEData"></param> public void SetPrimitivePropertiesFromHapEData(HapEData hapEData) { if (!HapEDeviceAvailable()) { return; } if (hapEData.primitive != null) { PrimitiveProperties primitiveProperties = hapEData.primitive; float f = primitiveProperties.draw_frequency; float A = primitiveProperties.A; float B = primitiveProperties.B; hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.DrawFrequency, f); hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigA, A); hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigB, B); hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.a, (float)primitiveProperties.a); hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.b, (float)primitiveProperties.b); // These properties are not always set in our DefaultBrush Assets, hence they could be null/NaN // TODO: Include null checks for every property, to be safe?... if ((primitiveProperties.max_t != null) && !float.IsNaN(primitiveProperties.max_t.Value)) { hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.MaxT, (float)primitiveProperties.max_t); } if ((primitiveProperties.k != null) && !float.IsNaN(primitiveProperties.k.Value)) { hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.k, (float)primitiveProperties.k); } if ((primitiveProperties.d != null) && !float.IsNaN(primitiveProperties.d.Value)) { hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.d, (float)primitiveProperties.d); } } else { Debug.LogWarning("HapEData had no valid Primitive Data set!"); } } /// <summary> /// Sets All Primitive Parameters froma a HapEBrush scriptable object /// </summary> /// <param name="brush"></param> public void SetPrimitivePropertiesFromHapEBrushDefault(HapEBrushDefault brush) { foreach (HapEBrushDefault.HapEPrimitiveParameter P in brush.primitiveParams) { hapticDevice.HapE.V3PrimitiveSetParameter(P.parameter, P.value); } } /// <summary> /// Loads HapEData onto the device. Does not play back. /// </summary> /// <param name="hapEData"></param> public void LoadHapEData(HapEData hapEData, bool skipReset = false) { if (!skipReset) { ResetHapEState(); } if (hapEData.animator != null) { SetAnimatorTransformsFromHapEData(hapEData); } if (hapEData.primitive != null) { SetPrimitivePropertiesFromHapEData(hapEData); } if (hapEData.painter != null) { UpdatePainterNodesFromHapEData(hapEData); } if (hapEData.envelope != null) { SetEnvelopeNodesFromHapEData(hapEData); } } /// <summary> /// Plays back a Hap-e Sensation from HapEData Object, typically derived from Hap-e JSON. /// </summary> /// <param name="hapEData"></param> public void PlayHapEData(HapEData hapEData, bool skipReset = false) { LoadHapEData(hapEData, skipReset: skipReset); UpdateHaptics = true; if (hapticDevice != null) { hapticDevice.HapE.Start(softValue: SoftStartStopValue); } } /// <summary> /// Plays back a Hap-e Sensation from HapEData Object, typically derived from Hap-e JSON, /// Allows override of the soft start stop value. /// </summary> /// <param name="hapEData"></param> public void PlayHapEDataWithSoftStartStop(HapEData hapEData, bool skipReset = false, float softValue = 0.0f) { LoadHapEData(hapEData, skipReset: skipReset); UpdateHaptics = true; if (hapticDevice != null) { hapticDevice.HapE.Start(softValue: SoftStartStopValue); } } /// <summary> /// Just plays back what ever is the current Hap-E Data on the device, tracking updates stil occur /// </summary> /// <param name="hapEData"></param> public void PlayCurrentDeviceHapEData() { if (hapticDevice != null) { UpdateHaptics = true; hapticDevice.HapE.Start(softValue: SoftStartStopValue); } } /// <summary> /// Updates the Painter Nodes on device from HapeData Object /// </summary> /// <param name="hapEData"></param> public void UpdatePainterNodesFromHapEData(HapEData hapEData) { if (hapticDevice == null) { return; } PainterProperties painterProperties = hapEData.painter; if (painterProperties == null || !hapEData.painter.enabled) { hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnablePainter, 0); return; } uint numPoints = (uint)painterProperties.nodes.Count; if (numPoints == 1 && UpdateHaptics) { PlayStaticPointFromHapEData(hapEData); return; } int painterRepeatCount = (int)painterProperties.repeat_count; HapE.V3PainterMode painterMode = painterProperties.PainterMode(); hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnablePainter, 1); List<PainterNode> painterNodes = painterProperties.nodes; for (int ix = 0; ix < numPoints; ix++) { PainterNode pt = painterNodes[ix]; uint nodeId = (uint)(ix); //Debug.Log("Setting painter node:" + nodeId + ", time: " + pt.t + ", x: " + pt.x + ", y:" + pt.y); hapticDevice.HapE.V3PainterUpdateNode(nodeId, pt.x, pt.y, pt.z_rotation, pt.x_scale, pt.y_scale, pt.t); } hapticDevice.HapE.V3PainterSetMode(painterMode); hapticDevice.HapE.V3PainterSetLength(numPoints); hapticDevice.HapE.V3PainterSetRepeatCount((uint)painterRepeatCount); } public void SetEnvelopeNodesFromHapEData(HapEData hapEData) { if (hapticDevice == null) { return; } EnvelopeProperties envelopeProperties = hapEData.envelope; if (envelopeProperties == null || !hapEData.envelope.enabled) { hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnableEnvelope, 0); return; } List<EnvelopeNode> envelopeNodes = envelopeProperties.nodes; uint envelopeLength = (uint)envelopeNodes.Count; hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnableEnvelope, 1); for (int ix = 0; ix < envelopeLength; ix++) { uint nodeId = (uint)(ix); float intensity = envelopeNodes[ix].intensity; float time = envelopeNodes[ix].t; //Debug.Log("Setting envelope node ID: " + nodeId + ", intensity:" + intensity + ", time: " + time); hapticDevice.HapE.V3EnvelopeUpdateNode(nodeId, intensity, time); } // Envelope Settings int envelopeRepeatCount = (int)envelopeProperties.repeat_count; hapticDevice.HapE.V3EnvelopeSetLength((uint)envelopeLength); // This sets the number of times the envelope repeats, 0 means it loops infinitely hapticDevice.HapE.V3EnvelopeSetRepeatCount((uint)envelopeRepeatCount); HapE.V3EnvelopeMode envelopeMode = envelopeProperties.EnvelopeMode(); hapticDevice.HapE.V3EnvelopeSetMode(envelopeMode); } public void SetAnimatorTransformsFromHapEData(HapEData hapEData) { //Debug.Log("HapeDeviceManager: SetAnimatorTransformsFromHapEData"); if (hapticDevice == null) { return; } if (hapEData.animator == null || !hapEData.animator.enabled) { //Debug.Log("Disabling Animator"); hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnableAnimator, 0); return; } hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnableAnimator, 1); if (hapEData.animator.T1 != null && hapEData.animator.T1.Length > 0) { hapticDevice.HapE.V3AnimatorSetTransform(HapE.V3AnimatorTransform.T1, hapEData.animator.T1); } if (hapEData.animator.T1a1 != null && hapEData.animator.T1a1.Length > 0) { hapticDevice.HapE.V3AnimatorSetTransform(HapE.V3AnimatorTransform.T1A1, hapEData.animator.T1a1); } if (hapEData.animator.T1a2 != null && hapEData.animator.T1a2.Length > 0) { hapticDevice.HapE.V3AnimatorSetTransform(HapE.V3AnimatorTransform.T1A2, hapEData.animator.T1a2); } if (hapEData.animator.T2 != null && hapEData.animator.T2.Length > 0) { hapticDevice.HapE.V3AnimatorSetTransform(HapE.V3AnimatorTransform.T2, hapEData.animator.T2); } if (hapEData.animator.T2a1 != null && hapEData.animator.T2a1.Length > 0) { hapticDevice.HapE.V3AnimatorSetTransform(HapE.V3AnimatorTransform.T2A1, hapEData.animator.T2a1); } if (hapEData.animator.T2a2 != null && hapEData.animator.T2a2.Length > 0) { hapticDevice.HapE.V3AnimatorSetTransform(HapE.V3AnimatorTransform.T2A2, hapEData.animator.T2a2); } if (hapEData.animator.T1a1_switch_count != 0) { hapticDevice.HapE.V3SendCommand(HapE.V3Command.T1A1SwitchCount, (uint)hapEData.animator.T1a1_switch_count); } if (hapEData.animator.T1a2_switch_count != 0) { hapticDevice.HapE.V3SendCommand(HapE.V3Command.T1A2SwitchCount, (uint)hapEData.animator.T1a2_switch_count); } if (hapEData.animator.T2a1_switch_count != null) { hapticDevice.HapE.V3SendCommand(HapE.V3Command.T2A1SwitchCount, (uint)hapEData.animator.T2a1_switch_count.Value); } if (hapEData.animator.T2a2_switch_count != null) { hapticDevice.HapE.V3SendCommand(HapE.V3Command.T2A2SwitchCount, (uint)hapEData.animator.T2a2_switch_count.Value); } } #endregion } }
●抜粋
csharp
using Ultraleap.Haptics;
using Leap.Unity;
public class HapEDeviceManager : MonoBehaviour
{
public Device hapticDevice;
//~~~~~~~~~~~~省略~~~~~~~~~~~~~~~
//シーンから呼び出してたコード(再生)
public void PlayHapEJSON(string jsonPath)
{
if (hapticDevice == null)
{
Debug.LogWarning("Unable to Play HapEJSON, no valid hapticDevice available!");
return;
}
if (File.Exists(jsonPath))
{
UpdateHaptics = true;
hapticDevice.HapE.V3LoadJSONFile(jsonPath);
//多分ここで再生してる
hapticDevice.HapE.Start(softValue: SoftStartStopValue);
}
else
{
Debug.LogWarning("JSON Path could not be found:" + jsonPath);
}
}
//シーンから呼び出してたコード2(停止)
public virtual void StopHaptics()
{
UpdateHaptics = false;
hapticRenderer.ClearParticles();
// We have to wrap this in a try catch, because if the device gets disconnected,
// there's no signal to allow us to null the hapticDevice.
try
{
//多分ここで停止してる
hapticDevice?.HapE.Stop();
}
catch (Exception e)
{
Debug.LogWarning($"Error Stopping {hapticDevice}. Message = {e.Message}");
Debug.LogWarning("Unable to Stop Haptics... Was the device disconnected?");
}
}
}●HapETest
後述のコードで制御したモーションを再生、編集するデモ。
シーンにおいてあったコード
csharpusing System.Collections.Generic; using UnityEngine; using System; using System.IO; /// <summary> /// Hap-E API usage examples /// PainterNodes represent the position of pattern. /// EnvelopeNodes represent the intensity envelope of the sensation. /// See also HapticLibraryPlayer.cs, which plays Sensations serialised out to JSON (from Hap-e Designer for instance) /// </summary> namespace HapE.Unity { using Ultraleap.Haptics; public class HapETest : MonoBehaviour { public HapEDeviceManager deviceManager; public bool useMockIfRequired = true; public bool playing = false; private float[] trackerTransform = new float[16]; public float x = 0.0f; public float y = 0.0f; public float z = 0.2f; public List<Vector3> dummyPath = new List<Vector3>(); [Header("Haptic Params")] public float bigA = 0.01f; public float bigB = 0.01f; public float a = 2f; public float b = 2f; public float drawFrequency = 40f; // Start is called before the first frame update void Start() { //deviceManager.ConnectToHapEDevice(); dummyPath.Add(new Vector3(0.029f, 0.028f, 0f)); dummyPath.Add(new Vector3(-0.032f, 0f, 0.333f)); dummyPath.Add(new Vector3(0.036f, -0.022f, 0.667f)); dummyPath.Add(new Vector3(0.03f, -0.050f, 1f)); } public void SetHapticParamsFromBrushDefault(HapEBrushDefault brush) { deviceManager.ResetHapEState(); foreach (HapEBrushDefault.HapEPrimitiveParameter primParam in brush.primitiveParams) { deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(primParam.parameter, primParam.value); } if (brush.animatorTransforms.Count > 0) { foreach (HapEBrushDefault.HapETransform t in brush.animatorTransforms) { deviceManager.hapticDevice.HapE.V3AnimatorSetTransform(t.animatorTransform, t.transformArray); } } if (brush.hapECommands.Count > 0) { foreach (HapEBrushDefault.HapECommand cmd in brush.hapECommands) { deviceManager.hapticDevice.HapE.V3SendCommand(cmd.command, cmd.value); } } uint painterNodeCount = (uint)brush.painterNodes.Count; uint painterNodeID = 0; if (brush.painterNodes.Count > 0) { foreach (HapEBrushDefault.HapEPainterNode node in brush.painterNodes) { deviceManager.hapticDevice.HapE.V3PainterUpdateNode(painterNodeID, node.x, node.y, node.zRotation, node.scaleX, node.scaleY, node.time); painterNodeID += 1; } deviceManager.hapticDevice.HapE.V3PainterSetLength(painterNodeCount); Debug.Log("brush.painterMode:" + brush.painterMode); deviceManager.hapticDevice.HapE.V3PainterSetMode(brush.painterMode); deviceManager.hapticDevice.HapE.V3PainterSetRepeatCount(brush.painterRepeatCount); } uint envelopeNodeCount = (uint)brush.envelopeNodes.Count; uint envelopeNodeID = 0; if (brush.envelopeNodes.Count > 0) { foreach (HapEBrushDefault.HapEEnvelopeNode node in brush.envelopeNodes) { deviceManager.hapticDevice.HapE.V3EnvelopeUpdateNode(envelopeNodeID, node.intensity, node.time); painterNodeID += 1; } deviceManager.hapticDevice.HapE.V3EnvelopeSetLength(envelopeNodeCount); deviceManager.hapticDevice.HapE.V3EnvelopeSetMode(brush.envelopeMode); deviceManager.hapticDevice.HapE.V3PainterSetRepeatCount(brush.envelopeRepeatCount); } PlayAtFixedLocation(); } public void SetCircleParams() { Debug.Log("Setting 2cm Circle 50Hz"); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigA, 0.02f); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigB, 0.02f); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.DrawFrequency, drawFrequency); deviceManager.hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnableAnimator, 0); PlayAtFixedLocation(); } public void SetLineParams() { Debug.Log("Setting Fat Line"); deviceManager.ResetHapEState(); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigA, 0.005f); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigB, 0.05f); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.DrawFrequency, 50); PlayAtFixedLocation(); } public void SetDialParams() { deviceManager.ResetHapEState(); deviceManager.hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnableAnimator, 1); Debug.Log("Setting Dial Params"); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigA, 0.01f); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigB, 0.01f); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.DrawFrequency, 64); float[] T1 = MathHelpers.Identity3x3(); T1[2] = 0.02f; deviceManager.hapticDevice.HapE.V3AnimatorSetTransform(HapE.V3AnimatorTransform.T1, T1); float[] T2a1 = MathHelpers.Identity3x3(); T2a1[0] = 0.992546f; T2a1[1] = -0.121869f; T2a1[3] = 0.121869f; T2a1[4] = 0.992546f; deviceManager.hapticDevice.HapE.V3AnimatorSetTransform(HapE.V3AnimatorTransform.T2A1, T2a1); PlayAtFixedLocation(); } public void SetDialParamTime(float time) { float radius = 0.03f; deviceManager.ResetHapEState(); deviceManager.hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnablePainter, 1); deviceManager.hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnableAnimator, 1); Debug.Log("Setting Dial Params for a radius of:" + time.ToString()); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigA, 0.01f); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigB, 0.01f); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.DrawFrequency, 127); float[] T1 = MathHelpers.Identity3x3(); T1[2] = radius; deviceManager.hapticDevice.HapE.V3AnimatorSetTransform(HapE.V3AnimatorTransform.T1, T1); deviceManager.hapticDevice.HapE.V3PainterUpdateNode(0, 0, 0, 0, 1.0f, 1.0f, time); deviceManager.hapticDevice.HapE.V3PainterUpdateNode(1, 0, 0, (float)Math.PI * 2, 1.0f, 1.0f, 0.0f); deviceManager.hapticDevice.HapE.V3PainterSetLength(2); deviceManager.hapticDevice.HapE.V3PainterSetRepeatCount(0); PlayAtFixedLocation(); } public void SetLineScanParams() { Debug.Log("Setting Line Scan Params"); deviceManager.ResetHapEState(); deviceManager.hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnableAnimator, 1); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigA, 0.04f); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigB, 0.001f); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.DrawFrequency, 50); // Scan Up float lineLength = 0.008f; float[] T1a1 = MathHelpers.Identity3x3(); T1a1[5] = lineLength / 2; deviceManager.hapticDevice.HapE.V3SendCommand(HapE.V3Command.T1A1SwitchCount, 13); deviceManager.hapticDevice.HapE.V3AnimatorSetTransform(HapE.V3AnimatorTransform.T1A1, T1a1); // Scan Down float[] T1a2 = MathHelpers.Identity3x3(); T1a2[5] = -lineLength / 2; deviceManager.hapticDevice.HapE.V3SendCommand(HapE.V3Command.T1A2SwitchCount, 13); deviceManager.hapticDevice.HapE.V3AnimatorSetTransform(HapE.V3AnimatorTransform.T1A2, T1a2); PlayAtFixedLocation(); } public void SetPainterScanParams() { Debug.Log("Setting Painter Scan Params"); deviceManager.ResetHapEState(); deviceManager.hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnableAnimator, 0); deviceManager.hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnableEnvelope, 1); deviceManager.hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnablePainter, 1); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigA, 0.04f); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigB, 0.001f); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.DrawFrequency, 127); deviceManager.hapticDevice.HapE.V3PainterUpdateNode(0, 0, -0.05f, 0, 1f, 1f, 1f); deviceManager.hapticDevice.HapE.V3PainterUpdateNode(1, 0, 0.05f, 0, 1f, 1f, 0f); deviceManager.hapticDevice.HapE.V3EnvelopeUpdateNode(0, 1f, 1f); deviceManager.hapticDevice.HapE.V3EnvelopeUpdateNode(1, 1f, 0f); deviceManager.hapticDevice.HapE.V3PainterSetLength(2); deviceManager.hapticDevice.HapE.V3EnvelopeSetLength(2); deviceManager.hapticDevice.HapE.V3PainterSetRepeatCount(0); deviceManager.hapticDevice.HapE.V3EnvelopeSetRepeatCount(0); deviceManager.hapticDevice.HapE.V3PainterSetMode(HapE.V3PainterMode.PingPong); deviceManager.hapticDevice.HapE.V3EnvelopeSetMode(HapE.V3EnvelopeMode.PingPong); PlayAtFixedLocation(); } public void SetTriangleParams() { Debug.Log("Setting Triangle Params"); deviceManager.ResetHapEState(); deviceManager.hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnableAnimator, 1); deviceManager.hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnablePainter, 1); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigA, 0.01f); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigB, 0.01f); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.DrawFrequency, 80); deviceManager.hapticDevice.HapE.V3PainterSetRepeatCount(0); deviceManager.hapticDevice.HapE.V3PainterSetLength(3); deviceManager.hapticDevice.HapE.V3PainterSetMode(HapE.V3PainterMode.Forward); deviceManager.hapticDevice.HapE.V3PainterUpdateNode(0, 0.04f, 0, 0, 1, 1, 0.33f); deviceManager.hapticDevice.HapE.V3PainterUpdateNode(1, 0, 0.04f, 0, 1, 1, 0.33f); deviceManager.hapticDevice.HapE.V3PainterUpdateNode(2, -0.04f, 0, 0, 1, 1, 0.33f); PlayAtFixedLocation(); } // Allows the Hap-E Designer to set points public void SetTracingPath(List<Vector3> points) { Debug.Log("Setting Triangle Params"); deviceManager.ResetHapEState(); deviceManager.hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnableAnimator, 1); deviceManager.hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnablePainter, 1); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigA, 0.01f); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigB, 0.01f); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.DrawFrequency, 80); deviceManager.hapticDevice.HapE.V3PainterSetRepeatCount(0); uint numPoints = (uint)points.Count; deviceManager.hapticDevice.HapE.V3PainterSetLength(numPoints); deviceManager.hapticDevice.HapE.V3PainterSetMode(HapE.V3PainterMode.PingPong); uint ix = 0; foreach (Vector3 pt in points) { // Hard-code time to be 1 second for the tracing for now deviceManager.hapticDevice.HapE.V3PainterUpdateNode(ix, pt.x, pt.y, 0, 1, 1, 1f); ix++; } PlayAtFixedLocation(); } string GetAssetPath(string assetFilename) { string hapticRootPath = Path.Combine(Application.streamingAssetsPath, "haptics"); DirectoryInfo dir = new DirectoryInfo(hapticRootPath); FileInfo[] info = dir.GetFiles(assetFilename); foreach (FileInfo f in info) { return f.ToString(); // only return first one } throw new Exception("File not found"); } public void LoadAndPlayRotorSalutTest() { deviceManager.PlayHapEJSON(GetAssetPath("Rotor Salut.json")); } public void LoadAndPlayButtonClickTest() { deviceManager.PlayHapEJSON(GetAssetPath("08.Button Click.json")); } public void LoadAndPlayButtonDoubleClickTest() { deviceManager.PlayHapEJSON(GetAssetPath("09.Button Double Tap.json")); } public void DoubleTapCircleTest() { SetDoubleTap(); } // A double-tap Circle haptic public void SetDoubleTap(float preDelay = 0f, float onTime = 0.33f, float offTime = 0.15f, float postDelay = 0f) { Debug.Log("Setting 2cm Circle 50Hz"); deviceManager.ResetHapEState(); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigA, 0.01f); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigB, 0.01f); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.DrawFrequency, 80f); deviceManager.hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnableEnvelope, 1); deviceManager.hapticDevice.HapE.V3EnvelopeSetMode(HapE.V3EnvelopeMode.Forward); // This sets the number of times the envelope repeats. deviceManager.hapticDevice.HapE.V3EnvelopeSetRepeatCount(1); // This sets the number of nodes in the envelope deviceManager.hapticDevice.HapE.V3EnvelopeSetLength(9); // Set keyframes for: WARMUP-ON-OFF-ON-OFF-COOLDOWN deviceManager.hapticDevice.HapE.V3EnvelopeUpdateNode(0, 0, preDelay); deviceManager.hapticDevice.HapE.V3EnvelopeUpdateNode(1, 1, 0); deviceManager.hapticDevice.HapE.V3EnvelopeUpdateNode(2, 1, onTime); deviceManager.hapticDevice.HapE.V3EnvelopeUpdateNode(3, 0, 0); deviceManager.hapticDevice.HapE.V3EnvelopeUpdateNode(4, 0, offTime); deviceManager.hapticDevice.HapE.V3EnvelopeUpdateNode(5, 1, 0); deviceManager.hapticDevice.HapE.V3EnvelopeUpdateNode(6, 1, onTime); deviceManager.hapticDevice.HapE.V3EnvelopeUpdateNode(7, 0, 0); deviceManager.hapticDevice.HapE.V3EnvelopeUpdateNode(8, 0, postDelay); PlayAtFixedLocation(); } public void PlayAtFixedLocation() { UpdateTrackerTransform(); deviceManager.hapticDevice.HapE.Start(); playing = true; } public void SetCircleOpenParams() { Debug.Log("Setting Circle Open Params"); deviceManager.ResetHapEState(); deviceManager.hapticDevice.HapE.V3SendCommand(HapE.V3Command.EnableAnimator, 1); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigA, 0.02f); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigB, 0.02f); deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.DrawFrequency, 50); float scaleValue = 0.9f; // Scale Down float[] T1a1 = new float[9]; T1a1[0] = 1 / scaleValue; T1a1[4] = 1 / scaleValue; T1a1[8] = 1f; deviceManager.hapticDevice.HapE.V3SendCommand(HapE.V3Command.T1A1SwitchCount, 20); deviceManager.hapticDevice.HapE.V3AnimatorSetTransform(HapE.V3AnimatorTransform.T1A1, T1a1); // Scale Up float[] T1a2 = new float[9]; T1a2[0] = scaleValue; T1a2[4] = scaleValue; T1a2[8] = 1f; deviceManager.hapticDevice.HapE.V3SendCommand(HapE.V3Command.T1A2SwitchCount, 20); deviceManager.hapticDevice.HapE.V3AnimatorSetTransform(HapE.V3AnimatorTransform.T1A2, T1a2); UpdateTrackerTransform(); deviceManager.hapticDevice.HapE.Start(); } // Test path tracing from x-y-t vectors public void SetDummyPath() { SetTracingPath(dummyPath); } /// <summary> /// Update Drawing Params /// </summary> /// <param name="value"></param> /// public void SetBigA(float value) { bigA = value; deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigA, bigA); } public void SetBigB(float value) { bigB = value; deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.BigB, bigB); } public void SetA(float value) { a = value; deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.a, a); } public void SetB(float value) { b = value; deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.b, b); } public void SetDrawFrequnecy(float value) { drawFrequency = value; deviceManager.hapticDevice.HapE.V3PrimitiveSetParameter(HapE.V3PrimitiveParameter.DrawFrequency, drawFrequency); } public void UpdateTrackerTransform() { trackerTransform[0] = 1f; trackerTransform[3] = x; trackerTransform[5] = 1f; trackerTransform[7] = y; trackerTransform[10] = 1f; trackerTransform[11] = z; trackerTransform[15] = 1f; deviceManager.hapticDevice.HapE.V3SetTracker(trackerTransform); } public void TogglePlayback() { UpdateTrackerTransform(); if (playing) { deviceManager.hapticDevice.HapE.Stop(); } else { deviceManager.hapticDevice.HapE.Start(); } playing = !playing; } // Update is called once per frame void Update() { if (Input.GetKeyUp("space")) { TogglePlayback(); } } } }
Aで横幅、Bで縦幅、DrawFrequencyで描画順(?)(線上で感覚を動かす感じ)を変えれました。
今後試したいこと
●モーションデザイナーで作ったモーションをエクスポートしてシーンにインポートしたい。
これはデモシーン(JSONPlayer)を使えば実証は簡単そう。
●つかんだら~などの条件を追加
LeapMotionDesigner
モーションを作ることができるソフト。
操作説明
全体の説明

エディタ/モーションの大きさなどの編集画面

●BigA 円の横幅
●BigB 円の縦幅
●a 円を構成するためのsin cosどちらかの値だと思います。
●b 同上
●k 円を構成するためのtanの値っぽいです。
(a==b && k==1だと完全な円が描けます。)
●d y軸上に円を回転させられます。
●maxT 円の大きさを変えられます。
エディタ/点ごとのスケールの編集

エディタ/点ごとの出力の強さの編集

エディタ/ライブラリの使用

点の追加 手のマークがあるエディタ上で追加したい位置をクリック。
点の削除 消したい点にカーソルを合わせ、右クリック。
点の移動 移動させたい点にカーソルを合わせ、四角く枠が表示された状態でドラッグ。
Author: 松崎 | Source:
松崎\LeapMotionHaptics e3b72ba9c2184bb8a29bfb90961baaab.md