Skip to content

Commit

Permalink
Add io_fbx plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
luboslenco committed Jan 19, 2024
1 parent ca96a89 commit 2863959
Show file tree
Hide file tree
Showing 10 changed files with 37,259 additions and 39 deletions.
51 changes: 51 additions & 0 deletions armorpaint/Assets/plugins/embed/import_fbx.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@

let a = Krom_import_fbx;
class R {
get buffer() { return Krom_import_fbx._buffer(); }
}
let r = new R();

// import_fbx.js
let import_fbx = function(path, done) {
Data.getBlob(path, function(b) {
let buf_off = a._init(b.byteLength); //// Allocate r.buffer
let buf = new Uint8Array(r.buffer, buf_off, b.byteLength);
let bbuf = new Uint8Array(b);
for (let i = 0; i < b.byteLength; ++i) buf[i] = bbuf[i];
a._parse();
let vertex_count = a._get_vertex_count();
let index_count = a._get_index_count();
let inda = new Uint32Array(r.buffer, a._get_indices(), index_count);
let posa = new Int16Array(r.buffer, a._get_positions(), vertex_count * 4);
let nora = new Int16Array(r.buffer, a._get_normals(), vertex_count * 2);
let texa = a._get_uvs() > 0 ? new Int16Array(r.buffer, a._get_uvs(), vertex_count * 2) : null;
let cola = a._get_colors() > 0 ? new Int16Array(r.buffer, a._get_colors(), vertex_count * 4) : null;

// Use .slice() for now as the next mesh will overwrite buffer data corrupting the old vertex data
done({
name: a._get_name(),
posa: posa.slice(),
nora: nora.slice(),
texa: texa != null ? texa.slice() : null,
cola: cola != null ? cola.slice() : null,
inda: inda.slice(),
scale_pos: a._get_scale_pos(),
scale_tex: 1.0,
transform: a._get_transform(),
has_next: a._has_next()
});
// a._destroy(); //// Destroys r.buffer
// Data.deleteBlob(path);
});
}

let plugin = new Plugin();
let formats = Path.meshFormats;
let importers = Path.meshImporters;
formats.push("fbx");
importers.set("fbx", import_fbx);

plugin.delete = function() {
formats.splice(formats.indexOf("fbx"), 1);
importers.delete("fbx");
};
238 changes: 238 additions & 0 deletions armorpaint/Plugins/Sources/io_fbx/io_fbx.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
#include "ufbx/ufbx.h"
#include <stdlib.h>
#include <math.h>

#ifdef WITH_PLUGIN_EMBED
#define EMSCRIPTEN_KEEPALIVE
#else
#include <emscripten.h>
#endif

static uint8_t *buffer = NULL;
static uint32_t bufferLength = 0;
static int bufOff; /* Pointer to fbx file data */
static size_t size; /* Size of the file data */

static int index_count;
static int vertex_count;
static int indaOff;
static int posaOff;
static int noraOff;
static int texaOff;
static int colaOff;
static float scale_pos;
static char name[128];
static float transform[16];
static bool has_next = false;
static int current_node;

EMSCRIPTEN_KEEPALIVE uint8_t *io_fbx_getBuffer() { return buffer; }
EMSCRIPTEN_KEEPALIVE uint32_t io_fbx_getBufferLength() { return bufferLength; }

static int allocate(int size) {
#ifdef WITH_PLUGIN_EMBED
size += size % 4; // Byte align
#endif
bufferLength += size;
buffer = buffer == NULL ? (uint8_t *)malloc(bufferLength) : (uint8_t *)realloc(buffer, bufferLength);
return bufferLength - size;
}

EMSCRIPTEN_KEEPALIVE int io_fbx_init(int bufSize) {
if (!has_next) {
current_node = 0;
scale_pos = 0;
}

size = bufSize;
bufOff = allocate(sizeof(uint8_t) * bufSize);
return bufOff;
}

