【OSG案例详细分析与讲解】之五:【3D交互式动画】

发布时间:2024年01月12日

文章目录

一、【3D交互式动画】前言

二、【3D交互式动画】实现效果

三、【3D交互式动画】创建动画几何体

1、实现目的

2、创建动画路径步骤

3、核心代码

4、知识要点

5、osgAnimation::Motion详讲

四、【3D交互式动画】定义动画更新

1.实现目的

2.创建动画更新步骤

3、核心代码

4、知识要点

五、【3D交互式动画】具有鼠标事件的颜色标签

1、实现目的

2、实现步骤

3、核心代码

4、知识要点

5、osgWidget::Label详讲

六、【3D交互式动画】具有菜单功能的颜色标签

1、实现目的

2、实现步骤

3、核心代码

4、知识要点

七、【3D交互式动画】程序

1、程序代码

2、qt pro文件

八、【3D交互式动画】总结


一、【3D交互式动画】前言

? ? ? ?OpenSceneGraph(OSG)是一个强大的3D图形引擎,用于创建高质量、交互式的3D图形应用程序。其中一个常见的应用场景是创建动画效果。本节文章将介绍如何使用OSG和osgWidget库创建一个交互式的3D动画程序,包括创建动画几何体、定义动画更新、具有鼠标事件的颜色标签、具有菜单功能的颜色标签等内容。


二、【3D交互式动画】实现效果


三、【3D交互式动画】创建动画几何体

1、实现目的

? ? ? ?根据给定的动画对象(motion)创建一个几何体,用于在场景中显示动画效果。创建的几何体可以反映出动画的数值变化,从而让用户可以直观地看到动画效果。

2、创建动画路径步骤

  • 首先创建一个空的几何体对象geom,并初始化顶点数组v和颜色数组cols。
  • 然后使用循环遍历动画时间范围内的每一个时间点,计算出对应时间点上动画的数值,并将时间和数值转换成对应的几何体顶点坐标。
  • 将计算得到的顶点坐标添加到顶点数组v中,并设置一个统一的颜色值添加到颜色数组cols中。
  • 最后根据顶点数组和颜色数组创建几何体geom,并设置其绘制方式为线条段(LINE_STRIP),然后返回该几何体。

3、核心代码

// 创建动画几何体
osg::ref_ptr<osg::Geometry> createEaseMotionGeometry(osgAnimation::Motion* motion, float startTime, float endTime, float step)
{
    // 创建一个空的几何体对象
    osg::ref_ptr<osg::Geometry> geom = new osg::Geometry();

    // 初始化顶点数组和颜色数组
    osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array();
    osg::ref_ptr<osg::Vec4Array> cols = new osg::Vec4Array();
    cols->push_back(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));

    // 循环遍历动画时间范围内的每一个时间点
    for (float t = startTime; t <= endTime; t += step)
    {
        // 计算出对应时间点上动画的数值
        float value = motion->getValueAt(t);

        // 将时间和数值转换成对应的几何体顶点坐标
        osg::Vec3 position(t, value, 0.0f);

        // 将计算得到的顶点坐标添加到顶点数组中
        v->push_back(position);
    }

    // 根据顶点数组和颜色数组创建几何体对象
    geom->setVertexArray(v);
    geom->setColorArray(cols);
    geom->setColorBinding(osg::Geometry::BIND_OVERALL);
    geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, v->size()));

    // 返回创建的几何体对象
    return geom;
}

4、知识要点

(1)OpenSceneGraph中的几何体表示:

  • osg::Geometry类:OpenSceneGraph中用于表示几何体的类,可以通过设置顶点数组、颜色数组等属性来定义几何体的形状和外观。
  • osg::Vec3Array类:用于存储三维向量的数组,这里用来存储顶点坐标。
  • osg::Vec4Array类:用于存储四维向量的数组,这里用来存储颜色信息。
  • osg::PrimitiveSet类:用于定义几何体的绘制方式,如点、线、面等。

(2)动画对象osgAnimation::Motion:

  • osgAnimation::Motion是一个表示动画的基类,其派生类可以根据时间获取动画的数值。
  • motion->getValueAt(i)方法用于获取动画在时间点i的数值,用于计算几何体的顶点坐标。

(3)几何体的创建和设置:

  • 创建空的几何体对象:使用osg::Geometry类的构造函数创建一个空的几何体对象geom。
  • 初始化顶点数组和颜色数组:使用osg::Vec3Array和osg::Vec4Array分别创建顶点数组v和颜色数组cols,并进行初始化。
  • 添加顶点坐标和颜色:通过循环遍历动画时间范围内的每一个时间点,计算顶点坐标并添加到顶点数组v中,将统一颜色值添加到颜色数组cols中。
  • 设置几何体属性:通过geom对象的方法设置顶点数组、颜色数组和绘制方式等属性,以定义几何体的形状和外观。

