查看: 4298|回复: 16

对《OSG程序设计》一书中第六章路径漫游程序的疑惑

[复制链接]

该用户从未签到

发表于 2011-6-13 22:42:58 | 显示全部楼层 |阅读模式
在学习《osg程序设计一书》时,看到第六章,示例十七,路径漫游程序,调试源代码,得到的视点离模型很近,而我自己做出的程序却达不到这个效果,如下图所示:
未命名1.JPG
找了一晚上设置视点的代码,自以为在InitOSG中相机的初始化函数里,可是照此设置,我依然得到的是这个结果。请问高手们,FreeSouth大侠完成这个视点转换的代码在哪?

该用户从未签到

发表于 2011-6-14 08:32:24 | 显示全部楼层
请不要假设别人都看过这本书并且牢记住书中的每段代码……因此请给出更多带有代码段的信息来

该用户从未签到

 楼主| 发表于 2011-6-14 21:50:33 | 显示全部楼层
South.cpp

#include "stdafx.h"
#include "South.h"
//------------------------------//------------------------------
CSouth::CSouth(void): m_fMoveSpeed(1.5f)
, m_bLeftButtonDown(false)
, m_fpushX(0)
, m_fAngle(2.5)
, m_bPeng(false)
, m_fpushY(0)
{
        m_vPosition = osg::Vec3(0.0f, 0.0f,5.0f);

        m_vRotation = osg::Vec3(osg:I_2, 0.0f, 0.0f);               
}
//------------------------------//------------------------------
CSouth::~CSouth(void)
{
}
//------------------------------//------------------------------
South*South::TravelToScene(osg::ref_ptr<osgViewer::Viewer> viewer)
{
     South* camera = new South;
     viewer->setCameraManipulator(camera);
    camera->m_pHostViewer = viewer;
return camera;
}
void CSouth::setByMatrix(const osg::Matrixd & matrix)
{

}
//------------------------------//------------------------------
void CSouth::setByInverseMatrix(const osg::Matrixd& matrix)
{

}
//------------------------------//------------------------------
osg::Matrixd CSouth::getMatrix(void) const
{
        //得到旋转后的矩阵,其实也就是视口矩阵
        osg::Matrixd mat;

        mat.makeRotate(m_vRotation._v[0], osg::Vec3(1.0f, 0.0f, 0.0f),

                m_vRotation._v[1], osg::Vec3(0.0f, 1.0f, 0.0f),

                m_vRotation._v[2], osg::Vec3(0.0f, 0.0f, 1.0f));

        return mat * osg::Matrixd::translate(m_vPosition);
}
//------------------------------//------------------------------
osg::Matrixd CSouth::getInverseMatrix(void) const
{
        osg::Matrixd mat;

        mat.makeRotate(m_vRotation._v[0], osg::Vec3(1.0f, 0.0f, 0.0f),

                m_vRotation._v[1], osg::Vec3(0.0f, 1.0f, 0.0f),

                m_vRotation._v[2], osg::Vec3(0.0f, 0.0f, 1.0f));

        return osg::Matrixd::inverse(mat * osg::Matrixd::translate(m_vPosition));
}
//------------------------------//------------------------------
bool CSouth::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &us)
{
        float mouseX = ea.getX();

        float mouseY = ea.getY();


        switch(ea.getEventType())
        {
        case(osgGA::GUIEventAdapter::KEYDOWN):

                {

                        if (ea.getKey() == 0x20)//' '
                        {
                                us.requestRedraw();

                                us.requestContinuousUpdate(false);

                                return true;
                        }
                        if (ea.getKey() == 0xFF50)//home
                        {

                                ChangePosition(osg::Vec3 (0, 0, m_fMoveSpeed)) ;

                                return true;
                        }
                        if (ea.getKey() == 0xFF57) //end
                        {

                                ChangePosition(osg::Vec3 (0, 0, -m_fMoveSpeed)) ;
                                return true;
                        }

                        if (ea.getKey() == 0x2B)//+
                        {
                                m_fMoveSpeed += 1.0f;

                                return true;
                        }

                        if (ea.getKey() == 0x2D)//-
                        {
                                m_fMoveSpeed -= 1.0f;

                                if (m_fMoveSpeed < 1.0f)
                                {
                                        m_fMoveSpeed = 1.0f;
                                }
                                return true;
                        }
                        if (ea.getKey() == 0xFF52 || ea.getKey () == 0x57 || ea.getKey () == 0x77)//up
                        {

                                ChangePosition(osg::Vec3 (0, m_fMoveSpeed * sinf(osg::PI_2+m_vRotation._v[2]), 0)) ;

                                ChangePosition(osg::Vec3 (m_fMoveSpeed * cosf(osg::PI_2+m_vRotation._v[2]), 0, 0)) ;

                                return true;
                        }
                        if (ea.getKey() == 0xFF54 || ea.getKey () == 0x53 || ea.getKey () == 0x73 )//down
                        {

                                ChangePosition(osg::Vec3 (0, -m_fMoveSpeed * sinf(osg::PI_2+m_vRotation._v[2]), 0)) ;

                                ChangePosition(osg::Vec3(-m_fMoveSpeed * cosf(osg::PI_2+m_vRotation._v[2]), 0, 0)) ;

                                return true;
                        }

                        if (ea.getKey () == 0x41||ea.getKey () == 0x61)
                        {
                                ChangePosition(osg::Vec3 (0, m_fMoveSpeed * cosf(osg::PI_2+m_vRotation._v[2]), 0)) ;

                                ChangePosition(osg::Vec3 (-m_fMoveSpeed * sinf(osg::PI_2+m_vRotation._v[2]), 0, 0)) ;

                                return true;
                        }
                        if (ea.getKey () == 0x44||ea.getKey () == 0x64)
                        {
                                ChangePosition(osg::Vec3 (0,-m_fMoveSpeed * cosf(osg::PI_2+m_vRotation._v[2]), 0)) ;

                                ChangePosition(osg::Vec3 (m_fMoveSpeed * sinf(osg::PI_2+m_vRotation._v[2]), 0, 0)) ;
                                return true;
                        }

                        if (ea.getKey() == 0xFF53)//Right
                        {
                                m_vRotation._v[2] -= osg:egreesToRadians(m_fAngle);
                        }
                        if (ea.getKey()== 0xFF51)//Left
                        {
                                m_vRotation._v[2] += osg::DegreesToRadians(m_fAngle);
                        }
                        if (ea.getKey() == 0x46 || ea.getKey() == 0x66)//F

                        {
                                computeHomePosition();
                                m_fAngle -= 0.2 ;

                                return true ;
                        }

                        if (ea.getKey() == 0x47 || ea.getKey() == 0x67)//G

                        {
                                m_fAngle += 0.2 ;

                                return true ;
                        }



                        return false;

                }

        case (osgGA::GUIEventAdapter ::PUSH ):

                if ( ea.getButton () == 1)

                {
                        m_fpushX = mouseX ;
                        m_fpushY = mouseY ;

                        m_bLeftButtonDown = true ;
                }

                return false ;
        case (osgGA::GUIEventAdapter ::DRAG ):

                if ( m_bLeftButtonDown)

                {
                        m_vRotation._v[2] -= osg::DegreesToRadians(m_fAngle * (mouseX-m_fpushX));

                        m_vRotation._v[0] += osg::DegreesToRadians(1.1*(mouseY-m_fpushY)) ;

                        if (m_vRotation._v [0] >= 3.14)
                                m_vRotation._v [0] = 3.14 ;
                        if (m_vRotation._v [0] <= 0)
                                m_vRotation._v [0] = 0 ;
                }

                return false ;

        case (osgGA::GUIEventAdapter ::RELEASE ):

                if ( ea.getButton () == 1)

                {
                        m_bLeftButtonDown = false ;
                }

                return false ;       

        default:
                return false;
        }
}
//------------------------------//------------------------------
void CSouth::ChangePosition(osg::Vec3 &delta)
{
        if (m_bPeng)
        {
                //看新值与旧值之间的连线是否与模型有交点!
                osg::Vec3 newPos = m_vPosition + delta;
                osgUtil::IntersectVisitor iv;

                osg::ref_ptr<osg:ineSegment> line = new osg::LineSegment(newPos,
                        m_vPosition);

                osg::ref_ptr<osg::LineSegment> lineZ = new osg::LineSegment(newPos + osg::Vec3(0.0f, 0.0f, m_fMoveSpeed),
                        newPos - osg::Vec3(0.0f, 0.0f, m_fMoveSpeed));   
                iv.addLineSegment(lineZ.get());
                iv.addLineSegment (line.get()) ;
                m_node ->accept(iv);
                if (!iv.hits())
                {
                        m_vPosition += delta;
                }
        }
        else
                m_vPosition += delta;
}
//------------------------------//------------------------------
float CSouth::getSpeed()
{
        return m_fMoveSpeed ;
}
//------------------------------//------------------------------
void CSouth::setSpeed(float sp)
{
        m_fMoveSpeed = sp ;
}
//------------------------------//------------------------------
void CSouth::SetPosition(osg::Vec3 &position)
{
        m_vPosition = position ;
}
void CSouth::SetPosition(double* position)
{
        m_vPosition._v[0] = position[0] ;
        m_vPosition._v[1] = position[1] ;
        m_vPosition._v[2] = position[2] ;
}
//------------------------------//------------------------------
osg::Vec3 CSouth::GetPosition()
{
        return m_vPosition ;
}
//------------------------------//------------------------------
void CSouth::setNode(osg::Node* node)
{
        m_node = node ;
}
//------------------------------//------------------------------
void CSouth::computeHomePosition()
{
        if(m_node.get())
        {
                const osg::BoundingSphere& boundingSphere=m_node->getBound();
                osg::Vec3 bp = boundingSphere._center;
                SetPosition(bp) ;               
        }
}
//------------------------------//------------------------------
void CSouth::setPeng(bool peng)
{
        m_bPeng = peng ;
}
//------------------------------//------------------------------
bool CSouth::getPeng()
{
        return m_bPeng ;
}
//------------------------------//------------------------------
void CSouth::setFpeng()
{
        m_bPeng = !m_bPeng ;
}