void io_fbx_parse_mesh(ufbx_mesh *mesh) {
uint32_t indices_size = mesh->max_face_triangles * 3;
uint32_t *indices = (uint32_t *)malloc(sizeof(uint32_t) * mesh->max_face_triangles * 3);

bool has_tex = mesh->vertex_uv.exists;
bool has_col = mesh->vertex_color.exists;

int numtri = mesh->num_triangles;
float *posa32 = (float *)malloc(sizeof(float) * numtri * 3 * 3);
float *nora32 = (float *)malloc(sizeof(float) * numtri * 3 * 3);
float *texa32 = has_tex ? (float *)malloc(sizeof(float) * numtri * 3 * 2) : NULL;
float *cola32 = has_col ? (float *)malloc(sizeof(float) * numtri * 3 * 4) : NULL;
int pi = 0;
int ni = 0;
int ti = 0;
int ci = 0;

for (int j = 0; j < mesh->faces.count; ++j) {
ufbx_face face = mesh->faces.data[j];
uint32_t num_triangles = ufbx_triangulate_face(indices, indices_size, mesh, face);
for (uint32_t v_ix = 0; v_ix < num_triangles * 3; v_ix++) {
uint32_t a = indices[v_ix];

posa32[pi++] = ufbx_get_vertex_vec3(&mesh->vertex_position, a).x;
posa32[pi++] = ufbx_get_vertex_vec3(&mesh->vertex_position, a).y;
posa32[pi++] = ufbx_get_vertex_vec3(&mesh->vertex_position, a).z;

nora32[ni++] = ufbx_get_vertex_vec3(&mesh->vertex_normal, a).x;
nora32[ni++] = ufbx_get_vertex_vec3(&mesh->vertex_normal, a).y;
nora32[ni++] = ufbx_get_vertex_vec3(&mesh->vertex_normal, a).z;

if (has_tex) {
texa32[ti++] = ufbx_get_vertex_vec2(&mesh->vertex_uv, a).x;
texa32[ti++] = ufbx_get_vertex_vec2(&mesh->vertex_uv, a).y;
}

if (has_col) {
cola32[ci++] = ufbx_get_vertex_vec4(&mesh->vertex_color, a).x;
cola32[ci++] = ufbx_get_vertex_vec4(&mesh->vertex_color, a).y;
cola32[ci++] = ufbx_get_vertex_vec4(&mesh->vertex_color, a).z;
cola32[ci++] = ufbx_get_vertex_vec4(&mesh->vertex_color, a).w;
}
}
}

free(indices);

vertex_count = pi / 3;
index_count = vertex_count;

indaOff = allocate(sizeof(uint32_t) * index_count);
uint32_t *inda = (uint32_t *)&buffer[indaOff];
for (int i = 0; i < index_count; ++i) {
inda[i] = i;
}

// Pack positions to (-1, 1) range
float hx = 0.0;
float hy = 0.0;
float hz = 0.0;
for (int i = 0; i < vertex_count; ++i) {
float f = fabsf(posa32[i * 3]);
if (hx < f) hx = f;
f = fabsf(posa32[i * 3 + 1]);
if (hy < f) hy = f;
f = fabsf(posa32[i * 3 + 2]);
if (hz < f) hz = f;
}
float _scale_pos = fmax(hx, fmax(hy, hz));
if (_scale_pos > scale_pos) scale_pos = _scale_pos;
float inv = 1 / scale_pos;

// Pack into 16bit
posaOff = allocate(sizeof(short) * vertex_count * 4);
short *posa = (short *)&buffer[posaOff];
for (int i = 0; i < vertex_count; ++i) {
posa[i * 4 ] = posa32[i * 3 ] * 32767 * inv;
posa[i * 4 + 1] = posa32[i * 3 + 1] * 32767 * inv;
posa[i * 4 + 2] = posa32[i * 3 + 2] * 32767 * inv;
}

noraOff = allocate(sizeof(short) * vertex_count * 2);
short *nora = (short *)&buffer[noraOff];
if (nora32 != NULL) {
for (int i = 0; i < vertex_count; ++i) {
nora[i * 2 ] = nora32[i * 3 ] * 32767;
nora[i * 2 + 1] = nora32[i * 3 + 1] * 32767;
posa[i * 4 + 3] = nora32[i * 3 + 2] * 32767;
}
free(nora32);
}

free(posa32);

if (texa32 != NULL) {
texaOff = allocate(sizeof(short) * vertex_count * 2);
short *texa = (short *)&buffer[texaOff];
for (int i = 0; i < vertex_count; ++i) {
texa[i * 2 ] = texa32[i * 2 ] * 32767;
texa[i * 2 + 1] = (1.0 - texa32[i * 2 + 1]) * 32767;
}
free(texa32);
}
else texaOff = 0;

if (cola32 != NULL) {
colaOff = allocate(sizeof(short) * vertex_count * 4);
short *cola = (short *)&buffer[colaOff];
for (int i = 0; i < vertex_count; ++i) {
cola[i * 4 ] = cola32[i * 4 ] * 32767;
cola[i * 4 + 1] = cola32[i * 4 + 1] * 32767;
cola[i * 4 + 2] = cola32[i * 4 + 2] * 32767;
cola[i * 4 + 3] = cola32[i * 4 + 3] * 32767;
}
free(cola32);
}
else colaOff = 0;
}