5、osgAnimation::Motion详讲

? ? ? ?osgAnimation::Motion是OpenSceneGraph中用于表示动画的基类。它提供了获取动画在指定时间点上的数值的方法,以及一些用于设置和管理动画的函数。

osgAnimation::Motion类的主要成员函数和功能如下:

  1. float getStartTime() const:获取动画的起始时间。
  2. float getEndTime() const:获取动画的结束时间。
  3. float getDuration() const:获取动画的持续时间,即结束时间减去起始时间。
  4. virtual osgAnimation::Motion* clone() const:克隆当前动画对象,返回一个新的动画对象。
  5. virtual float getValueAt(float time) const:根据给定的时间点,获取动画在该时间点上的数值。
  • 这是一个纯虚函数,需要在派生类中实现具体的逻辑。
  • 派生类可以根据不同的插值算法和数据结构来计算动画的数值。

? ? ? osgAnimation::Motion的派生类可以根据不同的需求实现不同的动画效果,例如线性插值、贝塞尔曲线插值等。以下是osgAnimation::Motion的一些常用派生类:

  • osgAnimation::FloatLinearChannel:通过线性插值计算浮点数值的动画。
  • osgAnimation::Vec3LinearChannel:通过线性插值计算三维向量的动画。
  • osgAnimation::QuatSlerpChannel:通过球面线性插值计算四元数的动画。
  • osgAnimation::Vec3CubicBezierChannel:通过三次贝塞尔曲线插值计算三维向量的动画。

? ? ? 派生类需要实现getValueAt函数来计算动画在指定时间点上的数值。插值算法的选择和数值计算的具体逻辑将根据派生类的不同而有所区别。

? ? ? 使用osgAnimation::Motion可以将动画效果应用于场景中的节点或者其他可动画的属性上,从而实现动态的效果,比如模型的运动、变形等。

? ? ? 总结来说,osgAnimation::Motion是OpenSceneGraph中用于表示动画的基类,通过派生类实现具体的插值算法和数值计算逻辑,可以实现各种不同类型的动画效果,并将其应用于场景中的节点或属性上。


四、【3D交互式动画】定义动画更新

1.实现目的

? ? ? ?定义了一个名为EaseMotionSampler的类,继承自osg::NodeCallback类,用于更新动画。通过重载()运算符作为回调函数,并在每次场景遍历时调用该回调函数,来更新指定节点上的动画效果。

2.创建动画更新步骤

  • 在类中定义了一些成员变量,包括_previous(上一帧的时间)、_pos(位置向量)和_motion(动画对象)。
  • 重载()运算符,作为回调函数,在每次场景遍历时被调用。
  • 获取节点的类型,如果节点不是osg::MatrixTransform类型,则直接返回。
  • 获取当前的仿真时间t,并进行一些初始化操作。
  • 调用_motion->update(t - _previous)来更新动画的状态。
  • 将_previous更新为当前时间t,用于下一帧计算时使用。
  • 根据_motion->getValue()获取动画的数值,并将_pos乘以该数值,得到最终的位移向量,并将其应用于节点的矩阵变换上。

3、核心代码

// 定义动画更新
class EaseMotionSampler: public osg::NodeCallback
{
public:
    float     _previous; // 记录上一帧的时间
    osg::Vec3 _pos;      // 位置向量

    osg::ref_ptr<osgAnimation::Motion> _motion; // 动画对象

    EaseMotionSampler(const osg::Vec3& pos):
        _previous (0.0f),
        _pos      (pos) {
    }

    void operator()(osg::Node* node, osg::NodeVisitor* nv) {
        if(!_motion.valid()) return; // 如果动画对象为空,则直接返回

        osg::MatrixTransform* mt = dynamic_cast<osg::MatrixTransform*>(node); // 获取节点类型为osg::MatrixTransform

        if(!mt) return; // 如果节点不是osg::MatrixTransform类型,则直接返回

        double t = nv->getFrameStamp()->getSimulationTime(); // 获取当前的仿真时间

        // 进行一些初始化操作
        if(_previous == 0.0f) _previous = t;

        _motion->update(t - _previous); // 更新动画状态

        _previous = t; // 更新上一帧时间

        mt->setMatrix(osg::Matrix::translate(_pos * _motion->getValue())); // 将动画数值和位置向量相乘并应用于节点的矩阵变换上
    }

    // 设置动画对象
    template<typename T>
    void setMotion() {
        _motion = new T(M_START, M_DURATION, M_CHANGE, osgAnimation::Motion::LOOP);

        EASE_MOTION_GEODE->removeDrawables(0, EASE_MOTION_GEODE->getNumDrawables());
        EASE_MOTION_GEODE->addDrawable(createEaseMotionGeometry(_motion.get())); // 创建动画几何体并添加到场景图中
    }
};

