耗时一个多月,完成了放顶煤、薄煤层液压支架仿真程序的编写。
主要思想:
偶也是菜鸟,刚开始想用骨骼动画实现,但是没有人可给帮我做机械的骨骼动画,只好放弃。
第二种思路,想用Max导出的关键帧动画来实现,每个支架有非常多的动作,十几种,每种动作与其他动作都有可能同时发生,用Max做好关键帧动画之后,导出时分段导出动画,对每个动作分别进行导出,导出后导入程序,当两个动作同时播放时发现模型裂了,如图:,纠结起原因,读OgreMax导入场景的代码,发现当播放两个动作时,此时模型的每个mesh都做了两次位移、旋转、等等,肯定会裂开,同时播放几个动画,此时模型就会位移几次~~,研究了将近一周的时间,美工没有用过OgreMax,加上我是个菜鸟~~最后多方求友,不得不放弃,(如果那位网友知道如何避免这个问题,请不吝赐教)。
最后不得不采用控制节点,使用节点动画来实现,通过一个controller来实现,因为最终模型需要通过硬件来控制,控制器与PC是通过串口进行通讯的,所以本程序共分为三个部分:
1、通讯部分
通过串口,不同的支架传输不同字节长度的数据,根据数据的某一位控制支架做相应的动作。在数据传输过程中使用CRC校验。
示例代码如下:
1: unsigned short CRC16(unsigned char * puchMsg, unsigned short usDataLen)2: {
3: unsigned char uchCRCHi = 0xFF ; /* 高CRC 字节初始化 */4: unsigned char uchCRCLo = 0xFF ; /* 低CRC 字节初始化 */5: unsigned uIndex ; /* CRC 循环中的索引 */6: while (usDataLen--) /* 传输消息缓冲区 */7: {
8: uIndex = uchCRCHi ^ *(puchMsg++) ; /* 计算CRC */9: uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;
10: uchCRCLo = auchCRCLo[uIndex] ;
11: }
12: return (uchCRCHi << 8 | uchCRCLo) ;13: }
14:
15: //构造液压支架通信命令16: int make_support_command(const char * buf, int cnt, std::vector<char>& result)17: {
18: result.resize(cnt+3);
19: result[0] = 0xaf;
20:
21: memcpy(&result[1], buf, cnt);
22:
23: unsigned short crc = CRC16((unsigned char *)&result[0], cnt+1);24: result[cnt + 1] = crc >> 8;
25: result[cnt + 2] = crc & 0xff;
26:
27: return cnt+3;28: }
29:
30: //CRC校验31: int check_support_command(const char * buf, int cnt, std::vector<char>& result)32: {
33: //if(cnt != 9)34: //{35: // TRACE(_T("数据长度错误!\n"));36: // //AfxMessageBox(_T("数据长度错误!"));37: //}38:
39: unsigned short recv_crc = CRC16((unsigned char *)(buf+0), cnt-2);40: unsigned short send_crc = *((unsigned char *)(buf+1 + cnt-3));41: send_crc <<= 8;
42: send_crc |= *((unsigned char *)(buf+2 + cnt-3));43: if(send_crc != recv_crc)44: {
45: TRACE(_T("校验码错误!\n"));46: //AfxMessageBox(_T("校验码错误!"));47: return -1;48: }
49:
50: result.resize(cnt-3);
51: memcpy(&result[0], buf+1, cnt-3);
52:
53: return cnt-3;54: }
2、支架组装,定义部分
此部分将支架组装,使用了一种极其笨拙的方法,将支架的各个部分使用OgreMax导出后,然后根据OgreMax导出的scene文件的坐标值,然后在程序中组装支架。定义的变量相当繁琐,有很大缺陷,当换一个支架的时候,程序的数据需要重写。就是根据支架的结构创建支架,将支架的各个部分一一组装起来。代码如下:
1: mSupportRoot = mSManager->getRootSceneNode()->createChildSceneNode(mName);
2: mFulcrum = mSupportRoot->createChildSceneNode(mName + "supportFulrum", Vector3(0, 0, 0));3:
4: //推溜节点5: mMoveSupport = mSupportRoot->createChildSceneNode(mName + "moveSupport", Vector3(-1.18005, 0.115254, 0));6: Entity *e = mSManager->createEntity(mName + "moveSupport", "TSmoveSupport.mesh");7: mMoveSupport->attachObject(e);
8:
9: mScraper = mMoveSupport->createChildSceneNode(mName + "scraper", Vector3(-1.03653, -0.0424172, 0));10: e = mSManager->createEntity(mName + "scraper", "TSscraper.mesh");11: mScraper->attachObject(e);
12: mScraper->setVisible(mIsShowScraper);
13:
14: //底座节点15: mBase = mFulcrum->createChildSceneNode(mName + "supportBase");16: e = mSManager->createEntity(mName + "base", "TSbase.mesh");17: mBase->attachObject(e);
18: mBasePillar = mBase->createChildSceneNode(mName + "basePillar", Vector3(-1.05936, 0.141, 0));19:
20: //抬底节点21: mLiftBase = mFulcrum->createChildSceneNode(mName + "liftBase", Vector3(-1.26348, 0.30187, 0));22: e = mSManager->createEntity(mName + "liftBase", "TSliftBase.mesh");23: mLiftBase->attachObject(e);
24:
25: //下立柱节点26: mDownPillar = mBase->createChildSceneNode(mName + "downPillar", Vector3(-1.07193, 0.538018, 0));27: e = mSManager->createEntity(mName + "downPillar", "TSdownPillar.mesh");28: mDownPillar->attachObject(e);
29: mDownPillar->setOrientation(0.989742, 0, 0, 0.142869);
30:
31: //上立柱节点32: //mUpPillar = mDownPillar->createChildSceneNode(mName + "upPillar", Vector3(-0.13854,0.961672, 0));33: //mUpPillar = mDownPillar->createChildSceneNode(mName + "upPillar", Vector3(0, 0.961672, 0));34: mUpPillar = mDownPillar->createChildSceneNode(mName + "upPillar", Vector3(0, 1.181672, 0));35: e = mSManager->createEntity(mName + "upPillar", "TSupPillar.mesh");36: mUpPillar->attachObject(e);
37: //mUpPillar->setOrientation(0.997184, 0, 0, 0.0749965);38:
39: //顶梁节点40: mTopBeamAxis = mUpPillar->createChildSceneNode(mName + "topBeamAxis", Vector3(0.00674, -0.00362, 0));41: mTopBeamAxis->roll(Radian(Math::DegreesToRadians(-17)));
42: mTopBeam = mTopBeamAxis->createChildSceneNode(mName + "topBeam");43: mTopPillar = mUpPillar->createChildSceneNode(mName + "topPillar");44: //mTopBeamTrack = mTopBeamAxis->createChildSceneNode(mName + "topBeamTrack", Vector3(0.915483, -0.10226, 0));45: mTopBeamTrack = mTopBeamAxis->createChildSceneNode(mName + "topBeamTrack", Vector3(/*0.915483*/0.715483, -0.10226, 0));46: //e = mSManager->createEntity("TStopTarget", "ninja.mesh");47: //mTopBeamTrack->attachObject(e);48: //mTopBeamTrack->setScale(0.001, 0.001, 0.001);49: e = mSManager->createEntity(mName + "topBeam", "TStopBeam.mesh");50: mTopBeam->attachObject(e);
51:
52: //恻护节点53: mSideGuard = mTopBeam->createChildSceneNode(mName + "sideGurad", Vector3(-0.00456, -0.00654, 0));54: e = mSManager->createEntity(mName +"sideGuard", "TSsideGuard.mesh");55: mSideGuard->attachObject(e);
56:
57: //平衡千斤顶顶梁部分58: mShieldJack2Axis = mTopBeam->createChildSceneNode(mName + "shiledJack2Axis", Vector3(0.435347, -0.00176, 0));59: mShieldJack2Axis->setInheritOrientation(false);
60: mShieldJack2 = mShieldJack2Axis->createChildSceneNode(mName + "ShieldJack1");61: mShieldJack2->setOrientation(0.907374, 0, 0, 0.420324);
62: e = mSManager->createEntity(mName + "shieldJack2", "TSshieldJack2.mesh");63: mShieldJack2->attachObject(e);
64:
65: //掩护梁节点66: mShieldBeamAxis = mTopBeam->createChildSceneNode(mName + "shieldBeamAxis", Vector3(0.715483, -0.01226, 0));67: mShieldBeamAxis->setOrientation(0.909066, 0, 0, -0.416652);
68: //mShieldBeamAxis->roll(Radian(Degree(-50)));69: mShieldBeam = mShieldBeamAxis->createChildSceneNode(mName + "shieldBeam");70: mShieldBeam->translate(0.35, 0.05, 0, Node::TransformSpace::TS_LOCAL);
71: e = mSManager->createEntity(mName + "shieldBeam", "TSshieldBeam.mesh");72: mShieldBeam->attachObject(e);
73: //mShieldBeam->setVisible(false);74:
75: //掩护梁恻护节点76: mShieldSideGuard = mShieldBeam->createChildSceneNode(mName + "shieldSideGuard", Vector3(0, 0, -0.503772));77: e = mSManager->createEntity(mName + "shieldSideGuard", "TSshieldSideGuard.mesh");78: mShieldSideGuard->attachObject(e);
79:
80: //平衡千斤顶掩护梁部分节点81: //mShieldJack2Axis = mShieldBeam->createChildSceneNode(mName + "shieldJack2Axis", Vector3(-0.074592, -0.184284, 0));82: mShieldJack1Axis = mShieldBeam->createChildSceneNode(mName + "shieldJack1Axis", Vector3(0.1 ,-0.104284, 0));83: mShieldJack1Axis->setInheritOrientation(false);
84: mShieldJack1 = mShieldJack1Axis->createChildSceneNode(mName + "shieldJack1");85: e = mSManager->createEntity(mName + "shieldJack1", "TSshieldJack1.mesh");86: mShieldJack1->attachObject(e);
87:
88:
89: mShieldJack2->setAutoTracking(true, mShieldJack1Axis, Vector3::NEGATIVE_UNIT_Y);
90: mShieldJack1->setAutoTracking(true, mShieldJack2Axis, Vector3::UNIT_Y);
91:
92: //mFrontRodTarget = mShieldBeam->createChildSceneNode(mName + "frontRodTarget", Vector3(0.0817407, -0.278777, 0));93: SceneNode *mFrontTargetTo = mShieldBeam->createChildSceneNode(mName + "frontRodTargetTo", Vector3(0.3517407, -0.098777, 0));94: mFrontRodTarget = mShieldBeam->createChildSceneNode(mName + "frontRodTarget", Vector3(0.0517407, -0.178777, 0));95: //e = mSManager->createEntity("frontRodTarget", "ninja.mesh");96: //mFrontRodTarget->attachObject(e);97: //mFrontRodTarget->setScale(0.0005, 0.0005, 0.0005);98: mFrontRodAxis = mBase->createChildSceneNode(mName + "frontRodAxis", Vector3(-0.579555, 0.413417, 0));99: mFrontRod = mFrontRodAxis->createChildSceneNode(mName + "frontRod");100: e = mSManager->createEntity(mName + "frontRod", "TSfrontRod.mesh");101: mFrontRod->attachObject(e);
102:
103:
104: //mBackRodTarget = mShieldBeam->createChildSceneNode(mName + "backRodTarget", Vector3(0.245769, -0.543867, 0));105: SceneNode *mBackRodTo = mShieldBeam->createChildSceneNode(mName + "backRodTargetTo", Vector3(0.68769, -0.05, 0));106: mBackRodTarget = mShieldBeam->createChildSceneNode(mName + "backRodTarget", Vector3(0.00769, -0.05, 0));107: //e = mSManager->createEntity("backRodTarget", "ninja.mesh");108: //mBackRodTarget->attachObject(e);109: //mBackRodTarget->setScale(0.0005, 0.0005, 0.0005);110: mBackRodAxis = mBase->createChildSceneNode(mName + "backRodAxis", Vector3(-0.201678, 0.140705, 0));111: mBackRod = mBackRodAxis->createChildSceneNode(mName + "backRod");112: e = mSManager->createEntity(mName + "backRod", "TSbackRod.mesh");113: mBackRod->attachObject(e);
114:
115: mFrontRod->setAutoTracking(true, mFrontTargetTo, Vector3::UNIT_Y);
116: mBackRod->setAutoTracking(true, mBackRodTo, Vector3::UNIT_Y);
3、动画控制部分
对于每个动作创建一个动画控制器(controller),根据动作的速度与每一帧的时间控制支架做动作,创建的动画控制器为createFrameTimePassthroughController,这样就可以使用frame time传递给控制器函数(controller function)。
为每个动作定义一个控制器,控制其中对每个支架的各个部分进行控制。在处理多个部分联动的时候可能要稍微注意一下,需要注意各部分的关系,本程序中在液压支架进行升柱、降柱的时候进对液压支架的顶梁、掩护梁、后连杆、底座、立柱进行了一系列余弦定理的处理,保证其动作的准确性。代码如下:
1: public:2: MonitorControllerValuePtr mTopPillarDownController; //上立柱降柱动画控制器3: MonitorControllerValuePtr mTopPillarUpController; //上立柱升柱动画控制器4: MonitorControllerValuePtr mDownPillarDownController; //下立柱降柱动画控制器5: MonitorControllerValuePtr mDownPillarUpController; //下立柱升柱动画控制器6:
7: MonitorControllerValuePtr mBaseFrontController; //移架动画控制器8: MonitorControllerValuePtr mScraperPushController; //推溜动画控制器9:
10: MonitorControllerValuePtr mBalancePullController; //伸平衡动画控制器11: MonitorControllerValuePtr mBalancePushController; //收平衡动画控制器12:
13: MonitorControllerValuePtr mTopAngleCalController; //顶梁角度控制器
1: ControllerManager &contMgr = ControllerManager::getSingleton();
2:
3: //上立柱降柱动画控制器4: mTopPillarDownController = MonitorControllerValuePtr(new MovePillarDown(mUpPillar, 1.03, mSpeedPillarDown, this));5: contMgr.createFrameTimePassthroughController(mTopPillarDownController);
6: MonitorControlDest *pController = (MonitorControlDest *)mTopPillarDownController.get();
7:
8: //上立柱升柱动画控制器9: //mTopPillarUpController = MonitorControllerValuePtr(new MovePillarUp(mUpPillar, 1.49607, mSpeedPillarUp, this));10: mTopPillarUpController = MonitorControllerValuePtr(new MovePillarUp(mUpPillar, 1.181672, mSpeedPillarUp, this));11: contMgr.createFrameTimePassthroughController(mTopPillarUpController);
12:
13: //下立柱降柱动画控制器14: mDownPillarDownController = MonitorControllerValuePtr(new MovePillarDown(mDownPillar, 0.168, mSpeedPillarDown, this));15: contMgr.createFrameTimePassthroughController(mDownPillarDownController);
16:
17: //下立柱升柱动画控制器18: mDownPillarUpController = MonitorControllerValuePtr(new MovePillarUp(mDownPillar, 0.538018, mSpeedPillarUp, this));19: contMgr.createFrameTimePassthroughController(mDownPillarUpController);
20:
21: //伸平衡动画控制器22: mBalancePullController = MonitorControllerValuePtr(new PullBalance(mShieldBeamAxis, 3.0, mSpeedBalance, this));23: contMgr.createFrameTimePassthroughController(mBalancePullController);
24:
25: //收平衡动画控制器26: mBalancePushController = MonitorControllerValuePtr(new PushBalance(mShieldBeamAxis, 3.0, mSpeedBalance, this));27: contMgr.createFrameTimePassthroughController(mBalancePushController);
28:
29: //推流动画控制器30: mScraperPushController = MonitorControllerValuePtr(new MoveScraperFront(mMoveSupport, mFulcrum, 2.0, mSpeedPush));31: contMgr.createFrameTimePassthroughController(mScraperPushController);
32:
33: //移架动画控制器34: mBaseFrontController = MonitorControllerValuePtr(new MoveScraperFront(mFulcrum, mMoveSupport, -1.18005, mSpeedMove));35: contMgr.createFrameTimePassthroughController(mBaseFrontController);
36:
37: mTopAngleCalController = MonitorControllerValuePtr(new CalAngle(this));38: contMgr.createFrameTimePassthroughController(mTopAngleCalController);
各个控制器,控制函数定义代码:
1: class MonitorControlDest : public Ogre::ControllerValue<Ogre::Real>2: {
3: public:4: MonitorControlDest() : mControlValue(0), mControlFlow(0){}
5:
6: //设置控制值7: void setControlValue(Ogre::Real controlValue)8: {
9: mControlValue = controlValue;
10: }
11:
12: //得到控制值13: Ogre::Real getControlValue()
14: {
15: return mControlValue;16: }
17:
18: //设置动画控制器19: void setControlFlow(ControlFlow *pFlow)20: {
21: mControlFlow = pFlow;
22: }
23:
24: //得到动画控制器25: ControlFlow* getControlFlow()
26: {
27: return mControlFlow;28: }
29: protected:30: Ogre::Real mControlValue; //控制值31: ControlFlow *mControlFlow; //流程动画控制器32: };
33:
34: //收平衡动画控制器35: class PushBalance : public MonitorControlDest36: {
37: public:38: PushBalance(Ogre::SceneNode *node, Ogre::Real stopPos, Ogre::Real speed, ThinSeamSupport *support)
39: : mNode(node), mStopPos(stopPos), mSpeed(speed), mSupport(support)
40: {
41:
42: }
43:
44: //得到当前的值45: virtual Ogre::Real getValue(void) const46: {
47: if(mNode)48: return mNode->getOrientation().getPitch().valueDegrees();49: return 0;50: }
51:
52: //设置当前值53: virtual void setValue(Ogre::Real value);54: protected:55: Ogre::SceneNode *mNode; //被控制对象56: Ogre::Radian mStopPos; //停止位置57: Ogre::Real mSpeed; //速度58: ThinSeamSupport *mSupport; //液压支架对象59: };
程序运行如下: