#include #include #include #include "tiny_gltf.h" #include "asset.h" #include "dumbLog.h" // forward declarations void dumpNodes(tinygltf::Model t_mdl); model* initModel(Assets* assets, memory_arena* arena, tinygltf::Model t_mdl, const char* filename); bool parseMeshNode(mesh* m, memory_arena* arena, const tinygltf::Node& node, const tinygltf::Model& t_mdl); // interface // FIXME: move to internal when finished # if 0 util_image* copyDiffuseTexture(texture_assets* textures, memory_arena* arena, const tinygltf::Model& t_mdl) { // NOTE: assuming material[0] since we're using pallete texture assert(t_mdl.materials.size() == 1 && t_mdl.textures.size() == 1 && t_mdl.images.size() == 1 && t_mdl.images[0].image.size() > 0); tinygltf::Image t_img = t_mdl.images[0]; // TODO: re-alloc array when out of space assert(textures->count < textures->max && arena != nullptr); util_image* dtex = &textures->images[textures->count]; textures->count++; dtex->w = t_img.width; dtex->h = t_img.height; dtex->bits_per_channel = t_img.bits; dtex->num_channels = t_img.component; dtex->data_len = t_img.image.size(); dtex->pixels = ARENA_ALLOC(arena, u8, dtex->data_len); // FIXME: overwriting memory here... also fix in libTangerine std::strncpy(dtex->file_path, t_img.uri.c_str(), t_img.uri.size()); dtex->filepath_hash = utilFNV64a_str(t_img.uri.c_str()); std::memcpy(dtex->pixels, t_img.image.data(), dtex->data_len); return dtex; } #endif model* assetLoadFromFile(Assets* assets, memory_arena* arena, const char* filename) { tinygltf::Model t_mdl; tinygltf::TinyGLTF gltf_ctx; std::string err; std::string warn; LOG(Info) << "Loading model: " << filename << "\n"; if (!gltf_ctx.LoadASCIIFromFile(&t_mdl, &err, &warn, filename)) { LOG(Error) << "Error loading file: " << filename << " , msg: " << err << "\n"; return nullptr; } #if 0 dumpNodes(t_mdl); #endif // NOTE: assume we're working with a single buffer assert(t_mdl.buffers.size() == 1); model* mdl = initModel(assets, arena, t_mdl, filename); #if 0 // FIXME: probably a bug here with overwriting arena memory mdl->diffuse_texture = copyDiffuseTexture(textures, arena, t_mdl); if (mdl->diffuse_texture == nullptr) { LOG(Error) << "Error Loading diffuse texture\n"; // TODO: reclaim arena memory return nullptr; } #endif uint mesh_idx = 0; for (tinygltf::Node node : t_mdl.nodes) { if (node.mesh >= 0) { if (!parseMeshNode(&mdl->meshes[mesh_idx++], arena, node, t_mdl)) { LOG(Error) << "Error parsing node\n"; return nullptr; } } } return mdl; } model* assetGetCached(Assets* assets, uint64_t path_hash) { for (uint i = 0; i < assets->num_models; i++) { if (assets->models[i].filepath_hash == path_hash) return &assets->models[i]; } LOG(Debug) << "asset not cached: " << path_hash << "\n"; return nullptr; } // internal model* initModel(Assets* assets, memory_arena* arena, tinygltf::Model t_mdl, const char* filename) { // TODO: re-alloc array when out of space assert(assets->num_models < assets->max_models && arena != nullptr); model* mdl = &assets->models[assets->num_models]; assets->num_models++; uint buf_count = t_mdl.bufferViews.size(); mdl->meshes = ARENA_ALLOC(arena, mesh, buf_count); mdl->num_meshes = t_mdl.meshes.size(); mdl->filepath = arenaCopyCStr(arena, filename, MAX_PATH_SIZE); mdl->filepath_hash = utilFNV64a_str(mdl->filepath); return mdl; } bool copyBuffer(uint8_t*& buffer_ref, memory_arena* arena, int acc_idx, const tinygltf::Model& t_mdl) { const tinygltf::Accessor& acc = t_mdl.accessors[acc_idx]; const tinygltf::BufferView& bv = t_mdl.bufferViews[acc.bufferView]; const tinygltf::Buffer& t_buf = t_mdl.buffers[bv.buffer]; // TODO: clean up validation if (bv.target == TINYGLTF_TARGET_ARRAY_BUFFER) { assert(acc.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT && (acc.type == TINYGLTF_TYPE_VEC3 || acc.type == TINYGLTF_TYPE_VEC2)); } else if (bv.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) { assert(acc.type == TINYGLTF_TYPE_SCALAR && acc.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT); } else { LOG(Error) << "unknown target\n"; return false; } assert(bv.byteStride == 0); buffer_ref = ARENA_ALLOC(arena, u8, bv.byteLength); std::memcpy(buffer_ref, &t_buf.data[bv.byteOffset], bv.byteLength); return buffer_ref != nullptr; } // FIXME: need to implement tree structure for blender models to work properly glm::mat4* parseNodeTransform(memory_arena* arena, const tinygltf::Node* node) { if (node->rotation.size() == 4 && node->scale.size() == 3 && node->translation.size() == 3) { glm::mat4* xform = ARENA_ALLOC(arena, glm::mat4, 1); *xform = glm::mat4(1.0f); *xform = glm::rotate(*xform, (float) node->rotation[3], glm::vec3((float) node->rotation[0], (float) node->rotation[1], (float) node->rotation[2]) ); *xform = glm::scale(*xform, glm::vec3((float) node->scale[0], (float) node->scale[0], (float) node->scale[0]) ); *xform = glm::translate(*xform, glm::vec3((float) node->translation[0], (float) node->translation[0], (float) node->translation[0]) ); return xform; } LOG(Error) << "Unknown transform\n"; return nullptr; } bool parseMeshNode(mesh* m, memory_arena* arena, const tinygltf::Node& node, const tinygltf::Model& t_mdl) { tinygltf::Mesh t_mesh = t_mdl.meshes[node.mesh]; // NOTE: assume only 1 primitive object per mesh assert(t_mdl.meshes[node.mesh].primitives.size() == 1); tinygltf::Primitive prim = t_mesh.primitives[0]; // NOTE: verify assumptions about input assert(prim.attributes.find("POSITION") != prim.attributes.end() && prim.attributes.find("NORMAL") != prim.attributes.end() && prim.attributes.find("TEXCOORD_0") != prim.attributes.end() && prim.indices >= 0); const tinygltf::Accessor& vert_acc = t_mdl.accessors[prim.attributes["POSITION"]]; const tinygltf::Accessor& index_acc = t_mdl.accessors[prim.indices]; m->num_vertices = vert_acc.count; m->num_indices = index_acc.count; // FIXME: the node transforms from blender only work as part of a node tree #if 1 m->xform = ARENA_ALLOC(arena, glm::mat4, 1); *m->xform = glm::mat4(1.0f); #else m->xform = parseNodeTransform(arena, &node); #endif if (m->xform != nullptr && copyBuffer((uint8_t*&) m->vertices, arena, prim.attributes["POSITION"], t_mdl) && copyBuffer((uint8_t*&) m->normals, arena, prim.attributes["NORMAL"], t_mdl) && copyBuffer((uint8_t*&) m->uvs, arena, prim.attributes["TEXCOORD_0"], t_mdl) && copyBuffer((uint8_t*&) m->indices, arena, prim.indices, t_mdl)) { return true; } return false; } const char* getTargetStr(int target) { switch (target) { case 34962: return "GL_ARRAY_BUFFER"; case 34963: return "GL_ELEMENT_ARRAY_BUFFER"; default: return "UNKNOWN"; } } const char* getElementType(int type) { switch (type) { case 2: return "TINYGLTF_TYPE_VEC2"; case 3: return "TINYGLTF_TYPE_VEC3"; case 4: return "TINYGLTF_TYPE_VEC4"; case 65: return "TINYGLTF_TYPE_SCALAR"; default: return "UNKNOWN"; } } const char* getComponentType(int componentType) { switch (componentType) { case 5123: return "TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT"; case 5124: return "TINYGLTF_COMPONENT_TYPE_INT"; case 5125: return "TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT"; case 5126: return "TINYGLTF_COMPONENT_TYPE_FLOAT"; case 5130: return "TINYGLTF_COMPONENT_TYPE_DOUBLE"; default: return "UNKOWN"; } } const char* getDrawMode(int drawMode) { switch (drawMode) { case 0: return "TINYGLTF_MODE_POINTS"; case 1: return "TINYGLTF_MODE_LINE"; case 2: return "TINYGLTF_MODE_LINE_LOOP"; case 3: return "TINYGLTF_MODE_LINE_STRIP"; case 4: return "TINYGLTF_MODE_TRIANGLES"; case 5: return "TINYGLTF_MODE_TRIANGLE_STRIP"; case 6: return "TINYGLTF_MODE_TRIANGLE_FAN"; default: return "UNKOWN MODE"; } } void dumpBuffer(tinygltf::Model model, tinygltf::Accessor acc) { size_t bv_idx = acc.bufferView; assert(bv_idx >= 0 && bv_idx < model.bufferViews.size()); tinygltf::BufferView bv = model.bufferViews[bv_idx]; LOG(Debug) << "-----------------------\n"; LOG(Debug) << "buf idx: " << bv_idx << "\n"; LOG(Debug) << "buf target: " << getTargetStr(bv.target) << "\n"; LOG(Debug) << "buf len: " << bv.byteLength << "\n"; LOG(Debug) << "buf offset: " << bv.byteOffset << "\n"; LOG(Debug) << "buf stride: " << bv.byteStride << "\n"; LOG(Debug) << "acc component type: " << getComponentType(acc.componentType) << "\n"; LOG(Debug) << "acc element type: " << getElementType(acc.type) << "\n"; } void dumpNodes(tinygltf::Model t_mdl) { for (tinygltf::Node node : t_mdl.nodes) { LOG(Debug) << "##################\n"; LOG(Debug) << "node name: " << node.name << "\n"; LOG(Debug) << "node mesh idx: " << node.mesh << "\n"; if (node.mesh >= 0) { tinygltf::Mesh t_mesh = t_mdl.meshes[node.mesh]; LOG(Debug) << "node mesh name: " << t_mesh.name << "\n"; // NOTE: assume only 1 primitive object per mesh assert(t_mdl.meshes[node.mesh].primitives.size() == 1); tinygltf::Primitive prim = t_mesh.primitives[0]; LOG(Debug) << "node draw mode: " << getDrawMode(prim.mode) << "\n"; for (auto& att : prim.attributes) { LOG(Debug) << "dumping buffer: " << att.first << "\n"; dumpBuffer(t_mdl, t_mdl.accessors[att.second]); } LOG(Debug) << "dumping index buffer\n"; dumpBuffer(t_mdl, t_mdl.accessors[prim.indices]); } else { LOG(Debug) << "Not a mesh node\n"; } } }