Added touch animation.

Designed for 2d objects, such as a canvas placements, this permits the receipt of touch (mouse click) events to enable the simulation of avionics with a touchscreen.

The coordinates are passed in as arguments to the action; these can be accessed with Nasal via the cmdarg() method.

example:

    <animation>
         <type>touch</type>
         <visible>true</visible>
         <object-name>VSDImage</object-name>
         <action>
             <touch>0</touch>
             <repeatable>false</repeatable>
             <binding>
                 <command>nasal</command>
                 <script>print("touch input
(",cmdarg().getNode("x").getValue(),",",cmdarg().getNode("y").getValue())</script>
             </binding>
         </action>
     </animation>
This commit is contained in:
Richard Harrison
2017-10-29 01:04:36 +02:00
parent 880c063d04
commit 2a60e5e338
4 changed files with 172 additions and 1 deletions

View File

@@ -832,3 +832,157 @@ SGSliderAnimation::setupCallbacks(SGSceneUserData* ud, osg::Group*)
{
ud->setPickCallback(new KnobSliderPickCallback(getConfig(), getModelRoot()));
}
/*
* touch screen is a 2d surface that will pass parameters to the callbacks indicating the
* normalized coordinates of hover or touch. Touch is defined as a button click.
* For compatibility with touchscreen operations this class does not differentiate between
* which buttons are touched, simply because this isn't how touchscreens work.
* Some touchscreens (e.g. SAW) can have a Z-axis indicating the pressure. This is not
* simulated.
*/
/**
* Handle picking events on object with a canvas placed onto
*/
class TouchPickCallback : public SGPickCallback {
public:
TouchPickCallback(const SGPropertyNode* configNode,
SGPropertyNode* modelRoot) :
_repeatable(configNode->getBoolValue("repeatable", false)),
_repeatInterval(configNode->getDoubleValue("interval-sec", 0.1)),
SGPickCallback(PriorityPanel)
{
std::vector<SGPropertyNode_ptr> bindings;
bindings = configNode->getChildren("touch");
for (unsigned int i = 0; i < bindings.size(); ++i) {
_touches.insert(bindings[i]->getIntValue());
}
_bindingsTouched = readBindingList(configNode->getChildren("binding"), modelRoot);
readOptionalBindingList(configNode, modelRoot, "mod-up", _bindingsReleased);
if (configNode->hasChild("cursor")) {
_cursorName = configNode->getStringValue("cursor");
}
}
void addHoverBindings(const SGPropertyNode* hoverNode,
SGPropertyNode* modelRoot)
{
_hover = readBindingList(hoverNode->getChildren("binding"), modelRoot);
}
virtual bool buttonPressed(int touchIdx,
const osgGA::GUIEventAdapter& event,
const Info& info)
{
if (_touches.find(touchIdx) == _touches.end()) {
return false;
}
if (!anyBindingEnabled(_bindingsTouched)) {
return false;
}
SGPropertyNode_ptr params(new SGPropertyNode);
params->setDoubleValue("x", info.uv[0]);
params->setDoubleValue("y", info.uv[1]);
_repeatTime = -_repeatInterval; // anti-bobble: delay start of repeat
fireBindingList(_bindingsTouched, params.ptr());
return true;
}
virtual void buttonReleased(int keyModState,
const osgGA::GUIEventAdapter&,
const Info* info)
{
SG_UNUSED(keyModState);
SGPropertyNode_ptr params(new SGPropertyNode);
params->setDoubleValue("x", info->uv[0]);
params->setDoubleValue("y", info->uv[1]);
fireBindingList(_bindingsReleased, params.ptr());
}
virtual void update(double dt, int keyModState)
{
SG_UNUSED(keyModState);
if (!_repeatable)
return;
_repeatTime += dt;
while (_repeatInterval < _repeatTime) {
_repeatTime -= _repeatInterval;
fireBindingList(_bindingsTouched);
}
}
virtual bool hover(const osg::Vec2d& windowPos,
const Info& info)
{
if (!anyBindingEnabled(_hover)) {
return false;
}
SGPropertyNode_ptr params(new SGPropertyNode);
params->setDoubleValue("x", info.uv[0]);
params->setDoubleValue("y", info.uv[1]);
fireBindingList(_hover, params.ptr());
return true;
}
std::string getCursor() const
{
return _cursorName;
}
virtual bool needsUV() const { return true; }
private:
SGBindingList _bindingsTouched;
SGBindingList _bindingsReleased;
SGBindingList _hover;
std::set<int> _touches;
std::string _cursorName;
bool _repeatable;
double _repeatInterval;
double _repeatTime;
};
SGTouchAnimation::SGTouchAnimation(simgear::SGTransientModelData &modelData) :
SGPickAnimation(modelData)
{
}
osg::Group* SGTouchAnimation::createMainGroup(osg::Group* pr)
{
SGRotateTransform* transform = new SGRotateTransform();
pr->addChild(transform);
return transform;
}
void SGTouchAnimation::setupCallbacks(SGSceneUserData* ud, osg::Group*)
{
TouchPickCallback* touchCb = NULL;
// add actions that become macro and command invocations
std::vector<SGPropertyNode_ptr> actions;
actions = getConfig()->getChildren("action");
for (unsigned int i = 0; i < actions.size(); ++i) {
touchCb = new TouchPickCallback(actions[i], getModelRoot());
ud->addPickCallback(touchCb);
}
if (getConfig()->hasChild("hovered")) {
if (!touchCb) {
// make a trivial PickCallback to hang the hovered off of
SGPropertyNode_ptr dummyNode(new SGPropertyNode);
touchCb = new TouchPickCallback(dummyNode.ptr(), getModelRoot());
ud->addPickCallback(touchCb);
}
touchCb->addHoverBindings(getConfig()->getNode("hovered"), getModelRoot());
}
// ud->setPickCallback(new TouchPickCallback(getConfig(), getModelRoot()));
}

View File

@@ -114,5 +114,19 @@ private:
SGSharedPtr<SGExpressiond const> _animationValue;
};
class SGTouchAnimation : public SGPickAnimation
{
public:
SGTouchAnimation(simgear::SGTransientModelData &modelData);
protected:
virtual osg::Group* createMainGroup(osg::Group* pr);
virtual void setupCallbacks(SGSceneUserData* ud, osg::Group* parent);
private:
};
#endif // of SG_SCENE_PICK_ANIMATION_HXX

View File

@@ -226,7 +226,7 @@ namespace {
{
string typeString(aNode->getStringValue("type"));
// exclude these so we don't show yellow outlines in preview mode
return (typeString == "pick") || (typeString == "knob") || (typeString == "slider");
return (typeString == "pick") || (typeString == "knob") || (typeString == "slider") || (typeString == "touch");
}
};

View File

@@ -497,6 +497,9 @@ SGAnimation::animate(simgear::SGTransientModelData &modelData)
} else if (type == "slider") {
SGSliderAnimation anim(modelData);
anim.apply(modelData.getNode());
} else if (type == "touch") {
SGTouchAnimation anim(modelData);
anim.apply(modelData.getNode());
} else if (type == "range") {
SGRangeAnimation anim(modelData);
anim.apply(modelData);