4、知识要点

  • osg::NodeCallback:这是OpenSceneGraph中的一个基类,用于在场景遍历时为节点提供回调函数的接口。通过继承该类,并重载()运算符,可以自定义节点的更新行为。
  • osg::MatrixTransform:这是OpenSceneGraph中的一个节点类型,表示矩阵变换。该类可以用于将矩阵变换应用于其子节点,实现节点的平移、旋转和缩放等操作。
  • osgAnimation::Motion:这是OpenSceneGraph中用于表示动画的基类,派生类可以根据不同的需求实现不同的动画效果。在这段代码中,_motion变量被用于存储具体的动画对象,并在回调函数中更新动画的状态。
  • osg::NodeVisitor:这是OpenSceneGraph中的一个访问者类,用于遍历场景图中的节点。在这段代码中,nv参数是osg::NodeVisitor的一个实例,通过调用getFrameStamp()->getSimulationTime()获取当前的仿真时间。

五、【3D交互式动画】具有鼠标事件的颜色标签

1、实现目的

? ? ? ?实现了一个名为ColorLabel的类,继承自osgWidget::Label类,并具有鼠标事件。ColorLabel类的目的是创建一个带有特定颜色和鼠标交互效果的标签。

2、实现步骤

  1. 定义ColorLabel类,继承自osgWidget::Label类。
  2. 在构造函数中,传入一个label参数,并调用基类的构造函数初始化Label。
  3. 设置标签的字体、字体大小、字体颜色等属性。
  4. 设置标签的背景颜色、内边距和填充。
  5. 添加标签的尺寸大小。
  6. 设置标签的文本内容。
  7. 设置标签响应的鼠标事件类型为鼠标按下和鼠标移动。
  8. 实现鼠标按下事件的回调函数mousePush。在该函数中,首先通过动态类型转换获取父节点,然后隐藏该节点。接着根据标签的名称设置不同的运动样式,每种样式都使用了osgAnimation命名空间下的不同的Motion类。
  9. 实现鼠标进入事件的回调函数mouseEnter。在该函数中,将标签的颜色设置为指定的鼠标进入颜色。
  10. 实现鼠标离开事件的回调函数mouseLeave。在该函数中,将标签的颜色恢复为默认的背景颜色。

3、核心代码

// 具有菜单功能的颜色标签
struct ColorLabel: public osgWidget::Label {
    ColorLabel(const char* label):
        osgWidget::Label(label, "") {
        // 设置字体文件路径
        setFont("fonts/VeraMono.ttf");
        // 设置字体大小
        setFontSize(14);
        // 设置字体颜色
        setFontColor(1.0f, 1.0f, 1.0f, 1.0f);

        // 设置标签的背景颜色
        setColor(0.3f, 0.3f, 0.3f, 1.0f);
        // 设置内边距
        setPadding(2.0f);
        // 设置可以填充背景
        setCanFill(true);

        // 添加标签的尺寸大小
        addSize(150.0f, 25.0f);

        // 设置标签的文本内容
        setLabel(label);
        // 设置标签响应的鼠标事件类型为鼠标按下和鼠标移动
        setEventMask(osgWidget::EVENT_MOUSE_PUSH | osgWidget::EVENT_MASK_MOUSE_MOVE);
    }

