Re: [w3c/gamepad] Added a supplemental "Extensions" spec (#32)

I have given this a bit of thought and iterated on some WebIDL for an interface that makes the easy "pulse" effects simple while enabling high frequency buffered haptic effects.

Here is a rough draft of proposed WebIDL:

```
// Each value is normalized to a range between 0.0 and 1.0
// The actuator type determines the force applied for a given
// "value" in GamepadHapticActuatorBuffer or GamepadHapticActuator
enum GamepadHapticActuatorType {
  // Vibration is a rumbling effect often implemented as an offset weight
  // driven on a rotational axis.
  // The "value" of a vibration force determines the frequency of the rumble
  // effect and is normalized between 0.0 and 1.0
  "vibration",
  
  // Linear actuators shift a weight to an absolute position, changing the
  // center of gravity of the controller and resulting in an opposing force
  // that can be felt by the holder.
  // The "value" of a linear force is the absolute position of the actuator
  // relative to its center position and is normalized between -1.0 and 1.0
  "linear",
  
  // XXX - Angular force (steering wheels), springs, dampeners, and other
  //       actuators should be defined as separate GamepadHapticActuatorType's
  //       here instead of being mapped to "vibration" or "linear".
};

// Each GamepadHapticActuator corresponds to a motor or other actuator that can
// apply a force for the purposes of haptic feedback.  In some cases, the
// physical arrangement of devices may be represented as a simpler model to this
// api.  For example, a device with multiple degrees of freedom may process
// the actuator values passed into the api with inverse kinematics to determine
// the position required for the physical actuators.
dictionary GamepadHapticActuator {
  // type determines the range and effect of the "value"
  // passed to pulse() and within the GamepadHapticActuatorBuffer.
  readonly attribute GamepadHapticActuatorType type;
  
  // Pulse applies a value to the actuator for duration milliseconds.
  // If any GamepadHapticEffect is playing, the value passed to pulse()
  // is applied additively, clamped to limits defined by the actuator type.
  // The returned promise will resolve true once the pulse has completed.
  Promise<void> pulse(float value, float duration);
};

// GamepadHapticBuffer represents a timeline of values to drive a
// GamepadActuator.
dictionary GamepadHapticBuffer {
  // actuator identifies the actuator that will be driven by the
  // effect.  If multiple GamepadHapticBuffer are driving the same
  // actuator, the values are combined additively and clamped within the
  // range defined by the actuator type.
  GamepadHapticActuator actuator;
  
  // It is recommended to normalize these values, as they can
  // be attenuated by setting GamepadHapticEffect.gain
  sequence<float> values = [ ];
  
  // Durations are expressed in milliseconds
  sequence<float> durations = [ ];
  
  // The number of iterations to repeat the set of values.
  // If iterations is 0, then repeat infinitely.
  attribute unsigned long iterations;
};

// GamepadHapticEffect describes any haptic effect that is more complex than
// a simple pulse.
interface GamepadHapticEffect {
  // buffers defines which actuators are being driven and with which pattern of
  // values.
  // A GamepadHapticEffect can affect a single or multiple Gamepads.
  // GamepadHapticEffect is associated to a Gamepad through
  // GamepadHapticBuffer.actuator
  // If multiple effects or pulses are playing simultaneously, the values of each
  // actuator are combined additively.
  attribute bool sequence<GamepadHapticBuffer> buffers;
  
  // When GamepadHapticEffect is playing, IsPlaying is true.
  // The GamepadHapticEffect does not begin playing until play() is called.
  readonly attribute bool IsPlaying;
  
  // play() starts playing the GamepadHapticEffect.
  // The returned promise will resolve true once the effect is completed or
  // stop() is called.
  // If any of the buffers have iterations set to 0 (infinite), the promise
  // will not resolve true until stop() is called.
  // Changes to the buffers are not reflected in a playing haptic effect and
  // will only be applied once the effect is played again.
  // If the haptic effect is already playing, play() will not interrupt
  // the haptic effect and will resolve fail the returned promise.
  // If you wish to play multiple of the same effect additively, multiple
  // GamepadHapticEffect's should be created, but can share GamepadHapticBuffer's.
  [Throws]
  Promise<void> play();
  
  // stop() ends playback of the effect and returns the values of the actuators
  // to the neutral value as defined by the actuator type.
  // It is not necessary to call stop() if all of the buffers have non-zero
  // iterations.
  void stop();
  
  // XXX Do we wish to have a pause() function?

  // gain is a 0.0 - 1.0 attenuation of HapticBuffer values.
  // Unlike changes to buffers, changes to gain are effective immediately on
  // a playing haptic effect.
  // Simple dynamic collision effects can be modelled with a set of short,
  // infinitely repeating HapticBuffer's and an ADSR envelope applied with the
  // gain attribute.
  attribute float gain; 
};

partial interface Gamepad {
  // hapticActuators enumerates haptic feedback actuators such as rumble motors.
  readonly attribute GamepadHapticActuator[] hapticActuators;
  
  // playingHapticEffects includes any GamepadHapticEffect that are playing
  // and driving any actuator in this Gamepad.
  readonly attribute GamepadHapticEffect[] playingHapticEffects;
};
```

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/w3c/gamepad/pull/32#issuecomment-247204121

Received on Thursday, 15 September 2016 01:07:34 UTC