int main()
{
  osg::ref_ptr<osg::Viewer> viewer = new osgViewer::Viewer();
  South::TravelToScene(viewer.get());
osg::ref_ptr<osg::Group> root = new osg::Group();
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("lz.osg");
root->addChild(node.get());
osgUtil::Optimizer optimizer;
optimizer.optimizer(root.get());
viewer->setSceneData(root.get());
viewer->realizer();
viewer->run();
return 0;
}

代码大概就是这样子的,疑惑的是,初始位置是怎么设置的,构造函数中只给出了初始的位置,并没有调用setPosition函数,这个函数别的地方也没有调用过,所以想问问

该用户从未签到

发表于 2011-6-15 08:29:47 | 显示全部楼层
CSouth::computeHomePosition()
这个虚函数会被系统内部自动调用

该用户从未签到

 楼主| 发表于 2011-6-15 17:10:48 | 显示全部楼层
下面这段代码也实现了漫游的功能,但是没有computeHomePosition()函数,请问这个的初始位置是如何设置的?

TravelManipulator.cpp
TravelManipulator::TravelManipulator(void) : m_fMoveSpeed(1.0f)
        ,m_bLeftButtonDown(false)
        ,m_fpushX(0)
        ,m_fAngle(2.5)
        ,m_bPeng(true)
        ,m_fpushY(0)
{
        m_vPosition = osg::Vec3(-22.0f,-274.0f,100.0f);       
        m_vRotation = osg::Vec3(osg:I_2,0.0F,0.0F);
}


