所谓ply文件格式,是由斯坦福大学开发的一套三维mesh模型数据格式,图形学领域内很多著名的模型数据,比如斯坦福的三维扫描数据库(其中包括很多文章中会见到的Happy Buddha, Dragon, Bunny兔子)最初的模型都是基于这个格式的。其开发目标是,建立一套针对多边形模型的,结构简单但是能够满足大多数图形应用需要的模型格式,而且它允许以ASCII码格式或二进制形式存储文件。这样一套既简单又灵活的文件格式,能够帮助开发人员避免重复开发文件格式的问题。尽管在工业领域内,新的文件格式仍然在不断的出现,但是在图形学的研究领域中,PLY还是种常用且重要的文件格式。
ply数据文件格式中包含用来描述多边形的一系列点的信息:
<顶点列表>
<面片列表>
<其他元素列表>
头部是一系列以回车结尾的文本行,用来描述文件的剩余部分。头部包含一个对每个元素类型的描述,包括元素名(如“边”),这个元素在工程里有多少,以及一个与这个元素关联的不同属性的列表。头部还说明这个文件是二进制的或者是ASCII的。头部后面的是一个每个元素类型的元素列表,按照在头部中描述的顺序出现。
以下是一个立方体的完整ASCII描述。相同工程的二进制版本头部的唯一不同是用词“binary_little_endian”或者“binary_big_endian”替换词“ascii”。大括号中的注释不是文件的一部分,它们是这个例子的注解。文件中的注释一般在“comment”开始的关键词。
format ascii 1.0;
comment made by anonymous;
/*comment this is a cube*/
element vertex 8;
property float32 x ;
property float32 y ;
property float32 z ;
element face 6 ;
property list uint8 int32 vertex_index ;
end_header;
/*list vertex*/
0 0 0
0 0 1
0 1 1
0 1 0
1 0 0
1 0 1
1 1 1
1 1 0
/*list face*/
4 0 1 2 3
4 7 6 5 4
4 0 4 5 1
4 1 5 6 2
4 2 6 7 3
4 3 7 4 0
这个例子说明头部的基本组成。头部的每个部分都是以一个关键词开头以回车结尾的ASCII串。即使是头部的开始和结尾(“ply”和“end_header”)也是以这种形式。因为字符“ply”是文件的魔法数字,必须是文件的头四个字符。跟在文件头部开头之后的是关键词“format”和一个特定的ASCII或者二进制的格式,接下来是一个版本号。再下面是多边形文件中每个元素的描述,在每个元素里还有多属性的说明。一般元素以下面的格式描述:
element <元素名> <在文件中的个数>
property <数据类型> <属性名-1>
property <数据类型> <属性名-2>
property <数据类型> <属性名-3>
...
属性罗列在“element”(元素)行后面定义,既包含属性的数据类型也包含属性在每个元素中出现的次序。一个属性可以有三种数据类型:标量,字符串和列表。属性可能具有的标量数据类型列表如下:
int8 字符 1
uint8 非负字符 1
int16 短整型 2
uint16 非负短整型 2
int32 整型 4
uint32 非负整型 4
float32 单精度浮点数 4
float64 双精度浮点数 8
这些字节计数很重要,而且在实现过程中不能修改以使这些文件可移植。使用列表数据类型的属性定义有一种特殊的格式:
property list <数值类型> <数值类型> <属性名>
这种格式的一类例子是上面的立方体文件中的property list uint8 int32 vertex_index,这表示属性“vertex_index”首先包含一个非负字符报苏在属性里包含多少索引,接下来是一个列表包含许多整数。在这个边长列表里的每个整数都是一个顶点的索引。
以下是一个基于C++的读入ASCII文件或二进制文件,输出ply文件的程序。首先定义头文件ply.h
/*
Header for PLY polygon files.
- Greg Turk, March 1994
A PLY file contains a single polygonal _object_.
An object is composed of lists of _elements_. Typical elements are
vertices, faces, edges and materials.
Each type of element for a given object has one or more _properties_
associated with the element type. For instance, a vertex element may
have as properties three floating-point values x,y,z and three unsigned
chars for red, green and blue.
*/
#ifndef __PLY_H__
#define __PLY_H__
#ifdef __cplusplus
extern "C" {
#endif
#include
#include
#define PLY_ASCII 1 /* ascii PLY file */
#define PLY_BINARY_BE 2 /* binary PLY file, big endian */
#define PLY_BINARY_LE 3 /* binary PLY file, little endian */
#define PLY_OKAY 0 /* ply routine worked okay */
#define PLY_ERROR -1 /* error in ply routine */
/* scalar data types supported by PLY format */
#define PLY_START_TYPE 0
#define PLY_CHAR 1
#define PLY_SHORT 2
#define PLY_INT 3
#define PLY_UCHAR 4
#define PLY_USHORT 5
#define PLY_UINT 6
#define PLY_FLOAT 7
#define PLY_DOUBLE 8
#define PLY_END_TYPE 9
#define PLY_SCALAR 0
#define PLY_LIST 1
typedef struct PlyProperty { /* description of a property */
char *name; /* property name */
int external_type; /* file's data type */
int internal_type; /* program's data type */
int offset; /* offset bytes of prop in a struct */
int is_list; /* 1 = list, 0 = scalar */
int count_external; /* file's count type */
int count_internal; /* program's count type */
int count_offset; /* offset byte for list count */
} PlyProperty;
typedef struct PlyElement { /* description of an element */
char *name; /* element name */
int num; /* number of elements in this object */
int size; /* size of element (bytes) or -1 if variable */
int nprops; /* number of properties for this element */
PlyProperty **props; /* list of properties in the file */
char *store_prop; /* flags: property wanted by user? */
int other_offset; /* offset to un-asked-for props, or -1 if none*/
int other_size; /* size of other_props structure */
} PlyElement;
typedef struct PlyOtherProp { /* describes other properties in an element */
char *name; /* element name */
int size; /* size of other_props */
int nprops; /* number of properties in other_props */
PlyProperty **props; /* list of properties in other_props */
} PlyOtherProp;
typedef struct OtherData { /* for storing other_props for an other element */
void *other_props;
} OtherData;
typedef struct OtherElem { /* data for one "other" element */
char *elem_name; /* names of other elements */
int elem_count; /* count of instances of each element */
OtherData **other_data; /* actual property data for the elements */
PlyOtherProp *other_props; /* description of the property data */
} OtherElem;
typedef struct PlyOtherElems { /* "other" elements, not interpreted by user */
int num_elems; /* number of other elements */
OtherElem *other_list; /* list of data for other elements */
} PlyOtherElems;
typedef struct PlyFile { /* description of PLY file */
FILE *fp; /* file pointer */
int file_type; /* ascii or binary */
float version; /* version number of file */
int nelems; /* number of elements of object */
PlyElement **elems; /* list of elements */
int num_comments; /* number of comments */
char **comments; /* list of comments */
int num_obj_info; /* number of items of object information */
char **obj_info; /* list of object info items */
PlyElement *which_elem; /* which element we're currently writing */
PlyOtherElems *other_elems; /* "other" elements from a PLY file */
} PlyFile;
/* memory allocation */
extern char *my_alloc();
#define myalloc(mem_size) my_alloc((mem_size), __LINE__, __FILE__)
/*** delcaration of routines ***/
extern PlyFile *ply_write(FILE *, int, char **, int);
extern PlyFile *ply_open_for_writing(char *, int, char **, int, float *);
extern void ply_describe_element(PlyFile *, char *, int, int, PlyProperty *);
extern void ply_describe_property(PlyFile *, char *, PlyProperty *);
extern void ply_element_count(PlyFile *, char *, int);
extern void ply_header_complete(PlyFile *);
extern void ply_put_element_setup(PlyFile *, char *);
extern void ply_put_element(PlyFile *, void *);
extern void ply_put_comment(PlyFile *, char *);
extern void ply_put_obj_info(PlyFile *, char *);
extern PlyFile *ply_read(FILE *, int *, char ***);
extern PlyFile *ply_open_for_reading( char *, int *, char ***, int *, float *);
extern PlyProperty **ply_get_element_description(PlyFile *, char *, int*, int*);
extern void ply_get_element_setup( PlyFile *, char *, int, PlyProperty *);
extern void ply_get_property(PlyFile *, char *, PlyProperty *);
extern PlyOtherProp *ply_get_other_properties(PlyFile *, char *, int);
extern ply_get_element(PlyFile *, void *);
extern char **ply_get_comments(PlyFile *, int *);
extern char **ply_get_obj_info(PlyFile *, int *);
extern void ply_close(PlyFile *);
extern void ply_get_info(PlyFile *, float *, int *);
extern PlyOtherElems *ply_get_other_element (PlyFile *, char *, int);
extern void ply_describe_other_elements ( PlyFile *, PlyOtherElems *);
extern void ply_put_other_elements (PlyFile *);
extern void ply_free_other_elements (PlyOtherElems *);
#ifdef __cplusplus
}
#endif
#endif /* !__PLY_H__ */
Part 1 preparation
/*
The interface routines for reading and writing PLY polygon files.
Greg Turk, February 1994
---------------------------------------------------------------
A PLY file contains a single polygonal _object_.
An object is composed of lists of _elements_. Typical elements are
vertices, faces, edges and materials.
Each type of element for a given object has one or more _properties_
associated with the element type. For instance, a vertex element may
have as properties the floating-point values x,y,z and the three unsigned
chars representing red, green and blue.
*/
#include
#include
#include
#include
#include
#include
#include "ply.h" #include
using namespace std; char *type_names[] = { "invalid", "char", "short", "int", "uchar", "ushort", "uint", "float", "double", }; int ply_type_size[] = { 0, 1, 2, 4, 1, 2, 4, 4, 8 }; #define NO_OTHER_PROPS -1 #define DONT_STORE_PROP 0 #define STORE_PROP 1 #define OTHER_PROP 0 #define NAMED_PROP 1 /* returns 1 if strings are equal, 0 if not */ /* find an element in a plyfile's list */ PlyElement *find_element(PlyFile *, char *); /* find a property in an element's list */ PlyProperty *find_property(PlyElement *, char *, int *); /* write to a file the word describing a PLY file data type */ void write_scalar_type (FILE *, int); /* read a line from a file and break it up into separate words */ char **get_words(FILE *, int *, char **); char **old_get_words(FILE *, int *); /* write an item to a file */ void write_binary_item(FILE *, int, unsigned int, double, int); void write_ascii_item(FILE *, int, unsigned int, double, int); double old_write_ascii_item(FILE *, char *, int); /* add information to a PLY file descriptor */ void add_element(PlyFile *, char **, int); void add_property(PlyFile *, char **, int); void add_comment(PlyFile *, char *); void add_obj_info(PlyFile *, char *); /* copy a property */ void copy_property(PlyProperty *, PlyProperty *); /* store a value into where a pointer and a type specify */ void store_item(char *, int, int, unsigned int, double); /* return the value of a stored item */ void get_stored_item( void *, int, int *, unsigned int *, double *); /* return the value stored in an item, given ptr to it and its type */ double get_item_value(char *, int); /* get binary or ascii item and store it according to ptr and type */ void get_ascii_item(char *, int, int *, unsigned int *, double *); void get_binary_item(FILE *, int, int *, unsigned int *, double *); /* get a bunch of elements from a file */ void ascii_get_element(PlyFile *, char *); void binary_get_element(PlyFile *, char *); /* memory allocation */ char *my_alloc(int, int, char *); /*************/ /* Writing */ /*************/ /****************************************************************************** Given a file pointer, get ready to write PLY data to the file. Entry: fp - the given file pointer nelems - number of elements in object elem_names - list of element names file_type - file type, either ascii or binary Exit: returns a pointer to a PlyFile, used to refer to this file, or NULL if error ******************************************************************************/ PlyFile *ply_write( FILE *fp, int nelems, char **elem_names, int file_type ) { int i; PlyFile *plyfile; PlyElement *elem; /* check for NULL file pointer */ if (fp == NULL) return (NULL); /* create a record for this object */ plyfile = (PlyFile *) myalloc (sizeof (PlyFile)); plyfile->file_type = file_type; plyfile->num_comments = 0; plyfile->num_obj_info = 0; plyfile->nelems = nelems; plyfile->version = 1.0; plyfile->fp = fp; plyfile->other_elems = NULL; /* tuck aside the names of the elements */ plyfile->elems = (PlyElement **) myalloc (sizeof (PlyElement *) * nelems); for (i = 0; i < nelems; i++) { elem = (PlyElement *) myalloc (sizeof (PlyElement)); plyfile->elems[i] = elem; elem->name = strdup (elem_names[i]); elem->num = 0; elem->nprops = 0; } /* return pointer to the file descriptor */ return (plyfile); } /****************************************************************************** Open a polygon file for writing. Entry: filename - name of file to read from nelems - number of elements in object elem_names - list of element names file_type - file type, either ascii or binary Exit: version - version number of PLY file returns a file identifier, used to refer to this file, or NULL if error ******************************************************************************/ PlyFile *ply_open_for_writing( char *filename, int nelems, char **elem_names, int file_type, float *version ) { int i; PlyFile *plyfile; PlyElement *elem; char *name; FILE *fp; /* tack on the extension .ply, if necessary */ name = (char *) myalloc (sizeof (char) * (strlen (filename) + 5)); strcpy (name, filename); if (strlen (name) < 4 || strcmp (name + strlen (name) - 4, ".ply") != 0) strcat (name, ".ply"); /* open the file for writing */ fp = inFile (name, "w"); if (fp == NULL) { return (NULL); } /* create the actual PlyFile structure */ plyfile = ply_write (fp, nelems, elem_names, file_type); if (plyfile == NULL) return (NULL); /* say what PLY file version number we're writing */ *version = plyfile->version; /* return pointer to the file descriptor */ return (plyfile); }
Part 2 description
/******************************************************************************
Describe an element, including its properties and how many will be written
to the file.
Entry:
plyfile - file identifier
elem_name - name of element that information is being specified about
nelems - number of elements of this type to be written
nprops - number of properties contained in the element
prop_list - list of properties
******************************************************************************/
void ply_describe_element(
PlyFile *plyfile,
char *elem_name,
int nelems,
int nprops,
PlyProperty *prop_list
)
{
int i;
PlyElement *elem;
PlyProperty *prop;
/* look for appropriate element */
elem = find_element (plyfile, elem_name);
if (elem == NULL) {
cerr << " ply_describe_element: can't find element" << elem_name << endl;
exit (-1);
}
elem->num = nelems;
/* copy the list of properties */
elem->nprops = nprops;
elem->props = (PlyProperty **) myalloc (sizeof (PlyProperty *) * nprops);
elem->store_prop = (char *) myalloc (sizeof (char) * nprops);
for (i = 0; i < nprops; i++) {
prop = (PlyProperty *) myalloc (sizeof (PlyProperty));
elem->props[i] = prop;
elem->store_prop[i] = NAMED_PROP;
copy_property (prop, &prop_list[i]);
}
}
/******************************************************************************
Describe a property of an element.
Entry:
plyfile - file identifier
elem_name - name of element that information is being specified about
prop - the new property
******************************************************************************/
void ply_describe_property(
PlyFile *plyfile,
ch