0
\$\begingroup\$
#include <zlib.h>

#include "fbx_loader.h"
#include "../libs/str.h"
#include "../sys/sys_local.h"
#include "../framework/file.h"

enum Property_Value_Type {
    PROPERTY_VALUE_TYPE_BOOL,
    PROPERTY_VALUE_TYPE_S8,
    PROPERTY_VALUE_TYPE_S16,
    PROPERTY_VALUE_TYPE_S32,
    PROPERTY_VALUE_TYPE_S64,
    PROPERTY_VALUE_TYPE_REAL32,
    PROPERTY_VALUE_TYPE_REAL64,
    PROPERTY_VALUE_TYPE_STRING
};

enum Fbx_Property_Type {
    PROPERTY_TYPE_VALUE,
    PROPERTY_TYPE_VALUE_ARRAY
};

struct Fbx_Property {

    Fbx_Property_Type type;
    Property_Value_Type value_type;
    union {
        union {
            bool boolean;
            s8  int8;
            s16 int16;
            s32 int32;
            s64 int64;
            float  real32;
            double real64;
            char *string = NULL;
        } value;
        struct {
            union {
                bool *boolean;
                s32 *int32;
                s64 *int64;
                float  *real32;
                double *real64;
            };
            u32 count;
        } array;
    };
    Fbx_Property() {};
    ~Fbx_Property();
    void copy_data_to_array(u8 *data, u32 array_byte_len, u32 array_count, u8 type);
    bool is_property_type_value() { return type == PROPERTY_TYPE_VALUE; }
    bool is_property_type_value_array() { return type == PROPERTY_TYPE_VALUE_ARRAY; }
    bool is_property_value_type_string() { return value_type == PROPERTY_VALUE_TYPE_STRING; }
};

struct Fbx_Node {
    char *name = NULL;
    Array<Fbx_Property *> properties;
    Array<Fbx_Node *> sub_nodes;

    ~Fbx_Node();
    void add_property_value(FILE *file);
    bool is_null();
    u32 read(FILE *file, u32 start_offset);
};


struct Fbx_Binary_File {
    char *file_name;
    Array<Fbx_Node *> root_nodes;

    
    void read(const char *file_path);
    void fill_out_mesh(Triangle_Mesh *mesh);
    bool check_title(FILE *file);
    char *get_texture_name();
    Fbx_Node *find_node(const char *name);
};

Fbx_Node *find_fbx_node(const Array<Fbx_Node *> *nodes, const char *name);

static u32 property_value_type_size(u8 type)
{
    if (type == 'C' || type == 'B') {
        return 1;
    } else if (type == 'Y') {
        return 2;
    } else if (type == 'I') {
        return 4;
    } else if (type == 'F') {
        return 4;
    } else if (type == 'D') {
        return 8;
    } else if (type == 'L') {
        return 8;
    } else {
        return 0;
    }
}

static void read_primitive_type_value(Fbx_Node *node, FILE *file, u8 type)
{
    Fbx_Property *value = new Fbx_Property();
    value->type = PROPERTY_TYPE_VALUE;
    if (type == 'C' || type == 'B') {
        value->value_type = PROPERTY_VALUE_TYPE_BOOL;
        value->value.boolean = (bool)read_u8(file);
    } else if (type == 'Y') {
        value->value_type = PROPERTY_VALUE_TYPE_S16;
        value->value.int16 = read_s16(file);
    } else if (type == 'I') {
        value->value_type = PROPERTY_VALUE_TYPE_S32;
        value->value.int32 = read_s32(file);
    } else if (type == 'L') {
        value->value_type = PROPERTY_VALUE_TYPE_S64;
        value->value.int64 = read_s64(file);
    } else if (type == 'F') {
        value->value_type = PROPERTY_VALUE_TYPE_REAL32;
        value->value.real32 = read_real32(file);
    } else if (type == 'D') {
        value->value_type = PROPERTY_VALUE_TYPE_REAL64;
        value->value.real64 = read_real64(file);
    } else if (type == 'S' || type == 'R') {
        u32 len = read_u32(file);
        value->value_type = PROPERTY_VALUE_TYPE_STRING;
        value->value.string = read_string(file, len);
    } else {
        print("read_primitive_type_value can not determine type of value");
        assert(false);
    }
    node->properties.push(value);
}

static void read_array_data(Fbx_Node *node, FILE *file, u8 type)
{
    Fbx_Property *array = new Fbx_Property();
    array->type = PROPERTY_TYPE_VALUE_ARRAY;
    
    u32 array_count = read_u32(file);
    u32 encoding = read_u32(file);
    u64 compressed_len = read_u32(file);

    u64 array_byte_len = property_value_type_size(type - ('a' - 'A')) * array_count;
    if (encoding) {
        u8 *decompressed_buffer = new u8[array_byte_len];
        u8 *compressed_buffer = new u8[compressed_len];

        fread(compressed_buffer, sizeof(u8), compressed_len, file);
        int success = uncompress(decompressed_buffer, &array_byte_len, compressed_buffer, compressed_len);
        if (success != Z_OK) {
            print("Fbx property array decompressing failed");
        }
        array->copy_data_to_array(decompressed_buffer, array_byte_len, array_count, type);
        
        DELETE_PTR(decompressed_buffer);
        DELETE_PTR(compressed_buffer);
    } else {
        u8 *buffer = new u8[array_byte_len];
        fread(buffer, sizeof(u8), array_byte_len, file);
        array->copy_data_to_array(buffer, array_byte_len, array_count, type);
        
        DELETE_PTR(buffer);
    }
    node->properties.push(array);
}


static void read_property_value(Fbx_Node *node, FILE *file)
{
    u8 type = read_u8(file);
    if (type > 'A' && type < 'Z') {
        read_primitive_type_value(node, file, type);
    } else if (type > 'a' && type < 'z') {
        read_array_data(node, file, type);
    }
}

Fbx_Property::~Fbx_Property()
{
    if (type = PROPERTY_TYPE_VALUE_ARRAY) {
        switch (value_type) {
            case PROPERTY_VALUE_TYPE_BOOL: {
                delete array.boolean;
                break;
            }
            case PROPERTY_VALUE_TYPE_S32: {
                delete array.int32;
                break;
            }
            case PROPERTY_VALUE_TYPE_S64: {
                delete array.int64;
                break;
            }
            case PROPERTY_VALUE_TYPE_REAL32: {
                delete array.real32;
                break;
            }
            case PROPERTY_VALUE_TYPE_REAL64: {
                delete array.real64;
                break;
            }
        }
    } else {
        if (value_type == PROPERTY_VALUE_TYPE_STRING) {
            delete value.string;
        }
    }
}

void Fbx_Property::copy_data_to_array(u8 *data, u32 array_byte_len, u32 array_count, u8 type)
{
    array.count = array_count;
    if (type == 'f') {
        value_type = PROPERTY_VALUE_TYPE_REAL32;
        array.real32 = new float[array_count];
        memcpy(array.real32, data, array_byte_len);
    } else if (type == 'd') {
        value_type = PROPERTY_VALUE_TYPE_REAL64;
        array.real64 = new double[array_count];
        memcpy(array.real64, data, array_byte_len);
    } else if (type == 'i') {
        value_type = PROPERTY_VALUE_TYPE_S32;
        array.int32 = new s32[array_count];
        memcpy(array.int32, data, array_byte_len);
    } else if (type == 'l') {
        value_type = PROPERTY_VALUE_TYPE_S64;
        array.int64 = new s64[array_count];
        memcpy(array.int64, data, array_byte_len);
    } else if (type == 'b') {
        // @Node: this code isn't tested
        value_type = PROPERTY_VALUE_TYPE_BOOL;
        array.boolean = new bool[array_count];
        for (u32 i = 0; i < array_count; i++) {
            array.boolean[i] = (bool)data[i] != 0;
        }
    } else {
        assert(false);
    }
}

Fbx_Node::~Fbx_Node()
{
    if (name) {
        delete name;
    }
}

void Fbx_Node::add_property_value(FILE *file)
{
    read_property_value(this, file);
}

bool Fbx_Node::is_null()
{
    return sub_nodes.count == 0 && properties.count == 0 && strlen(name) == 0;
}

Fbx_Node *find_fbx_node(const Array<Fbx_Node *> *nodes, const char *name)
{
    for (int i = 0; i < nodes->count; i++) {
        Fbx_Node *node = nodes->at(i);
        if (node->is_null()) {
            return NULL;
        }

        if (!strcmp(node->name, name)) {
            return node;
        } else {
            Fbx_Node * searched_node = find_fbx_node(&node->sub_nodes, name);
            if (!searched_node) {
                continue;
            }
            return searched_node;
        }
    }
    return NULL;
}

u32 Fbx_Node::read(FILE *file, u32 start_offset)
{
    u32 bytes = 13;
    u32 end_offset = read_u32(file);
    u32 number_properties = read_u32(file);
    u32 property_list_len = read_u32(file);
    u8 name_len = read_u8(file);
    name = read_string(file, name_len);


    for (u32 i = 0; i < number_properties; i++) {
        add_property_value(file);
    }

    bytes += name_len;
    bytes += property_list_len;

    while (start_offset + bytes < end_offset) {
        Fbx_Node *node = new Fbx_Node();
        bytes += node->read(file, start_offset + bytes);
        sub_nodes.push(node);
    }
    return bytes;
}


void Fbx_Binary_File::read(const char *file_path)
{
    FILE *file;
    file_name = extract_file_name(file_path);

    if (fopen_s(&file, file_path, "rb")) {
        print("Fbx_Binary_FIle::read: can't open the file {}", file_name);
        return;
    }

    bool success = check_title(file);
    u32 start_offset = 27;
    while (1) {
        Fbx_Node *node = new Fbx_Node();
        start_offset += node->read(file, start_offset);
        if (node->is_null()) {
            break;
        }
        root_nodes.push(node);
    }

}

bool Fbx_Binary_File::check_title(FILE *file)
{
    bool result = true;
    u32 max_fbx_version = 7400;
    char *magic_string = new char[21];
    static char fbx_title[21] = "Kaydara FBX Binary  ";

    fread(magic_string, sizeof(u8), 21, file);
    if (strcmp(fbx_title, magic_string)) {
        print("bx_Binary_File::check_title: Fbx magic string failed, it can be not fbx file\n");
        result = false;
    }

    if (read_u8(file) != 0x1A) {
        result = false;
    }

    if (read_u8(file) != 0x00) {
        result = false;
    }

    u32 version = read_u32(file);
    if (version > max_fbx_version) {
        print("Fbx_Binary_File::check_title: File {} has unsupported version {}, needed file version {} or less", file_name, version, max_fbx_version);
        result = false;
    }
    DELETE_PTR(magic_string);
    return result;
}


Fbx_Node *Fbx_Binary_File::find_node(const char *name)
{

    Fbx_Node *node = find_fbx_node(&root_nodes, name);
    if (!node) {
        print("Fbx_Binary_File::find_node: Fbx node {} is not found in file {}", name, file_name);
    }
    return node;
}

inline void copy_fbx_mesh_data_to_triangle_mesh(Triangle_Mesh *mesh, Fbx_Property *vertex_property, Fbx_Property *uv_property, Fbx_Property *uv_index_property)
{
    for (u32 i = 0; i < mesh->vertex_count; i++) {
        //s32 uv_index = uv_index_property->array.int32[i];
        //s32 uv_index2 = uv_index_property->array.int32[i * 3 + 1];
        //s32 uv_index3 = uv_index_property->array.int32[i * 3 + 2];

        mesh->vertices[i].position.x = static_cast<float32>(vertex_property->array.real64[i * 3]);
        mesh->vertices[i].position.y = static_cast<float32>(vertex_property->array.real64[i * 3 + 1]);
        mesh->vertices[i].position.z = static_cast<float32>(vertex_property->array.real64[i * 3 + 2]);
        
        //mesh->vertices[i].uv.x = uv_property->array.real64[uv_index];
        //mesh->vertices[i].uv.x = uv_property->array.real64[uv_index + 1];
        
        //mesh->vertices[i].uv.x = 1 - uv_property->array.real64[uv_index * 2];
        //mesh->vertices[i].uv.y =  1- uv_property->array.real64[uv_index * 2 + 1];

        //mesh->vertices[i].uv.x = 1- uv_property->array.real64[uv_index2 * 2 ];
        //mesh->vertices[i].uv.y = 1- uv_property->array.real64[uv_index2 * 2 + 1];

        //mesh->vertices[i].uv.x =  1- uv_property->array.real64[uv_index3  * 2];
        //mesh->vertices[i].uv.y = 1 - uv_property->array.real64[uv_index3  * 2 + 1];

    }
}

