mirror of
https://github.com/Karaka-Management/cOMS.git
synced 2026-01-11 11:18:40 +00:00
1023 lines
36 KiB
C
1023 lines
36 KiB
C
/**
|
|
* Jingga
|
|
*
|
|
* @copyright Jingga
|
|
* @license OMS License 2.0
|
|
* @version 1.0.0
|
|
* @link https://jingga.app
|
|
*/
|
|
#ifndef TOS_OBJECT_MESH_H
|
|
#define TOS_OBJECT_MESH_H
|
|
|
|
#include "Vertex.h"
|
|
#include "FaceType.h"
|
|
#include "../stdlib/Types.h"
|
|
|
|
#if _WIN32
|
|
#include "../platform/win32/UtilsWin32.h"
|
|
#else
|
|
#include "../platform/linux/UtilsLinux.h"
|
|
#endif
|
|
|
|
#include "../memory/RingMemory.h"
|
|
#include "../stdlib/simd/SIMD_I32.h"
|
|
#include "../utils/EndianUtils.h"
|
|
|
|
#define MESH_VERSION 1
|
|
|
|
// @todo how to handle different objects and groups?
|
|
// maybe make a mesh hold other meshes?
|
|
// @todo handle vertice arrays where for example no texture coordinates are defined/used
|
|
struct Mesh {
|
|
byte* data; // memory owner that subdevides into the pointers below
|
|
|
|
// @todo Implement the version into the file, currently not implemented
|
|
int32 version;
|
|
uint32 object;
|
|
|
|
uint32 group_count;
|
|
uint32* groups;
|
|
|
|
// the following section allows us to create 2 types of data
|
|
// Interleaved: [position normal tex_coord] [color] (elements depend on vertex_type)
|
|
// Separate: [position] [normal] [tex_coord] [color] (separate array for elements)
|
|
uint32 vertex_type;
|
|
uint32 vertex_count; // can mean only position or combination of position, normal, tex, ...
|
|
uint32 normal_count;
|
|
uint32 tex_coord_count;
|
|
uint32 color_count;
|
|
float* vertices;
|
|
|
|
// The following references only exist in some situations
|
|
// depends on the settings above
|
|
float* normals;
|
|
float* tex_coords;
|
|
float* colors;
|
|
|
|
uint32 face_type;
|
|
uint32 face_count;
|
|
uint32 face_normal_count;
|
|
uint32 face_tex_coord_count;
|
|
uint32 face_color_count;
|
|
uint32* faces;
|
|
|
|
// The following references only exist in some situations
|
|
// depends on the settings above
|
|
uint32* face_textures;
|
|
uint32* face_normals;
|
|
uint32* face_colors;
|
|
|
|
// @todo this only works if you have sub meshes e.g. one for body, one for hat, one for weapon etc.
|
|
uint32 vertex_ref;
|
|
uint32 vao;
|
|
uint32 vbo;
|
|
uint32 ebo;
|
|
|
|
uint32 material_count;
|
|
uint32* materials;
|
|
|
|
uint32 texture;
|
|
|
|
uint32 animation_count;
|
|
uint32* animations;
|
|
|
|
uint32 hitbox_count;
|
|
uint32* hitboxes;
|
|
|
|
uint32 audio_count;
|
|
uint32* audios;
|
|
|
|
uint32 mesh_count;
|
|
Mesh* meshes;
|
|
};
|
|
|
|
// @todo also handle textures etc.
|
|
// WARNING: mesh needs to have memory already reserved and asigned to data
|
|
void mesh_from_file_txt(
|
|
Mesh* mesh,
|
|
byte* data
|
|
) {
|
|
char* pos = (char *) data;
|
|
|
|
// move past the version string
|
|
pos += 8;
|
|
|
|
mesh->version = strtol(pos, &pos, 10); ++pos;
|
|
|
|
int32 object_index = 0;
|
|
int32 group_index = 0;
|
|
|
|
mesh->vertices = (f32 *) mesh->data;
|
|
|
|
mesh->vertex_count = 0;
|
|
mesh->normal_count = 0;
|
|
mesh->tex_coord_count = 0;
|
|
mesh->color_count = 0;
|
|
|
|
mesh->face_count = 0;
|
|
mesh->face_normal_count = 0;
|
|
mesh->face_tex_coord_count = 0;
|
|
mesh->face_color_count = 0;
|
|
|
|
uint32 temp_color_count = 0;
|
|
|
|
while (*pos != '\0') {
|
|
while (*pos == ' ' || *pos == '\t' || *pos == '\n') {
|
|
++pos;
|
|
}
|
|
|
|
if (*pos == '\0') {
|
|
break;
|
|
}
|
|
|
|
// Parse type
|
|
// WARNING: The code below could fail if [1] is outside of range
|
|
// However that should never happen for well formed files
|
|
int32 state = 0;
|
|
if (*pos == 'v' && pos[1] == ' ') {
|
|
state = 1;
|
|
} else if (*pos == 'v' && pos[1] == 'n') {
|
|
state = 2;
|
|
} else if (*pos == 'v' && pos[1] == 't') {
|
|
state = 3;
|
|
} else if (*pos == 'v' && pos[1] == 'p') {
|
|
state = 4;
|
|
} else if (*pos == 'o' && pos[1] == ' ') {
|
|
state = 5;
|
|
} else if (*pos == 's' && pos[1] == ' ') {
|
|
state = 6;
|
|
} else if (*pos == 'f' && pos[1] == ' ') {
|
|
state = 7;
|
|
} else if (*pos == 'g' && pos[1] == ' ') {
|
|
state = 8;
|
|
} else if (*pos == 'l' && pos[1] == ' ') {
|
|
state = 9;
|
|
} else if (*pos == 'm' && pos[1] == 't') {
|
|
state = 10;
|
|
} else if (*pos == 'h' && pos[1] == 'i') {
|
|
state = 11;
|
|
} else if (*pos == 'a' && pos[1] == 'n') {
|
|
state = 12;
|
|
} else if (*pos == 'u' && pos[3] == 'm') {
|
|
state = 13;
|
|
} else if (*pos == 'u' && pos[3] == 'h') {
|
|
state = 14;
|
|
} else {
|
|
// not supported or comment
|
|
while (*pos != '\n' && *pos != '\0') {
|
|
++pos;
|
|
}
|
|
}
|
|
|
|
// move past keyword
|
|
while (*pos != ' ' && *pos != '\n' && *pos != '\0') {
|
|
++pos;
|
|
}
|
|
|
|
// move past whitespaces and newline
|
|
bool is_next_line = false;
|
|
while (*pos == ' ' || *pos == '\n') {
|
|
is_next_line |= *pos == '\n';
|
|
++pos;
|
|
}
|
|
|
|
if (*pos == '\0' || is_next_line) {
|
|
continue;
|
|
}
|
|
|
|
// NOTE: we always load a file in the format: POSITON + NORMAL + TEXTURE + COLOR
|
|
// EVEN if some of the data is missing. This is necessary to keep the memory kinda in line.
|
|
// The actual binary file later will have the minimized layout.
|
|
|
|
// handle data types
|
|
switch (state) {
|
|
case 0: break;
|
|
case 1: {
|
|
// 'v'
|
|
if (mesh->vertex_count == 0) {
|
|
mesh->vertex_type |= VERTEX_TYPE_POSITION;
|
|
}
|
|
|
|
mesh->vertices[mesh->vertex_count * 3] = strtof(pos, &pos); ++pos;
|
|
mesh->vertices[mesh->vertex_count * 3 + 1] = strtof(pos, &pos); ++pos;
|
|
mesh->vertices[mesh->vertex_count * 3 + 2] = strtof(pos, &pos); ++pos;
|
|
|
|
// has color information
|
|
// @todo Move to own case statement // 'co'
|
|
if (*pos != '\n' && pos[1] != ' ' && pos[1] != '\n') {
|
|
if (mesh->vertex_count == 0) {
|
|
mesh->vertex_type |= VERTEX_TYPE_COLOR;
|
|
}
|
|
|
|
mesh->vertices[mesh->vertex_count * 12 + 8] = strtof(pos, &pos); ++pos;
|
|
mesh->vertices[mesh->vertex_count * 12 + 9] = strtof(pos, &pos); ++pos;
|
|
mesh->vertices[mesh->vertex_count * 12 + 10] = strtof(pos, &pos); ++pos;
|
|
|
|
// handle optional alpha [a]
|
|
if (*pos != '\n' && pos[1] != ' ' && pos[1] != '\n') {
|
|
mesh->vertices[mesh->vertex_count * 12 + 11] = strtof(pos, &pos); ++pos;
|
|
} else {
|
|
mesh->vertices[mesh->vertex_count * 12 + 11] = 1.0f;
|
|
}
|
|
|
|
++temp_color_count;
|
|
}
|
|
|
|
++mesh->vertex_count;
|
|
} break;
|
|
case 2: {
|
|
// 'vn'
|
|
// @bug This requires normals to be defined before textures
|
|
if (mesh->normal_count == 0) {
|
|
mesh->normals = mesh->vertices + mesh->vertex_count * 3;
|
|
}
|
|
|
|
mesh->normals[mesh->normal_count * 3] = strtof(pos, &pos); ++pos;
|
|
mesh->normals[mesh->normal_count * 3 + 1] = strtof(pos, &pos); ++pos;
|
|
mesh->normals[mesh->normal_count * 3 + 2] = strtof(pos, &pos); ++pos;
|
|
|
|
++mesh->normal_count;
|
|
} break;
|
|
case 3: {
|
|
// 'vt'
|
|
if (mesh->tex_coord_count == 0) {
|
|
mesh->tex_coords = mesh->vertices + mesh->vertex_count * 3 + mesh->normal_count * 3;
|
|
}
|
|
|
|
mesh->tex_coords[mesh->tex_coord_count * 2] = strtof(pos, &pos); ++pos;
|
|
mesh->tex_coords[mesh->tex_coord_count * 2 + 1] = strtof(pos, &pos); ++pos;
|
|
|
|
++mesh->tex_coord_count;
|
|
} break;
|
|
case 4: {
|
|
// 'vp'
|
|
strtof(pos, &pos); ++pos;
|
|
|
|
// handle optional [v]
|
|
if (*pos != '\n' && pos[1] != ' ' && pos[1] != '\n') {
|
|
strtof(pos, &pos); ++pos;
|
|
|
|
// handle optional [w]
|
|
if (*pos != '\n' && pos[1] != ' ' && pos[1] != '\n') {
|
|
strtof(pos, &pos); ++pos;
|
|
}
|
|
}
|
|
} break;
|
|
case 5: {
|
|
// 'o'
|
|
char text[100];
|
|
int32 i = 0;
|
|
while (*pos != '\0' && *pos != ' ') {
|
|
text[i++] = *pos++;
|
|
}
|
|
text[i] = '\0';
|
|
|
|
++object_index;
|
|
} break;
|
|
case 6: {
|
|
// 's'
|
|
strtol(pos, &pos, 10); ++pos;
|
|
} break;
|
|
case 7: {
|
|
// 'f'
|
|
if (mesh->face_count == 0) {
|
|
mesh->faces = (uint32 *) (mesh->vertices + mesh->vertex_count * 3 + mesh->normal_count * 3 + mesh->tex_coord_count * 2 + mesh->color_count * 4);
|
|
}
|
|
|
|
int32 ftype = 0;
|
|
char* tmp = pos;
|
|
while (*tmp != ' ') {
|
|
if (*tmp++ == '/') {
|
|
++ftype;
|
|
|
|
if (*tmp++ == '/') {
|
|
ftype = 3;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const int32 max_blocks = 3; // @todo this could actually be N. Might have to change in the future
|
|
int32 block = 0;
|
|
|
|
while (*pos != '\0' && *pos != '\n') {
|
|
if (ftype == 0) {
|
|
// v1 v2 v3 ...
|
|
if (mesh->face_count == 0) {
|
|
mesh->face_type = FACE_TYPE_VERTICES;
|
|
}
|
|
|
|
mesh->faces[(mesh->face_count * max_blocks * 1) + block] = strtol(pos, &pos, 10) - 1; ++pos;
|
|
} else if (ftype == 1) {
|
|
// v1/vt1 v2/vt2 v3/vt3 ...
|
|
if (mesh->face_count == 0) {
|
|
mesh->face_type = FACE_TYPE_VERTICES | FACE_TYPE_TEXTURES;
|
|
}
|
|
|
|
mesh->faces[(mesh->face_count * max_blocks * 2) + block * 2] = strtol(pos, &pos, 10) - 1; ++pos;
|
|
mesh->faces[(mesh->face_count * max_blocks * 2) + block * 2 + 1] = strtol(pos, &pos, 10) - 1; ++pos;
|
|
} else if (ftype == 2) {
|
|
// v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 ...
|
|
if (mesh->face_count == 0) {
|
|
mesh->face_type = FACE_TYPE_VERTICES | FACE_TYPE_TEXTURES | FACE_TYPE_NORMALS;
|
|
}
|
|
|
|
mesh->faces[(mesh->face_count * max_blocks * 3) + block * 3] = strtol(pos, &pos, 10) - 1; ++pos;
|
|
mesh->faces[(mesh->face_count * max_blocks * 3) + block * 3 + 1] = strtol(pos, &pos, 10) - 1; ++pos;
|
|
mesh->faces[(mesh->face_count * max_blocks * 3) + block * 3 + 2] = strtol(pos, &pos, 10) - 1; ++pos;
|
|
} else if (ftype == 3) {
|
|
// v1//vn1 v2//vn2 v3//vn3 ...
|
|
if (mesh->face_count == 0) {
|
|
mesh->face_type = FACE_TYPE_VERTICES | FACE_TYPE_NORMALS;
|
|
}
|
|
|
|
mesh->faces[(mesh->face_count * max_blocks * 2) + block * 2] = strtol(pos, &pos, 10) - 1; ++pos;
|
|
++pos;
|
|
mesh->faces[(mesh->face_count * max_blocks * 2) + block * 2 + 1] = strtol(pos, &pos, 10) - 1; ++pos;
|
|
}
|
|
|
|
++block;
|
|
}
|
|
|
|
++mesh->face_count;
|
|
} break;
|
|
case 8: {
|
|
// 'g'
|
|
char text[100];
|
|
int32 i = 0;
|
|
while (*pos != '\0' && *pos != ' ') {
|
|
text[i++] = *pos++;
|
|
}
|
|
text[i] = '\0';
|
|
|
|
++group_index;
|
|
} break;
|
|
case 9: {
|
|
//l
|
|
while (*pos != '\0' && *pos != '\n') {
|
|
strtol(pos, &pos, 10); ++pos;
|
|
}
|
|
} break;
|
|
case 10: {
|
|
// 'mtllib'
|
|
char text[100];
|
|
int32 i = 0;
|
|
while (*pos != '\0' && *pos != ' ') {
|
|
text[i++] = *pos++;
|
|
}
|
|
text[i] = '\0';
|
|
} break;
|
|
case 11: {
|
|
// 'hitlib'
|
|
char text[100];
|
|
int32 i = 0;
|
|
while (*pos != '\0' && *pos != ' ') {
|
|
text[i++] = *pos++;
|
|
}
|
|
text[i] = '\0';
|
|
} break;
|
|
case 12: {
|
|
// 'anilib'
|
|
char text[100];
|
|
int32 i = 0;
|
|
while (*pos != '\0' && *pos != ' ') {
|
|
text[i++] = *pos++;
|
|
}
|
|
text[i] = '\0';
|
|
} break;
|
|
case 13: {
|
|
// 'usemtl'
|
|
char text[100];
|
|
int32 i = 0;
|
|
while (*pos != '\0' && *pos != ' ') {
|
|
text[i++] = *pos++;
|
|
}
|
|
text[i] = '\0';
|
|
} break;
|
|
case 14: {
|
|
// 'usehit'
|
|
char text[100];
|
|
int32 i = 0;
|
|
while (*pos != '\0' && *pos != ' ') {
|
|
text[i++] = *pos++;
|
|
}
|
|
text[i] = '\0';
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
|
|
enum ObjectLoadingRestriction {
|
|
OBJECT_LOADING_RESTRICTION_POSITION = 1,
|
|
OBJECT_LOADING_RESTRICTION_NORMAL = 2,
|
|
OBJECT_LOADING_RESTRICTION_TEXTURE = 4,
|
|
OBJECT_LOADING_RESTRICTION_COLOR = 8,
|
|
OBJECT_LOADING_RESTRICTION_FACES = 16,
|
|
OBJECT_LOADING_RESTRICTION_EVERYTHING = 31
|
|
};
|
|
|
|
// @todo sometimes we don't care about some data, we should have an option which defines which data should be loaded
|
|
// this can improve performance for algorithms on this. e.g.:
|
|
// on the server side we only care about the vertex positions for collision (no normals, no color, ...)
|
|
int32 mesh_from_file(
|
|
RingMemory* ring,
|
|
const char* path,
|
|
Mesh* mesh,
|
|
const char* group = NULL,
|
|
int32 load_format = OBJECT_LOADING_RESTRICTION_EVERYTHING,
|
|
int32 size = 8
|
|
)
|
|
{
|
|
FileBody file;
|
|
file_read(path, &file, ring);
|
|
|
|
byte* pos = file.content;
|
|
|
|
// Read version
|
|
mesh->version = *((int32 *) pos);
|
|
pos += sizeof(mesh->version);
|
|
|
|
// Read base data
|
|
mesh->vertex_type = *((int32 *) pos);
|
|
pos += sizeof(mesh->vertex_type);
|
|
|
|
mesh->vertex_count = *((int32 *) pos);
|
|
pos += sizeof(mesh->vertex_count);
|
|
|
|
mesh->normal_count = *((int32 *) pos);
|
|
pos += sizeof(mesh->normal_count);
|
|
|
|
mesh->tex_coord_count = *((int32 *) pos);
|
|
pos += sizeof(mesh->tex_coord_count);
|
|
|
|
mesh->color_count = *((int32 *) pos);
|
|
pos += sizeof(mesh->color_count);
|
|
|
|
#if !_WIN32 && !__LITTLE_ENDIAN
|
|
mesh->version = endian_swap(mesh->version);
|
|
mesh->vertex_type = endian_swap(mesh->vertex_type);
|
|
mesh->verted_count = endian_swap(mesh->verted_count);
|
|
mesh->normal_count = endian_swap(mesh->normal_count);
|
|
mesh->tex_coord_count = endian_swap(mesh->tex_coord_count);
|
|
mesh->color_count = endian_swap(mesh->color_count);
|
|
#endif
|
|
|
|
int32 vertex_size = 0;
|
|
if (mesh->vertex_type & VERTEX_TYPE_POSITION) {
|
|
vertex_size += 3;
|
|
}
|
|
|
|
if (mesh->vertex_type & VERTEX_TYPE_NORMAL) {
|
|
vertex_size += 3;
|
|
}
|
|
|
|
if (mesh->vertex_type & VERTEX_TYPE_TEXTURE_COORD) {
|
|
vertex_size += 2;
|
|
}
|
|
|
|
if (mesh->vertex_type & VERTEX_TYPE_COLOR) {
|
|
vertex_size += 4;
|
|
}
|
|
|
|
int32 offset = 0;
|
|
if (mesh->vertex_count > 0) {
|
|
memcpy(mesh->data, pos, sizeof(f32) * vertex_size * mesh->vertex_count);
|
|
mesh->vertices = (f32 *) mesh->data;
|
|
|
|
pos += sizeof(f32) * vertex_size * mesh->vertex_count;
|
|
offset += sizeof(f32) * vertex_size * mesh->vertex_count;
|
|
}
|
|
|
|
if (mesh->normal_count > 0) {
|
|
memcpy(mesh->data + offset, pos, sizeof(f32) * 3 * mesh->normal_count);
|
|
mesh->normals = (f32 *) (mesh->data + offset);
|
|
|
|
pos += sizeof(f32) * 3 * mesh->normal_count;
|
|
offset += sizeof(f32) * 3 * mesh->normal_count;
|
|
}
|
|
|
|
if (mesh->tex_coord_count > 0) {
|
|
memcpy(mesh->data + offset, pos, sizeof(f32) * 3 * mesh->tex_coord_count);
|
|
mesh->tex_coords = (f32 *) (mesh->data + offset);
|
|
|
|
pos += sizeof(f32) * 2 * mesh->tex_coord_count;
|
|
offset += sizeof(f32) * 2 * mesh->tex_coord_count;
|
|
}
|
|
|
|
if (mesh->color_count > 0) {
|
|
memcpy(mesh->data + offset, pos, sizeof(f32) * 4 * mesh->color_count);
|
|
mesh->colors = (f32 *) (mesh->data + offset);
|
|
|
|
pos += sizeof(f32) * 4 * mesh->color_count;
|
|
offset += sizeof(f32) * 4 * mesh->color_count;
|
|
}
|
|
|
|
// Read face data
|
|
mesh->face_type = *((int32 *) pos);
|
|
pos += sizeof(mesh->face_type);
|
|
|
|
mesh->face_count = *((int32 *) pos);
|
|
pos += sizeof(mesh->face_count);
|
|
|
|
mesh->face_normal_count = *((int32 *) pos);
|
|
pos += sizeof(mesh->face_normal_count);
|
|
|
|
mesh->face_tex_coord_count = *((int32 *) pos);
|
|
pos += sizeof(mesh->face_tex_coord_count);
|
|
|
|
mesh->face_color_count = *((int32 *) pos);
|
|
pos += sizeof(mesh->face_color_count);
|
|
|
|
#if !_WIN32 && !__LITTLE_ENDIAN
|
|
mesh->face_type = endian_swap(mesh->face_type);
|
|
mesh->face_count = endian_swap(mesh->face_count);
|
|
mesh->face_normal_count = endian_swap(mesh->face_normal_count);
|
|
mesh->face_tex_coord_count = endian_swap(mesh->face_tex_coord_count);
|
|
mesh->face_color_count = endian_swap(mesh->face_color_count);
|
|
#endif
|
|
|
|
int32 face_size = 0;
|
|
if (mesh->face_type & FACE_TYPE_VERTICES) {
|
|
face_size += 3;
|
|
}
|
|
|
|
if ((mesh->face_type & FACE_TYPE_NORMALS)) {
|
|
face_size += 3;
|
|
}
|
|
|
|
if ((mesh->face_type & FACE_TYPE_TEXTURES)) {
|
|
face_size += 3;
|
|
}
|
|
|
|
if ((mesh->face_type & FACE_TYPE_COLORS)) {
|
|
face_size += 3;
|
|
}
|
|
|
|
// faces can be either in the form
|
|
// f: v/vt/vn ...
|
|
// or:
|
|
// f: v ...
|
|
// f: vn ...
|
|
// f: vt ...
|
|
if (mesh->face_count > 0) {
|
|
memcpy(mesh->data + offset, pos, sizeof(uint32) * face_size * mesh->face_count);
|
|
mesh->faces = (uint32 *) (mesh->data + offset);
|
|
|
|
pos += sizeof(uint32) * face_size * mesh->face_count;
|
|
offset += sizeof(uint32) * face_size * mesh->face_count;
|
|
}
|
|
|
|
if (mesh->face_normal_count > 0) {
|
|
memcpy(mesh->data + offset, pos, sizeof(uint32) * 3 * mesh->face_normal_count);
|
|
mesh->face_normals = (uint32 *) (mesh->data + offset);
|
|
|
|
pos += sizeof(uint32) * 3 * mesh->face_normal_count;
|
|
offset += sizeof(uint32) * 3 * mesh->face_normal_count;
|
|
}
|
|
|
|
if (mesh->face_tex_coord_count > 0) {
|
|
memcpy(mesh->data + offset, pos, sizeof(uint32) * 3 * mesh->face_tex_coord_count);
|
|
mesh->face_textures = (uint32 *) (mesh->data + offset);
|
|
|
|
pos += sizeof(uint32) * 3 * mesh->face_tex_coord_count;
|
|
offset += sizeof(uint32) * 3 * mesh->face_tex_coord_count;
|
|
}
|
|
|
|
if (mesh->face_color_count > 0) {
|
|
memcpy(mesh->data + offset, pos, sizeof(uint32) * 3 * mesh->face_color_count);
|
|
mesh->face_colors = (uint32 *) (mesh->data + offset);
|
|
|
|
pos += sizeof(uint32) * 3 * mesh->face_color_count;
|
|
offset += sizeof(uint32) * 3 * mesh->face_color_count;
|
|
}
|
|
|
|
SWAP_ENDIAN_LITTLE_SIMD(
|
|
(int32 *) mesh->data,
|
|
(int32 *) mesh->data,
|
|
offset / 4, // everything is 4 bytes -> super easy to swap
|
|
steps
|
|
);
|
|
|
|
return offset;
|
|
}
|
|
|
|
void mesh_to_file(
|
|
RingMemory* ring,
|
|
const char* path,
|
|
const Mesh* mesh,
|
|
int32 vertex_save_format = VERTEX_TYPE_POSITION,
|
|
int32 face_save_format = FACE_TYPE_VERTICES,
|
|
int32 size = 8
|
|
)
|
|
{
|
|
FileBody file;
|
|
|
|
// Temporary file size for buffer
|
|
// @todo check the actual size, we are currently more or less guessing
|
|
file.size = sizeof(mesh)
|
|
+ sizeof(Vertex3D) * mesh->vertex_count
|
|
+ sizeof(f32) * 12 * mesh->vertex_count
|
|
+ 4096;
|
|
|
|
file.content = ring_get_memory(ring, file.size, 64);
|
|
byte* pos = file.content;
|
|
|
|
// version
|
|
memcpy(pos, &mesh->version, sizeof(mesh->version));
|
|
pos += sizeof(mesh->version);
|
|
|
|
// vertices
|
|
memcpy(pos, &vertex_save_format, sizeof(vertex_save_format));
|
|
pos += sizeof(vertex_save_format);
|
|
|
|
memcpy(pos, &mesh->vertex_count, sizeof(mesh->vertex_count));
|
|
pos += sizeof(mesh->vertex_count);
|
|
|
|
// We are can save the mesh in a different format from the current format -> need to adjust some values
|
|
uint32 normal_count = mesh->normal_count == 0 && (mesh->vertex_type & VERTEX_TYPE_NORMAL)
|
|
? mesh->vertex_count
|
|
: mesh->normal_count;
|
|
|
|
memcpy(pos, &normal_count, sizeof(mesh->normal_count));
|
|
pos += sizeof(mesh->normal_count);
|
|
|
|
uint32 tex_coord_count = mesh->tex_coord_count == 0 && (mesh->vertex_type & VERTEX_TYPE_TEXTURE_COORD)
|
|
? mesh->vertex_count
|
|
: mesh->tex_coord_count;
|
|
|
|
memcpy(pos, &tex_coord_count, sizeof(mesh->tex_coord_count));
|
|
pos += sizeof(mesh->tex_coord_count);
|
|
|
|
uint32 color_count = mesh->color_count == 0 && (mesh->vertex_type & VERTEX_TYPE_COLOR)
|
|
? mesh->vertex_count
|
|
: mesh->color_count;
|
|
|
|
memcpy(pos, &color_count, sizeof(mesh->color_count));
|
|
pos += sizeof(mesh->color_count);
|
|
|
|
// verticies
|
|
if (mesh->vertex_count > 0) {
|
|
int32 vertex_size = 0;
|
|
if (mesh->vertex_type & VERTEX_TYPE_POSITION) {
|
|
vertex_size += 3;
|
|
}
|
|
|
|
if (mesh->vertex_type & VERTEX_TYPE_NORMAL) {
|
|
vertex_size += 3;
|
|
}
|
|
|
|
if (mesh->vertex_type & VERTEX_TYPE_TEXTURE_COORD) {
|
|
vertex_size += 2;
|
|
}
|
|
|
|
if (mesh->vertex_type & VERTEX_TYPE_COLOR) {
|
|
vertex_size += 4;
|
|
}
|
|
|
|
int32 out_vertex_size = 0;
|
|
if (vertex_save_format & VERTEX_TYPE_POSITION) {
|
|
out_vertex_size += 3;
|
|
}
|
|
|
|
if (vertex_save_format & VERTEX_TYPE_NORMAL) {
|
|
out_vertex_size += 3;
|
|
}
|
|
|
|
if (vertex_save_format & VERTEX_TYPE_TEXTURE_COORD) {
|
|
out_vertex_size += 2;
|
|
}
|
|
|
|
if (vertex_save_format & VERTEX_TYPE_COLOR) {
|
|
out_vertex_size += 4;
|
|
}
|
|
|
|
if ((mesh->vertex_type == VERTEX_TYPE_ALL && vertex_save_format == VERTEX_TYPE_ALL)
|
|
|| (mesh->vertex_type == VERTEX_TYPE_POSITION && vertex_save_format == VERTEX_TYPE_POSITION)
|
|
) {
|
|
// data is the same as in the array
|
|
memcpy(pos, mesh->vertices, vertex_size * sizeof(f32) * mesh->vertex_count);
|
|
pos += vertex_size * sizeof(f32) * mesh->vertex_count;
|
|
} else {
|
|
f32* temp = mesh->vertices;
|
|
f32* end = mesh->vertices + mesh->vertex_count * vertex_size;
|
|
|
|
int32 offset;
|
|
|
|
byte* vertice_start = pos;
|
|
|
|
// @bug index gets increased every iteration BUT different groups and objects in the source may have different data
|
|
// This comes again down to how to handle hierarchal data with multiple groups and objects
|
|
int32 index = 0;
|
|
|
|
// iterate over all vertices to create new output format
|
|
while (temp < end) {
|
|
// @question why do I even need offset? couldn't I just directly manipulate temp?
|
|
offset = 0;
|
|
|
|
// First we save everything in one large array if that is the setting
|
|
if (vertex_save_format & VERTEX_TYPE_POSITION) {
|
|
if (mesh->vertex_type & VERTEX_TYPE_POSITION) {
|
|
memcpy(pos, temp, sizeof(f32) * 3);
|
|
pos += sizeof(f32) * 3;
|
|
|
|
offset += 3;
|
|
} else {
|
|
memset(pos, 0, sizeof(f32) * 3);
|
|
pos += sizeof(f32) * 3;
|
|
}
|
|
}
|
|
|
|
// We want separate arrays for some data
|
|
if ((mesh->vertex_type & VERTEX_TYPE_NORMAL) && !(vertex_save_format & VERTEX_TYPE_NORMAL)) {
|
|
// go to end of vertices * shift by previous array sizes + shift by current vertex -> creates one continous array with this data
|
|
memcpy(vertice_start
|
|
+ sizeof(f32) * out_vertex_size * mesh->vertex_count
|
|
+ index * sizeof(f32) * 3, temp + offset, sizeof(f32) * 3);
|
|
|
|
offset += 3;
|
|
} else if (vertex_save_format & VERTEX_TYPE_NORMAL) {
|
|
if (mesh->vertex_type & VERTEX_TYPE_NORMAL) {
|
|
memcpy(pos, temp + offset, sizeof(f32) * 3);
|
|
pos += sizeof(f32) * 3;
|
|
|
|
offset += 3;
|
|
} else {
|
|
memset(pos, 0, sizeof(f32) * 3);
|
|
pos += sizeof(f32) * 3;
|
|
}
|
|
}
|
|
|
|
// We want separate arrays for some data
|
|
if ((mesh->vertex_type & VERTEX_TYPE_TEXTURE_COORD) && !(vertex_save_format & VERTEX_TYPE_TEXTURE_COORD)) {
|
|
// go to end of vertices * shift by previous array sizes + shift by current vertex -> creates one continous array with this data
|
|
memcpy(vertice_start
|
|
+ sizeof(f32) * out_vertex_size * mesh->vertex_count
|
|
+ sizeof(f32) * normal_count * 3
|
|
+ index * sizeof(f32) * 3, temp + offset, sizeof(f32) * 3);
|
|
|
|
offset += 2;
|
|
} else if (vertex_save_format & VERTEX_TYPE_TEXTURE_COORD) {
|
|
if (mesh->vertex_type & VERTEX_TYPE_TEXTURE_COORD) {
|
|
memcpy(pos, temp + offset, sizeof(f32) * 2);
|
|
pos += sizeof(f32) * 2;
|
|
|
|
offset += 2;
|
|
} else {
|
|
memset(pos, 0, sizeof(f32) * 2);
|
|
pos += sizeof(f32) * 2;
|
|
}
|
|
}
|
|
|
|
// We want separate arrays for some data
|
|
if ((mesh->vertex_type & VERTEX_TYPE_COLOR) && !(vertex_save_format & VERTEX_TYPE_COLOR)) {
|
|
// go to end of vertices * shift by previous array sizes + shift by current vertex -> creates one continous array with this data
|
|
memcpy(vertice_start
|
|
+ sizeof(f32) * out_vertex_size * mesh->vertex_count
|
|
+ sizeof(f32) * normal_count * 3
|
|
+ sizeof(f32) * tex_coord_count * 2
|
|
+ index * sizeof(f32) * 4, temp + offset, sizeof(f32) * 4);
|
|
|
|
offset += 4;
|
|
} else if (vertex_save_format & VERTEX_TYPE_COLOR) {
|
|
if (mesh->vertex_type & VERTEX_TYPE_COLOR) {
|
|
memcpy(pos, temp + offset, sizeof(f32) * 4);
|
|
pos += sizeof(f32) * 4;
|
|
|
|
offset += 4;
|
|
} else {
|
|
memset(pos, 0, sizeof(f32) * 4);
|
|
pos += sizeof(f32) * 4;
|
|
}
|
|
}
|
|
|
|
temp += offset;
|
|
++index;
|
|
}
|
|
}
|
|
|
|
// check if we have clean array data already -> output this array data directly
|
|
if (mesh->normals && mesh->normal_count > 0) {
|
|
memcpy(pos, mesh->normals, mesh->normal_count * sizeof(f32) * 3);
|
|
pos += mesh->normal_count * sizeof(f32) * 3;
|
|
}
|
|
|
|
if (mesh->tex_coords && mesh->tex_coord_count > 0) {
|
|
memcpy(pos, mesh->tex_coords, mesh->tex_coord_count * sizeof(f32) * 2);
|
|
pos += mesh->tex_coord_count * sizeof(f32) * 2;
|
|
}
|
|
|
|
if (mesh->colors && mesh->color_count > 0) {
|
|
memcpy(pos, mesh->colors, mesh->color_count * sizeof(f32) * 4);
|
|
pos += mesh->color_count * sizeof(f32) * 4;
|
|
}
|
|
}
|
|
|
|
// faces/indices
|
|
memcpy(pos, &face_save_format, sizeof(face_save_format));
|
|
pos += sizeof(face_save_format);
|
|
|
|
memcpy(pos, &mesh->face_count, sizeof(mesh->face_count));
|
|
pos += sizeof(mesh->face_count);
|
|
|
|
// We are can save the mesh in a different format from the current format -> need to adjust some values
|
|
uint32 face_normal_count = mesh->face_normal_count == 0 && (mesh->face_type & FACE_TYPE_NORMALS)
|
|
? mesh->face_count
|
|
: mesh->face_normal_count;
|
|
|
|
memcpy(pos, &face_normal_count, sizeof(mesh->face_normal_count));
|
|
pos += sizeof(mesh->face_normal_count);
|
|
|
|
uint32 face_tex_coord_count = mesh->face_tex_coord_count == 0 && (mesh->face_type & FACE_TYPE_TEXTURES)
|
|
? mesh->face_count
|
|
: mesh->face_tex_coord_count;
|
|
|
|
memcpy(pos, &face_tex_coord_count, sizeof(mesh->face_tex_coord_count));
|
|
pos += sizeof(mesh->face_tex_coord_count);
|
|
|
|
uint32 face_color_count = mesh->face_color_count == 0 && (mesh->face_type & FACE_TYPE_COLORS)
|
|
? mesh->face_count
|
|
: mesh->face_color_count;
|
|
|
|
memcpy(pos, &face_color_count, sizeof(mesh->face_color_count));
|
|
pos += sizeof(mesh->face_color_count);
|
|
|
|
if (mesh->face_count > 0) {
|
|
// WARNING: Carefull, we again assume only 3 elements per face
|
|
|
|
int32 face_size = 0;
|
|
if (mesh->face_type & FACE_TYPE_VERTICES) {
|
|
face_size += 3;
|
|
}
|
|
|
|
if (mesh->face_type & FACE_TYPE_NORMALS) {
|
|
face_size += 3;
|
|
}
|
|
|
|
if (mesh->face_type & FACE_TYPE_TEXTURES) {
|
|
face_size += 3;
|
|
}
|
|
|
|
if (mesh->face_type & FACE_TYPE_COLORS) {
|
|
face_size += 3;
|
|
}
|
|
|
|
int32 out_face_size = 0;
|
|
if (face_save_format & FACE_TYPE_VERTICES) {
|
|
out_face_size += 3;
|
|
}
|
|
|
|
if (face_save_format & FACE_TYPE_NORMALS) {
|
|
out_face_size += 3;
|
|
}
|
|
|
|
if (face_save_format & FACE_TYPE_TEXTURES) {
|
|
out_face_size += 3;
|
|
}
|
|
|
|
if (face_save_format & FACE_TYPE_COLORS) {
|
|
out_face_size += 3;
|
|
}
|
|
|
|
if ((mesh->face_type == FACE_TYPE_ALL && face_save_format == FACE_TYPE_ALL)
|
|
|| (mesh->face_type == FACE_TYPE_VERTICES && face_save_format == FACE_TYPE_VERTICES)
|
|
) {
|
|
// data is the same as in the array
|
|
memcpy(pos, mesh->faces, face_size * sizeof(uint32) * mesh->face_count);
|
|
pos += face_size * sizeof(uint32) * mesh->face_count;
|
|
} else {
|
|
uint32* temp = mesh->faces;
|
|
uint32* end = mesh->faces + mesh->face_count * face_size;
|
|
|
|
int32 offset;
|
|
|
|
byte* face_start = pos;
|
|
|
|
// @bug index gets increased every iteration BUT different groups and objects in the source may have different data
|
|
// This comes again down to how to handle hierarchal data with multiple groups and objects
|
|
int32 index = 0;
|
|
|
|
// iterate over all faces to create new output format
|
|
// one iteration represents 1 block (a block could be v/vt/vn or, v, or v//vn, ...)
|
|
while (temp < end) {
|
|
// @question why do I even need offset? couldn't I just directly manipulate temp?
|
|
offset = 0;
|
|
|
|
// First we save everything in one large array if that is the setting
|
|
if (face_save_format & FACE_TYPE_VERTICES) {
|
|
if (mesh->face_type & FACE_TYPE_VERTICES) {
|
|
memcpy(pos, temp, sizeof(uint32));
|
|
pos += sizeof(uint32);
|
|
|
|
offset += 1;
|
|
} else {
|
|
memset(pos, 0, sizeof(f32));
|
|
pos += sizeof(f32);
|
|
}
|
|
}
|
|
|
|
// We want separate arrays for some data
|
|
if (mesh->face_type & FACE_TYPE_NORMALS && !(face_save_format & FACE_TYPE_NORMALS)) {
|
|
// go to end of faces * shift by previous array sizes + shift by current face -> creates one continous array with this data
|
|
memcpy(face_start
|
|
+ sizeof(uint32) * out_face_size * mesh->face_count
|
|
+ index * sizeof(uint32), temp + offset, sizeof(uint32));
|
|
|
|
offset += 1;
|
|
} else if (face_save_format & FACE_TYPE_NORMALS) {
|
|
if (mesh->face_type & FACE_TYPE_NORMALS) {
|
|
memcpy(pos, temp + offset, sizeof(uint32));
|
|
pos += sizeof(uint32);
|
|
|
|
offset += 1;
|
|
} else {
|
|
memset(pos, 0, sizeof(uint32));
|
|
pos += sizeof(uint32);
|
|
}
|
|
}
|
|
|
|
// We want separate arrays for some data
|
|
if (mesh->face_type & FACE_TYPE_TEXTURES && !(face_save_format & FACE_TYPE_TEXTURES)) {
|
|
// go to end of faces * shift by previous array sizes + shift by current face -> creates one continous array with this data
|
|
memcpy(face_start
|
|
+ sizeof(uint32) * out_face_size * mesh->face_count
|
|
+ sizeof(uint32) * 3 * face_normal_count
|
|
+ index * sizeof(uint32), temp + offset, sizeof(uint32));
|
|
|
|
offset += 1;
|
|
} else if (face_save_format & FACE_TYPE_TEXTURES) {
|
|
if (mesh->face_type & FACE_TYPE_TEXTURES) {
|
|
memcpy(pos, temp + offset, sizeof(uint32));
|
|
pos += sizeof(uint32);
|
|
|
|
offset += 1;
|
|
} else {
|
|
memset(pos, 0, sizeof(uint32));
|
|
pos += sizeof(uint32);
|
|
}
|
|
}
|
|
|
|
// We want separate arrays for some data
|
|
if (mesh->face_type & VERTEX_TYPE_COLOR && !(face_save_format & VERTEX_TYPE_COLOR)) {
|
|
// go to end of faces * shift by previous array sizes + shift by current face -> creates one continous array with this data
|
|
memcpy(face_start
|
|
+ sizeof(uint32) * out_face_size * mesh->face_count
|
|
+ sizeof(uint32) * 3 * face_normal_count
|
|
+ sizeof(uint32) * 3 * face_tex_coord_count
|
|
+ index * sizeof(uint32), temp + offset, sizeof(uint32));
|
|
|
|
offset += 1;
|
|
} else if (face_save_format & VERTEX_TYPE_COLOR) {
|
|
if (mesh->face_type & VERTEX_TYPE_COLOR) {
|
|
memcpy(pos, temp + offset, sizeof(uint32));
|
|
pos += sizeof(uint32);
|
|
|
|
offset += 1;
|
|
} else {
|
|
memset(pos, 0, sizeof(uint32));
|
|
pos += sizeof(uint32);
|
|
}
|
|
}
|
|
|
|
temp += offset;
|
|
++index;
|
|
}
|
|
}
|
|
|
|
// check if we have clean array data already -> output this array data directly
|
|
if (mesh->face_normals && mesh->face_normal_count > 0) {
|
|
memcpy(pos, mesh->face_normals, mesh->face_normal_count * sizeof(uint32) * 3);
|
|
pos += mesh->face_normal_count * sizeof(uint32) * 3;
|
|
}
|
|
|
|
if (mesh->face_textures && mesh->face_tex_coord_count > 0) {
|
|
memcpy(pos, mesh->face_textures, mesh->face_tex_coord_count * sizeof(uint32) * 3);
|
|
pos += mesh->face_tex_coord_count * sizeof(uint32) * 3;
|
|
}
|
|
|
|
if (mesh->face_colors && mesh->face_color_count > 0) {
|
|
memcpy(pos, mesh->face_colors, mesh->face_color_count * sizeof(uint32) * 3);
|
|
pos += mesh->face_color_count * sizeof(uint32) * 3;
|
|
}
|
|
}
|
|
|
|
file.size = pos - file.content;
|
|
|
|
SWAP_ENDIAN_LITTLE_SIMD(
|
|
(int32 *) file.content,
|
|
(int32 *) file.content,
|
|
file.size / 4, // everything in here is 4 bytes -> super easy to swap
|
|
steps
|
|
);
|
|
|
|
/*
|
|
FileBody file2;
|
|
file2.content = ring_get_memory(ring, file.size, 64);
|
|
file2.size = encode_lzp(file.content, file.size, file2.content);
|
|
|
|
file_write(path, &file2);
|
|
*/
|
|
|
|
file_write(path, &file);
|
|
}
|
|
|
|
#endif |