Q116:PBRT-V3场景描述文件.pbrt格式解析
2017-06-17 20:43
549 查看
对于PBRT-V3场景文件.pbrt格式,小编自己根据自己的理解总结如下:
在api.cpp中:
关键字“LookAt”对应的处理函数是pbrtLookAt();
关键字“Camera”对应的处理函数是pbrtCamera();
关键字“Film”对应的处理函数是pbrtFilm();
关键字“Integrator”对应的处理函数是pbrtIntegrator();
关键字“WorldBegin”对应的处理函数是pbrtWorldBegin();
关键字“AttributeBegin”对应的处理函数是pbrtAttributeBegin();
关键字“CoordSysTransform”对应的处理函数是pbrtCoordSysTransform();
关键字“LightSource”对应的处理函数是pbrtLightSource();
关键字“AttributeEnd”对应的处理函数是pbrtAttributeEnd();
关键字还有:Translate,Rotate,Scale,Material,Shape等等。
如上有三对AttributeBegin/AttributeEnd,刚好也有三组仿射变换Translate/Rotate。
第二组Translate/Rotate只作用于第二对AttributeBegin/AttributeEnd包住的sphere;
第三组Translate/Rotate只作用于第三对AttributeBegin/AttributeEnd包住的cone;
由于第一对AttributeBegin/AttributeEnd包住的是sphere和cone,所以,第一组Translate/Rotate对sphere和cone都起作用。
处理函数原型:
举例5:
处理函数原型:
即:类型+变量+赋值
举例6:
解析:
关键字是Film;
这个Film的类型是image;
这个image类型的Film有三个属性:
属性一:有个string类型的属性叫做filename,这个filename的值是“sphere.exr”
属性二:有个integer类型的属性叫做xresolution,这个xresolution的值为200
属性三:有个integer类型的属性叫做yresolution,这个yresolution的值为200
处理函数原型:
举例7:
解析:
关键字是LightSource;
这个LightSource的类型是distant;
这个distant类型的LightSource有三个属性:
属性一:有个point类型的属性叫做from,这个from的值为[0 0 0]
属性二:有个point类型的属性叫做to,这个to的值为[0 0 1]
属性三:有个color类型的属性叫做L,这个L的值为[3 3 3]
处理函数原型:
举例8:
解析:
关键字是Material;
这个Material的类型是uber;
这个uber类型的Material有五个属性:
属性一:有个color类型的属性叫做Kd,这个Kd的值为[0.1 0.1 0.9]
属性二:有个color类型的属性叫做Kr,这个Kr的值为[0.9 0.9 0.9]
属性三:有个color类型的属性叫做Ks,这个Ks的值为[0.1 0.1 0.1]
属性四:有个float类型的属性叫做roughness,这个roughness的值为0.9
属性五:有个float类型的属性叫做index,这个index的值为1.34
处理函数原型:
举例9:
处理函数原型:
其实,所有情况都可以归到“X+X+X+属性”。
只是,
有的只有X+X+X;
有的X多;
有的X少;
不管是哪种情况,最终都是取决于“处理函数原型”。
接下来的内容是“程序对.pbrt文件的处理过程”
接下来,小编以解析LightSource为例说明一下从开始解析到存储数据的过程。
举例10:
在pbrtparse.y中有:
这个是告诉flex、bison在读到关键字“LightSource”时要做的事情。
先看这句:
这句是最终调用处理函数pbrtLightSource(),给该函数传了两个参数:
$2,存放的是LightSource的类型名,此处即为“distant”;
params,存放的是这个distant类型的LightSource的所有属性。
ParamSet的定义看起来蛮复杂的,其实就干四件事(有点类似于数据库及对数据的CRUD):
接下来,看看具体是怎么为params填充数据的,调用的是这个函数:
在partparse.y中有这么一段code,“定义”了所有“属性类型”:
然后,在InitParamSet()中会根据属性类型调用ParamSet对应的成员函数来给params填充数据。
对于:
就会调用到:
具体实现在paramset.cpp中:
这个函数在src/core/api.cpp中。
完成将LightSource的数据写入renderOptions->lights中。
渲染相关的数据都是存在renderOptions中的。
renderOptions的类是长这个样子:
简单说来,
程序外,描述场景的是.pbrt格式的文件;
程序内,描述场景的是RenderOptions对象;
另外,注意一下,对于LightSource,pbrtLightSource()直接调用了MakeLight()创建了LightSource对象。
所以,renderOptions->lights中保存的是LightSource对象的指针。
而像RenderOptions中的这些:
对应的“pbrt关键字”函数中只是将相关属性参数存入RenderOptions的对应成员变量中。
注意到:
在RenderOptions类定义的前面有几个成员方法:
这个就是用来创建那些还没有在对应“pbrt关键字”函数中创建对象的类的对象的。
在pbrtWorldEnd()有非常重要的三行代码:
1,MakeIntegrator()
2,MakeScene()
3,integrator->Render(*scene)
即:
创建积分器;创建场景;用积分器渲染场景;
重点提一下MakeScene()。
创建Scene对象。
之前小编一直搞不清RenderOption对象和Scene对象的关系:
RenderOption侧重数据,直接和场景描述对应;
Scene是将RenderOption的数据再封装了一下,同时加入了和渲染相关的方法。
贴出Scene的定义代码:
场景描述文件解析结束时得到两样东西:
一个积分器对象;
一个场景对象;
然后,开始用“积分器对象”渲染“场景对象”。
1,“#”后面的内容为注释
举例1:#sphere.pbrt #first we set up the eye
2,每一行的第一个词为“关键字”,在api.cpp中对应着处理函数。
举例2:#first we set up the eye LookAt 1 1 10 0 0 -1 0 1 0 #ex ey ez lx ly lz ux uy uz #the camera Camera "perspective" "float fov" [30] #name the file Film "image" "string filename" ["sphere.exr"] "integer xresolution" [200] "integer yresolution" [200] Integrator "whitted" #begin describing scene WorldBegin #light source AttributeBegin CoordSysTransform "camera" LightSource "distant" "point from" [0 0 0] "point to" [0 0 1] "color L" [3 3 3] AttributeEnd
在api.cpp中:
关键字“LookAt”对应的处理函数是pbrtLookAt();
关键字“Camera”对应的处理函数是pbrtCamera();
关键字“Film”对应的处理函数是pbrtFilm();
关键字“Integrator”对应的处理函数是pbrtIntegrator();
关键字“WorldBegin”对应的处理函数是pbrtWorldBegin();
关键字“AttributeBegin”对应的处理函数是pbrtAttributeBegin();
关键字“CoordSysTransform”对应的处理函数是pbrtCoordSysTransform();
关键字“LightSource”对应的处理函数是pbrtLightSource();
关键字“AttributeEnd”对应的处理函数是pbrtAttributeEnd();
关键字还有:Translate,Rotate,Scale,Material,Shape等等。
3,AttributeBegin和AttributeEnd是成对顺序出现,最临近的一对包含的内容可是为一个block。
举例3:AttributeBegin Translate 0 -1 0 Rotate 35 0 1 0 #define a sphere AttributeBegin Translate -1 .75 -1.5 Rotate -90 1 0 0 Material "matte" "color Kd" [0.1 0.9 0.1] Shape "sphere" "float radius" [.75] AttributeEnd #define a cone AttributeBegin Translate 0 0 2.5 Rotate -90 1 0 0 #this describes the material properties Material "matte" "color Kd" [0.9 0.1 0.1] #this is the shape Shape "cone" "float radius" [.75] "float height" [2] AttributeEnd AttributeEnd
如上有三对AttributeBegin/AttributeEnd,刚好也有三组仿射变换Translate/Rotate。
第二组Translate/Rotate只作用于第二对AttributeBegin/AttributeEnd包住的sphere;
第三组Translate/Rotate只作用于第三对AttributeBegin/AttributeEnd包住的cone;
由于第一对AttributeBegin/AttributeEnd包住的是sphere和cone,所以,第一组Translate/Rotate对sphere和cone都起作用。
4,对关键字后参数的解析
4.1 关键字后直接跟参数值,直接解析,处理函数形参中顺序对应这些参数。
举例4:LookAt 1 1 10 0 0 -1 0 1 0 #ex ey ez lx ly lz ux uy uz
处理函数原型:
void pbrtLookAt(Float ex, Float ey, Float ez, Float lx, Float ly, Float lz, Float ux, Float uy, Float uz);
举例5:
Translate 0 -1 0 Rotate 35 0 1 0
处理函数原型:
void pbrtTranslate(Float dx, Float dy, Float dz); void pbrtRotate(Float angle, Float ax, Float ay, Float az);
4.2 关键字后跟:类型+属性,其中“属性”的格式是:“属性类型 属性名称”[该名称属性对应的值]
“属性”的格式类似于:float num = 3.0;
即:类型+变量+赋值
举例6:
Film "image" "string filename" ["sphere.exr"] "integer xresolution" [200] "integer yresolution" [200]
解析:
关键字是Film;
这个Film的类型是image;
这个image类型的Film有三个属性:
属性一:有个string类型的属性叫做filename,这个filename的值是“sphere.exr”
属性二:有个integer类型的属性叫做xresolution,这个xresolution的值为200
属性三:有个integer类型的属性叫做yresolution,这个yresolution的值为200
处理函数原型:
void pbrtFilm(const std::string &type, const ParamSet ¶ms);
举例7:
LightSource "distant" "point from" [0 0 0] "point to" [0 0 1] "color L" [3 3 3]
解析:
关键字是LightSource;
这个LightSource的类型是distant;
这个distant类型的LightSource有三个属性:
属性一:有个point类型的属性叫做from,这个from的值为[0 0 0]
属性二:有个point类型的属性叫做to,这个to的值为[0 0 1]
属性三:有个color类型的属性叫做L,这个L的值为[3 3 3]
处理函数原型:
void pbrtLightSource(const std::string &name, const ParamSet ¶ms);
举例8:
Material "uber" "color Kd" [0.1 0.1 0.9] "color Kr" [0.9 0.9 0.9] "color Ks" [0.1 0.1 0.1] "float roughness" [0.9] "float index" [1.34]
解析:
关键字是Material;
这个Material的类型是uber;
这个uber类型的Material有五个属性:
属性一:有个color类型的属性叫做Kd,这个Kd的值为[0.1 0.1 0.9]
属性二:有个color类型的属性叫做Kr,这个Kr的值为[0.9 0.9 0.9]
属性三:有个color类型的属性叫做Ks,这个Ks的值为[0.1 0.1 0.1]
属性四:有个float类型的属性叫做roughness,这个roughness的值为0.9
属性五:有个float类型的属性叫做index,这个index的值为1.34
处理函数原型:
void pbrtMaterial(const std::string &name, const ParamSet ¶ms);
4.3 关键字后跟:类型+X+X+……+属性
这种情况和4.2比较类似,只是在“类型”、“属性”之间加了若干其他信息。举例9:
Texture "grid" "color" "imagemap" "string filename" ["textures/lines.png"]
处理函数原型:
void pbrtTexture(const std::string &name, const std::string &type, const std::string &texname, const ParamSet ¶ms);
其实,所有情况都可以归到“X+X+X+属性”。
只是,
有的只有X+X+X;
有的X多;
有的X少;
不管是哪种情况,最终都是取决于“处理函数原型”。
———————————————-
“.pbrt格式解析”完毕!!!!!接下来的内容是“程序对.pbrt文件的处理过程”
———————————————-
参考:Q111:PBRT-V3系统概述5,程序中解析的过程
PBRT-V3是用第三方程序flex、bison来解析场景描述文件的。接下来,小编以解析LightSource为例说明一下从开始解析到存储数据的过程。
举例10:
LightSource "distant" "point from" [0 0 0] "point to" [0 0 1] "color L" [3 3 3]
在pbrtparse.y中有:
| LIGHTSOURCE STRING paramlist { pbrt::ParamSet params; pbrt::InitParamSet(params, pbrt::SpectrumType::Illuminant); pbrt::pbrtLightSource($2, params); pbrt::FreeArgs(); }
这个是告诉flex、bison在读到关键字“LightSource”时要做的事情。
先看这句:
pbrt::pbrtLightSource($2, params);
这句是最终调用处理函数pbrtLightSource(),给该函数传了两个参数:
$2,存放的是LightSource的类型名,此处即为“distant”;
params,存放的是这个distant类型的LightSource的所有属性。
5.1 初始化params
存放属性的params是一个ParamSet对象。ParamSet是在src/core/paramset.h中定义的。class ParamSet { public: // ParamSet Public Methods ParamSet() {} void AddFloat(const std::string &, std::unique_ptr<Float[]> v, int nValues = 1); void AddInt(const std::string &, std::unique_ptr<int[]> v, int nValues); void AddBool(const std::string &, std::unique_ptr<bool[]> v, int nValues); void AddPoint2f(const std::string &, std::unique_ptr<Point2f[]> v, int nValues); void AddVector2f(const std::string &, std::unique_ptr<Vector2f[]> v, int nValues); void AddPoint3f(const std::string &, std::unique_ptr<Point3f[]> v, int nValues); void AddVector3f(const std::string &, std::unique_ptr<Vector3f[]> v, int nValues); void AddNormal3f(const std::string &, std::unique_ptr<Normal3f[]> v, int nValues); void AddString(const std::string &, std::unique_ptr<std::string[]> v, int nValues); void AddTexture(const std::string &, const std::string &); void AddRGBSpectrum(const std::string &, std::unique_ptr<Float[]> v, int nValues); void AddXYZSpectrum(const std::string &, std::unique_ptr<Float[]> v, int nValues); void AddBlackbodySpectrum(const std::string &, std::unique_ptr<Float[]> v, int nValues); void AddSampledSpectrumFiles(const std::string &, const char **, int nValues); void AddSampledSpectrum(const std::string &, std::unique_ptr<Float[]> v, int nValues); bool EraseInt(const std::string &); bool EraseBool(const std::string &); bool EraseFloat(const std::string &); bool ErasePoint2f(const std::string &); bool EraseVector2f(const std::string &); bool ErasePoint3f(const std::string &); bool EraseVector3f(const std::string &); bool EraseNormal3f(const std::string &); bool EraseSpectrum(const std::string &); bool EraseString(const std::string &); bool EraseTexture(const std::string &); Float FindOneFloat(const std::string &, Float d) const; int FindOneInt(const std::string &, int d) const; bool FindOneBool(const std::string &, bool d) const; Point2f FindOnePoint2f(const std::string &, const Point2f &d) const; Vector2f FindOneVector2f(const std::string &, const Vector2f &d) const; Point3f FindOnePoint3f(const std::string &, const Point3f &d) const; Vector3f FindOneVector3f(const std::string &, const Vector3f &d) const; Normal3f FindOneNormal3f(const std::string &, const Normal3f &d) const; Spectrum FindOneSpectrum(const std::string &, const Spectrum &d) const; std::string FindOneString(const std::string &, const std::string &d) const; std::string FindOneFilename(const std::string &, const std::string &d) const; std::string FindTexture(const std::string &) const; const Float *FindFloat(const std::string &, int *n) const; const int *FindInt(const std::string &, int *nValues) const; const bool *FindBool(const std::string &, int *nValues) const; const Point2f *FindPoint2f(const std::string &, int *nValues) const; const Vector2f *FindVector2f(const std::string &, int *nValues) const; const Point3f *FindPoint3f(const std::string &, int *nValues) const; const Vector3f *FindVector3f(const std::string &, int *nValues) const; const Normal3f *FindNormal3f(const std::string &, int *nValues) const; const Spectrum *FindSpectrum(const std::string &, int *nValues) const; const std::string *FindString(const std::string &, int *nValues) const; void ReportUnused() const; void Clear(); std::string ToString() const; void Print(int indent) const; private: // ParamSet Private Data std::vector<std::shared_ptr<ParamSetItem<bool>>> bools; std::vector<std::shared_ptr<ParamSetItem<int>>> ints; std::vector<std::shared_ptr<ParamSetItem<Float>>> floats; std::vector<std::shared_ptr<ParamSetItem<Point2f>>> point2fs; std::vector<std::shared_ptr<ParamSetItem<Vector2f>>> vector2fs; std::vector<std::shared_ptr<ParamSetItem<Point3f>>> point3fs; std::vector<std::shared_ptr<ParamSetItem<Vector3f>>> vector3fs; std::vector<std::shared_ptr<ParamSetItem<Normal3f>>> normals; std::vector<std::shared_ptr<ParamSetItem<Spectrum>>> spectra; std::vector<std::shared_ptr<ParamSetItem<std::string>>> strings; std::vector<std::shared_ptr<ParamSetItem<std::string>>> textures; static std::map<std::string, Spectrum> cachedSpectra; };
ParamSet的定义看起来蛮复杂的,其实就干四件事(有点类似于数据库及对数据的CRUD):
> 其一:定义了存放各种属性类型数据的容器;比如:bools, ints, floats, spectra, strings等等。 > 其二:对各类容器进行添加数据; > 其三:对各类容器进行删除数据; > 其四:在各类容器查找数据;(这里注意一下,是根据“属性名称”进行查找的)
接下来,看看具体是怎么为params填充数据的,调用的是这个函数:
pbrt::InitParamSet(params, pbrt::SpectrumType::Illuminant);
在partparse.y中有这么一段code,“定义”了所有“属性类型”:
TRY_DECODING_TYPE("float", PARAM_TYPE_FLOAT) else TRY_DECODING_TYPE("integer", PARAM_TYPE_INT) else TRY_DECODING_TYPE("bool", PARAM_TYPE_BOOL) else TRY_DECODING_TYPE("point2", PARAM_TYPE_POINT2) else TRY_DECODING_TYPE("vector2", PARAM_TYPE_VECTOR2) else TRY_DECODING_TYPE("point3", PARAM_TYPE_POINT3) else TRY_DECODING_TYPE("vector3", PARAM_TYPE_VECTOR3) else TRY_DECODING_TYPE("point", PARAM_TYPE_POINT3) else TRY_DECODING_TYPE("vector", PARAM_TYPE_VECTOR3) else TRY_DECODING_TYPE("normal", PARAM_TYPE_NORMAL) else TRY_DECODING_TYPE("string", PARAM_TYPE_STRING) else TRY_DECODING_TYPE("texture", PARAM_TYPE_TEXTURE) else TRY_DECODING_TYPE("color", PARAM_TYPE_RGB) else TRY_DECODING_TYPE("rgb", PARAM_TYPE_RGB) else TRY_DECODING_TYPE("xyz", PARAM_TYPE_XYZ) else TRY_DECODING_TYPE("blackbody", PARAM_TYPE_BLACKBODY) else TRY_DECODING_TYPE("spectrum", PARAM_TYPE_SPECTRUM)
然后,在InitParamSet()中会根据属性类型调用ParamSet对应的成员函数来给params填充数据。
对于:
LightSource "distant" "point from" [0 0 0] "point to" [0 0 1] "color L" [3 3 3]
就会调用到:
void AddPoint3f(const std::string &, std::unique_ptr<Point3f[]> v, int nValues); void AddRGBSpectrum(const std::string &, std::unique_ptr<Float[]> v, int nValues);
具体实现在paramset.cpp中:
void ParamSet::AddPoint3f(const std::string &name, std::unique_ptr<Point3f[]> values, int nValues) { ErasePoint3f(name); ADD_PARAM_TYPE(Point3f, point3fs); }
void ParamSet::AddRGBSpectrum(const std::string &name, std::unique_ptr<Float[]> values, int nValues) { EraseSpectrum(name); CHECK_EQ(nValues % 3, 0); nValues /= 3; std::unique_ptr<Spectrum[]> s(new Spectrum[nValues]); for (int i = 0; i < nValues; ++i) s[i] = Spectrum::FromRGB(&values[3 * i]); std::shared_ptr<ParamSetItem<Spectrum>> psi( new ParamSetItem<Spectrum>(name, std::move(s), nValues)); spectra.push_back(psi); }
5.2 pbrtLightSource()
pbrtLightSource将数据写入渲染内存中。void pbrtLightSource(const std::string &name, const ParamSet ¶ms) { VERIFY_WORLD("LightSource"); WARN_IF_ANIMATED_TRANSFORM("LightSource"); MediumInterface mi = graphicsState.CreateMediumInterface(); std::shared_ptr<Light> lt = MakeLight(name, params, curTransform[0], mi); if (!lt) Error("LightSource: light type \"%s\" unknown.", name.c_str()); else renderOptions->lights.push_back(lt); if (PbrtOptions.cat || PbrtOptions.toPly) { printf("%*sLightSource \"%s\" ", catIndentCount, "", name.c_str()); params.Print(catIndentCount); printf("\n"); } }
这个函数在src/core/api.cpp中。
完成将LightSource的数据写入renderOptions->lights中。
渲染相关的数据都是存在renderOptions中的。
renderOptions的类是长这个样子:
struct RenderOptions { // RenderOptions Public Methods Integrator *MakeIntegrator() const; Scene *MakeScene(); Camera *MakeCamera() const; // RenderOptions Public Data Float transformStartTime = 0, transformEndTime = 1; std::string FilterName = "box"; ParamSet FilterParams; std::string FilmName = "image"; ParamSet FilmParams; std::string SamplerName = "halton"; ParamSet SamplerParams; std::string AcceleratorName = "bvh"; ParamSet AcceleratorParams; std::string IntegratorName = "path"; ParamSet IntegratorParams; std::string CameraName = "perspective"; ParamSet CameraParams; TransformSet CameraToWorld; std::map<std::string, std::shared_ptr<Medium>> namedMedia; std::vector<std::shared_ptr<Light>> lights; std::vector<std::shared_ptr<Primitive>> primitives; std::map<std::string, std::vector<std::shared_ptr<Primitive>>> instances; std::vector<std::shared_ptr<Primitive>> *currentInstance = nullptr; bool haveScatteringMedia = false; };
简单说来,
程序外,描述场景的是.pbrt格式的文件;
程序内,描述场景的是RenderOptions对象;
另外,注意一下,对于LightSource,pbrtLightSource()直接调用了MakeLight()创建了LightSource对象。
所以,renderOptions->lights中保存的是LightSource对象的指针。
而像RenderOptions中的这些:
std::string FilterName = "box"; ParamSet FilterParams; std::string FilmName = "image"; ParamSet FilmParams; std::string SamplerName = "halton"; ParamSet SamplerParams; std::string AcceleratorName = "bvh"; ParamSet AcceleratorParams; std::string IntegratorName = "path"; ParamSet IntegratorParams; std::string CameraName = "perspective"; ParamSet CameraParams; TransformSet CameraToWorld;
对应的“pbrt关键字”函数中只是将相关属性参数存入RenderOptions的对应成员变量中。
注意到:
在RenderOptions类定义的前面有几个成员方法:
Integrator *MakeIntegrator() const; Scene *MakeScene(); Camera *MakeCamera() const;
这个就是用来创建那些还没有在对应“pbrt关键字”函数中创建对象的类的对象的。
6,pbrtWorldEnd()
最后处理关键字“WorldEnd”。在pbrtWorldEnd()有非常重要的三行代码:
std::unique_ptr<Integrator> integrator(renderOptions->MakeIntegrator()); std::unique_ptr<Scene> scene(renderOptions->MakeScene()); if (scene && integrator) integrator->Render(*scene);
1,MakeIntegrator()
2,MakeScene()
3,integrator->Render(*scene)
即:
创建积分器;创建场景;用积分器渲染场景;
重点提一下MakeScene()。
创建Scene对象。
之前小编一直搞不清RenderOption对象和Scene对象的关系:
RenderOption侧重数据,直接和场景描述对应;
Scene是将RenderOption的数据再封装了一下,同时加入了和渲染相关的方法。
贴出Scene的定义代码:
// Scene Declarations class Scene { public: // Scene Public Methods Scene(std::shared_ptr<Primitive> aggregate, const std::vector<std::shared_ptr<Light>> &lights) : lights(lights), aggregate(aggregate) { // Scene Constructor Implementation worldBound = aggregate->WorldBound(); for (const auto &light : lights) { light->Preprocess(*this); if (light->flags & (int)LightFlags::Infinite) infiniteLights.push_back(light); } } const Bounds3f &WorldBound() const { return worldBound; } bool Intersect(const Ray &ray, SurfaceInteraction *isect) const; bool IntersectP(const Ray &ray) const; bool IntersectTr(Ray ray, Sampler &sampler, SurfaceInteraction *isect, Spectrum *transmittance) const; // Scene Public Data std::vector<std::shared_ptr<Light>> lights; // Store infinite light sources separately for cases where we only want // to loop over them. std::vector<std::shared_ptr<Light>> infiniteLights; private: // Scene Private Data std::shared_ptr<Primitive> aggregate; Bounds3f worldBound; };
场景描述文件解析结束时得到两样东西:
一个积分器对象;
一个场景对象;
然后,开始用“积分器对象”渲染“场景对象”。
相关文章推荐
- MP3文件格式解析
- 在VS.NET2005中使用java代码段以及SOL文件格式的解析
- ActiveBPEL引擎文件格式描述
- Windows快捷方式文件格式解析
- Linux操作系统的可执行文件格式详细解析
- vCalendar文件格式解析
- 解析Java的Class文件格式——解析魔数和版本号
- PLY文件格式及其解析
- java class文件格式解析
- 【转】Windows快捷方式文件格式解析(中文)
- 解析Java的Class文件格式——解析魔数和版本号(一)
- Windows快捷方式文件格式解析
- Windows快捷方式文件格式解析
- Java 的 Class 文件格式——解析魔数和版本号
- (原创)bls文件格式的解析
- MP3文件格式解析
- activebpel所用文件格式描述
- activebpel所用文件格式描述
- JPEG文件格式解析
- 解析Java的Class文件格式——解析魔数和版本号