TravelManipulator::~TravelManipulator(void)
{
}

//把漫游加入到场景之中
TravelManipulator* TravelManipulator::TravelToScene(osg::ref_ptr<osgViewer::Viewer> viewer)
{
        TravelManipulator* camera = new TravelManipulator;
        viewer->setCameraManipulator(camera);
        camera->m_pHostViewer = viewer;
        return camera;
}

//设置矩阵
void TravelManipulator::setByMatrix(const osg::Matrixd& matrix)
{
}

//设置逆矩阵
void TravelManipulator::setByInverseMatrix(const osg::Matrixd& matrix)
{
}

//得到矩阵
osg::Matrixd TravelManipulator::getMatrix(void) const
{
        osg::Matrixd mat;
        mat.makeRotate(m_vRotation._v[0],osg::Vec3(1.0f,0.0f,0.0f),
                m_vRotation._v[1],osg::Vec3(0.0f,1.0f,0.0f),
                m_vRotation._v[2],osg::Vec3(0.0f,0.0f,1.0f));
        return mat* osg::Matrixd::translate(m_vPosition);
}

//得到逆矩阵
osg::Matrixd TravelManipulator::getInverseMatrix(void) const
{
        osg::Matrixd mat;
        mat.makeRotate(m_vRotation._v[0],osg::Vec3(1.0f,0.0f,0.0f),
                m_vRotation._v[1],osg::Vec3(0.0f,1.0f,0.0f),
                m_vRotation._v[2],osg::Vec3(0.0f,0.0f,1.0f));
        return osg::Matrixd::inverse(mat* osg::Matrixd::translate(m_vPosition));
}