    // 鼠标按下事件的回调函数
    bool mousePush(double, double, const osgWidget::WindowManager*) {
        // 获取父节点
        osgWidget::Table* p = dynamic_cast<osgWidget::Table*>(_parent);

        if(!p) return false;

        // 隐藏父节点
        p->hide();

        // 根据标签的名称设置不同的运动样式
        const std::string& name = getName();
        if(!name.compare("OutQuadMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::OutQuadMotion>();

        else if(!name.compare("InQuadMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::InQuadMotion>();

        else if(!name.compare("InOutQuadMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::InOutQuadMotion>();

        // 省略其他运动样式的判断...

        else EASE_MOTION_SAMPLER->setMotion<osgAnimation::LinearMotion>();

        return true;
    }

    // 鼠标进入事件的回调函数
    bool mouseEnter(double, double, const osgWidget::WindowManager*) {
        // 将标签的背景颜色设置为鼠标进入颜色
        setColor(0.9f, 0.6f, 0.1f, 1.0f);

        return true;
    }

    // 鼠标离开事件的回调函数
    bool mouseLeave(double, double, const osgWidget::WindowManager*) {
        // 将标签的背景颜色恢复为默认值
        setColor(0.3f, 0.3f, 0.3f, 1.0f);

        return true;
    }
};

4、知识要点

  1. 继承:ColorLabel类继承自osgWidget::Label类,通过继承可以获得父类的属性和方法,减少重复代码。

  2. 设置字体、颜色和大小:通过setFont()、setFontSize()和setFontColor()等方法,可以设置标签的字体、字体大小和字体颜色。

  3. 鼠标事件处理:通过设置setEventMask()方法,可以让标签响应鼠标事件,例如鼠标按下和鼠标移动事件。在ColorLabel类中,实现了鼠标按下、鼠标进入和鼠标离开事件的回调函数。

  4. 动态类型转换:在鼠标按下事件的回调函数中,使用了dynamic_cast操作符,将父节点强制转换为osgWidget::Table类型,以便进行隐藏操作。

  5. 设置运动样式:在鼠标按下事件的回调函数中,根据标签的名称设置不同的运动样式。这里使用了osgAnimation命名空间下的不同的Motion类。

5、osgWidget::Label详讲

? ? ?osgWidget::Label是OpenSceneGraph中的一个类,用于在场景中显示文本标签。它可以显示静态文本内容,并提供了一些方法来设置标签的字体、颜色、大小、边框等属性。

主要特点和功能如下:

  1. 显示文本内容:osgWidget::Label可以显示文本内容。通过构造函数或setLabel()方法,可以设置标签的文本内容。

  2. 设置字体和字体大小:通过setFont()方法,可以设置标签的字体,可以是系统字体或自定义字体文件路径。通过setFontSize()方法,可以设置标签的字体大小。

  3. 设置字体颜色和背景颜色:通过setFontColor()方法,可以设置字体的颜色。通过setColor()方法,可以设置标签的背景颜色。

  4. 设置边框和内边距:通过setBorder()方法,可以设置标签的边框属性,包括边框的宽度和颜色。通过setPadding()方法,可以设置标签的内边距。

  5. 设置尺寸和位置:通过addSize()方法,可以添加标签的尺寸大小。通过setPosition()方法,可以设置标签的位置。

  6. 响应鼠标事件:通过设置setEventMask()方法,可以让标签响应鼠标事件,例如鼠标按下、鼠标移动等。可以根据需要,重写相应的鼠标事件回调函数来处理事件。

  7. 提供其他功能:osgWidget::Label还提供了一些其他的功能,如设置文本对齐方式、设置是否可以填充背景等。

? ? ? ?osgWidget::Label类是OpenSceneGraph中用于显示文本标签的重要类之一,可以方便地在场景中添加和控制文本内容的显示。它在各种图形应用中都有广泛的应用,如游戏界面、用户界面、数据可视化等。


六、【3D交互式动画】具有菜单功能的颜色标签

1、实现目的

? ? ? ?实现了一个具有菜单功能的颜色标签 ColorLabelMenu 类。通过单击标签,可以显示或隐藏一个包含多个标签的表格,实现了菜单的功能。

2、实现步骤

  1. 继承自颜色标签 ColorLabel 类,并添加一个名为 _window 的 osgWidget::Table 对象。
  2. 在构造函数中,初始化 _window 表格,并添加多个颜色标签到表格中。
  3. 实现 managed() 方法和 positioned() 方法,用于设置窗口的位置和显示/隐藏状态。
  4. 实现 mousePush() 方法,监听鼠标点击事件,并在点击时切换窗口的显示状态。

3、核心代码

// 具有菜单功能的颜色标签
class ColorLabelMenu: public ColorLabel { // 继承自颜色标签 ColorLabel 类
    osg::ref_ptr<osgWidget::Table> _window; // 定义一个 osgWidget::Table 指针对象 _window

public:
    ColorLabelMenu(const char* label): // 构造函数,接收标签字符串参数
        ColorLabel(label) { // 调用父类构造函数,初始化标签内容
        _window = new osgWidget::Table(std::string("Menu_") + label, 6, 5); // 初始化表格 _window,包含多个颜色标签

        // 将多个颜色标签添加到表格中(下面是伪代码)
        _window->addWidget(new ColorLabel("OutQuadMotion"), 0, 0);
        _window->addWidget(new ColorLabel("InQuadMotion"), 1, 0);
        ...
        _window->resize(); // 调整表格大小
    }

    void managed(osgWidget::WindowManager* wm) { // 窗口管理器添加控件时调用
        osgWidget::Label::managed(wm); // 调用父类 managed() 方法

        wm->addChild(_window.get()); // 添加表格到窗口管理器中
        _window->hide(); // 隐藏表格
    }

    void positioned() { // 控件已经定位后调用
        osgWidget::Label::positioned(); // 调用父类 positioned() 方法

        _window->setOrigin(_parent->getX(), _parent->getY() +  _parent->getHeight()); // 设置表格的位置在标签下方
    }

    bool mousePush(double, double, const osgWidget::WindowManager*) { // 鼠标点击事件处理方法
        if(!_window->isVisible()) _window->show(); // 如果表格不可见,显示表格

        else _window->hide(); // 否则隐藏表格

        return true; // 返回 true 表示事件已被处理
    }
};

4、知识要点

  1. osgWidget:osgWidget 是 OpenSceneGraph 中用于创建用户界面的插件,提供了多种 UI 控件,如按钮、标签、文本框等。osgWidget 使用 OpenSceneGraph 的场景图模型来实现用户界面,可以将 UI 控件嵌入到场景图中,与其他场景元素一起渲染。osgWidget 还提供了鼠标事件处理等功能,可以用于实现交互式应用程序。

  2. 鼠标事件处理:osgWidget 中提供了多种鼠标事件处理方法,如 mousePush()、mouseMove() 等。当用户在 UI 控件上执行鼠标操作时,会触发相应的鼠标事件。开发者可以重写这些方法,自定义事件处理逻辑,实现交互式应用程序。常见的鼠标事件包括按下、弹起、移动等。


七、【3D交互式动画】程序

结合上述代码,添加程序入口,编制qt pro文件,可实现对应的效果。

1、程序代码

#include <osg/Geode>
#include <osg/MatrixTransform>
#include <osg/ShapeDrawable>
#include <osgGA/TrackballManipulator>
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include <osgAnimation/EaseMotion>
#include <osgWidget/WindowManager>
#include <osgWidget/Box>
#include <osgWidget/Table>
#include <osgWidget/Label>

class EaseMotionSampler;

const unsigned int WINDOW_WIDTH  = 800;
const unsigned int WINDOW_HEIGHT = 600;
const unsigned int MASK_2D       = 0xF0000000;
const unsigned int MASK_3D       = 0x0F000000;
const float        M_START       = 0.0f;
const float        M_DURATION    = 2.0f;
const float        M_CHANGE      = 1.0f;

EaseMotionSampler* EASE_MOTION_SAMPLER = 0;
osg::Geode*        EASE_MOTION_GEODE   = 0;

// 创建动画几何体
osg::Geometry* createEaseMotionGeometry(osgAnimation::Motion* motion)
 {
    // 创建一个空的几何体对象
    osg::Geometry*  geom = new osg::Geometry();

    // 初始化顶点数组和颜色数组
    osg::Vec4Array* cols = new osg::Vec4Array();
    osg::Vec3Array* v    = new osg::Vec3Array();

    // 循环遍历动画时间范围内的每一个时间点
    for(float i = 0.0f; i < M_DURATION; i += M_DURATION / 256.0f)
        v->push_back( osg::Vec3(i * 30.0f, motion->getValueAt(i) * 30.0f, 0.0f) );

    cols->push_back(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));

    geom->setUseDisplayList(false);
    // 根据顶点数组和颜色数组创建几何体对象
    geom->setVertexArray(v);
    geom->setColorArray(cols, osg::Array::BIND_OVERALL);
    geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, v->size()));
    // 返回创建的几何体对象
    return geom;
}

// 定义动画更新
class EaseMotionSampler: public osg::NodeCallback
{
public:
    float     _previous;    // 记录上一帧的时间
    osg::Vec3 _pos;         // 位置向量

    osg::ref_ptr<osgAnimation::Motion> _motion; // 动画对象

    EaseMotionSampler(const osg::Vec3& pos):
        _previous (0.0f),
        _pos      (pos) {
    }

    void operator()(osg::Node* node, osg::NodeVisitor* nv) {
        if(!_motion.valid()) return;    // 如果动画对象为空,则直接返回

        // 获取节点类型为osg::MatrixTransform
        osg::MatrixTransform* mt = dynamic_cast<osg::MatrixTransform*>(node);

        // 如果节点不是osg::MatrixTransform类型,则直接返回
        if(!mt) return;

        // 获取当前的仿真时间
        double t = nv->getFrameStamp()->getSimulationTime();

        // 进行一些初始化操作
        if(_previous == 0.0f) _previous = t;
        // 更新动画状态
        _motion->update(t - _previous);
        // 更新上一帧时间
        _previous = t;
        // 将动画数值和位置向量相乘并应用于节点的矩阵变换上
        mt->setMatrix(osg::Matrix::translate(_pos * _motion->getValue()));
    }

    // 设置动画对象
    template<typename T>
    void setMotion() {
        _motion = new T(M_START, M_DURATION, M_CHANGE, osgAnimation::Motion::LOOP);

        EASE_MOTION_GEODE->removeDrawables(0, EASE_MOTION_GEODE->getNumDrawables());
        // 创建动画几何体并添加到场景图中
        EASE_MOTION_GEODE->addDrawable(createEaseMotionGeometry(_motion.get()));
    }
};

// 具有菜单功能的颜色标签
struct ColorLabel: public osgWidget::Label {
    ColorLabel(const char* label):
        osgWidget::Label(label, "") {
        // 设置字体文件路径
        setFont("fonts/VeraMono.ttf");
        // 设置字体大小
        setFontSize(14);
        // 设置字体颜色
        setFontColor(1.0f, 1.0f, 1.0f, 1.0f);

        // 设置标签的背景颜色
        setColor(0.3f, 0.3f, 0.3f, 1.0f);
        // 设置内边距
        setPadding(2.0f);
        // 设置可以填充背景
        setCanFill(true);

        // 添加标签的尺寸大小
        addSize(150.0f, 25.0f);

        // 设置标签的文本内容
        setLabel(label);
        // 设置标签响应的鼠标事件类型为鼠标按下和鼠标移动
        setEventMask(osgWidget::EVENT_MOUSE_PUSH | osgWidget::EVENT_MASK_MOUSE_MOVE);
    }

    // 鼠标按下事件的回调函数
    bool mousePush(double, double, const osgWidget::WindowManager*) {
        // 获取父节点
        osgWidget::Table* p = dynamic_cast<osgWidget::Table*>(_parent);

        if(!p) return false;

        // 隐藏父节点
        p->hide();

        // 根据标签的名称设置不同的运动样式
        const std::string& name = getName();

        if(!name.compare("OutQuadMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::OutQuadMotion>()
                ;

        else if(!name.compare("InQuadMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::InQuadMotion>()
                ;

        else if(!name.compare("InOutQuadMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::InOutQuadMotion>()
                ;

        else if(!name.compare("OutCubicMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::OutCubicMotion>()
                ;

        else if(!name.compare("InCubicMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::InCubicMotion>()
                ;

        else if(!name.compare("InOutCubicMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::InOutCubicMotion>()
                ;

        else if(!name.compare("OutQuartMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::OutQuartMotion>()
                ;

        else if(!name.compare("InQuartMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::InQuartMotion>()
                ;

        else if(!name.compare("InOutQuartMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::InOutQuartMotion>()
                ;

        else if(!name.compare("OutBounceMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::OutBounceMotion>()
                ;

        else if(!name.compare("InBounceMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::InBounceMotion>()
                ;

        else if(!name.compare("InOutBounceMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::InOutBounceMotion>()
                ;

        else if(!name.compare("OutElasticMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::OutElasticMotion>()
                ;

        else if(!name.compare("InElasticMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::InElasticMotion>()
                ;

        else if(!name.compare("InOutElasticMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::InOutElasticMotion>()
                ;

        else if(!name.compare("OutSineMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::OutSineMotion>()
                ;

        else if(!name.compare("InSineMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::InSineMotion>()
                ;

        else if(!name.compare("InOutSineMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::InOutSineMotion>()
                ;

        else if(!name.compare("OutBackMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::OutBackMotion>()
                ;

        else if(!name.compare("InBackMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::InBackMotion>()
                ;

        else if(!name.compare("InOutBackMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::InOutBackMotion>()
                ;

        else if(!name.compare("OutCircMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::OutCircMotion>()
                ;

        else if(!name.compare("InCircMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::InCircMotion>()
                ;

        else if(!name.compare("InOutCircMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::InOutCircMotion>()
                ;

        else if(!name.compare("OutExpoMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::OutExpoMotion>()
                ;

        else if(!name.compare("InExpoMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::InExpoMotion>()
                ;

        else if(!name.compare("InOutExpoMotion"))
            EASE_MOTION_SAMPLER->setMotion<osgAnimation::InOutExpoMotion>()
                ;

        else EASE_MOTION_SAMPLER->setMotion<osgAnimation::LinearMotion>();

        return true;
    }

    // 鼠标进入事件的回调函数
    bool mouseEnter(double, double, const osgWidget::WindowManager*) {
        // 将标签的背景颜色设置为鼠标进入颜色
        setColor(0.9f, 0.6f, 0.1f, 1.0f);

        return true;
    }

    // 鼠标离开事件的回调函数
    bool mouseLeave(double, double, const osgWidget::WindowManager*) {
        // 将标签的背景颜色恢复为默认值
        setColor(0.3f, 0.3f, 0.3f, 1.0f);

        return true;
    }
};

// 具有菜单功能的颜色标签
class ColorLabelMenu: public ColorLabel {
    // 定义一个 osgWidget::Table 指针对象 _window
    osg::ref_ptr<osgWidget::Table> _window;

public:
    ColorLabelMenu(const char* label):
        ColorLabel(label) {
        _window = new osgWidget::Table(std::string("Menu_") + label, 6, 5);

        // 将多个颜色标签添加到表格中
        _window->addWidget(new ColorLabel("OutQuadMotion"), 0, 0);
        _window->addWidget(new ColorLabel("InQuadMotion"), 1, 0);
        _window->addWidget(new ColorLabel("InOutQuadMotion"), 2, 0);
        _window->addWidget(new ColorLabel("OutCubicMotion"), 3, 0);
        _window->addWidget(new ColorLabel("InCubicMotion"), 4, 0);
        _window->addWidget(new ColorLabel("InOutCubicMotion"), 5, 0);

        _window->addWidget(new ColorLabel("OutQuartMotion"), 0, 1);
        _window->addWidget(new ColorLabel("InQuartMotion"), 1, 1);
        _window->addWidget(new ColorLabel("InOutQuartMotion"), 2, 1);
        _window->addWidget(new ColorLabel("OutBounceMotion"), 3, 1);
        _window->addWidget(new ColorLabel("InBounceMotion"), 4, 1);
        _window->addWidget(new ColorLabel("InOutBounceMotion"), 5, 1);

        _window->addWidget(new ColorLabel("OutElasticMotion"), 0, 2);
        _window->addWidget(new ColorLabel("InElasticMotion"), 1, 2);
        _window->addWidget(new ColorLabel("InOutElasticMotion"), 2, 2);
        _window->addWidget(new ColorLabel("OutSineMotion"), 3, 2);
        _window->addWidget(new ColorLabel("InSineMotion"), 4, 2);
        _window->addWidget(new ColorLabel("InOutSineMotion"), 5, 2);

        _window->addWidget(new ColorLabel("OutBackMotion"), 0, 3);
        _window->addWidget(new ColorLabel("InBackMotion"), 1, 3);
        _window->addWidget(new ColorLabel("InOutBackMotion"), 2, 3);
        _window->addWidget(new ColorLabel("OutCircMotion"), 3, 3);
        _window->addWidget(new ColorLabel("InCircMotion"), 4, 3);
        _window->addWidget(new ColorLabel("InOutCircMotion"), 5, 3);

        _window->addWidget(new ColorLabel("OutExpoMotion"), 0, 4);
        _window->addWidget(new ColorLabel("InExpoMotion"), 1, 4);
        _window->addWidget(new ColorLabel("InOutExpoMotion"), 2, 4);
        _window->addWidget(new ColorLabel("Linear"), 3, 4);

        // 调整表格大小
        _window->resize();
    }

    // 窗口管理器添加控件时调用
    void managed(osgWidget::WindowManager* wm) {
        // 调用父类 managed() 方法
        osgWidget::Label::managed(wm);
        // 添加表格到窗口管理器中
        wm->addChild(_window.get());

        _window->hide();
    }

    // 窗口管理器添加控件时调用
    void positioned() {
        // 调用父类 positioned() 方法
        osgWidget::Label::positioned();
        // 设置表格的位置在标签下方
        _window->setOrigin(_parent->getX(), _parent->getY() +  _parent->getHeight());
    }

    // 鼠标点击事件处理方法
    bool mousePush(double, double, const osgWidget::WindowManager*) {
        // 如果表格不可见,显示表格
        if(!_window->isVisible()) _window->show();
        // 否则隐藏表格
        else _window->hide();

        return true;
    }
};

int main(int, char**)
{
    osgViewer::Viewer viewer;

    // 创建osgWidget::WindowManager对象,用于管理窗口
    osgWidget::WindowManager* wm = new osgWidget::WindowManager(
        &viewer,
        WINDOW_WIDTH,
        WINDOW_HEIGHT,
        MASK_2D
        );

    // 创建菜单窗口
    osgWidget::Window* menu = new osgWidget::Box("menu", osgWidget::Box::HORIZONTAL);

    // 向菜单窗口添加一个颜色标签的菜单项
    menu->addWidget(new ColorLabelMenu("Choose EaseMotion"));
    // 设置菜单窗口的背景颜色为白色
    menu->getBackground()->setColor(1.0f, 1.0f, 1.0f, 1.0f);
    // 设置菜单窗口在屏幕上的位置
    menu->setPosition(15.0f, 15.0f, 0.0f);

    // 将菜单窗口添加到窗口管理器中
    wm->addChild(menu);

    // 创建osg::Group对象,用于存放场景节点
    osg::Group*           group = new osg::Group();
    // 创建osg::Geode对象,用于显示几何节点
    osg::Geode*           geode = new osg::Geode();
    // 创建osg::MatrixTransform对象,用于实现变换操作
    osg::MatrixTransform* mt    = new osg::MatrixTransform();

    // 向geode中添加一个球形几何体
    geode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(), 4.0f)));

    // 创建EaseMotionSampler对象,用于实现平滑运动
    EASE_MOTION_SAMPLER = new EaseMotionSampler(osg::Vec3(50.0f, 0.0f, 0.0f));
    // 创建osg::Geode对象,用于显示平滑运动的结果
    EASE_MOTION_GEODE   = new osg::Geode();

    // 将geode添加到mt中
    mt->addChild(geode);
    // 设置mt的更新回调为EASE_MOTION_SAMPLER
    mt->setUpdateCallback(EASE_MOTION_SAMPLER);
    // 设置mt的节点遮罩
    mt->setNodeMask(MASK_3D);

    // 设置视图的摄像机操作器为TrackballManipulator
    viewer.setCameraManipulator(new osgGA::TrackballManipulator());
    viewer.getCameraManipulator()->setHomePosition(
        osg::Vec3d(0.0f, 0.0f, 200.0f),
        osg::Vec3d(20.0f, 0.0f, 0.0f),
        osg::Vec3d(0.0f, 1.0f, 0.0f)
        );
    viewer.home();

    // 将mt添加到group中
    group->addChild(mt);
    // 将EASE_MOTION_GEODE添加到group中
    group->addChild(EASE_MOTION_GEODE);
    // 运行osgWidget库的示例程序,并返回结果
    return osgWidget::createExample(viewer, wm, group);
}

2、qt pro文件

QT += core

TEMPLATE = app
CONFIG += console

DESTDIR = ../3rdParty
if(contains(DEFINES,MSVC2015)){
    DESTDIR = ../3rdParty-2015
    CONFIG(debug, debug|release){
        TARGET = eg_easemotiond
        MOC_DIR = ../build-OpenSceneGraph-2015/eg_easemotion/Debug/moc
        RCC_DIR = ../build-OpenSceneGraph-2015/eg_easemotion/Debug/rcc
        UI_DIR = ../build-OpenSceneGraph-2015/eg_easemotion/Debug/ui
        OBJECTS_DIR = ../build-OpenSceneGraph-2015/eg_easemotion/Debug/obj
    }else{
        TARGET = eg_easemotion
        MOC_DIR = ../build-OpenSceneGraph-2015/eg_easemotion/Release/moc
        RCC_DIR = ../build-OpenSceneGraph-2015/eg_easemotion/Release/rcc
        UI_DIR = ../build-OpenSceneGraph-2015/eg_easemotion/Release/ui
        OBJECTS_DIR = ../build-OpenSceneGraph-2015/eg_easemotion/Release/obj
    }
}else{
    DESTDIR = ../3rdParty
    CONFIG(debug, debug|release){
        TARGET = eg_easemotiond
        MOC_DIR = ../build-OpenSceneGraph/eg_easemotion/Debug/moc
        RCC_DIR = ../build-OpenSceneGraph/eg_easemotion/Debug/rcc
        UI_DIR = ../build-OpenSceneGraph/eg_easemotion/Debug/ui
        OBJECTS_DIR = ../build-OpenSceneGraph/eg_easemotion/Debug/obj
    }else{
        TARGET = eg_easemotion
        MOC_DIR = ../build-OpenSceneGraph/eg_easemotion/Release/moc
        RCC_DIR = ../build-OpenSceneGraph/eg_easemotion/Release/rcc
        UI_DIR = ../build-OpenSceneGraph/eg_easemotion/Release/ui
        OBJECTS_DIR = ../build-OpenSceneGraph/eg_easemotion/Release/obj
    }
}

DEFINES -= UNICODE _UNICODE
win32 {
    DEFINES += _CRT_SECURE_NO_DEPRECATE _CRT_NONSTDC_NO_DEPRECATE
}

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

#当前目录
INCLUDEPATH += ./ ./include

#LIBS
if(contains(DEFINES,MSVC2015)){
    LIBS += -L../3rdParty-2015
}else{
    LIBS += -L../3rdParty
}
CONFIG(debug, debug|release){
    LIBS += -lOpenThreadsd -losgd -losgDBd -losgUtild -losgGAd -losgAnimationd -losgWidgetd -losgViewerd
}else{
    LIBS += -lOpenThreads -losg -losgDB -losgUtil -losgGA -losgAnimation -losgWidget -losgViewer
}
#win32: LIBS += -lopengl32

SOURCES +=  ./examples/osganimationeasemotion/osganimationeasemotion.cpp


# Default rules for deployment.
#unix {
#    target.path = /usr/lib
#}
#!isEmpty(target.path): INSTALLS += target

八、【3D交互式动画】总结

? ? ? 本节文章介绍了如何使用OSG和osgWidget库创建一个交互式的3D动画程序。其中,利用osg::Geometry创建动画几何体,使用osgAnimation::Sampler类定义动画采样器,利用ColorLabel类和ColorLabelMenu类添加鼠标事件,创建具有菜单功能的颜色标签。通过整合这些组件,可以实现一个高质量、交互式的3D动画程序。

文章来源:https://blog.csdn.net/LuckyHanMo/article/details/135536890
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。