osg操控器之动画路径操控器osgGA::AnimationPathManipulator分析

osg::AnimationPath osg::AnimationPath::ControlPoint osg::AnimationPathCallback 使用     osg::AnimationPath* animationPath = new osg::AnimationPath;    //设置循环,还可设成摇摆:SWING 或仅动作1次:NO_LOOPING     animationPath->setLoopMode(osg::AnimationPath::LOOP);    //插入时间,和对应的位置和 阅读详情

目录

1. 前言

2. 示例代码

3. 动画路径操控器源码分析

3.1. 构造函数

3.2. home函数

3.3. handle函数

3.3.1 帧事件处理

3.3.2. 按键事件处理

4. 主要接口说明


1. 前言

      osg官方提供了很多操控器,在源码目录下的src\osgGA目录下,cpp文件名含有Manipulator的都是操控器,每个这样的cpp表示一种类型的操控器。其中AnimationPathManipulator.cpp文件实现了动画路径操控器。

        所谓操控器是指:操控相机运动,从而实现场景视图矩阵变化,反映到观察场景用户的感官上就是:人眼感觉场景变化了,但现实中的物体是没变化的,只是相机方位或距离远近在改变,从而给人视觉上的感觉而已。

        其它操控器参见如下博文:

osg操控器之键盘切换操控器osgGA::KeySwitchMatrixManipulator。

2. 示例代码

           如下用动画路径操控器写的例子,实现奶牛根据预先设定好的路径进行动画动作:

#include<osgViewer/Viewer>
#include<osgGA/AnimationPathManipulator>
#include<osgDB/readFile>
int main(int argc, char* argv[])
{
    osg::ArgumentParser arguments(&argc, argv);

    osgViewer::Viewer viewer(arguments);
    auto pNode = osgDB::readNodeFile(R"(E:\osg\OpenSceneGraph-Data\cow.osg)");
    if (nullptr == pNode)
    {
        OSG_WARN << "file not exist!\r\n";
        return 1;
    }

    auto pAnimationMani = new osgGA::AnimationPathManipulator("AnimationPath.txt");
    viewer.setCameraManipulator(pAnimationMani);
    viewer.setSceneData(pNode);
    return viewer.run();
}

结果如下:

                                                               图1

       按键盘p键,停止动画;再按p键又开启动画。按键盘(键,则减慢动画速率到原速率的1.1倍、按键盘)键,则加快动画速率到原速率的1.1倍。

3. 动画路径操控器源码分析

3.1. 构造函数

       osg动画路径操控器是osgGA::AnimationPathManipulator类实现的。该类有两个构造函数,分别如下:

AnimationPathManipulator::AnimationPathManipulator(osg::AnimationPath* animationPath)
{
      ..... // 为节省篇幅,代码略。
}

AnimationPathManipulator::AnimationPathManipulator( const std::string& filename )
{
    ..... // 为节省篇幅,其它代码略。

    _animationPath->read(in);

}

       第1个构造函数参数是指向动画路径对象的指针。动画路径用osg::AnimationPath类表达,该类封装时间变化和模型变换的对应关系,被用来更新相机位置和模型位置。

       第2个构造函数参数是指向包含动画路径对象参数的文件名。动画路径对象参数文件的格式是一般的ASCII文本文件,每行8个float数据,每个数据以空格隔开即可,这8个数值按如下格式组织:                                          

time  px py pz ax ay az aw

       其中time表示动画帧所在的时刻,以秒为单位,该时刻是距离动画开始时逝去的秒数;px、 py、 pz分别表示模型在笛卡尔坐标系下的世界坐标; ax、ay、az、aw以四元组形式表示模型方向、姿态。本文第2节中的AnimationPath.txt是一个自己制作的动画路径对象参数文件,内容如下:

