粒子效果
void setupParticles()
{
ParticleSystem::setDefaultNonVisibleUpdateTimeout(5); // set nonvisible timeout
ParticleSystem* ps;
// create some nice fireworks and place it at the origin
ps = mSceneMgr->createParticleSystem("Fireworks", "Examples/Fireworks");
mSceneMgr->getRootSceneNode()->attachObject(ps);
// create a green nimbus around the ogre head
ps = mSceneMgr->createParticleSystem("Nimbus", "Examples/GreenyNimbus");
mSceneMgr->getRootSceneNode()->attachObject(ps);
ps = mSceneMgr->createParticleSystem("Rain", "Examples/Rain"); // create a rainstorm
ps->fastForward(5); // fast-forward the rain so it looks more natural
mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3(0, 1000, 0))->attachObject(ps);
// create aureola around ogre head perpendicular to the ground
ps = mSceneMgr->createParticleSystem("Aureola", "Examples/Aureola");
mSceneMgr->getRootSceneNode()->attachObject(ps);
// create shared pivot node for spinning the fountains
mFountainPivot = mSceneMgr->getRootSceneNode()->createChildSceneNode();
ps = mSceneMgr->createParticleSystem("Fountain1", "Examples/PurpleFountain"); // create fountain 1
// attach the fountain to a child node of the pivot at a distance and angle
mFountainPivot->createChildSceneNode(Vector3(200, -100, 0), Quaternion(Degree(20), Vector3::UNIT_Z))->attachObject(ps);
ps = mSceneMgr->createParticleSystem("Fountain2", "Examples/PurpleFountain"); // create fountain 2
// attach the fountain to a child node of the pivot at a distance and angle
mFountainPivot->createChildSceneNode(Vector3(-200, -100, 0), Quaternion(Degree(-20), Vector3::UNIT_Z))->attachObject(ps);
}
ps = mSceneMgr->createParticleSystem(“Fireworks”, “Examples/Fireworks”);
粒子材质
particle_system Examples/Fireworks
{
material Examples/Flare
point_rendering false
particle_width 10
particle_height 10
cull_each false
quota 1000
emit_emitter_quota 10
billboard_type point
// Emitter that emits multiple Point emitters with name 'explosion'
emitter Box
{
name mainEmitter
emit_emitter explosion
angle 30
emission_rate 1000
time_to_live 3
direction 0 1 0
velocity 200
}
// This Point emitter is emitted by the Box emitter and emits billboard particles itself
emitter Point
{
name explosion
angle 180
emission_rate 100
time_to_live 2
direction 0 1 0
velocity 80
duration -1
repeat_delay_min 2
repeat_delay_max 3
}
// Make em float downwards
affector LinearForce
{
force_vector 0 -100 0
force_application add
}
// Give em some nice colours
affector ColourInterpolator
{
time0 0
colour0 1 1 0
time1 0.5
colour1 1 0 0
time2 0.9
colour2 0 0 1
}
}
粒子以及粒子发射器属性配置,暂时不深究
mSceneMgr->getRootSceneNode()->attachObject(ps);
这里有个注意点,创建的所有的ParticleSystem都 attach 到了 RootSceneNode 上。
也就是说 相同类型的 object 可以 attach 多个到同一节点上,也就意味着不能通过类型获取到 object。
阴影
if (mRoot->getRenderSystem()->getCapabilities()->hasCapability(RSC_HWSTENCIL))
{
mSceneMgr->setShadowTechnique(SHADOWTYPE_STENCIL_MODULATIVE);
mCurrentShadowTechnique = SHADOWTYPE_STENCIL_MODULATIVE;
}
else
{
mSceneMgr->setShadowTechnique(SHADOWTYPE_TEXTURE_MODULATIVE);
mCurrentShadowTechnique = SHADOWTYPE_TEXTURE_MODULATIVE;
}
启用阴影
pPlaneEnt->setCastShadows(false);
设置是否产生阴影
mSceneMgr->setShadowTextureSettings(1024, 2);
设置基于纹理阴影的纹理属性
mSceneMgr->setShadowColour(ColourValue(0.5, 0.5, 0.5));
设置阴影颜色
mSceneMgr->setShadowTexturePixelFormat(PF_BYTE_RGBA);
设置基于纹理阴影的纹理像素格式
mSceneMgr->setShadowTextureCasterMaterial(themat);
设置阴影的材质
mSceneMgr->setShadowTextureSelfShadow(true);
设置是否启用基于纹理阴影的自阴影
骨骼动画
void setupContent()
{
mShaderGenerator->invalidateScheme(Ogre::RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME);
mViewport->setMaterialScheme(RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME);
//Add the hardware skinning to the shader generator default render state
mSrsHardwareSkinning = mShaderGenerator->createSubRenderState<RTShader::HardwareSkinning>();
Ogre::RTShader::RenderState* renderState = mShaderGenerator->getRenderState(Ogre::RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME);
renderState->addTemplateSubRenderState(mSrsHardwareSkinning);
}
设置RTSystem属性,并启用硬件蒙皮
SkeletonPtr skel = static_pointer_cast<Skeleton>(SkeletonManager::getSingleton().load("jaiqua.skeleton",
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME));
加载骨骼
void setupModels()
{
tweakSneakAnim();
SceneNode* sn = NULL;
Entity* ent = NULL;
AnimationState* as = NULL;
// make sure we can get the buffers for bbox calculations
MeshManager::getSingleton().load("jaiqua.mesh",
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY,
HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY, true, true);
auto& controllerMgr = ControllerManager::getSingleton();
for (int i = 0; i < NUM_MODELS; i++)
{
// create scene nodes for the models at regular angular intervals
sn = mSceneMgr->getRootSceneNode()->createChildSceneNode();
sn->yaw(Radian(Math::TWO_PI * (float)i / (float)NUM_MODELS));
sn->translate(0, 0, -20, Node::TS_LOCAL);
mModelNodes.push_back(sn);
// create and attach a jaiqua entity
ent = mSceneMgr->createEntity("Jaiqua" + StringConverter::toString(i + 1), "jaiqua.mesh");
ent->setMaterialName("jaiqua");
sn->attachObject(ent);
// enable the entity's sneaking animation at a random speed and loop it manually since translation is involved
as = ent->getAnimationState("Sneak");
as->setEnabled(true);
as->setLoop(false);
controllerMgr.createController(controllerMgr.getFrameTimeSource(),
AnimationStateControllerValue::create(as, true),
ScaleControllerFunction::create(Math::RangeRandom(0.5, 1.5)));
mAnimStates.push_back(as);
}
// create name and value for skinning mode
StringVector names;
names.push_back("Help");
names.push_back("Skinning");
names.push_back(mBoneBoundingBoxesItemName);
// create a params panel to display the help and skinning mode
mStatusPanel = mTrayMgr->createParamsPanel(TL_TOPLEFT, "HelpMessage", 200, names);
mStatusPanel->setParamValue("Help", "H / F1");
String value = "Software";
enableBoneBoundingBoxMode( false ); // update status panel entry
// change the value if hardware skinning is enabled
MaterialPtr entityMaterial = ent->getSubEntity(0)->getMaterial();
if(entityMaterial)
{
Technique* bestTechnique = entityMaterial->getBestTechnique();
if(bestTechnique)
{
Pass* pass = bestTechnique->getPass(0);
if (pass && pass->hasVertexProgram() && pass->getVertexProgram()->isSkeletalAnimationIncluded())
{
value = "Hardware";
}
}
}
mStatusPanel->setParamValue("Skinning", value);
}
tweakSneakAnim();
从代码可以看出来,动画是在.skeleton里的。调整骨骼动画,通常这一步不需要的,动画通常由动画师制作好,程序不需要去修改。不在此对这一操作深入。想深入,需要对骨骼动画有所了解。现在这个阶段,可以不看。
加载 mesh,确保可以获得用于bbox计算的缓冲区? 这个操作有点奇怪,先记住。HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY 硬件可写。硬件读写状态,从硬件读取数据会非常耗时。
创建动画控制器
auto& controllerMgr = ControllerManager::getSingleton();
for (int i = 0; i < NUM_MODELS; i++)
{
// create scene nodes for the models at regular angular intervals
sn = mSceneMgr->getRootSceneNode()->createChildSceneNode();
sn->yaw(Radian(Math::TWO_PI * (float)i / (float)NUM_MODELS));
sn->translate(0, 0, -20, Node::TS_LOCAL);
mModelNodes.push_back(sn);
// create and attach a jaiqua entity
ent = mSceneMgr->createEntity("Jaiqua" + StringConverter::toString(i + 1), "jaiqua.mesh");
ent->setMaterialName("jaiqua");
sn->attachObject(ent);
// enable the entity's sneaking animation at a random speed and loop it manually since translation is involved
as = ent->getAnimationState("Sneak");
as->setEnabled(true);
as->setLoop(false);
controllerMgr.createController(controllerMgr.getFrameTimeSource(),
AnimationStateControllerValue::create(as, true),
ScaleControllerFunction::create(Math::RangeRandom(0.5, 1.5)));
mAnimStates.push_back(as);
}
从上面的操作,只创建了一个mesh Entity,并没有绑定骨骼。然后就开始获取AnimationState。因为动画在skeleton里,能获取就说明绑定了,什么时候绑定的?
看看 Entity的代码,发现里面有 Mesh,Mesh里有skeleton,通过调试,发现,在 read Mesh的时候,有一个 M_MESH_SKELETON_LINK。也就是说在读取mesh的时候就绑定了骨骼。
Controller<Real>* ControllerManager::createController(const ControllerValueRealPtr& src,
const ControllerValueRealPtr& dest, const ControllerFunctionRealPtr& func);
创建动画控制器,该函数没有详细的说明。看看 Controller 类。这个类很简单,除了一个get,set,只有一个Updata
void update(void)
{
if(mEnabled)
mDest->setValue(mFunc->calculate(mSource->getValue()));
}
AnimationStateControllerValue::create(as, true),通过entity获取到的AnimationState创建一个ControllerValue,该ControllerValue应该是绑定在 该Entity的AnimationState上。
通过第三个参数func,与第一个参数计算当前的值,设置给Dest。
按照这个猜测,上面应该是获取ControllerManager最后一帧的时间,给获取到的AnimationState 随机加 [0.5 - 1.5] 的时间。
地形
void setupContent()
{
//! [terrain_create]
mTerrainGroup = new Ogre::TerrainGroup(mSceneMgr, Ogre::Terrain::ALIGN_X_Z, TERRAIN_SIZE, TERRAIN_WORLD_SIZE);
mTerrainGroup->setFilenameConvention(TERRAIN_FILE_PREFIX, TERRAIN_FILE_SUFFIX);
mTerrainGroup->setOrigin(mTerrainPos);
//! [terrain_create]
configureTerrainDefaults(l);
#ifdef PAGING
// Paging setup
mPageManager = OGRE_NEW PageManager();
// Since we're not loading any pages from .page files, we need a way just
// to say we've loaded them without them actually being loaded
mPageManager->setPageProvider(&mDummyPageProvider);
mPageManager->addCamera(mCamera);
mTerrainPaging = OGRE_NEW TerrainPaging(mPageManager);
PagedWorld* world = mPageManager->createWorld();
mTerrainPaging->createWorldSection(world, mTerrainGroup, 2000, 3000,
TERRAIN_PAGE_MIN_X, TERRAIN_PAGE_MIN_Y,
TERRAIN_PAGE_MAX_X, TERRAIN_PAGE_MAX_Y);
#else
//! [define_loop]
for (long x = TERRAIN_PAGE_MIN_X; x <= TERRAIN_PAGE_MAX_X; ++x)
for (long y = TERRAIN_PAGE_MIN_Y; y <= TERRAIN_PAGE_MAX_Y; ++y)
defineTerrain(x, y);
// sync load since we want everything in place when we start
mTerrainGroup->loadAllTerrains(true);
//! [define_loop]
#endif
//! [init_blend]
if (mTerrainsImported)
{
for (const auto& ti : mTerrainGroup->getTerrainSlots())
{
initBlendMaps(ti.second->instance);
}
}
mTerrainGroup->freeTemporaryResources();
//! [init_blend]
}
先创建一个 TreeainGroup, 指定场景;对齐方式(指定一个平面),这里是XZ平面(水平面);地形大小(顶点数);在世界当中的大小
TerrainGroup::TerrainGroup(SceneManager* sm, Terrain::Alignment align,
uint16 terrainSize, Real terrainWorldSize)
{
}
mTerrainGroup->setFilenameConvention 设置命名约定?
mTerrainGroup->defineTerrain(x, y, 0.0f); 定义一个指定位置的固定高度
String filename = mTerrainGroup->generateFilename(x, y);
if (ResourceGroupManager::getSingleton().resourceExists(mTerrainGroup->getResourceGroup(), filename))
{
mTerrainGroup->defineTerrain(x, y);
}
else
{
Image img;
getTerrainImage(x % 2 != 0, y % 2 != 0, img);
mTerrainGroup->defineTerrain(x, y, &img);
mTerrainsImported = true;
}
按照定义生成一个dat文件
mTerrainGroup->loadAllTerrains(true);
导入地形,参数:是否异步。
从这两天对几个特定sample的学习,发现ogre在每个功能的基本使用上都挺简单的。
但是,随着时间的推移,一步步深入,越来越多的东西没有理解,都只能去按照注释简单的翻译,无法知道它的具体作用。终归为对ogre的实现都没有深入的去看。
毕竟这是一个学习过程。
所以到这里,把引擎最基本的功能(Material, Mesh, Scene, Light, Skybox, Particle, Skybox, Shadow, Animation, Terrain)稍微过了一遍之后,剩下的sample就暂时不看了,那些sample基本都是一些高级应用,看了也无法真正理解(说难听点,就是看也看不懂)。
接下来去看这些基本功能的代码,去分析理解代码。