//事件处理函数
bool TravelManipulator::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& us)
{
        //得到鼠标的位置
        float mouseX = ea.getX();
        float mouseY = ea.getY();
        switch(ea.getEventType())
        {
        case(osgGA::GUIEventAdapter::KEYDOWN):
                {
                        //空格键
                        if(ea.getKey() == 0x20)
                        {
                                us.requestRedraw();
                                us.requestContinuousUpdate(false);
                                return true;
                        }
                        //上移动
                        if(ea.getKey() == 0xFF50)
                        {
                                ChangePosition(osg::Vec3(0,0,m_fMoveSpeed));
                                return true;
                        }
                        //下移动
                        if(ea.getKey() == 0xFF57)
                        {
                                ChangePosition(osg::Vec3(0,0,-m_fMoveSpeed));
                                return true;
                        }
                        //增加速度
                        if(ea.getKey() == 0x2B)
                        {
                                m_fMoveSpeed += 1.0f;
                                return true;
                        }
                        //减少速度
                        if(ea.getKey() == 0x2D)
                        {
                                m_fMoveSpeed -= 1.0f;
                                if(m_fMoveSpeed < 1.0f)
                                {
                                        m_fMoveSpeed = 1.0f;
                                }
                                return true;
                        }
                        //前进
                        if(ea.getKey() == 0xFF52 || ea.getKey() == 0x57 || ea.getKey() == 0x77)
                        {
                                ChangePosition(osg::Vec3(0,m_fMoveSpeed*sinf(osg::PI_2+m_vRotation._v[2]),0));
                                ChangePosition(osg::Vec3(m_fMoveSpeed*cosf(osg::PI_2+m_vRotation._v[2]),0,0));
                                return true;
                        }
                        //后退
                        if(ea.getKey() == 0xFF54 || ea.getKey() == 0x53 || ea.getKey() == 0x73)
                        {
                                ChangePosition(osg::Vec3(0,-m_fMoveSpeed*sinf(osg::PI_2+m_vRotation._v[2]),0));
                                ChangePosition(osg::Vec3(-m_fMoveSpeed*cosf(osg::PI_2+m_vRotation._v[2]),0,0));
                                return true;
                        }
                        //向左
                        if(ea.getKey() == 0x41 || ea.getKey() == 0x61)
                        {
                                ChangePosition(osg::Vec3(0,m_fMoveSpeed*cosf(osg::PI_2+m_vRotation._v[2]),0));
                                ChangePosition(osg::Vec3(-m_fMoveSpeed*sinf(osg::PI_2+m_vRotation._v[2]),0,0));
                                return true;
                        }
                        //向右
                        if(ea.getKey() == 0x44 || ea.getKey() == 0x64)
                        {
                                ChangePosition(osg::Vec3(0,-m_fMoveSpeed*cosf(osg::PI_2+m_vRotation._v[2]),0));
                                ChangePosition(osg::Vec3(m_fMoveSpeed*sinf(osg::PI_2+m_vRotation._v[2]),0,0));
                                return true;
                        }
                        //Right
                        if(ea.getKey() == 0xFF53)
                        {
                                m_vRotation._v[2] -= osg:egreesToRadians(m_fAngle);
                        }
                        //Left
                        if(ea.getKey() == 0xFF51)
                        {
                                m_vRotation._v[2] += osg::DegreesToRadians(m_fAngle);
                        }
                    //改变屏角
                        if(ea.getKey() == 0x46 || ea.getKey() == 0x66)  //F
                        {
                                m_fAngle -= 0.2;
                                return true;
                        }
                        if(ea.getKey() == 0x47 || ea.getKey() == 0x67)  //G
                        {
                                m_fAngle += 0.2;
                                return true;
                        }
                        return false;
                }
                //鼠标按下
        case(osgGA::GUIEventAdapter::PUSH):
                if(ea.getButton() == 1)
                {
                        m_fpushX = mouseX;
                        m_fpushY = mouseY;
                        m_bLeftButtonDown = true;
                }
                return false;
                //拖动
        case(osgGA::GUIEventAdapter::DRAG):
                if(m_bLeftButtonDown)
                {
                        m_vRotation._v[2] -= osg::DegreesToRadians(m_fAngle*(mouseX-m_fpushX));
                        m_vRotation._v[2] += osg::DegreesToRadians(1.1*(mouseY-m_fpushY));
                        if(m_vRotation._v[0] >= 3.14)
                        {
                                m_vRotation._v[0] = 3.14;
                        }
                        if(m_vRotation._v[0] <= 0)
                        {
                                m_vRotation._v[0] = 0 ;
                        }
                }
                return false;
                //鼠标释放
        case (osgGA::GUIEventAdapter::RELEASE):
                if(ea.getButton() == 1)
                {
                        m_bLeftButtonDown = false;
                }
                return false;
        default:
                return false;
        }
}

