2024-01-11 12:09:54 -07:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <ply.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
2024-01-11 12:18:52 -07:00
|
|
|
const char* const ply_formats[] = {
|
|
|
|
"ascii",
|
|
|
|
"binary_little_endian",
|
|
|
|
"binary_big_endian"
|
|
|
|
};
|
|
|
|
|
|
|
|
const char* const ply_header_items[] = {
|
|
|
|
"comment",
|
|
|
|
"end_header",
|
|
|
|
"element",
|
|
|
|
"property",
|
|
|
|
};
|
|
|
|
|
|
|
|
const char* const ply_type_strings[] = {
|
|
|
|
"char",
|
|
|
|
"uchar",
|
|
|
|
"short",
|
|
|
|
"ushort",
|
|
|
|
"int",
|
|
|
|
"uint",
|
|
|
|
"float",
|
|
|
|
"double",
|
|
|
|
};
|
|
|
|
|
|
|
|
const size_t ply_type_sizes[] = {
|
|
|
|
1,
|
|
|
|
1,
|
|
|
|
2,
|
|
|
|
2,
|
|
|
|
4,
|
|
|
|
4,
|
|
|
|
4,
|
|
|
|
8,
|
|
|
|
};
|
|
|
|
|
2024-01-11 12:09:54 -07:00
|
|
|
PlyPropertyType ply_parse_type(const char* type_string) {
|
|
|
|
for(uint32_t i = 0; i < sizeof(ply_type_strings)/sizeof(char*); i++) {
|
|
|
|
if(strcmp(ply_type_strings[i], type_string) == 0) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return PLY_TYPE_INVALID;
|
|
|
|
}
|
|
|
|
|
2024-01-11 16:16:10 -07:00
|
|
|
void ply_free_elements(PlyElement* elements_head) {
|
2024-01-11 12:09:54 -07:00
|
|
|
PlyElement* elem = elements_head;
|
|
|
|
while(elem != NULL) {
|
|
|
|
PlyProperty* prop = elem->properties;
|
|
|
|
while(prop != NULL) {
|
|
|
|
if(prop->count == PLY_TYPE_INVALID) {
|
|
|
|
if(prop->elements != NULL) {
|
|
|
|
free(prop->elements);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if(prop->elements != NULL) {
|
|
|
|
void** lists = prop->elements;
|
|
|
|
for(int i = 0; i < elem->count; i++) {
|
|
|
|
if(lists[i] != NULL) {
|
|
|
|
free(lists[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(prop->elements);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(prop->counts != NULL) {
|
|
|
|
free(prop->counts);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(prop->name != NULL) {
|
|
|
|
free(prop->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PlyProperty* last = prop;
|
|
|
|
prop = prop->next;
|
|
|
|
free(last);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(elem->name != NULL) {
|
|
|
|
free(elem->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
PlyElement* last = elem;
|
|
|
|
elem = elem->next;
|
|
|
|
free(last);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-11 21:45:30 -07:00
|
|
|
PlyMappings default_ply_mappings = {
|
|
|
|
.vertex_element = "vertex",
|
2024-01-13 02:24:35 -07:00
|
|
|
.position = {"x", "y", "z"},
|
|
|
|
.normal = {"nx", "ny", "nz"},
|
|
|
|
.colour = {"red", "green", "blue", "alpha"},
|
|
|
|
.texture = {"s", "t"},
|
2024-01-11 21:45:30 -07:00
|
|
|
.face_element = "face",
|
|
|
|
.index = "vetex_indices",
|
|
|
|
};
|
|
|
|
|
2024-01-13 02:24:35 -07:00
|
|
|
uint16_t ply_conv_index(PlyPropertyType type, void* value, int swap_order) {
|
|
|
|
if(type == PLY_TYPE_INVALID) {
|
|
|
|
return 0.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t* ptr = value;
|
|
|
|
uint8_t tmp[8];
|
|
|
|
size_t type_size = ply_type_sizes[type];
|
|
|
|
for(uint32_t i = 0; i < type_size; i++) {
|
|
|
|
if(swap_order) {
|
|
|
|
tmp[type_size-i] = ptr[i];
|
|
|
|
} else {
|
|
|
|
tmp[i] = ptr[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(type) {
|
|
|
|
case PLY_TYPE_CHAR:
|
|
|
|
return (uint16_t)*(int8_t*)(tmp);
|
|
|
|
case PLY_TYPE_UCHAR:
|
|
|
|
return (uint16_t)*(uint8_t*)(tmp);
|
|
|
|
case PLY_TYPE_SHORT:
|
|
|
|
return *(int16_t*)(tmp);
|
|
|
|
case PLY_TYPE_USHORT:
|
|
|
|
return (uint16_t)*(uint16_t*)(tmp);
|
|
|
|
case PLY_TYPE_INT:
|
|
|
|
return (uint16_t)*(int32_t*)(tmp);
|
|
|
|
case PLY_TYPE_UINT:
|
|
|
|
return (uint16_t)*(uint32_t*)(tmp);
|
|
|
|
default:
|
|
|
|
return 0.0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
float ply_conv_value(PlyPropertyType type, void* value, int swap_order) {
|
|
|
|
if(type == PLY_TYPE_INVALID) {
|
|
|
|
return 0.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t* ptr = value;
|
|
|
|
uint8_t tmp[8];
|
|
|
|
size_t type_size = ply_type_sizes[type];
|
|
|
|
for(uint32_t i = 0; i < type_size; i++) {
|
|
|
|
if(swap_order) {
|
|
|
|
tmp[type_size-i] = ptr[i];
|
|
|
|
} else {
|
|
|
|
tmp[i] = ptr[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(type) {
|
|
|
|
case PLY_TYPE_CHAR:
|
|
|
|
return (float)*(int8_t*)(tmp)/(float)INT8_MAX;
|
|
|
|
case PLY_TYPE_UCHAR:
|
|
|
|
return (float)*(uint8_t*)(tmp)/(float)UINT8_MAX;
|
|
|
|
case PLY_TYPE_SHORT:
|
|
|
|
return (float)*(int16_t*)(tmp)/(float)INT16_MAX;
|
|
|
|
case PLY_TYPE_USHORT:
|
|
|
|
return (float)*(uint16_t*)(tmp)/(float)UINT16_MAX;
|
|
|
|
case PLY_TYPE_INT:
|
|
|
|
return (float)*(int32_t*)(tmp)/(float)INT32_MAX;
|
|
|
|
case PLY_TYPE_UINT:
|
|
|
|
return (float)*(uint32_t*)(tmp)/(float)UINT32_MAX;
|
|
|
|
case PLY_TYPE_FLOAT:
|
|
|
|
return *(float*)(tmp);
|
|
|
|
case PLY_TYPE_DOUBLE:
|
|
|
|
return *(float*)(tmp);
|
|
|
|
default:
|
|
|
|
return 0.0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int ply_map_values(char* name, int stride, int count, PlyPropertyType type, int mapping_count, char** mapping, void* data, float* out) {
|
|
|
|
for(int i = 0; i < mapping_count; i++) {
|
|
|
|
if(strcmp(name, mapping[i]) == 0) {
|
|
|
|
for(int j = 0; j < count; j++) {
|
|
|
|
size_t type_size = ply_type_sizes[type];
|
|
|
|
out[(stride*j)+i] = ply_conv_value(type, data + (type_size*j), 0);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-01-11 21:45:30 -07:00
|
|
|
PlyMesh ply_load_mesh(char* filename, PlyMappings mappings) {
|
2024-01-11 16:16:10 -07:00
|
|
|
PlyMesh mesh = {
|
|
|
|
.vertex_count = 0,
|
|
|
|
.position = NULL,
|
|
|
|
.colour = NULL,
|
|
|
|
.uv = NULL,
|
|
|
|
.normal = NULL,
|
|
|
|
|
|
|
|
.index_count = 0,
|
|
|
|
.index = NULL,
|
|
|
|
};
|
|
|
|
|
2024-01-11 12:09:54 -07:00
|
|
|
FILE* ply = fopen(filename, "r");
|
|
|
|
if(ply == NULL) {
|
|
|
|
fprintf(stderr, "file not found %s\n", filename);
|
2024-01-11 16:16:10 -07:00
|
|
|
return mesh;
|
2024-01-11 12:09:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t buffer[1024] = {0};
|
|
|
|
char magic[] = {'p', 'l', 'y', '\n'};
|
|
|
|
int read = fread(buffer, 1, sizeof(magic), ply);
|
|
|
|
if(read != sizeof(magic)) {
|
|
|
|
fprintf(stderr, "magic not read\n");
|
|
|
|
goto close;
|
|
|
|
} else if (memcmp(buffer, magic, sizeof(magic)) != 0) {
|
|
|
|
fprintf(stderr, "magic not match\n");
|
|
|
|
goto close;
|
|
|
|
}
|
|
|
|
|
|
|
|
char* line = fgets((char*)buffer, 1024, ply);
|
|
|
|
if(line == NULL) {
|
|
|
|
fprintf(stderr, "file read err\n");
|
|
|
|
goto close;
|
|
|
|
}
|
|
|
|
|
|
|
|
char* format_str = "format ";
|
|
|
|
if(memcmp(format_str, line, 7) != 0) {
|
|
|
|
fprintf(stderr, "format start not found\n");
|
|
|
|
goto close;
|
|
|
|
}
|
|
|
|
|
|
|
|
char* format_start = &line[7];
|
|
|
|
PlyFormatType format_id = PLY_FORMAT_INVALID;
|
|
|
|
for(uint32_t i = 0; i < sizeof(ply_formats)/sizeof(char*); i++) {
|
|
|
|
if(memcmp(format_start, ply_formats[i], strlen(ply_formats[i])) == 0) {
|
|
|
|
format_id = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-11 12:12:07 -07:00
|
|
|
if(format_id != PLY_FORMAT_BE && format_id != PLY_FORMAT_LE) {
|
|
|
|
fprintf(stderr, "can only parse binary PLY files\n");
|
2024-01-11 12:09:54 -07:00
|
|
|
goto close;
|
|
|
|
}
|
|
|
|
|
|
|
|
PlyElement* elements_head = NULL;
|
|
|
|
PlyElement* element = NULL;
|
|
|
|
|
|
|
|
while(1) {
|
|
|
|
char* line = fgets((char*)buffer, 1024, ply);
|
|
|
|
PlyHeaderType header_id = PLY_HEADER_INVALID;
|
|
|
|
for(uint32_t i = 0; i < sizeof(ply_header_items)/sizeof(char*); i++) {
|
|
|
|
if(memcmp(line, ply_header_items[i], strlen(ply_header_items[i])) == 0) {
|
|
|
|
header_id = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(header_id == PLY_FORMAT_INVALID) {
|
|
|
|
fprintf(stderr, "unknown header line: %s\n", line);
|
2024-01-11 16:16:10 -07:00
|
|
|
goto clean;
|
2024-01-11 12:09:54 -07:00
|
|
|
} else if (header_id == PLY_HEADER_COMMENT) {
|
|
|
|
continue;
|
|
|
|
} else if (header_id == PLY_HEADER_END) {
|
|
|
|
break;
|
|
|
|
} else if (header_id == PLY_HEADER_ELEMENT) {
|
|
|
|
PlyElement* new_element = malloc(sizeof(PlyElement));
|
|
|
|
new_element->next = NULL;
|
|
|
|
new_element->properties = NULL;
|
|
|
|
|
|
|
|
char* first = strtok((char*)buffer, " ");
|
|
|
|
(void)first;
|
|
|
|
char* name = strtok(NULL, " ");
|
|
|
|
char* count = strtok(NULL, " ");
|
|
|
|
if(name == NULL || count == NULL) {
|
|
|
|
fprintf(stderr, "BAD_ELEMENT: %s\n", line);
|
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
unsigned long name_len = strlen(name) + 1;
|
|
|
|
new_element->name = malloc(name_len);
|
|
|
|
memcpy(new_element->name, name, name_len);
|
|
|
|
|
|
|
|
new_element->count = atoi(count);
|
|
|
|
|
|
|
|
if(elements_head == NULL) {
|
|
|
|
elements_head = new_element;
|
|
|
|
element = new_element;
|
|
|
|
} else {
|
|
|
|
element->next = new_element;
|
|
|
|
element = new_element;
|
|
|
|
}
|
|
|
|
} else if (header_id == PLY_HEADER_PROPERTY) {
|
|
|
|
if(element == NULL) {
|
|
|
|
fprintf(stderr, "parsed property before any element\n");
|
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
PlyProperty* new_property = malloc(sizeof(PlyProperty));
|
|
|
|
new_property->name = NULL;
|
|
|
|
new_property->count = PLY_TYPE_INVALID;
|
|
|
|
new_property->element = PLY_TYPE_INVALID;
|
|
|
|
new_property->next = NULL;
|
|
|
|
new_property->elements = NULL;
|
|
|
|
new_property->counts = NULL;
|
|
|
|
|
|
|
|
char* extra = strtok((char*)buffer, " ");
|
|
|
|
(void)extra;
|
|
|
|
|
|
|
|
char* type_str = strtok(NULL, " ");
|
|
|
|
if(strcmp(type_str, "list") == 0) {
|
|
|
|
type_str = strtok(NULL, " ");
|
|
|
|
new_property->count = ply_parse_type(type_str);
|
|
|
|
type_str = strtok(NULL, " ");
|
|
|
|
}
|
|
|
|
|
|
|
|
new_property->element = ply_parse_type(type_str);
|
|
|
|
|
|
|
|
type_str = strtok(NULL, " ");
|
|
|
|
char* name = strtok(type_str, "\n");
|
|
|
|
if(name == NULL) {
|
|
|
|
fprintf(stderr, "invalid property\n");
|
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned long name_len = strlen(name) + 1;
|
|
|
|
new_property->name = malloc(name_len);
|
|
|
|
memcpy(new_property->name, name, name_len);
|
|
|
|
|
|
|
|
PlyProperty* cur = element->properties;
|
|
|
|
if(cur == NULL) {
|
|
|
|
element->properties = new_property;
|
|
|
|
} else {
|
|
|
|
while(cur->next != NULL) {
|
|
|
|
cur = cur->next;
|
|
|
|
}
|
|
|
|
cur->next = new_property;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(line == NULL) {
|
2024-01-11 16:16:10 -07:00
|
|
|
goto clean;
|
2024-01-11 12:09:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PlyElement* elem = elements_head;
|
|
|
|
while(elem != NULL) {
|
|
|
|
PlyProperty* prop = elem->properties;
|
|
|
|
while(prop != NULL) {
|
|
|
|
if(prop->count == PLY_TYPE_INVALID) {
|
|
|
|
prop->elements = malloc(ply_type_sizes[prop->element]*elem->count);
|
|
|
|
prop->counts = NULL;
|
|
|
|
} else {
|
|
|
|
prop->elements = malloc(sizeof(void*)*elem->count);
|
|
|
|
prop->counts = malloc(sizeof(ply_type_sizes[prop->count])*elem->count);
|
|
|
|
}
|
|
|
|
prop = prop->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(int i = 0; i < elem->count; i++) {
|
|
|
|
PlyProperty* prop = elem->properties;
|
|
|
|
while(prop != NULL) {
|
|
|
|
if(prop->count == PLY_TYPE_INVALID) {
|
|
|
|
int read = fread(prop->elements + (i*ply_type_sizes[prop->element]), ply_type_sizes[prop->element], 1, ply);
|
|
|
|
if(read != 1) {
|
|
|
|
fprintf(stderr, "prop read error\n");
|
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
uint64_t size = 0;
|
|
|
|
uint64_t read = fread(&size, ply_type_sizes[prop->count], 1, ply);
|
|
|
|
if(read != 1) {
|
|
|
|
fprintf(stderr, "prop read error\n");
|
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(prop->counts + (i*ply_type_sizes[prop->count]), &size, ply_type_sizes[prop->count]);
|
|
|
|
void* elements = malloc(ply_type_sizes[prop->element]*size);
|
|
|
|
read = fread(elements, ply_type_sizes[prop->element], size, ply);
|
|
|
|
if(read != size) {
|
|
|
|
fprintf(stderr, "prop read error\n");
|
|
|
|
goto clean;
|
|
|
|
}
|
|
|
|
memcpy(prop->elements + (i*sizeof(void*)), &elements, sizeof(void*));
|
|
|
|
}
|
|
|
|
prop = prop->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
elem = elem->next;
|
|
|
|
}
|
|
|
|
|
2024-01-13 02:24:35 -07:00
|
|
|
elem = elements_head;
|
|
|
|
while(elem != NULL) {
|
|
|
|
int type = -1;
|
|
|
|
if(strcmp(elem->name, mappings.vertex_element) == 0) {
|
|
|
|
type = 0;
|
|
|
|
mesh.position = malloc(sizeof(vec3)*elem->count);
|
|
|
|
mesh.colour = malloc(sizeof(vec4)*elem->count);
|
|
|
|
mesh.normal = malloc(sizeof(vec3)*elem->count);
|
|
|
|
mesh.uv = malloc(sizeof(vec2)*elem->count);
|
|
|
|
mesh.vertex_count = elem->count;
|
|
|
|
} else if(strcmp(elem->name, mappings.face_element) == 0) {
|
|
|
|
type = 1;
|
|
|
|
mesh.index_count = 3*elem->count;
|
|
|
|
mesh.index = malloc(3*elem->count*sizeof(uint16_t));
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
PlyProperty* prop = elem->properties;
|
|
|
|
while(prop != NULL) {
|
|
|
|
if(prop->count == PLY_TYPE_INVALID && type == 0) {
|
|
|
|
if(ply_map_values(prop->name,
|
|
|
|
3,
|
|
|
|
elem->count,
|
|
|
|
prop->element,
|
|
|
|
sizeof(mappings.position)/sizeof(char*),
|
|
|
|
mappings.position,
|
|
|
|
prop->elements,
|
|
|
|
(float*)mesh.position)) {
|
|
|
|
} else if(ply_map_values(prop->name,
|
|
|
|
4,
|
|
|
|
elem->count,
|
|
|
|
prop->element,
|
|
|
|
sizeof(mappings.colour)/sizeof(char*),
|
|
|
|
mappings.colour,
|
|
|
|
prop->elements,
|
|
|
|
(float*)mesh.colour)) {
|
|
|
|
} else if(ply_map_values(prop->name,
|
|
|
|
3,
|
|
|
|
elem->count,
|
|
|
|
prop->element,
|
|
|
|
sizeof(mappings.normal)/sizeof(char*),
|
|
|
|
mappings.normal,
|
|
|
|
prop->elements,
|
|
|
|
(float*)mesh.normal)) {
|
|
|
|
} else if(ply_map_values(prop->name,
|
|
|
|
2,
|
|
|
|
elem->count,
|
|
|
|
prop->element,
|
|
|
|
sizeof(mappings.texture)/sizeof(char*),
|
|
|
|
mappings.texture,
|
|
|
|
prop->elements,
|
|
|
|
(float*)mesh.uv)) {
|
|
|
|
}
|
|
|
|
} else if (prop->count != PLY_TYPE_INVALID && type == 1) {
|
|
|
|
for(int i = 0; i < elem->count; i++) {
|
|
|
|
int type_size = ply_type_sizes[prop->element];
|
|
|
|
void ** lists = prop->elements;
|
|
|
|
mesh.index[i*3 + 0] = ply_conv_index(prop->element, lists[i] + (0*type_size), 0);
|
|
|
|
mesh.index[i*3 + 1] = ply_conv_index(prop->element, lists[i] + (1*type_size), 0);
|
|
|
|
mesh.index[i*3 + 2] = ply_conv_index(prop->element, lists[i] + (2*type_size), 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
prop = prop->next;
|
|
|
|
}
|
|
|
|
elem = elem->next;
|
|
|
|
}
|
|
|
|
|
2024-01-11 12:09:54 -07:00
|
|
|
clean:
|
2024-01-11 16:16:10 -07:00
|
|
|
ply_free_elements(elements_head);
|
2024-01-11 12:09:54 -07:00
|
|
|
close:
|
|
|
|
fclose(ply);
|
2024-01-11 16:16:10 -07:00
|
|
|
return mesh;
|
2024-01-11 12:09:54 -07:00
|
|
|
}
|