探索Partio(2) 继续探索粒子格式库partio
上一节内容讲了安装与Hello World例子。
原理
类模型
有三个核心的类:
- ParticlesInfo 是元数据。记录粒子数目、有哪些属性。
- ParticlesData 只读粒子数据
- ParticlesDataMutable 可写可读粒子数据
对应的有三个重要函数。原型都在Partio.h之中。
readHeaders()
返回元数据,如粒子数目和有哪些属性。
readCached()
返回只读的ParticlesData
create() and read()
返回可读可写的ParticlesDataMutable
属性数据模型
所有的粒子属性都是以下三种基本数据结构的组合
VECTOR[3]
FLOAT[d]
INT[d]
其中 VECTOR[3] 和FLOAT[3] 是一样的
VECTOR[4]是无效的,FLOAT[4]是有效的。FLOAT可以指定任意维度 FLOAT[1...infinity]
遍历
有多种遍历的方式:
- 若使用SIMD , 则推荐dataAsFloat()和data(arrayOfIndices)
- 推荐Cache ParticleAttributes 而非循环
- 使用迭代器,这比data()或dataAsFloat 的方式要快
后端
有三种实现
- SimpleParticles
- ParticleHeaders
- SimpleParticlesInterleaved
读写器
可以自己在io文件夹添加新的读写器。
- 需要实现这三个接口: ParticlesInfo, ParticlesData and ParticlesDataMutable
- 然后在io/readers.h中添加函数原型
- 在io/ParticleIO.cpp中添加后缀绑定
实践(例子)
参考src\tests\testio.cpp
读取文件
Partio::ParticlesDataMutable* data=Partio::read("E:/codes/try/partio/my_partio/test.bgeo");
参数:只需要提供绝对路径即可。
返回值:Partio::ParticlesDataMutable*
是partio内部数据结构的指针
注意路径必须是const char*
。如果是std::string
,需要利用.c_str()
方法转换为const char *
格式。
read方法会先忽略你的.gz后缀,然后调用reader()函数匹配后缀,最后在相应后缀的函数(每个io目录下的文件对应一种格式)中读取文件。入口函数在 src\lib\io\ParticleIO.cpp
写入文件
write函数同理。
Partio::write(filename,*p);
只需要给出绝对路径(同样需要const char *
格式),并给出一个指针即可。
这个指针的数据类型是partio内部数据结构的指针Partio::ParticlesData*
,
也可以是ParticlesDataMutable *
完整例子注解
// 首先必须引入头文件Partio.h
#include <Partio.h>
#include <iostream>
// 生产数据用于测试
Partio::ParticlesDataMutable* makeData()
{
// 利用create创建可读写对象foo(这里用的是引用)
Partio::ParticlesDataMutable& foo=*Partio::create();
// 利用addAttribute方法增加三种属性:位置、寿命、id。
Partio::ParticleAttribute positionAttr=foo.addAttribute("position",Partio::VECTOR,3);
Partio::ParticleAttribute lifeAttr=foo.addAttribute("life",Partio::FLOAT,2);
Partio::ParticleAttribute idAttr=foo.addAttribute("id",Partio::INT,1);
// 增加的同时遍历5个粒子:
for(int i=0;i<5;i++){
// 粒子的编号
Partio::ParticleIndex index=foo.addParticle();
// 获取对应属性、对应编号的粒子数据的指针。
float* pos=foo.dataWrite<float>(positionAttr,index);
float* life=foo.dataWrite<float>(lifeAttr,index);
int* id=foo.dataWrite<int>(idAttr,index);
// 在指针处写入数据
pos[0]=.1*i;
pos[1]=.1*(i+1);
pos[2]=.1*(i+2);
life[0]=-1.2+i;
life[1]=10.;
id[0]=index;
}
// 返回foo对象指针
return &foo;
}
void testEmptySaveLoad(const char* filename)
{
Partio::ParticlesDataMutable* p=Partio::create();
p->addAttribute("position",Partio::VECTOR,3);
std::cerr<<"Testing empty save with file '"<<filename<<"'"<<std::endl;
Partio::write(filename,*p);
p->release();
Partio::ParticlesDataMutable* pread=Partio::read(filename);
pread->release();
}
// 测试读与写。第一个参数是数据指针p, 第二个参数是文件绝对路径
void testSaveLoad(Partio::ParticlesData* p,const char* filename)
{
std::cerr<<"Testing with file '"<<filename<<"'"<<std::endl;
// 写数据
Partio::write(filename,*p);
// 读数据(与读无关)
Partio::ParticlesData* pnew=Partio::read(filename);
// 释放读了的数据对象
pnew->release();
}
int main(int argc,char *argv[])
{
{
// 生产数据
Partio::ParticlesDataMutable* foo=makeData();
// 测试各个格式的读写
testSaveLoad(foo,"test.bgeo");
testSaveLoad(foo,"test.bgeo.gz");
testSaveLoad(foo,"test.geo");
testSaveLoad(foo,"test.geo.gz");
testSaveLoad(foo,"test.ptc");
testSaveLoad(foo,"test.ptc.gz");
testSaveLoad(foo,"test.pdb");
testSaveLoad(foo,"test.pdb.gz");
testEmptySaveLoad("testEmpty.geo");
testEmptySaveLoad("testEmpty.bgeo");
testEmptySaveLoad("testEmpty.pdb");
testEmptySaveLoad("testEmpty.ptc");
// 释放对象
foo->release();
}
return 0;
}