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:
@@ -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()));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user