0 0 18 1 0 0 0 1
0.2 0 17.48 1 0 0 0 1
0.4 0 16.96 1 0 0 0 1
0.6 0 16.44 1 0 0 0 1
0.8 0 15.92 1 0 0 0 1
1 0 15.4 1 0 0 0 1
1.2 0 14.88 1 0 0 0 1
1.4 0 14.36 1 0 0 0 1
1.6 0 13.84 1 0 0 0 1
1.8 0 13.32 1 0 0 0 1
2 0 12.8 1 0 0 0 1
2.2 0 12.28 1 0 0 0 1
2.4 0 11.76 1 0 0 0 1
2.6 0 11.24 1 0 0 0 1
2.8 0 10.72 1 0 0 0 1
3 0 10.2 1 0 0 0 1
3.2 0 9.68000000000001 1 0 0 0 1
3.4 0 9.16000000000001 1 0 0 0 1
3.6 0 8.64000000000001 1 0 0 0 1
3.8 0 8.12000000000001 1 0 0 0 1
4 0 7.60000000000001 1 0 0 0 1
4.2 0 7.08000000000001 1 0 0 0 1
4.4 0 6.56000000000001 1 0 0 0 1
4.6 0 6.04000000000001 1 0 0 0 1
4.8 0 5.52000000000001 1 0 0 0 1
5 0 5.00000000000001 1 0 0 0 1
5.2 0 4.48000000000001 1 0 0 0 1
5.4 0 3.96000000000001 1 0 0 0 1
5.6 0 3.44000000000001 1 0 0 0 1
5.8 0 2.92000000000001 1 0 0 0 1
6 0 2.40000000000001 1 0 0 0 1
6.2 0 1.88000000000001 1 0 0 0 1
6.4 0 1.36000000000001 1 0 0 0 1
6.6 0 0.840000000000011 1 0 0 0 1
6.8 0 0.320000000000011 1 0 0 0 1
7 0 -0.199999999999989 1 0 0 0 1
7.2 0 -0.719999999999989 1 0 0 0 1
7.4 0 -1.23999999999999 1 0 0 0 1
7.6 0 -1.75999999999999 1 0 0 0 1
7.8 0 -2.27999999999999 1 0 0 0 1
8 0 -2.79999999999999 1 0 0 0 1
8.2 0 -3.31999999999999 1 0 0 0 1
8.4 0 -3.83999999999999 1 0 0 0 1
8.6 0 -4.35999999999999 1 0 0 0 1
8.8 0 -4.87999999999999 1 0 0 0 1
9 0 -5.39999999999999 1 0 0 0 1
9.2 0 -5.91999999999999 1 0 0 0 1
9.4 0 -6.43999999999999 1 0 0 0 1
9.6 0 -6.95999999999999 1 0 0 0 1
9.8 0 -7.47999999999999 1 0 0 0 1
10 0 -7.99999999999999 1 0 0 0 1

关于怎么生成动画路径文件,请参见如何生成osg的动画路径文件博文。

当调用第2个构造函数时,会通过调用如下代码:

 _animationPath->read(in);

将上述文件每行中的8个数值会成为动画路径对象的ControlPoint点(动画帧控制点),read实现如下:

void AnimationPath::read(std::istream& in)
{
    while (!in.eof())
    {
        double time;
        osg::Vec3d position;
        osg::Quat rotation;
        in >> time >> position.x() >> position.y() >> position.z() >> rotation.x() >> rotation.y() >> rotation.z() >> rotation.w();
        if(!in.eof())
            insert(time,osg::AnimationPath::ControlPoint(position,rotation));
    }
}

void AnimationPath::insert(double time,const ControlPoint& controlPoint)
{
    _timeControlPointMap[time] = controlPoint;
}

可以看到,将每行8个点读入后存入到map中(_timeControlPointMap是std::map类型),map的key是time,value是ControlPoint点(动画帧控制点)。

3.2. home函数

   该类中home函数有两个,这里只讲如下home函数:

void AnimationPathManipulator::home(double currentTime)
{
    if (_animationPath.valid())
    {
        _timeOffset = _animationPath->getFirstTime()-currentTime;

    }
    // reset the timing of the animation.
    _numOfFramesSinceStartOfTimedPeriod=-1;
}

     当启动2节的例子时,osg外层框架会调用该函数。因为事件才刚开始,所以参数currentTime一般为0。home函数的作用是:记录动画第一帧离起始时间currentTime的偏移量,将该时间偏移量保存在成员变量_timeOffset中;_numOfFramesSinceStartOfTimedPeriod表示动画运行到当前时刻的帧索引(0表示第1帧)。因为执行home时,动画还未开启,故这里将_numOfFramesSinceStartOfTimedPeriod置为-1,为后续动画的开始计数作准备。