//位置变换函数
void TravelManipulator::ChangePosition(osg::Vec3& delta)
{
        //碰撞检测
        if(m_bPeng)
        {
                //得到新位置
                osg::Vec3 newPos1 = m_vPosition + delta;
                osgUtil::IntersectVisitor ivXY;
                //根据新的位置得到两条线段检测
                osg::ref_ptr<osg:ineSegment> lineXY = new osg::LineSegment(newPos1,m_vPosition);
                osg::ref_ptr<osg::LineSegment> lineZ = new osg::LineSegment(newPos1 + osg::Vec3(0.0f,0.0f,10.0f),
                        newPos1-osg::Vec3(0.0f,0.0f,-10.0f));
                ivXY.addLineSegment(lineZ.get());
                ivXY.addLineSegment(lineXY.get());
                //结构交集检测
                m_pHostViewer->getSceneData()->accept(ivXY);
                //如果没有碰撞检测
                if(!ivXY.hits())
                {
                        m_vPosition += delta;
                }
        }
        else
        {
                m_vPosition += delta;
        }
}

//设置速度
void TravelManipulator::setSpeed(float &sp)
{
        m_fMoveSpeed = sp;
}

//得到当前速度
float TravelManipulator::getSpeed()
{
        return m_fMoveSpeed;
}

//设置起始的位置
void TravelManipulator::SetPosition(osg::Vec3& position)
{
        m_vPosition = position;
}

//得到当前的所在位置
osg::Vec3 TravelManipulator::GetPosition()
{
        return m_vPosition;
}

main.cpp
int _tmain(int argc, _TCHAR* argv[])
{
        osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
        //把漫游器加到场景中
        TravelManipulator::TravelToScene(viewer.get());
        osg::ref_ptr<osg::Group> root = new osg::Group();
        osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("lz.osg");
        root->addChild(node.get());

        osgUtil::Optimizer optimizer;
        optimizer.optimize(root.get());
        viewer->setSceneData(root.get());
        viewer->realize();
        viewer->run();
        return 0;
}

该用户从未签到

发表于 2011-6-16 08:31:55 | 显示全部楼层
所有漫游器的基类中就已经有computeHomePosition()的实现了

该用户从未签到

 楼主| 发表于 2011-6-16 21:28:46 | 显示全部楼层