EMSCRIPTEN_KEEPALIVE void io_fbx_parse() {
void *buf = &buffer[bufOff];
ufbx_load_opts opts = { .generate_missing_normals = true };
ufbx_scene *scene = ufbx_load_memory(buf, size, &opts, NULL);

for (size_t i = current_node; i < scene->nodes.count; ++i) {
current_node = i;
ufbx_node *n = scene->nodes.data[i];
if (n->mesh != NULL) {
strcpy(name, n->name.data);
// transform[0] = n->local_transform.translation.x;
// transform[1] = n->local_transform.translation.y;
// transform[2] = n->local_transform.translation.z;
// transform[3] = n->local_transform.scale.x;
// transform[4] = n->local_transform.scale.y;
// transform[5] = n->local_transform.scale.z;
// transform[6] = n->local_transform.rotation.x;
// transform[7] = n->local_transform.rotation.y;
// transform[8] = n->local_transform.rotation.z;
// transform[9] = n->local_transform.rotation.w;
transform[0] = n->unscaled_node_to_world.m00;
transform[1] = n->unscaled_node_to_world.m01;
transform[2] = n->unscaled_node_to_world.m02;
transform[3] = n->unscaled_node_to_world.m03;
transform[4] = n->unscaled_node_to_world.m10;
transform[5] = n->unscaled_node_to_world.m11;
transform[6] = n->unscaled_node_to_world.m12;
transform[7] = n->unscaled_node_to_world.m13;
transform[8] = n->unscaled_node_to_world.m20;
transform[9] = n->unscaled_node_to_world.m21;
transform[10] = n->unscaled_node_to_world.m22;
transform[11] = n->unscaled_node_to_world.m23;
transform[12] = 0.0;
transform[13] = 0.0;
transform[14] = 0.0;
transform[15] = 1.0;
io_fbx_parse_mesh(n->mesh);
break;
}
}
current_node++;

has_next = false;
for (size_t i = current_node; i < scene->nodes.count; ++i) {
ufbx_node *n = scene->nodes.data[i];
if (n->mesh != NULL) {
has_next = true;
break;
}
}
}

EMSCRIPTEN_KEEPALIVE void io_fbx_destroy() {
free(buffer);
buffer = NULL;
}

EMSCRIPTEN_KEEPALIVE int io_fbx_get_index_count() { return index_count; }
EMSCRIPTEN_KEEPALIVE int io_fbx_get_vertex_count() { return vertex_count; }
EMSCRIPTEN_KEEPALIVE float io_fbx_get_scale_pos() { return scale_pos; }
EMSCRIPTEN_KEEPALIVE int io_fbx_get_indices() { return indaOff; }
EMSCRIPTEN_KEEPALIVE int io_fbx_get_positions() { return posaOff; }
EMSCRIPTEN_KEEPALIVE int io_fbx_get_normals() { return noraOff; }
EMSCRIPTEN_KEEPALIVE int io_fbx_get_uvs() { return texaOff; }
EMSCRIPTEN_KEEPALIVE int io_fbx_get_colors() { return colaOff; }
EMSCRIPTEN_KEEPALIVE char *io_fbx_get_name() { return &name[0]; }
EMSCRIPTEN_KEEPALIVE float *io_fbx_get_transform() { return &transform[0]; }
EMSCRIPTEN_KEEPALIVE int io_fbx_has_next() { return has_next; }
39 changes: 39 additions & 0 deletions armorpaint/Plugins/Sources/io_fbx/ufbx/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2020 Samuli Raivio
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
----------------------------------------
Loading

0 comments on commit 2863959

Please sign in to comment.