3.3. handle函数

3.3.1 帧事件处理

     所谓帧事件是每帧都会处理的事件,其用GUIEventAdapter::FRAME表示,其在handleFrame函数实现,代码如下:

void AnimationPathManipulator::handleFrame( double time )
{
    osg::AnimationPath::ControlPoint cp;

    double animTime = (time+_timeOffset)*_timeScale;
    _animationPath->getInterpolatedControlPoint( animTime, cp );

    if (_numOfFramesSinceStartOfTimedPeriod==-1)
    {
        _realStartOfTimedPeriod = time;
        _animStartOfTimedPeriod = animTime;

    }

    ++_numOfFramesSinceStartOfTimedPeriod;

    double animDelta = (animTime-_animStartOfTimedPeriod);
    if (animDelta>=_animationPath->getPeriod())
    {
        if (_animationCompletedCallback.valid())
        {
            _animationCompletedCallback->completed(this);
        }

        if (_printOutTimingInfo)
        {
            double delta = time-_realStartOfTimedPeriod;
            double frameRate = (double)_numOfFramesSinceStartOfTimedPeriod/delta;
            OSG_NOTICE <<"AnimatonPath completed in "<<delta<<" seconds, completing "<<_numOfFramesSinceStartOfTimedPeriod<<" frames,"<<std::endl;
            OSG_NOTICE <<"             average frame rate = "<<frameRate<<std::endl;
        }

        // reset counters for next loop.
        _realStartOfTimedPeriod = time;
        _animStartOfTimedPeriod = animTime;
        _numOfFramesSinceStartOfTimedPeriod = 0;
    }

    cp.getMatrix( _matrix );
}

要理解上述代码,需借助如下的时间流逝图:

 图2

 其中紫色时间线的time表示handle调用以来即当前帧经过的总时间,其是以时刻0为参考点的。而实际中帧的起始时间参考点是_timeOffset,故在_timeScale为1时(即没有调节速率),当前帧时刻为:

_timeOffset + time

而帧速率可以通过按下键盘(、)键或通过接口setTimeScale还变快或变慢,故当前帧时刻还需乘以_timeScale,即为:

(time+_timeOffset)*_timeScale

if语句判断如果发现是第1帧,则记录当前实际时间(基于0时刻)和动画帧基于_timeOffset为基准的时刻,便于后面计算动画总时长。之后将帧索引加1。一旦发现当前帧和基准时间大于等于动画总周期(总时长),则表示动画完成,如果安装的动画完成回调对象有效,则调用动画完成回调对象的completed函数,则该函数可以打印出一些信息等。

3.3.2. 按键事件处理

       里面的代码很简单,结合图2的时间线理解不难。需要注意是:当按p暂停后再按p继续动画时,_timeOffset的值要从当前时刻减去暂停时刻,因为这段时间内,动画根本没动。

4. 主要接口说明

         如果需要调用osgGA::AnimationPathManipulator类,经常用到的接口说明如下:

  void setTimeScale(double s);
  double getTimeScale() const;

 设置、获取时间缩放因子,即上面提到的_timeScale,该值越大,动画越快;否则越慢。

void setAnimationCompletedCallback(AnimationCompletedCallback* acc);
AnimationCompletedCallback* getAnimationCompletedCallback();
const AnimationCompletedCallback* getAnimationCompletedCallback() const ;

设置、获取动画完成后的回调对象,用于动画完成时,通知外层调用方,外层调用方收到这个通知后,可以进行处理一些事情。注意:自定义的动画回调类必须从下面AnimationCompletedCallback 继承并实现completed方法,该方法的参数为AnimationPathManipulator类对象,一般情况下为动画路径对象本身。

struct AnimationCompletedCallback : public virtual osg::Referenced
{
    virtual void completed(const AnimationPathManipulator* apm) = 0;
};

可以通过如下函数: 

 void setPrintOutTimingInfo(bool printOutTimingInfo);
 bool getPrintOutTimingInfo() const;

获取或设置是否需要输出动画的一些时间线信息,如:动画帧总数、动画平均帧率、动画总时长等。

OSG 中osgGA 库 核心地位osgGA是OSG人机交互的基石,任何用户输入都需要通过它来处理。分工明确:使用处理常规的3D导航,使用自定义处理应用程序特定的业务逻辑(如UI交互、物体拾取、技能释放等)。事件传递:理解事件的传递链(自定义处理器 -> 操纵器)和返回值(truefalse) 的含义至关重要。:在开发阶段和最终应用程序中集成多个操纵器,可以极大地提升用户体验和调试便利性。FRAME 事件:善用FRAME事件来处理动画和状态更新,它是驱动动态场景的核心。 阅读详情

相关推荐

OSG编程指南<二十>:OSG路径动画

OSG编程指南<二十>:OSG路径动画

欧特GO 782

osgAnimation(三):动画种类介绍及实现流程

osg动画种类 osg中的动画主要的有五种分别是轨迹动画,序列动画,骨骼动画,变形动画和粒子动画。甚至自己也可以通过重写frame()函数或者说利用各种回调函数来实现动画效果。 轨迹动画 轨迹动画主要是指的osg::AnimationPathUpdateCallBack 这个回调类,其本质是一个随时间变化的4*4矩阵序列,用以控制刚体在轨迹上以给定姿态运动。 回调类的构造函数参数是轨迹osg::AnimationPath,轨迹由若干个控制点进行控制,一个控制点就是一个变换矩阵(控制点有很多种构造函数

h1063135843的博客 4520

(三) Debian 安装linuxcnc-ethercat中间件

1.在这里同样的也是需要下载一下依赖包 sudo apt install linuxcnc-uspace-dev 2.依赖包安装完成, 接着执行下面命令 sudo git clone https://github.com/sittner/linuxcnc-ethercat 还是可能出现下载网络错误下载不成功,可以直接下载我已上传百度云linuxcnc目录下 注意一下,后面make可能会出现头文件(xxx.h)的报错提示,在这里给大家一个处理办法 用aptitude 工具可以搞定 sudo apt-get

qq_30065813的博客 828

OSG-漫游

本文转至http://www.cnblogs.com/shapherd/archive/2010/08/10/osg.html 作者写的比较好,再次收藏,希望更多的人可以看到这个文章 互联网是是一个相互分形并学习的平台,我希望我每个人可以将我们认为有价值的东西,在不损害他人利益的情况下分享给更多的人。 MatrixManipulator 场景的核心管理器是viewer,

DreamLife&Yaothos 1694

Windows游戏多开检测实战:5种技术原理与破解方法详解(附C++代码)

本文深入解析了Windows游戏多开检测的5种核心技术原理,包括进程枚举、互斥体、信号量、窗口及文件映射检测,并详细探讨了对应的破解方法。通过分析这些基于进程和内核对象的攻防机制,为开发者与逆向爱好者提供了从底层理解Windows系统进程管理的实战视角,并附有核心C++代码示例。

weixin_29315091的博客 243

【扫地机器人】详细介绍扫地机器人软硬件测试和场景化测试

本文提出2025版扫地机器人全生命周期测试方案,涵盖实验室仿真、硬件台架、客户实景等全流程验证。测试以"清洁率≥95%、覆盖率≥98%、MTBF≥1000h"为核心指标,采用数字孪生与120套真实场景结合的双V模型。重点包括:传感器精度(LDS误差<±10mm)、清洁系统性能(10g粉尘吸入率≥99%)、动力续航(800次循环容量保持≥80%)等硬件测试;以及SLAM重定位(误差<5cm)、AI识别(mAP>92%)等软件验证。方案特别针对宠物家庭(毛发缠绕率≤3%)、

2501_92897788的博客 2283

OpenSceneGraph源码分析:osgGA模块

osgGA(OSG GUI Abstraction)通过与系统交互,使得OSG程序可以响应鼠标、键盘等各类事件的功能。 The 'GA' in osgGA stands for 'GUI Abstraction'; the osgGA namespace provides facilities to help developers write the glue to allow the osg to work with varying window systems. As a cross-platfor

国产CAx(CAD/CAE/CAM)软件研发 542

NeurIPS‘25新研究!PINN+GNN强强联手,模型精度飙升50%!

物理图神经网络研究取得重大进展!UCLA团队提出的PhysGNN架构在NeurIPS'2025上引发关注,该模型成功融合PINN的物理约束优势与GNN的图结构处理能力,解决了工程仿真中的关键瓶颈。研究显示,PhysGNN在城市热网预测中误差降低42%,电网负荷模拟的物理规则违反率下降68%。同步解析的SyncTREE模型则创新性地将RC树物理特性融入GNN,实现毫秒级IC时序分析;HSPGNN模型通过嵌入偏微分方程,显著提升多变量时间序列填补精度。

m0_73122726的博客 1021

osgGA::AnimationPathManipulator的使用(实现固定路径漫游)

osgGA::AnimationPathManipulator的使用(实现固定路径漫游)首先定义AnimationPathManipulato操纵器,在构造函数中输入路径文件名字,然后将其加入键盘选择器(keyswitchManipulator)。 l         可以直接设置操纵器 viewer.setCameraManipulator(apm.get()); l

boqing的专栏 5653

《QEMU/KVM源码分析与应用》读书笔记4 —— 第2章 QEMU基本组件(1)

《QEMU/KVM源码分析与应用》读书笔记4 —— 第2章 QEMU基本组件(1)

phmatthaus的专栏 1475

基于深度学习YOLOv11的布匹缺陷检测系统(Python+PySide6界面+训练代码)

YOLOv11模型训练:根据布匹缺陷数据集训练YOLOv11模型,以识别布匹中的各种缺陷,如裂纹、污渍、破损等。PySide6界面设计:设计一个图形用户界面,帮助用户上传布匹图像,并显示模型的检测结果。模型优化与精度提升:通过数据增强、超参数调优等手段提高模型的准确性。本文介绍了如何基于YOLOv11实现布匹缺陷检测系统,并通过PySide6构建了一个图形用户界面。该系统能够自动检测布匹中的各种缺陷,具有较高的效率和精度。

斌擎科技 529

场景交互与场景漫游-osgGA库(5)

osg 场景交互与场景漫游-osgGA库

liangfei868的博客 1176

PCL 改进快速欧式聚类【2025最新版】

在FEC: Fast Euclidean Clustering for Point Cloud Segmentation一文的基础上对算法进行改进。博客长期更新,本文最近更新时间为:2025年9月16日。

点云侠的博客 2569

10、osg::AnimationPath,osg::NodeVisitor类

一、osg::AnimationPath类 1、AnimationPath ()       AnimationPath (const AnimationPath &ap, const CopyOp &copyop=CopyOp::SHALLOW_COPY) 说明:构造函数,第二个构建函数为从已知值来构造新的AnimationPath. 2、bool getMatrix (double

sxj的专栏 2683

B07:AI 时代怎么考核团队?我把 KPI 改了 5 版才找到答案

最后说一句掏心窝的话。绩效考核不是"管人"的工具,是"引导团队行为"的工具。你定什么指标,团队就会做什么行为。你定"AI 占比",团队就凑占比你定"效率提升",团队就挑软柿子你定"代码行数",团队就刷行数你定"工具使用率",团队就刷使用只有定"业务价值 + 工程质量 + 能力成长"三个维度的指标,团队才会真正关注长期价值。这套 KPI,我在我带的团队里跑了 6 个月,没有任何一个指标被"刷"出来。考核的是结果,不是过程。永远考核"创造了多少价值",永远不考核"用了多少 AI"。

刘一说的IT专栏 499

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

osg例子--osganimate(路径动画)

【代码】osg例子--osganimate(路径动画)

qq970767225的博客 694

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

OSG路径漫游实现与应用

路径漫游简介 路径漫游在OSG中即为视点或者模型按照我们自己设定的路径进行漫游的一种方法,原本在OsgViewer中就有,在第一次按键盘小写“z”开始记录动画路径,大写"Z"保存路径文件,当我们用指定的物体按照自定的路径进行运动,此时也就称为路径动画,本质都是相同的。 现在通过屏幕取点实现小车按照给定点进行路径漫游运动一周的代码: 操作流程 1.点击路径编辑,开始屏幕取点,第一个点与其余点区分。 ...

weixin_43154360的博客 3264

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

如何生成osg的动画路径文件

在进行osg的开发中,有时需要对模型按某个路径或规则进行动画,如下:奶牛在10秒时间段从起始的osg::Vec3d(0.0, 18, 1.0)位置 匀速直线运动到osg::Vec3d(0.0, -8, 1.0)位置处。又比如下:飞机以和Y轴成倾斜45°角绕着Z轴在地面一定高度做圆周盘旋运动。

danshiming的博客 540

osg解析系列-[osg::osgGA]

osgGA::EventHandler相对于osgGA::Event定义,可理解为Event的处理者,它继承自osg::NodeCallback,osg::DrawableEventCallback。osgGLUT::Viewer有一个已经注册过的随时等候的回调函数负责刷新。2.2 osgGA::GUIEventHandler -> osgGA::EventHandler是事务处理者的必要接口的封装,任何想进入“事务处理者”队列的class都必须从此类继承且实现必须的接口。

qq_42531872的博客 248

osg中漫游器的原理——osgGA::CameraManipulator(二)

osgGA库中所有漫游器的基类是CameraManipulator,本文就对这个类进行一个完整的解析。通过之前的分析(详细请参考这一些列的第一篇),可以知道为了实现一个自定义的漫游器,我们至少需要实现以下几个纯虚函数: virtual void setByMatrix(const osg::Matrixd& matrix) = 0; //设置相机的位置姿态矩阵 virtual void setByInverseMatrix(const osg::Matrixd& matri...

whl0071的专栏 2189

OSG使用AnimationPathManipulator实现摄像机环绕物体旋转动画,并解决Viewer再次设置不能执行动画的问题

OSG使用AnimationPathManipulator实现摄像机环绕物体旋转动画,并解决Viewer再次设置不能执行动画的问题

qq_45523399的博客 496

AnimationPath类

bool getMatrix (double time, Matrixf &matrix) const bool getMatrix (double time, Matrixd &matrix) const bool getInverse (double time, Matrixf &matrix) const bool getInverse (double time, Matrixd

hscoder的博客 1052

osg漫游器的原理和执行流程

关键词:manipulator; 漫游器; 第一人称漫游器; osg; osg漫游器的原理; osg漫游器的流程 从CameraManipulator讲起吧,CameraManipulator是漫游器的基类,它继承于GUIEventHandler class OSGGA_EXPORT CameraManipulator : public GUIEventHandler { ... }; ...

三维小糊涂,泽云,三维开发技术分享 1万+

osgAnimation之动画基础篇

简介 osgAnimation是osg库中提供场景动画效果的一个类库,它为我们提供了许多与场景动画相关的类,比如关键帧、插值、采样、频道、骨骼动画、材质变化等。本课就对osgAnimation库中的基础类进行一些解析。以下都是我个人学习过程中的一些记录和体会,方便以后自己复习之用。 开始 Keyframe 对应文件 osgAnimation/keyframe

Frank的专栏 6190

osgGA::AnimationPathManipulator动画触发时间异常的解决方法

关于osgGA::AnimationPathManipulator的使用,各种教材、帖子中已经介绍的比较详细了,这里只介绍一下如何设置动画操作器的时钟。 实践中发现,对于触发型动画,即程序运行了一段时间后,通过鼠标、键盘事件触发的动画,如果不设置动画操作器的时钟,动画可能出现异常,甚至根本不触发动画。分析认为操作器默认的起始时间是软件的启动时间,如果软件启动后一段时间再触发动画,osg很

Gaoxing' Blog 1250
上一篇: LineSegmentIntersector::Intersections中ratio含义及LineSegmentIntersector相交点说明
下一篇: osg操控器之键盘切换操控器osgGA::KeySwitchMatrixManipulator
荆楚闲人
博客等级 码龄20年 3万+粉丝 473原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值