对这个程序有几点还是心生疑惑,希望能指点。
一、CameraManipulator类中一些函数setByMatrix等是自动调用的吧,如果需要可以重写其中的代码,如例子里面的那样,但是还有一些比如GetPosition()等函数,这些自己添加的函数也是自动调用的吗,什么时候调用的?
二、理论上相机的观察矩阵等于其位置矩阵乘以姿态矩阵,为什么在GetMatrix()函数里只用到位置向量,没有用姿态向量?这个函数不太明白里面是怎么计算的。
三、想做摄像机跟随节点运动的效果,如果我可以在外部得到用AnimationPath设置的路径的位置矩阵,该怎么设置摄像机的位置矩阵?在GetMatrix()函数里直接返回得到的这个矩阵就可以了吗?
  • TA的每日心情
    开心
    2019-11-11 10:36
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2011-6-16 22:28:24 | 显示全部楼层
    出生点的设置是在构造函数里有一个:
    1. m_vPosition = osg::Vec3(-22.0f,-274.0f,100.0f);        
    2. m_vRotation = osg::Vec3(osg:PI_2,0.0F,0.0F);
    复制代码
    一个设置朝向,一个设置位置。
    一:GetPosition是自己添加的,当你需要得到位置的时候调用,不使用或者删除该函数也没有关系。
    二:GetMatrix中有位置也有资态,姿态是rotate,位置是translate,应该可以理解吧。
    三:跟随节点有很多种方法,为什么不试试NodeTrackerManipulator

    该用户从未签到

     楼主| 发表于 2011-6-17 20:39:51 | 显示全部楼层
    小生愚钝,还是不解。m_vPosition是自己定义的一个变量,对吧,那么系统或者这个类怎么知道这个变量是用来设置初始位置的?它顶多知道这是个Vec3,即使给了这个变量初始化的值,怎么告诉系统这是个改变位置的?
    还有,在ChangePosition()这个函数里,改变位置也只是改变了m_vPosition的值,也没有再次设置什么,觉得似乎应该调用一个改变位置的函数。所以依然困惑。

    该用户从未签到

     楼主| 发表于 2011-6-20 10:33:24 | 显示全部楼层
    渴求解惑

    该用户从未签到

    发表于 2011-6-20 12:20:18 | 显示全部楼层
    OSG会自动在每帧调用getInverseMatrix()来取得当前的观察矩阵值,并传递给渲染系统,因此只要这个方法重写合理,就可以把自己设置的变量转换为观察矩阵的值了

    该用户从未签到

    发表于 2012-9-14 16:59:47 | 显示全部楼层
    本帖最后由 smengq 于 2012-9-14 17:09 编辑
    FreeSouth 发表于 2011-6-16 22:28
    出生点的设置是在构造函数里有一个:一个设置朝向,一个设置位置。
    一:GetPosition是自己添加的,当你需要 ...


    您好,viewer.setCameraManipulator(new Csouth)后,viewer.getCameraManipulator()->setHomePosition(eye,center,up);还会起作用吗?即自己定义的这个操作器我能不能让他通过setHomePosition(eye,center,up)这种方式指定到我要观察的地方?
    如果这种方式实现不了,我们怎样自己指定操作器的位置、朝向观察到指定点(可能操作器某时刻已经不是正对着场景了,已经转向了一定角度,该怎样设置朝向),非常感谢您的指导

    该用户从未签到

    发表于 2012-9-14 21:11:45 | 显示全部楼层
    smengq 发表于 2012-9-14 16:59
    您好,viewer.setCameraManipulator(new Csouth)后,viewer.getCameraManipulator()->setHomePosition( ...

    当然可以
    定义一个相机,。。。然后camera->setViewMatrixAsLookAt(eye,center,up)

    该用户从未签到

    发表于 2012-9-15 09:30:36 | 显示全部楼层
    buaahc 发表于 2012-9-14 21:11
    当然可以
    定义一个相机,。。。然后camera->setViewMatrixAsLookAt(eye,center,up)

    你好,可能我没有表达清楚意思,我想用楼上自己定义的操作器new csouth,viewer.setCameraManipulator(south)后,再通过viewer.getCamera()->setViewMatrixAsLookAt(eye,center,up)这种方式想观察特定点, 遗憾的是我通过实验证实没有实现这种效果,不知道是什么原因,请多多指教,谢谢

    该用户从未签到

    发表于 2012-12-10 11:27:21 | 显示全部楼层
    嗯,是可以的。

    该用户从未签到

    发表于 2013-8-6 10:00:39 | 显示全部楼层
    看了小小就头晕了,果然不适合我呀

    该用户从未签到

    发表于 2013-8-12 11:06:48 | 显示全部楼层
    楼主好厉害呀
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    OSG中国官方论坛-有您OSG在中国才更好

    网站简介:osgChina是国内首个三维相关技术开源社区,旨在为国内更多的技术开发人员提供最前沿的技术资讯,为更多的三维从业者提供一个学习、交流的技术平台。

    联系我们

    • 工作时间:09:00--18:00
    • 反馈邮箱:1315785073@qq.com
    快速回复 返回顶部 返回列表