#ifndef PLANET_H
#define PLANET_H
#include <QObject>
#include <osg/Geometry>
#include <osg/Geode>
#include <osg/MatrixTransform>
#include <osg/PrimitiveSet>
#include <osgParticle/Particle>
#include <osgParticle/PointPlacer>
#include "CSphereCallBack.h"
#include <QObject>
struct STPlanetData
{
STPlanetData(){}
STPlanetData(const double R, const double autoRS, const double revolR, const double doublerevolRS, const QString &texName)
: radius(R)
, autoRotSpeed(autoRS)
, revolRadius(revolR)
, revolRotSpeed(doublerevolRS)
, strTexName(texName) {}
QString name; //行星名字
QString orbitName; //轨迹名字
double radius; //半径
double autoRotSpeed; //自转速度
double revolRadius; //公转半径
double revolRotSpeed; //公转速度
QString strTexName; //纹理名称
};
class Planet : public QObject, public osg::Geode
{
Q_OBJECT
public:
Planet();
~Planet();
//设置行星数据
void setData(const STPlanetData &data, const osg::Matrix &translateMat = osg::Matrix());
//设置行星暂停转动
void setRotable(const bool rot)
{
if (m_rpCallBack)
m_rpCallBack->setRotatable(rot);
if(m_rpTailCallBack)
m_rpTailCallBack->setRotatable(rot);
}
//设置、获取行星半径
void setRadius(const double radius) { m_data.radius = radius; }
double radius()const { return m_data.radius; }
//设置、获取行星自传速度
void setAutoRotSpeed(const double speed) {m_data.autoRotSpeed = speed; updateCallBack();}
double autoRotSpeed()const { return m_data.autoRotSpeed; }
//设置、获取行星公转半径
void setRevolRadius(const double revolRadius) {m_data.revolRadius = revolRadius; updateCallBack();}
double revolRadius()const { return m_data.revolRadius; }
//设置、获取行星公转速度
void setRevolRotSpeed(const double revolSpeed) { m_data.revolRotSpeed = revolSpeed; updateCallBack(); }
double revolRotSpeed()const {return m_data.revolRotSpeed;}
//设置行星加速、减速
void setRevolrotPlusSpeed() { m_data.revolRotSpeed += 0.01; updateCallBack(); }
void setRevolrotSubSpeed() { m_data.revolRotSpeed -= 0.01; updateCallBack(); }
//设置、获取行星纹理名称
void setTextureName(const QString &texName) { m_data.strTexName = texName; }
QString textureName()const { return m_data.strTexName; }
//获取行星变换节点
osg::ref_ptr<osg::MatrixTransform> matrixTrans();
//获取轨迹节点
osg::ref_ptr<osg::Geode> orbitLine();
//设置轨迹是否可见
void setOrbitLineVisible(const bool vis);
//设置轨迹name
void setOrbitName(const QString &name);
//设置轨迹线宽
void setOrbitLineWidth(const bool plus);
//设置轨迹颜色
void setOrbitLineColor(const osg::Vec4 &color);
//改变轨迹样式
void setOrbitLineStyle(const osg::PrimitiveSet::Mode &lineStyle);
osg::ref_ptr<osg::MatrixTransform> createTail();
osg::ref_ptr<osg::Geode> createTailNode(int vertexNum);
private:
void createSphere();
void createOrbit();
void updateCallBack();
osg::ref_ptr<osg::MatrixTransform> m_matrixTrans;
osg::Matrix m_translateMat;
osg::ref_ptr<osg::Geode> m_orbitGeode;
osg::ref_ptr<osg::Geometry> m_orbitGeometry;
QString m_sphereName;
QString m_orbitName;
float m_orbitLineWidth;
osg::Vec4 m_orbitLineColor;
osg::PrimitiveSet::Mode m_lineStyle;
osg::ref_ptr<CSphereCallBack> m_rpCallBack;
osg::ref_ptr<TailCallback> m_rpTailCallBack;//粒子
osg::ref_ptr<CTrailerCallback> m_rpCTailCallBack;
STPlanetData m_data; //行星数据
};
#endif
#include "Planet.h"
#include <osgDB/ReadFile>
#include <osg/AnimationPath>
#include <osg/Texture2D>
#include <osg/Image>
#include <osg/Material>
#include <osg/LineWidth>
#include <osg/Program>
#include <osg/BlendColor>
#include <osgParticle/Particle>
#include <osgParticle/ParticleSystem>
#include <osgParticle/ModularEmitter>
#include <osgParticle/RandomRateCounter>
#include <osgParticle/RadialShooter>
#include <osgParticle/ModularProgram>
#include <osgParticle/AccelOperator>
#include <osgParticle/FluidFrictionOperator>
#include <osgParticle/ParticleSystemUpdater>
#include <QDebug>
#include <QTimer>
#include <QCoreApplication>
const int NUM = 50;
Planet::Planet()
: m_translateMat(osg::Matrix())
, m_orbitLineWidth(1.0)
, m_orbitLineColor(0.0, 0.0, 1.0, 1)
, m_lineStyle(osg::PrimitiveSet::LINE_LOOP)
, m_rpCallBack(new CSphereCallBack())
, m_rpCTailCallBack(nullptr)
{
}
Planet::~Planet()
{
}
void Planet::setData(const STPlanetData &data, const osg::Matrix &translateMat)
{
m_data = data;
m_translateMat = translateMat;
setName(data.name.toStdString());
setOrbitName(data.orbitName);
createSphere();
updateCallBack();
}
osg::ref_ptr<osg::MatrixTransform> Planet::matrixTrans()
{
return m_matrixTrans.get();
}
void Planet::createSphere()
{
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
osg::ref_ptr<osg::Vec3Array> vertexes = new osg::Vec3Array; // 顶点
osg::ref_ptr<osg::Vec3Array> normal = new osg::Vec3Array; // 法线
osg::ref_ptr<osg::Vec2Array> texCoord = new osg::Vec2Array; // 纹理
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array; // 颜色-为了不被轨迹节点的颜色影响
for (int i = 0; i <= NUM; ++i)//NUM:纬线数
{
for (int j = 0; j <= NUM * 2; ++j)//NUM * 2:经线数
{
osg::Vec3 vec3(
sin(osg::PI * i / NUM) * cos(osg::PI * j / NUM),
sin(osg::PI * i / NUM) * sin(osg::PI * j / NUM),
cos(osg::PI * i / NUM));
vertexes->push_back(vec3 * m_data.radius);
normal->push_back(-vec3);
texCoord->push_back(osg::Vec2(double(j) / (2.0 * NUM), 1.0 - double(i) / NUM));//y从上到下
colors->push_back(osg::Vec4(1.0, 1.0, 1.0, 0.0));
}
}
geometry->setVertexArray(vertexes);
geometry->setNormalArray(normal, osg::Array::BIND_PER_VERTEX);
geometry->setTexCoordArray(0, texCoord);
geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
QString path = QCoreApplication::applicationDirPath();
path.append("/../../data/Star/");
path.append(m_data.strTexName);
osg::StateSet* state = geometry->getOrCreateStateSet();
osg::ref_ptr<osg::Image> image = osgDB::readImageFile(path.toStdString());
osg::ref_ptr<osg::Texture2D> tex = new osg::Texture2D;
tex->setImage(image.get());
tex->setWrap(osg::Texture::WRAP_S ,osg::Texture::REPEAT);
tex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
state->setTextureAttributeAndModes(0, tex.get());
osg::ref_ptr<osg::DrawElementsUInt> elements = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLE_STRIP);
for (int i = 0; i < NUM; ++i)
{
for (int j = 0; j <= NUM * 2; ++j)
{
elements->push_back(i * (NUM * 2 + 1) + j);
elements->push_back((i + 1) * (NUM * 2 + 1) + j);
}
}
geometry->addPrimitiveSet(elements);
this->addDrawable(geometry);
//矩阵变换
m_matrixTrans = new osg::MatrixTransform;
m_matrixTrans->addChild(this);
m_matrixTrans->setUpdateCallback(m_rpCallBack);
if (!m_translateMat.isIdentity())//小行星带使用
m_rpCallBack->setTranslateMat(m_translateMat);
}
osg::ref_ptr<osg::MatrixTransform> Planet::createTail()
{
osgParticle::Particle parTemplate;
parTemplate.setLifeTime(1);
parTemplate.setSizeRange(osgParticle::rangef(1.0f, 0.2f));
parTemplate.setAlphaRange(osgParticle::rangef(0.0f, 1.0f));
parTemplate.setColorRange(osgParticle::rangev4(osg::Vec4f(1.0, 0.5, 0.3, 1.0), osg::Vec4f(0.0, 0.7, 1.0, 0.0)));
parTemplate.setRadius(0.02);
parTemplate.setMass(0.001);//重量
osg::ref_ptr<osgParticle::ParticleSystem> parSystem = new osgParticle::ParticleSystem();
parSystem->setDefaultAttributes("", false, false);
parSystem->setDefaultParticleTemplate(parTemplate);
//粒子放射器(计数器、放置器、发射器)
osg::ref_ptr<osgParticle::ModularEmitter> parEmitter = new osgParticle::ModularEmitter();
parEmitter->setParticleSystem(parSystem);
osg::ref_ptr<osgParticle::RandomRateCounter> parCounter = new osgParticle::RandomRateCounter();
parCounter->setRateRange(50, 50);//粒子数量
parEmitter->setCounter(parCounter);
osg::ref_ptr<osgParticle::PointPlacer> placer = new osgParticle::PointPlacer();//粒子生成位置
placer->setCenter(getBound().center());
parEmitter->setPlacer(placer);
//弧度发射器
osg::ref_ptr<osgParticle::RadialShooter> parShooter = new osgParticle::RadialShooter();
parShooter->setInitialSpeedRange(1.0,1.0);
//与z轴的夹角
parShooter->setThetaRange(osg::PI_2, osg::PI_2);
//与x y面夹角
parShooter->setPhiRange(osg::PI_2, osg::PI_2);;
parEmitter->setShooter(parShooter);
//重力、空气阻力模拟
osg::ref_ptr<osgParticle::ModularProgram> parProgram = new osgParticle::ModularProgram();
parProgram->setParticleSystem(parSystem);
osg::ref_ptr<osgParticle::AccelOperator> parAccOperator = new osgParticle::AccelOperator();
parAccOperator->setToGravity(2);
parProgram->addOperator(parAccOperator);
osg::ref_ptr<osgParticle::FluidFrictionOperator> parFluOperator = new osgParticle::FluidFrictionOperator();
parFluOperator->setFluidToAir();
parProgram->addOperator(parFluOperator);
osg::ref_ptr<osgParticle::ParticleSystemUpdater> parUpdater = new osgParticle::ParticleSystemUpdater();
parUpdater->addParticleSystem(parSystem);
osg::ref_ptr<osg::Geode> parGeode = new osg::Geode;
parGeode->addDrawable(parSystem);
osg::ref_ptr<osg::MatrixTransform> particleMat = new osg::MatrixTransform();//尾迹结点
m_rpTailCallBack = new TailCallback(m_data.revolRadius, m_data.revolRotSpeed);
particleMat->setUpdateCallback(m_rpTailCallBack);
particleMat->addChild(parEmitter);
particleMat->addChild(parProgram);
particleMat->addChild(parGeode);
particleMat->addChild(parUpdater);
return particleMat.release();
}
void Planet::createOrbit()
{
m_orbitGeode = new osg::Geode;
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
geometry->setName(m_orbitName.toStdString());
osg::ref_ptr<osg::Vec3Array> vertexes = new osg::Vec3Array;
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array;
osg::ref_ptr<osg::Vec3Array> normal = new osg::Vec3Array;
for (int j = 0; j <= NUM * 2; ++j)
{
osg::Vec3 vec3(cos(osg::PI * j / NUM), sin(osg::PI * j / NUM), 0);
vertexes->push_back(vec3 * m_data.revolRadius);
colors->push_back(m_orbitLineColor);
normal->push_back(osg::Vec3(0.0, 0.0, 1.0));
}
geometry->setVertexArray(vertexes);
geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
geometry->setNormalArray(normal, osg::Array::BIND_PER_VERTEX);
geometry->getOrCreateStateSet()->setAttribute(new osg::LineWidth(m_orbitLineWidth));
/*osg::Program *program = new osg::Program;
program->addShader(new osg::Shader(osg::Shader::VERTEX, vershader));
program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragshader));
geometry->getOrCreateStateSet()->setAttribute(program, osg::StateAttribute::ON);
geometry->getOrCreateStateSet()->setAttributeAndModes(lineProgram,osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED);
geometry->getOrCreateStateSet()->addUniform(new osg::Uniform("a_Color",osgEarth::Color(_style.lineColor())));*/// 线条颜色
geometry->addPrimitiveSet(new osg::DrawArrays(m_lineStyle, 0, vertexes->size()));
m_orbitGeode->addDrawable(geometry);
m_orbitGeometry = geometry;
}
osg::ref_ptr<osg::Geode> Planet::createTailNode(int vertexNum)
{
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
osg::ref_ptr<osg::Vec3Array> vec3Vertex = new osg::Vec3Array(vertexNum);
osg::ref_ptr<osg::Vec4Array> vec4Color = new osg::Vec4Array(vertexNum);
for (unsigned int i = 0; i < vertexNum; ++i)
{
(*vec3Vertex)[i] = osg::Vec3(0, 0, 0);
float alpha = sinf(osg::PI * (float)i / (float)vertexNum);
(*vec4Color)[i] = osg::Vec4(1.0, 1.0, 0.0, alpha);
}
geometry->setDataVariance(osg::Object::DYNAMIC); //场景数据动态改变
geometry->setUseDisplayList(false); //禁用显示列表,动态更新不安全
geometry->setUseVertexBufferObjects(true); //使用VBO模式
geometry->setVertexArray(vec3Vertex);
geometry->setColorArray(vec4Color);
geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
geometry->addPrimitiveSet(new osg::DrawArrays(GL_LINE_STRIP, 0, vertexNum));
geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); //关闭灯光
geometry->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); //打开混合
geometry->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); //透明度
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
geode->addDrawable(geometry);
geode->setNodeMask(0);
QTimer::singleShot(6000, this, [=]() {geode->setNodeMask(1); });
m_rpCTailCallBack = new CTrailerCallback(geometry, vertexNum);
m_matrixTrans->addUpdateCallback(m_rpCTailCallBack);
return geode.release();
}
void Planet::updateCallBack()
{
if(m_rpCallBack)
m_rpCallBack->setPlanetPama(m_data.autoRotSpeed, m_data.revolRadius, m_data.revolRotSpeed);
if(m_rpTailCallBack)
m_rpTailCallBack->updatePara(m_data.revolRadius, m_data.revolRotSpeed);
}
osg::ref_ptr<osg::Geode> Planet::orbitLine()
{
if(m_orbitGeode)
m_orbitGeode->setNodeMask(0);
return m_orbitGeode.get();
}
void Planet::setOrbitLineVisible(const bool vis)
{
if(m_orbitGeode)
m_orbitGeode->setNodeMask(vis);
}
void Planet::setOrbitName(const QString &name)
{
m_orbitName = name;
createOrbit();
}
void Planet::setOrbitLineWidth(const bool plus)
{
if (plus)
m_orbitLineWidth++;
else
m_orbitLineWidth--;
if(m_orbitGeometry)
m_orbitGeometry->getOrCreateStateSet()->setAttribute(new osg::LineWidth(m_orbitLineWidth),osg::StateAttribute::ON);
}
void Planet::setOrbitLineColor(const osg::Vec4 &color)
{
if (!m_orbitGeometry) return;
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array;
m_orbitLineColor = color;
for (int j = 0; j <= NUM * 2; ++j)
{
colors->push_back(m_orbitLineColor);
}
m_orbitGeometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
}
void Planet::setOrbitLineStyle(const osg::PrimitiveSet::Mode &lineStyle)
{
if (!m_orbitGeometry) return;
m_lineStyle = lineStyle;
m_orbitGeometry->removePrimitiveSet(0);
m_orbitGeometry->addPrimitiveSet(new osg::DrawArrays(m_lineStyle, 0, NUM * 2 + 1));
}