#define COPY_VERTICES_TO_MESH_FROM_FBX_PROPERTY_ARRAY(mesh, vertex_property, pointer_name, uv_property, uv_index_property) \
                for (u32 i = 0; i < mesh->vertex_count; i++) { \
                    mesh->vertices[i].position.x = static_cast<float32>(vertex_property->array. ## pointer_name ##[i * 3]); \
                    mesh->vertices[i].position.y = static_cast<float32>(vertex_property->array. ## pointer_name ##[i * 3 + 1]); \
                    mesh->vertices[i].position.z = static_cast<float32>(vertex_property->array. ## pointer_name ##[i * 3 + 2]); \
                } \
                                
void Fbx_Binary_File::fill_out_mesh(Triangle_Mesh *mesh)
{
    Fbx_Node *vertices = find_node("Vertices");
    Fbx_Node *indices = find_node("PolygonVertexIndex");
    Fbx_Node *normals = find_node("Normals");
    Fbx_Node *uv =  find_node("UV");
    Fbx_Node *uv_indices =  find_node("UVIndex");

    if (!vertices || !indices || !normals || !uv || !uv_indices) {
        print("Some mesh data from the fbx file for the triangle mesh wasn't found");
        return;
    }

    Fbx_Property *vertex_property = vertices->properties[0];
    Fbx_Property *index_property = indices->properties[0];
    Fbx_Property *normal_property = normals->properties[0];
    Fbx_Property *uv_property = uv->properties[0];
    Fbx_Property *uv_index_property = uv_indices->properties[0];
    
    if (vertex_property->type != PROPERTY_TYPE_VALUE_ARRAY || index_property->type != PROPERTY_TYPE_VALUE_ARRAY) {
        print("Vertex fbx property of index fbx property doesn't appropriate array type");
        return;
    }
    
    assert(vertex_property->array.count / 3 != 0);
    mesh->allocate_vertices(vertex_property->array.count / 3);

    switch (vertex_property->value_type) {
        case PROPERTY_VALUE_TYPE_REAL32: {
            COPY_VERTICES_TO_MESH_FROM_FBX_PROPERTY_ARRAY(mesh, vertex_property, real32, uv_property, uv_index_property);
            break;
        }
        case PROPERTY_VALUE_TYPE_REAL64: {
            //COPY_VERTICES_TO_MESH_FROM_FBX_PROPERTY_ARRAY(mesh, vertex_property, real64, uv_property, uv_index_property)
            copy_fbx_mesh_data_to_triangle_mesh(mesh, vertex_property, uv_property, uv_index_property);
            break;
        }
        case PROPERTY_VALUE_TYPE_S32: {
            COPY_VERTICES_TO_MESH_FROM_FBX_PROPERTY_ARRAY(mesh, vertex_property, int32, uv_property, uv_index_property)
            break;
        }
        case PROPERTY_VALUE_TYPE_S64: {
            COPY_VERTICES_TO_MESH_FROM_FBX_PROPERTY_ARRAY(mesh, vertex_property, int64, uv_property, uv_index_property)
            break;
        }
    }

    // Here i map uv coordinates on a mesh
    for (int i = 0; i < index_property->array.count; i++) {
        s32 index;
        s32 uv_index = uv_index_property->array.int32[i];
        if (index_property->array.int32[i] < 0) {
            index = (index_property->array.int32[i] * -1) - 1;
        } else {
            index = index_property->array.int32[i];
        }

        mesh->vertices[index].uv.x = uv_property->array.real64[uv_index * 2];
        mesh->vertices[index].uv.y = uv_property->array.real64[uv_index * 2 + 1];
        Vector3 * pos = &mesh->vertices[index].position;
        print("[{}] Vertex: x {} y {} z {},    Vertex index {}, UV x {}, y {},    UV_index {}", i, pos->x, pos->y, pos->z, index, mesh->vertices[index].uv.x, mesh->vertices[index].uv.y, uv_index);
    }


    if (index_property->array.int32[2] < 0) {
        mesh->allocate_indices(index_property->array.count);
        u32 triangle_index_count = mesh->index_count / 3;
        for (int i = 0; i < triangle_index_count; i++) {
            mesh->indices[i * 3]  = index_property->array.int32[i * 3];
            mesh->indices[i * 3 + 1] = index_property->array.int32[i * 3 + 1];
            mesh->indices[i * 3 + 2] = (index_property->array.int32[i * 3 + 2] * -1) - 1;

        }
    } else if (index_property->array.int32[3] < 0) {
        mesh->allocate_indices((index_property->array.count / 4) * 6);
        int k = 0;
        s32 quard_index_count = mesh->index_count / 4;
        for (int i = 0; i < quard_index_count; i++) {
            s32 first_index  = index_property->array.int32[i * 4];
            s32 second_index = index_property->array.int32[i * 4 + 1];
            s32 thrid_index  = index_property->array.int32[i * 4 + 2];
            s32 fourth_index = (index_property->array.int32[i * 4 + 3] * -1) -1;

            if (k < mesh->index_count) {
                mesh->indices[k] = first_index;
                mesh->indices[k + 1] = second_index;
                mesh->indices[k + 2] = thrid_index;

                mesh->indices[k + 3] = thrid_index;
                mesh->indices[k + 4] = fourth_index;
                mesh->indices[k + 5] = first_index;
                k += 6;
            }
        }
    }
    //for (int i = 0; i < mesh->index_count; i++) {
    //  s32 index = mesh->indices[i];
    //  s32 uv_index = uv_index_property->array.int32[i];
    //  mesh->vertices[index].uv.x = uv_property->array.int32[uv_index];
    //  mesh->vertices[index].uv.y = uv_property->array.int32[uv_index + 1];
    //}
}

char *Fbx_Binary_File::get_texture_name()
{
    Fbx_Node *texture_name = find_fbx_node(&root_nodes, "TextureName");
    if (!texture_name) {
        //@Note: here need to be warning message.
        return NULL;
    }
    Fbx_Property *property = texture_name->properties[0];

    if (property->is_property_type_value() && property->is_property_value_type_string()) {
        Array<char *> buffer;
        split(property->value.string, "::", &buffer);
        return _strdup(buffer[1]);
    }
    //@Note: here need to be warning message.
    return NULL;
}

enter image description here

I have tried to use fbx sdk and i wrote code like in the first answer on the question Importing and Displaying .fbx files. I also get the same data like in my written code.

\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

Consider the coordinate system that you are using. You can use a tool in Maya called "Game Exporter" this ensures that the Y coordinate is upwards and UV coordinates are in XY directions. Also, make sure that you are exporting the UV coordinates in the fbx file. Also, the v direction may be flipped, try 1-v instead of v.

\$\endgroup\$
3
  • \$\begingroup\$ I've tried to use 1-v but that didn't help me \$\endgroup\$ Feb 25, 2021 at 7:49
  • \$\begingroup\$ From the image, this is the orientation of the coordinate system. The tool that exports the fbx file is using a different coordinate system that you are using in your code. \$\endgroup\$ Feb 26, 2021 at 16:40
  • \$\begingroup\$ It's also possible that Maya is allowing multiple uv co-ordinates for a single vertex. I can't tell from the post but if you only have 8 verts on the cube then you will have 8 uv co-ords in OpenGL. You can try splitting the verts so that each face has 4 verts (6*4 unique vertices) \$\endgroup\$
    – Jay
    Nov 22, 2021 at 2:15

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .