diff --git a/.gitignore b/.gitignore index fe1a865..880e084 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ *.slo *.lo *.o -*.obj +build/*.obj *.elf *.lst *.nds diff --git a/CMakeLists.txt b/CMakeLists.txt index 9be5726..1eca64c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,7 @@ project(xrbDS) include_directories( engine include/xrbds + include/tinyobjloader ) file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS diff --git a/engine/components/component.cpp b/engine/components/component.cpp new file mode 100644 index 0000000..330f849 --- /dev/null +++ b/engine/components/component.cpp @@ -0,0 +1,5 @@ +#include "components/component.h" + +PtrUnq Component::Initialize() { + return PtrUnq(new Component()); +} \ No newline at end of file diff --git a/engine/components/mesh_filter.cpp b/engine/components/mesh_filter.cpp new file mode 100644 index 0000000..97e6e77 --- /dev/null +++ b/engine/components/mesh_filter.cpp @@ -0,0 +1,15 @@ +#include "components/mesh_filter.h" + +#include "resources/resource.h" +#include "utils/file.h" + +MeshFilter::MeshFilter(const FString &path) { + // Load the mesh from the file + auto vertices = Utils::File::ReadObjFile(path); + + mesh = LoadResource(vertices); +} + +PtrUnq MeshFilter::Initialize(const FString &path) { + return PtrUnq(new MeshFilter(path)); +} \ No newline at end of file diff --git a/engine/core/engine.cpp b/engine/core/engine.cpp index 5fbc955..f72abac 100644 --- a/engine/core/engine.cpp +++ b/engine/core/engine.cpp @@ -12,6 +12,7 @@ #include "graphics/renderer.h" #include "scene/3d/node_3d.h" #include "utils/file.h" +#include "scene/3d/mesh_instance_3d.h" ///////////////////////////////////////////////////////// // Main function @@ -43,24 +44,18 @@ PtrUnq &Engine::GetInstance() { } void Engine::run() { - // Initialize the main loop - // - VBlank IRQ - irqSet(IRQ_VBLANK, Engine::VblankCallback); - VblankCallback(); - // TEST ------------------------ - PtrShr node = NewNode(); - node->getComponent()->position = {1.0f, 2.0f, 3.0f}; - - FString ahuevo = Utils::File::ReadTextFile("hola.txt"); - iprintf("ahuevo: %s\n", ahuevo.c_str()); + // Create a MeshInstance3D + PtrShr meshInstance = + NewNode("meshes/mococo/mococo.obj"); // ----------------------------- while (pmMainLoop()) { processInput(); update(); + render(); } } @@ -70,9 +65,22 @@ void Engine::update() { // Update } -void Engine::render() { Renderer::GetInstance()->render(); } +void Engine::render() { + if (!ComponentManager::GetInstance()) + return; + PtrUnq &renderer = Renderer::GetInstance(); -void Engine::VblankCallback() { Engine::GetInstance()->render(); } + renderer->beginFrame(); + + auto view = ComponentManager::GetInstance()->view(); + for (auto &[entity, transform, meshFilter] : view) { + renderer->render(*transform, *meshFilter); + } + + renderer->endFrame(); + + swiWaitForVBlank(); +} Engine::Engine() { // Initialize nitroFS diff --git a/engine/graphics/renderer.cpp b/engine/graphics/renderer.cpp index d154e75..5588e29 100644 --- a/engine/graphics/renderer.cpp +++ b/engine/graphics/renderer.cpp @@ -1,30 +1,21 @@ #include "renderer.h" +#include "core/types.h" +#include "components/mesh_filter.h" +#include "components/transform.h" +#include "resources/mesh.h" #include #include -std::unique_ptr Renderer::Instance; +PtrUnq Renderer::Instance; -std::unique_ptr &Renderer::GetInstance() { +PtrUnq &Renderer::GetInstance() { if (!Instance) - Instance = std::unique_ptr(new Renderer()); + Instance = PtrUnq(new Renderer()); return Instance; } -void Renderer::render() { - clearScreen(); // Clear the screen - beginFrame(); // Prepare for rendering - glLoadIdentity(); // Load the identity matrix - - // Render scene - glTranslatef(0.f, 0.f, -4.f); // Move the camera back - glRotatef(45, 1, 1, 0); // Rotation - drawCube(1.0f); // Draw a cube - - endFrame(); // Finish rendering -} - void Renderer::beginFrame() { glMatrixMode(GL_PROJECTION); glLoadIdentity(); @@ -34,6 +25,30 @@ void Renderer::beginFrame() { glPushMatrix(); // Save the current matrix state glPolyFmt(POLY_ALPHA(31) | POLY_CULL_BACK); + + glLoadIdentity(); // Load the identity matrix + + // Render scene + glTranslatef(0.f, 0.f, -4.f); // Move the camera back + glRotatef(45, 1, 1, 0); // Rotation +} + +void Renderer::render(const Transform &transform, + const MeshFilter &meshFilter) { + // drawCube(1.0f); // Draw a cube + glBegin(GL_TRIANGLES); + + for (const auto &shapes : meshFilter.getMesh()->getVertices()) { + for (const auto &vertex : shapes) { + glColor3f(1.0, 0.0, 0.0); // Red + glVertex3f(vertex.position.x, vertex.position.y, vertex.position.z); + glNormal3f(vertex.normal.x, vertex.normal.y, vertex.normal.z); + glTexCoord2f(vertex.texCoords.x, vertex.texCoords.y); + } + } + + glEnd(); + glPopMatrix(1); } void Renderer::endFrame() { diff --git a/engine/graphics/renderer.h b/engine/graphics/renderer.h index 780b34e..2046f88 100644 --- a/engine/graphics/renderer.h +++ b/engine/graphics/renderer.h @@ -23,8 +23,12 @@ #ifndef RENDERER_H #define RENDERER_H +#include "core/types.h" #include +struct Transform; +struct MeshFilter; + /** * @class Renderer * @brief A base class for rendering operations. @@ -46,7 +50,14 @@ public: * * @return A unique pointer to a Renderer instance. */ - static std::unique_ptr &GetInstance(); + static PtrUnq &GetInstance(); + + /** + * @brief Prepares the renderer for a new frame. + * + * This method is called at the beginning of the rendering process. + */ + void beginFrame(); /** * @brief Renders the current frame. @@ -54,15 +65,7 @@ public: * This method handles the rendering process, including frame management * and drawing operations. */ - void render(); - -private: - /** - * @brief Prepares the renderer for a new frame. - * - * This method is called at the beginning of the rendering process. - */ - void beginFrame(); + void render(const Transform &transform, const MeshFilter &meshFilter); /** * @brief Finalizes the rendering of the current frame. @@ -71,14 +74,6 @@ private: */ void endFrame(); - /** - * @brief Clears the screen to prepare for rendering. - * - * This method resets the screen to a default state, typically clearing - * any previous drawings. - */ - void clearScreen(); - /** * @brief Draws a cube of the specified size. * @@ -86,7 +81,16 @@ private: */ void drawCube(float size); - static std::unique_ptr Instance; +private: + static PtrUnq Instance; + + /** + * @brief Clears the screen to prepare for rendering. + * + * This method resets the screen to a default state, typically clearing + * any previous drawings. + */ + void clearScreen(); /** * @brief Constructor for the Renderer class. diff --git a/engine/resources/mesh.cpp b/engine/resources/mesh.cpp index 34be853..b98f9e5 100644 --- a/engine/resources/mesh.cpp +++ b/engine/resources/mesh.cpp @@ -1 +1,8 @@ -#include "resources/mesh.h" \ No newline at end of file +#include "resources/mesh.h" + +Mesh Mesh::Load(const TVector> &vertices) { + return std::move(Mesh(vertices)); +} + +Mesh::Mesh(const TVector> &vertices) + : Super(), vertices(vertices) {} \ No newline at end of file diff --git a/engine/resources/resource.cpp b/engine/resources/resource.cpp index c961427..2990c37 100644 --- a/engine/resources/resource.cpp +++ b/engine/resources/resource.cpp @@ -1,6 +1,3 @@ #include "resources/resource.h" -Resource::Resource(const FString &name, const FString &path) - : name(name), path(path) { - // Constructor implementation -} \ No newline at end of file +Resource Resource::Load() { return std::move(Resource()); } \ No newline at end of file diff --git a/engine/scene/3d/mesh_instance_3d.cpp b/engine/scene/3d/mesh_instance_3d.cpp index 7424b9c..0d1bf6a 100644 --- a/engine/scene/3d/mesh_instance_3d.cpp +++ b/engine/scene/3d/mesh_instance_3d.cpp @@ -2,7 +2,7 @@ #include "components/mesh_filter.h" -MeshInstance3D::MeshInstance3D() { - ComponentManager::GetInstance()->addComponent(id, - MeshFilter("test")); +MeshInstance3D::MeshInstance3D(const FString &path) { + ComponentManager::GetInstance()->addComponent( + id, *MeshFilter::Initialize(path)); } \ No newline at end of file diff --git a/engine/utils/file.cpp b/engine/utils/file.cpp index 9f31720..4b22362 100644 --- a/engine/utils/file.cpp +++ b/engine/utils/file.cpp @@ -1,5 +1,8 @@ #include "utils/file.h" +#define TINYOBJLOADER_IMPLEMENTATION +#include "tiny_obj_loader.h" + namespace Utils::File { FString AssetPath(const FString &path) { return "nitro:/" + path; } @@ -27,4 +30,78 @@ FString ReadTextFile(const FString &path) { return std::move(result); } +TVector> ReadObjFile(const FString &path) { + tinyobj::attrib_t attrib; + TVector shapes; + TVector materials; + FString err; + + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, + AssetPath(path).c_str(), + AssetPath(path + "/..").c_str(), true); + + if (ret) + iprintf("Cargado correctamente\n"); + else + iprintf("Error al cargar el archivo: %s\n", err.c_str()); + + iprintf("Numero de materiales: %d\n", materials.size()); + + TVector> vertices; + + // Loop over shapes + for (size_t s = 0; s < shapes.size(); s++) { + vertices.push_back({}); + // Loop over faces(polygon) + size_t index_offset = 0; + for (size_t f = 0; f < shapes[s].mesh.num_face_vertices.size(); f++) { + size_t fv = size_t(shapes[s].mesh.num_face_vertices[f]); + + // Loop over vertices in the face. + for (size_t v = 0; v < fv; v++) { + // access to vertex + tinyobj::index_t idx = shapes[s].mesh.indices[index_offset + v]; + + tinyobj::real_t vx = attrib.vertices[3 * size_t(idx.vertex_index) + 0]; + tinyobj::real_t vy = attrib.vertices[3 * size_t(idx.vertex_index) + 1]; + tinyobj::real_t vz = attrib.vertices[3 * size_t(idx.vertex_index) + 2]; + + FVector3 positions(vx, vy, vz); + + // Check if `normal_index` is zero or positive. negative = no normal + // data + FVector3 normals(0, 0, 0); + if (idx.normal_index >= 0) { + tinyobj::real_t nx = attrib.normals[3 * size_t(idx.normal_index) + 0]; + tinyobj::real_t ny = attrib.normals[3 * size_t(idx.normal_index) + 1]; + tinyobj::real_t nz = attrib.normals[3 * size_t(idx.normal_index) + 2]; + + normals = FVector3(nx, ny, nz); + } + + // Check if `texcoord_index` is zero or positive. negative = no texcoord + // data + FVector2 texCoords(0, 0); + if (idx.texcoord_index >= 0) { + tinyobj::real_t tx = + attrib.texcoords[2 * size_t(idx.texcoord_index) + 0]; + tinyobj::real_t ty = + attrib.texcoords[2 * size_t(idx.texcoord_index) + 1]; + + texCoords = FVector2(tx, ty); + } + + vertices[s].push_back(FVertex(positions, normals, texCoords)); + } + + index_offset += fv; + + // per-face material + // shapes[s].mesh.material_ids[f]; + } + } + + return std::move(vertices); +} + } // namespace Utils::File diff --git a/include/tinyobjloader/tiny_obj_loader.h b/include/tinyobjloader/tiny_obj_loader.h new file mode 100644 index 0000000..ee44076 --- /dev/null +++ b/include/tinyobjloader/tiny_obj_loader.h @@ -0,0 +1,2029 @@ +/* +The MIT License (MIT) + +Copyright (c) 2012-2016 Syoyo Fujita and many contributors. + +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. +*/ + +// +// version 1.0.6 : Add TINYOBJLOADER_USE_DOUBLE option(#124) +// version 1.0.5 : Ignore `Tr` when `d` exists in MTL(#43) +// version 1.0.4 : Support multiple filenames for 'mtllib'(#112) +// version 1.0.3 : Support parsing texture options(#85) +// version 1.0.2 : Improve parsing speed by about a factor of 2 for large +// files(#105) +// version 1.0.1 : Fixes a shape is lost if obj ends with a 'usemtl'(#104) +// version 1.0.0 : Change data structure. Change license from BSD to MIT. +// + +// +// Use this in *one* .cc +// #define TINYOBJLOADER_IMPLEMENTATION +// #include "tiny_obj_loader.h" +// + +#ifndef TINY_OBJ_LOADER_H_ +#define TINY_OBJ_LOADER_H_ + +#include +#include +#include + +namespace tinyobj { + +// https://en.wikipedia.org/wiki/Wavefront_.obj_file says ... +// +// -blendu on | off # set horizontal texture blending +// (default on) +// -blendv on | off # set vertical texture blending +// (default on) +// -boost real_value # boost mip-map sharpness +// -mm base_value gain_value # modify texture map values (default +// 0 1) +// # base_value = brightness, +// gain_value = contrast +// -o u [v [w]] # Origin offset (default +// 0 0 0) +// -s u [v [w]] # Scale (default +// 1 1 1) +// -t u [v [w]] # Turbulence (default +// 0 0 0) +// -texres resolution # texture resolution to create +// -clamp on | off # only render texels in the clamped +// 0-1 range (default off) +// # When unclamped, textures are +// repeated across a surface, +// # when clamped, only texels which +// fall within the 0-1 +// # range are rendered. +// -bm mult_value # bump multiplier (for bump maps +// only) +// +// -imfchan r | g | b | m | l | z # specifies which channel of the file +// is used to +// # create a scalar or bump texture. +// r:red, g:green, +// # b:blue, m:matte, l:luminance, +// z:z-depth.. +// # (the default for bump is 'l' and +// for decal is 'm') +// bump -imfchan r bumpmap.tga # says to use the red channel of +// bumpmap.tga as the bumpmap +// +// For reflection maps... +// +// -type sphere # specifies a sphere for a "refl" +// reflection map +// -type cube_top | cube_bottom | # when using a cube map, the texture +// file for each +// cube_front | cube_back | # side of the cube is specified +// separately +// cube_left | cube_right + +#ifdef TINYOBJLOADER_USE_DOUBLE + //#pragma message "using double" + typedef double real_t; +#else + //#pragma message "using float" + typedef float real_t; +#endif + +typedef enum { + TEXTURE_TYPE_NONE, // default + TEXTURE_TYPE_SPHERE, + TEXTURE_TYPE_CUBE_TOP, + TEXTURE_TYPE_CUBE_BOTTOM, + TEXTURE_TYPE_CUBE_FRONT, + TEXTURE_TYPE_CUBE_BACK, + TEXTURE_TYPE_CUBE_LEFT, + TEXTURE_TYPE_CUBE_RIGHT +} texture_type_t; + +typedef struct { + texture_type_t type; // -type (default TEXTURE_TYPE_NONE) + real_t sharpness; // -boost (default 1.0?) + real_t brightness; // base_value in -mm option (default 0) + real_t contrast; // gain_value in -mm option (default 1) + real_t origin_offset[3]; // -o u [v [w]] (default 0 0 0) + real_t scale[3]; // -s u [v [w]] (default 1 1 1) + real_t turbulence[3]; // -t u [v [w]] (default 0 0 0) + // int texture_resolution; // -texres resolution (default = ?) TODO + bool clamp; // -clamp (default false) + char imfchan; // -imfchan (the default for bump is 'l' and for decal is 'm') + bool blendu; // -blendu (default on) + bool blendv; // -blendv (default on) + real_t bump_multiplier; // -bm (for bump maps only, default 1.0) +} texture_option_t; + +typedef struct { + std::string name; + + real_t ambient[3]; + real_t diffuse[3]; + real_t specular[3]; + real_t transmittance[3]; + real_t emission[3]; + real_t shininess; + real_t ior; // index of refraction + real_t dissolve; // 1 == opaque; 0 == fully transparent + // illumination model (see http://www.fileformat.info/format/material/) + int illum; + + int dummy; // Suppress padding warning. + + std::string ambient_texname; // map_Ka + std::string diffuse_texname; // map_Kd + std::string specular_texname; // map_Ks + std::string specular_highlight_texname; // map_Ns + std::string bump_texname; // map_bump, bump + std::string displacement_texname; // disp + std::string alpha_texname; // map_d + + texture_option_t ambient_texopt; + texture_option_t diffuse_texopt; + texture_option_t specular_texopt; + texture_option_t specular_highlight_texopt; + texture_option_t bump_texopt; + texture_option_t displacement_texopt; + texture_option_t alpha_texopt; + + // PBR extension + // http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr + real_t roughness; // [0, 1] default 0 + real_t metallic; // [0, 1] default 0 + real_t sheen; // [0, 1] default 0 + real_t clearcoat_thickness; // [0, 1] default 0 + real_t clearcoat_roughness; // [0, 1] default 0 + real_t anisotropy; // aniso. [0, 1] default 0 + real_t anisotropy_rotation; // anisor. [0, 1] default 0 + real_t pad0; + real_t pad1; + std::string roughness_texname; // map_Pr + std::string metallic_texname; // map_Pm + std::string sheen_texname; // map_Ps + std::string emissive_texname; // map_Ke + std::string normal_texname; // norm. For normal mapping. + + texture_option_t roughness_texopt; + texture_option_t metallic_texopt; + texture_option_t sheen_texopt; + texture_option_t emissive_texopt; + texture_option_t normal_texopt; + + int pad2; + + std::map unknown_parameter; +} material_t; + +typedef struct { + std::string name; + + std::vector intValues; + std::vector floatValues; + std::vector stringValues; +} tag_t; + +// Index struct to support different indices for vtx/normal/texcoord. +// -1 means not used. +typedef struct { + int vertex_index; + int normal_index; + int texcoord_index; +} index_t; + +typedef struct { + std::vector indices; + std::vector num_face_vertices; // The number of vertices per + // face. 3 = polygon, 4 = quad, + // ... Up to 255. + std::vector material_ids; // per-face material ID + std::vector tags; // SubD tag +} mesh_t; + +typedef struct { + std::string name; + mesh_t mesh; +} shape_t; + +// Vertex attributes +typedef struct { + std::vector vertices; // 'v' + std::vector normals; // 'vn' + std::vector texcoords; // 'vt' +} attrib_t; + +typedef struct callback_t_ { + // W is optional and set to 1 if there is no `w` item in `v` line + void (*vertex_cb)(void *user_data, real_t x, real_t y, real_t z, real_t w); + void (*normal_cb)(void *user_data, real_t x, real_t y, real_t z); + + // y and z are optional and set to 0 if there is no `y` and/or `z` item(s) in + // `vt` line. + void (*texcoord_cb)(void *user_data, real_t x, real_t y, real_t z); + + // called per 'f' line. num_indices is the number of face indices(e.g. 3 for + // triangle, 4 for quad) + // 0 will be passed for undefined index in index_t members. + void (*index_cb)(void *user_data, index_t *indices, int num_indices); + // `name` material name, `material_id` = the array index of material_t[]. -1 + // if + // a material not found in .mtl + void (*usemtl_cb)(void *user_data, const char *name, int material_id); + // `materials` = parsed material data. + void (*mtllib_cb)(void *user_data, const material_t *materials, + int num_materials); + // There may be multiple group names + void (*group_cb)(void *user_data, const char **names, int num_names); + void (*object_cb)(void *user_data, const char *name); + + callback_t_() + : vertex_cb(NULL), + normal_cb(NULL), + texcoord_cb(NULL), + index_cb(NULL), + usemtl_cb(NULL), + mtllib_cb(NULL), + group_cb(NULL), + object_cb(NULL) {} +} callback_t; + +class MaterialReader { + public: + MaterialReader() {} + virtual ~MaterialReader(); + + virtual bool operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, + std::string *err) = 0; +}; + +class MaterialFileReader : public MaterialReader { + public: + explicit MaterialFileReader(const std::string &mtl_basedir) + : m_mtlBaseDir(mtl_basedir) {} + virtual ~MaterialFileReader() {} + virtual bool operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, std::string *err); + + private: + std::string m_mtlBaseDir; +}; + +class MaterialStreamReader : public MaterialReader { + public: + explicit MaterialStreamReader(std::istream &inStream) + : m_inStream(inStream) {} + virtual ~MaterialStreamReader() {} + virtual bool operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, std::string *err); + + private: + std::istream &m_inStream; +}; + +/// Loads .obj from a file. +/// 'attrib', 'shapes' and 'materials' will be filled with parsed shape data +/// 'shapes' will be filled with parsed shape data +/// Returns true when loading .obj become success. +/// Returns warning and error message into `err` +/// 'mtl_basedir' is optional, and used for base directory for .mtl file. +/// In default(`NULL'), .mtl file is searched from an application's working +/// directory. +/// 'triangulate' is optional, and used whether triangulate polygon face in .obj +/// or not. +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *err, + const char *filename, const char *mtl_basedir = NULL, + bool triangulate = true); + +/// Loads .obj from a file with custom user callback. +/// .mtl is loaded as usual and parsed material_t data will be passed to +/// `callback.mtllib_cb`. +/// Returns true when loading .obj/.mtl become success. +/// Returns warning and error message into `err` +/// See `examples/callback_api/` for how to use this function. +bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, + void *user_data = NULL, + MaterialReader *readMatFn = NULL, + std::string *err = NULL); + +/// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve +/// std::istream for materials. +/// Returns true when loading .obj become success. +/// Returns warning and error message into `err` +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *err, + std::istream *inStream, MaterialReader *readMatFn = NULL, + bool triangulate = true); + +/// Loads materials into std::map +void LoadMtl(std::map *material_map, + std::vector *materials, std::istream *inStream, + std::string *warning); + +} // namespace tinyobj + +#endif // TINY_OBJ_LOADER_H_ + +#ifdef TINYOBJLOADER_IMPLEMENTATION +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace tinyobj { + +MaterialReader::~MaterialReader() {} + +#define TINYOBJ_SSCANF_BUFFER_SIZE (4096) + +struct vertex_index { + int v_idx, vt_idx, vn_idx; + vertex_index() : v_idx(-1), vt_idx(-1), vn_idx(-1) {} + explicit vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {} + vertex_index(int vidx, int vtidx, int vnidx) + : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {} +}; + +struct tag_sizes { + tag_sizes() : num_ints(0), num_reals(0), num_strings(0) {} + int num_ints; + int num_reals; + int num_strings; +}; + +struct obj_shape { + std::vector v; + std::vector vn; + std::vector vt; +}; + +// See +// http://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf +static std::istream &safeGetline(std::istream &is, std::string &t) { + t.clear(); + + // The characters in the stream are read one-by-one using a std::streambuf. + // That is faster than reading them one-by-one using the std::istream. + // Code that uses streambuf this way must be guarded by a sentry object. + // The sentry object performs various tasks, + // such as thread synchronization and updating the stream state. + + std::istream::sentry se(is, true); + std::streambuf *sb = is.rdbuf(); + + for (;;) { + int c = sb->sbumpc(); + switch (c) { + case '\n': + return is; + case '\r': + if (sb->sgetc() == '\n') sb->sbumpc(); + return is; + case EOF: + // Also handle the case when the last line has no line ending + if (t.empty()) is.setstate(std::ios::eofbit); + return is; + default: + t += static_cast(c); + } + } +} + +#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t')) +#define IS_DIGIT(x) \ + (static_cast((x) - '0') < static_cast(10)) +#define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0')) + +// Make index zero-base, and also support relative index. +static inline int fixIndex(int idx, int n) { + if (idx > 0) return idx - 1; + if (idx == 0) return 0; + return n + idx; // negative value = relative +} + +static inline std::string parseString(const char **token) { + std::string s; + (*token) += strspn((*token), " \t"); + size_t e = strcspn((*token), " \t\r"); + s = std::string((*token), &(*token)[e]); + (*token) += e; + return s; +} + +static inline int parseInt(const char **token) { + (*token) += strspn((*token), " \t"); + int i = atoi((*token)); + (*token) += strcspn((*token), " \t\r"); + return i; +} + +// Tries to parse a floating point number located at s. +// +// s_end should be a location in the string where reading should absolutely +// stop. For example at the end of the string, to prevent buffer overflows. +// +// Parses the following EBNF grammar: +// sign = "+" | "-" ; +// END = ? anything not in digit ? +// digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; +// integer = [sign] , digit , {digit} ; +// decimal = integer , ["." , integer] ; +// float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ; +// +// Valid strings are for example: +// -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2 +// +// If the parsing is a success, result is set to the parsed value and true +// is returned. +// +// The function is greedy and will parse until any of the following happens: +// - a non-conforming character is encountered. +// - s_end is reached. +// +// The following situations triggers a failure: +// - s >= s_end. +// - parse failure. +// +static bool tryParseDouble(const char *s, const char *s_end, double *result) { + if (s >= s_end) { + return false; + } + + double mantissa = 0.0; + // This exponent is base 2 rather than 10. + // However the exponent we parse is supposed to be one of ten, + // thus we must take care to convert the exponent/and or the + // mantissa to a * 2^E, where a is the mantissa and E is the + // exponent. + // To get the final double we will use ldexp, it requires the + // exponent to be in base 2. + int exponent = 0; + + // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED + // TO JUMP OVER DEFINITIONS. + char sign = '+'; + char exp_sign = '+'; + char const *curr = s; + + // How many characters were read in a loop. + int read = 0; + // Tells whether a loop terminated due to reaching s_end. + bool end_not_reached = false; + + /* + BEGIN PARSING. + */ + + // Find out what sign we've got. + if (*curr == '+' || *curr == '-') { + sign = *curr; + curr++; + } else if (IS_DIGIT(*curr)) { /* Pass through. */ + } else { + goto fail; + } + + // Read the integer part. + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { + mantissa *= 10; + mantissa += static_cast(*curr - 0x30); + curr++; + read++; + end_not_reached = (curr != s_end); + } + + // We must make sure we actually got something. + if (read == 0) goto fail; + // We allow numbers of form "#", "###" etc. + if (!end_not_reached) goto assemble; + + // Read the decimal part. + if (*curr == '.') { + curr++; + read = 1; + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { + static const double pow_lut[] = { + 1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001, + }; + const int lut_entries = sizeof pow_lut / sizeof pow_lut[0]; + + // NOTE: Don't use powf here, it will absolutely murder precision. + mantissa += static_cast(*curr - 0x30) * + (read < lut_entries ? pow_lut[read] : std::pow(10.0, -read)); + read++; + curr++; + end_not_reached = (curr != s_end); + } + } else if (*curr == 'e' || *curr == 'E') { + } else { + goto assemble; + } + + if (!end_not_reached) goto assemble; + + // Read the exponent part. + if (*curr == 'e' || *curr == 'E') { + curr++; + // Figure out if a sign is present and if it is. + end_not_reached = (curr != s_end); + if (end_not_reached && (*curr == '+' || *curr == '-')) { + exp_sign = *curr; + curr++; + } else if (IS_DIGIT(*curr)) { /* Pass through. */ + } else { + // Empty E is not allowed. + goto fail; + } + + read = 0; + end_not_reached = (curr != s_end); + while (end_not_reached && IS_DIGIT(*curr)) { + exponent *= 10; + exponent += static_cast(*curr - 0x30); + curr++; + read++; + end_not_reached = (curr != s_end); + } + exponent *= (exp_sign == '+' ? 1 : -1); + if (read == 0) goto fail; + } + +assemble: + *result = + (sign == '+' ? 1 : -1) * + (exponent ? std::ldexp(mantissa * std::pow(5.0, exponent), exponent) : mantissa); + return true; +fail: + return false; +} + +static inline real_t parseReal(const char **token, double default_value = 0.0) { + (*token) += strspn((*token), " \t"); + const char *end = (*token) + strcspn((*token), " \t\r"); + double val = default_value; + tryParseDouble((*token), end, &val); + real_t f = static_cast(val); + (*token) = end; + return f; +} + +static inline void parseReal2(real_t *x, real_t *y, const char **token, + const double default_x = 0.0, + const double default_y = 0.0) { + (*x) = parseReal(token, default_x); + (*y) = parseReal(token, default_y); +} + +static inline void parseReal3(real_t *x, real_t *y, real_t *z, const char **token, + const double default_x = 0.0, + const double default_y = 0.0, + const double default_z = 0.0) { + (*x) = parseReal(token, default_x); + (*y) = parseReal(token, default_y); + (*z) = parseReal(token, default_z); +} + +static inline void parseV(real_t *x, real_t *y, real_t *z, real_t *w, + const char **token, const double default_x = 0.0, + const double default_y = 0.0, + const double default_z = 0.0, + const double default_w = 1.0) { + (*x) = parseReal(token, default_x); + (*y) = parseReal(token, default_y); + (*z) = parseReal(token, default_z); + (*w) = parseReal(token, default_w); +} + +static inline bool parseOnOff(const char **token, bool default_value = true) { + (*token) += strspn((*token), " \t"); + const char *end = (*token) + strcspn((*token), " \t\r"); + + bool ret = default_value; + if ((0 == strncmp((*token), "on", 2))) { + ret = true; + } else if ((0 == strncmp((*token), "off", 3))) { + ret = false; + } + + (*token) = end; + return ret; +} + +static inline texture_type_t parseTextureType( + const char **token, texture_type_t default_value = TEXTURE_TYPE_NONE) { + (*token) += strspn((*token), " \t"); + const char *end = (*token) + strcspn((*token), " \t\r"); + texture_type_t ty = default_value; + + if ((0 == strncmp((*token), "cube_top", strlen("cube_top")))) { + ty = TEXTURE_TYPE_CUBE_TOP; + } else if ((0 == strncmp((*token), "cube_bottom", strlen("cube_bottom")))) { + ty = TEXTURE_TYPE_CUBE_BOTTOM; + } else if ((0 == strncmp((*token), "cube_left", strlen("cube_left")))) { + ty = TEXTURE_TYPE_CUBE_LEFT; + } else if ((0 == strncmp((*token), "cube_right", strlen("cube_right")))) { + ty = TEXTURE_TYPE_CUBE_RIGHT; + } else if ((0 == strncmp((*token), "cube_front", strlen("cube_front")))) { + ty = TEXTURE_TYPE_CUBE_FRONT; + } else if ((0 == strncmp((*token), "cube_back", strlen("cube_back")))) { + ty = TEXTURE_TYPE_CUBE_BACK; + } else if ((0 == strncmp((*token), "sphere", strlen("sphere")))) { + ty = TEXTURE_TYPE_SPHERE; + } + + (*token) = end; + return ty; +} + +static tag_sizes parseTagTriple(const char **token) { + tag_sizes ts; + + ts.num_ints = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return ts; + } + (*token)++; + + ts.num_reals = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return ts; + } + (*token)++; + + ts.num_strings = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r") + 1; + + return ts; +} + +// Parse triples with index offsets: i, i/j/k, i//k, i/j +static vertex_index parseTriple(const char **token, int vsize, int vnsize, + int vtsize) { + vertex_index vi(-1); + + vi.v_idx = fixIndex(atoi((*token)), vsize); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return vi; + } + (*token)++; + + // i//k + if ((*token)[0] == '/') { + (*token)++; + vi.vn_idx = fixIndex(atoi((*token)), vnsize); + (*token) += strcspn((*token), "/ \t\r"); + return vi; + } + + // i/j/k or i/j + vi.vt_idx = fixIndex(atoi((*token)), vtsize); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return vi; + } + + // i/j/k + (*token)++; // skip '/' + vi.vn_idx = fixIndex(atoi((*token)), vnsize); + (*token) += strcspn((*token), "/ \t\r"); + return vi; +} + +// Parse raw triples: i, i/j/k, i//k, i/j +static vertex_index parseRawTriple(const char **token) { + vertex_index vi(static_cast(0)); // 0 is an invalid index in OBJ + + vi.v_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return vi; + } + (*token)++; + + // i//k + if ((*token)[0] == '/') { + (*token)++; + vi.vn_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + return vi; + } + + // i/j/k or i/j + vi.vt_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + if ((*token)[0] != '/') { + return vi; + } + + // i/j/k + (*token)++; // skip '/' + vi.vn_idx = atoi((*token)); + (*token) += strcspn((*token), "/ \t\r"); + return vi; +} + +static bool ParseTextureNameAndOption(std::string *texname, + texture_option_t *texopt, + const char *linebuf, const bool is_bump) { + // @todo { write more robust lexer and parser. } + bool found_texname = false; + std::string texture_name; + + // Fill with default value for texopt. + if (is_bump) { + texopt->imfchan = 'l'; + } else { + texopt->imfchan = 'm'; + } + texopt->bump_multiplier = 1.0f; + texopt->clamp = false; + texopt->blendu = true; + texopt->blendv = true; + texopt->sharpness = 1.0f; + texopt->brightness = 0.0f; + texopt->contrast = 1.0f; + texopt->origin_offset[0] = 0.0f; + texopt->origin_offset[1] = 0.0f; + texopt->origin_offset[2] = 0.0f; + texopt->scale[0] = 1.0f; + texopt->scale[1] = 1.0f; + texopt->scale[2] = 1.0f; + texopt->turbulence[0] = 0.0f; + texopt->turbulence[1] = 0.0f; + texopt->turbulence[2] = 0.0f; + texopt->type = TEXTURE_TYPE_NONE; + + const char *token = linebuf; // Assume line ends with NULL + + while (!IS_NEW_LINE((*token))) { + if ((0 == strncmp(token, "-blendu", 7)) && IS_SPACE((token[7]))) { + token += 8; + texopt->blendu = parseOnOff(&token, /* default */ true); + } else if ((0 == strncmp(token, "-blendv", 7)) && IS_SPACE((token[7]))) { + token += 8; + texopt->blendv = parseOnOff(&token, /* default */ true); + } else if ((0 == strncmp(token, "-clamp", 6)) && IS_SPACE((token[6]))) { + token += 7; + texopt->clamp = parseOnOff(&token, /* default */ true); + } else if ((0 == strncmp(token, "-boost", 6)) && IS_SPACE((token[6]))) { + token += 7; + texopt->sharpness = parseReal(&token, 1.0); + } else if ((0 == strncmp(token, "-bm", 3)) && IS_SPACE((token[3]))) { + token += 4; + texopt->bump_multiplier = parseReal(&token, 1.0); + } else if ((0 == strncmp(token, "-o", 2)) && IS_SPACE((token[2]))) { + token += 3; + parseReal3(&(texopt->origin_offset[0]), &(texopt->origin_offset[1]), + &(texopt->origin_offset[2]), &token); + } else if ((0 == strncmp(token, "-s", 2)) && IS_SPACE((token[2]))) { + token += 3; + parseReal3(&(texopt->scale[0]), &(texopt->scale[1]), &(texopt->scale[2]), + &token, 1.0, 1.0, 1.0); + } else if ((0 == strncmp(token, "-t", 2)) && IS_SPACE((token[2]))) { + token += 3; + parseReal3(&(texopt->turbulence[0]), &(texopt->turbulence[1]), + &(texopt->turbulence[2]), &token); + } else if ((0 == strncmp(token, "-type", 5)) && IS_SPACE((token[5]))) { + token += 5; + texopt->type = parseTextureType((&token), TEXTURE_TYPE_NONE); + } else if ((0 == strncmp(token, "-imfchan", 8)) && IS_SPACE((token[8]))) { + token += 9; + token += strspn(token, " \t"); + const char *end = token + strcspn(token, " \t\r"); + if ((end - token) == 1) { // Assume one char for -imfchan + texopt->imfchan = (*token); + } + token = end; + } else if ((0 == strncmp(token, "-mm", 3)) && IS_SPACE((token[3]))) { + token += 4; + parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0); + } else { + // Assume texture filename + token += strspn(token, " \t"); // skip space + size_t len = strcspn(token, " \t\r"); // untile next space + texture_name = std::string(token, token + len); + token += len; + + token += strspn(token, " \t"); // skip space + + found_texname = true; + } + } + + if (found_texname) { + (*texname) = texture_name; + return true; + } else { + return false; + } +} + +static void InitMaterial(material_t *material) { + material->name = ""; + material->ambient_texname = ""; + material->diffuse_texname = ""; + material->specular_texname = ""; + material->specular_highlight_texname = ""; + material->bump_texname = ""; + material->displacement_texname = ""; + material->alpha_texname = ""; + for (int i = 0; i < 3; i++) { + material->ambient[i] = 0.f; + material->diffuse[i] = 0.f; + material->specular[i] = 0.f; + material->transmittance[i] = 0.f; + material->emission[i] = 0.f; + } + material->illum = 0; + material->dissolve = 1.f; + material->shininess = 1.f; + material->ior = 1.f; + + material->roughness = 0.f; + material->metallic = 0.f; + material->sheen = 0.f; + material->clearcoat_thickness = 0.f; + material->clearcoat_roughness = 0.f; + material->anisotropy_rotation = 0.f; + material->anisotropy = 0.f; + material->roughness_texname = ""; + material->metallic_texname = ""; + material->sheen_texname = ""; + material->emissive_texname = ""; + material->normal_texname = ""; + + material->unknown_parameter.clear(); +} + +static bool exportFaceGroupToShape( + shape_t *shape, const std::vector > &faceGroup, + const std::vector &tags, const int material_id, + const std::string &name, bool triangulate) { + if (faceGroup.empty()) { + return false; + } + + // Flatten vertices and indices + for (size_t i = 0; i < faceGroup.size(); i++) { + const std::vector &face = faceGroup[i]; + + vertex_index i0 = face[0]; + vertex_index i1(-1); + vertex_index i2 = face[1]; + + size_t npolys = face.size(); + + if (triangulate) { + // Polygon -> triangle fan conversion + for (size_t k = 2; k < npolys; k++) { + i1 = i2; + i2 = face[k]; + + index_t idx0, idx1, idx2; + idx0.vertex_index = i0.v_idx; + idx0.normal_index = i0.vn_idx; + idx0.texcoord_index = i0.vt_idx; + idx1.vertex_index = i1.v_idx; + idx1.normal_index = i1.vn_idx; + idx1.texcoord_index = i1.vt_idx; + idx2.vertex_index = i2.v_idx; + idx2.normal_index = i2.vn_idx; + idx2.texcoord_index = i2.vt_idx; + + shape->mesh.indices.push_back(idx0); + shape->mesh.indices.push_back(idx1); + shape->mesh.indices.push_back(idx2); + + shape->mesh.num_face_vertices.push_back(3); + shape->mesh.material_ids.push_back(material_id); + } + } else { + for (size_t k = 0; k < npolys; k++) { + index_t idx; + idx.vertex_index = face[k].v_idx; + idx.normal_index = face[k].vn_idx; + idx.texcoord_index = face[k].vt_idx; + shape->mesh.indices.push_back(idx); + } + + shape->mesh.num_face_vertices.push_back( + static_cast(npolys)); + shape->mesh.material_ids.push_back(material_id); // per face + } + } + + shape->name = name; + shape->mesh.tags = tags; + + return true; +} + +// Split a string with specified delimiter character. +// http://stackoverflow.com/questions/236129/split-a-string-in-c +static void SplitString(const std::string &s, char delim, + std::vector &elems) { + std::stringstream ss; + ss.str(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } +} + +void LoadMtl(std::map *material_map, + std::vector *materials, std::istream *inStream, + std::string *warning) { + // Create a default material anyway. + material_t material; + InitMaterial(&material); + + // Issue 43. `d` wins against `Tr` since `Tr` is not in the MTL specification. + bool has_d = false; + bool has_tr = false; + + std::stringstream ss; + + std::string linebuf; + while (inStream->peek() != -1) { + safeGetline(*inStream, linebuf); + + // Trim trailing whitespace. + if (linebuf.size() > 0) { + linebuf = linebuf.substr(0, linebuf.find_last_not_of(" \t") + 1); + } + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; // empty line + + if (token[0] == '#') continue; // comment line + + // new mtl + if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) { + // flush previous material. + if (!material.name.empty()) { + material_map->insert(std::pair( + material.name, static_cast(materials->size()))); + materials->push_back(material); + } + + // initial temporary material + InitMaterial(&material); + + has_d = false; + has_tr = false; + + // set new mtl name + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + std::sscanf(token, "%s", namebuf); +#endif + material.name = namebuf; + continue; + } + + // ambient + if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) { + token += 2; + real_t r, g, b; + parseReal3(&r, &g, &b, &token); + material.ambient[0] = r; + material.ambient[1] = g; + material.ambient[2] = b; + continue; + } + + // diffuse + if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) { + token += 2; + real_t r, g, b; + parseReal3(&r, &g, &b, &token); + material.diffuse[0] = r; + material.diffuse[1] = g; + material.diffuse[2] = b; + continue; + } + + // specular + if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) { + token += 2; + real_t r, g, b; + parseReal3(&r, &g, &b, &token); + material.specular[0] = r; + material.specular[1] = g; + material.specular[2] = b; + continue; + } + + // transmittance + if ((token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) || + (token[0] == 'T' && token[1] == 'f' && IS_SPACE((token[2])))) { + token += 2; + real_t r, g, b; + parseReal3(&r, &g, &b, &token); + material.transmittance[0] = r; + material.transmittance[1] = g; + material.transmittance[2] = b; + continue; + } + + // ior(index of refraction) + if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) { + token += 2; + material.ior = parseReal(&token); + continue; + } + + // emission + if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) { + token += 2; + real_t r, g, b; + parseReal3(&r, &g, &b, &token); + material.emission[0] = r; + material.emission[1] = g; + material.emission[2] = b; + continue; + } + + // shininess + if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) { + token += 2; + material.shininess = parseReal(&token); + continue; + } + + // illum model + if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) { + token += 6; + material.illum = parseInt(&token); + continue; + } + + // dissolve + if ((token[0] == 'd' && IS_SPACE(token[1]))) { + token += 1; + material.dissolve = parseReal(&token); + + if (has_tr) { + ss << "WARN: Both `d` and `Tr` parameters defined for \"" + << material.name << "\". Use the value of `d` for dissolve." + << std::endl; + } + has_d = true; + continue; + } + if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) { + token += 2; + if (has_d) { + // `d` wins. Ignore `Tr` value. + ss << "WARN: Both `d` and `Tr` parameters defined for \"" + << material.name << "\". Use the value of `d` for dissolve." + << std::endl; + } else { + // We invert value of Tr(assume Tr is in range [0, 1]) + // NOTE: Interpretation of Tr is application(exporter) dependent. For + // some application(e.g. 3ds max obj exporter), Tr = d(Issue 43) + material.dissolve = 1.0f - parseReal(&token); + } + has_tr = true; + continue; + } + + // PBR: roughness + if (token[0] == 'P' && token[1] == 'r' && IS_SPACE(token[2])) { + token += 2; + material.roughness = parseReal(&token); + continue; + } + + // PBR: metallic + if (token[0] == 'P' && token[1] == 'm' && IS_SPACE(token[2])) { + token += 2; + material.metallic = parseReal(&token); + continue; + } + + // PBR: sheen + if (token[0] == 'P' && token[1] == 's' && IS_SPACE(token[2])) { + token += 2; + material.sheen = parseReal(&token); + continue; + } + + // PBR: clearcoat thickness + if (token[0] == 'P' && token[1] == 'c' && IS_SPACE(token[2])) { + token += 2; + material.clearcoat_thickness = parseReal(&token); + continue; + } + + // PBR: clearcoat roughness + if ((0 == strncmp(token, "Pcr", 3)) && IS_SPACE(token[3])) { + token += 4; + material.clearcoat_roughness = parseReal(&token); + continue; + } + + // PBR: anisotropy + if ((0 == strncmp(token, "aniso", 5)) && IS_SPACE(token[5])) { + token += 6; + material.anisotropy = parseReal(&token); + continue; + } + + // PBR: anisotropy rotation + if ((0 == strncmp(token, "anisor", 6)) && IS_SPACE(token[6])) { + token += 7; + material.anisotropy_rotation = parseReal(&token); + continue; + } + + // ambient texture + if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.ambient_texname), + &(material.ambient_texopt), token, + /* is_bump */ false); + continue; + } + + // diffuse texture + if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.diffuse_texname), + &(material.diffuse_texopt), token, + /* is_bump */ false); + continue; + } + + // specular texture + if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.specular_texname), + &(material.specular_texopt), token, + /* is_bump */ false); + continue; + } + + // specular highlight texture + if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.specular_highlight_texname), + &(material.specular_highlight_texopt), token, + /* is_bump */ false); + continue; + } + + // bump texture + if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) { + token += 9; + ParseTextureNameAndOption(&(material.bump_texname), + &(material.bump_texopt), token, + /* is_bump */ true); + continue; + } + + // bump texture + if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) { + token += 5; + ParseTextureNameAndOption(&(material.bump_texname), + &(material.bump_texopt), token, + /* is_bump */ true); + continue; + } + + // alpha texture + if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) { + token += 6; + material.alpha_texname = token; + ParseTextureNameAndOption(&(material.alpha_texname), + &(material.alpha_texopt), token, + /* is_bump */ false); + continue; + } + + // displacement texture + if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) { + token += 5; + ParseTextureNameAndOption(&(material.displacement_texname), + &(material.displacement_texopt), token, + /* is_bump */ false); + continue; + } + + // PBR: roughness texture + if ((0 == strncmp(token, "map_Pr", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.roughness_texname), + &(material.roughness_texopt), token, + /* is_bump */ false); + continue; + } + + // PBR: metallic texture + if ((0 == strncmp(token, "map_Pm", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.metallic_texname), + &(material.metallic_texopt), token, + /* is_bump */ false); + continue; + } + + // PBR: sheen texture + if ((0 == strncmp(token, "map_Ps", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.sheen_texname), + &(material.sheen_texopt), token, + /* is_bump */ false); + continue; + } + + // PBR: emissive texture + if ((0 == strncmp(token, "map_Ke", 6)) && IS_SPACE(token[6])) { + token += 7; + ParseTextureNameAndOption(&(material.emissive_texname), + &(material.emissive_texopt), token, + /* is_bump */ false); + continue; + } + + // PBR: normal map texture + if ((0 == strncmp(token, "norm", 4)) && IS_SPACE(token[4])) { + token += 5; + ParseTextureNameAndOption( + &(material.normal_texname), &(material.normal_texopt), token, + /* is_bump */ false); // @fixme { is_bump will be true? } + continue; + } + + // unknown parameter + const char *_space = strchr(token, ' '); + if (!_space) { + _space = strchr(token, '\t'); + } + if (_space) { + std::ptrdiff_t len = _space - token; + std::string key(token, static_cast(len)); + std::string value = _space + 1; + material.unknown_parameter.insert( + std::pair(key, value)); + } + } + // flush last material. + material_map->insert(std::pair( + material.name, static_cast(materials->size()))); + materials->push_back(material); + + if (warning) { + (*warning) = ss.str(); + } +} + +bool MaterialFileReader::operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, + std::string *err) { + std::string filepath; + + if (!m_mtlBaseDir.empty()) { + filepath = std::string(m_mtlBaseDir) + matId; + } else { + filepath = matId; + } + + std::ifstream matIStream(filepath.c_str()); + if (!matIStream) { + std::stringstream ss; + ss << "WARN: Material file [ " << filepath << " ] not found." << std::endl; + if (err) { + (*err) += ss.str(); + } + return false; + } + + std::string warning; + LoadMtl(matMap, materials, &matIStream, &warning); + + if (!warning.empty()) { + if (err) { + (*err) += warning; + } + } + + return true; +} + +bool MaterialStreamReader::operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, + std::string *err) { + (void)matId; + if (!m_inStream) { + std::stringstream ss; + ss << "WARN: Material stream in error state. " << std::endl; + if (err) { + (*err) += ss.str(); + } + return false; + } + + std::string warning; + LoadMtl(matMap, materials, &m_inStream, &warning); + + if (!warning.empty()) { + if (err) { + (*err) += warning; + } + } + + return true; +} + +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *err, + const char *filename, const char *mtl_basedir, bool trianglulate) { + attrib->vertices.clear(); + attrib->normals.clear(); + attrib->texcoords.clear(); + shapes->clear(); + + std::stringstream errss; + + std::ifstream ifs(filename); + if (!ifs) { + errss << "Cannot open file [" << filename << "]" << std::endl; + if (err) { + (*err) = errss.str(); + } + return false; + } + + std::string baseDir; + if (mtl_basedir) { + baseDir = mtl_basedir; + } + MaterialFileReader matFileReader(baseDir); + + return LoadObj(attrib, shapes, materials, err, &ifs, &matFileReader, + trianglulate); +} + +bool LoadObj(attrib_t *attrib, std::vector *shapes, + std::vector *materials, std::string *err, + std::istream *inStream, MaterialReader *readMatFn /*= NULL*/, + bool triangulate) { + std::stringstream errss; + + std::vector v; + std::vector vn; + std::vector vt; + std::vector tags; + std::vector > faceGroup; + std::string name; + + // material + std::map material_map; + int material = -1; + + shape_t shape; + + std::string linebuf; + while (inStream->peek() != -1) { + safeGetline(*inStream, linebuf); + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; // empty line + + if (token[0] == '#') continue; // comment line + + // vertex + if (token[0] == 'v' && IS_SPACE((token[1]))) { + token += 2; + real_t x, y, z; + parseReal3(&x, &y, &z, &token); + v.push_back(x); + v.push_back(y); + v.push_back(z); + continue; + } + + // normal + if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { + token += 3; + real_t x, y, z; + parseReal3(&x, &y, &z, &token); + vn.push_back(x); + vn.push_back(y); + vn.push_back(z); + continue; + } + + // texcoord + if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { + token += 3; + real_t x, y; + parseReal2(&x, &y, &token); + vt.push_back(x); + vt.push_back(y); + continue; + } + + // face + if (token[0] == 'f' && IS_SPACE((token[1]))) { + token += 2; + token += strspn(token, " \t"); + + std::vector face; + face.reserve(3); + + while (!IS_NEW_LINE(token[0])) { + vertex_index vi = parseTriple(&token, static_cast(v.size() / 3), + static_cast(vn.size() / 3), + static_cast(vt.size() / 2)); + face.push_back(vi); + size_t n = strspn(token, " \t\r"); + token += n; + } + + // replace with emplace_back + std::move on C++11 + faceGroup.push_back(std::vector()); + faceGroup[faceGroup.size() - 1].swap(face); + + continue; + } + + // use mtl + if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + std::sscanf(token, "%s", namebuf); +#endif + + int newMaterialId = -1; + if (material_map.find(namebuf) != material_map.end()) { + newMaterialId = material_map[namebuf]; + } else { + // { error!! material not found } + } + + if (newMaterialId != material) { + // Create per-face material. Thus we don't add `shape` to `shapes` at + // this time. + // just clear `faceGroup` after `exportFaceGroupToShape()` call. + exportFaceGroupToShape(&shape, faceGroup, tags, material, name, + triangulate); + faceGroup.clear(); + material = newMaterialId; + } + + continue; + } + + // load mtl + if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { + if (readMatFn) { + token += 7; + + std::vector filenames; + SplitString(std::string(token), ' ', filenames); + + if (filenames.empty()) { + if (err) { + (*err) += + "WARN: Looks like empty filename for mtllib. Use default " + "material. \n"; + } + } else { + bool found = false; + for (size_t s = 0; s < filenames.size(); s++) { + std::string err_mtl; + bool ok = (*readMatFn)(filenames[s].c_str(), materials, + &material_map, &err_mtl); + if (err && (!err_mtl.empty())) { + (*err) += err_mtl; // This should be warn message. + } + + if (ok) { + found = true; + break; + } + } + + if (!found) { + if (err) { + (*err) += + "WARN: Failed to load material file(s). Use default " + "material.\n"; + } + } + } + } + + continue; + } + + // group name + if (token[0] == 'g' && IS_SPACE((token[1]))) { + // flush previous face group. + bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, + triangulate); + if (ret) { + shapes->push_back(shape); + } + + shape = shape_t(); + + // material = -1; + faceGroup.clear(); + + std::vector names; + names.reserve(2); + + while (!IS_NEW_LINE(token[0])) { + std::string str = parseString(&token); + names.push_back(str); + token += strspn(token, " \t\r"); // skip tag + } + + assert(names.size() > 0); + + // names[0] must be 'g', so skip the 0th element. + if (names.size() > 1) { + name = names[1]; + } else { + name = ""; + } + + continue; + } + + // object name + if (token[0] == 'o' && IS_SPACE((token[1]))) { + // flush previous face group. + bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, + triangulate); + if (ret) { + shapes->push_back(shape); + } + + // material = -1; + faceGroup.clear(); + shape = shape_t(); + + // @todo { multiple object name? } + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 2; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + std::sscanf(token, "%s", namebuf); +#endif + name = std::string(namebuf); + + continue; + } + + if (token[0] == 't' && IS_SPACE(token[1])) { + tag_t tag; + + char namebuf[4096]; + token += 2; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + std::sscanf(token, "%s", namebuf); +#endif + tag.name = std::string(namebuf); + + token += tag.name.size() + 1; + + tag_sizes ts = parseTagTriple(&token); + + tag.intValues.resize(static_cast(ts.num_ints)); + + for (size_t i = 0; i < static_cast(ts.num_ints); ++i) { + tag.intValues[i] = atoi(token); + token += strcspn(token, "/ \t\r") + 1; + } + + tag.floatValues.resize(static_cast(ts.num_reals)); + for (size_t i = 0; i < static_cast(ts.num_reals); ++i) { + tag.floatValues[i] = parseReal(&token); + token += strcspn(token, "/ \t\r") + 1; + } + + tag.stringValues.resize(static_cast(ts.num_strings)); + for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { + char stringValueBuffer[4096]; + +#ifdef _MSC_VER + sscanf_s(token, "%s", stringValueBuffer, + (unsigned)_countof(stringValueBuffer)); +#else + std::sscanf(token, "%s", stringValueBuffer); +#endif + tag.stringValues[i] = stringValueBuffer; + token += tag.stringValues[i].size() + 1; + } + + tags.push_back(tag); + } + + // Ignore unknown command. + } + + bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name, + triangulate); + // exportFaceGroupToShape return false when `usemtl` is called in the last + // line. + // we also add `shape` to `shapes` when `shape.mesh` has already some + // faces(indices) + if (ret || shape.mesh.indices.size()) { + shapes->push_back(shape); + } + faceGroup.clear(); // for safety + + if (err) { + (*err) += errss.str(); + } + + attrib->vertices.swap(v); + attrib->normals.swap(vn); + attrib->texcoords.swap(vt); + + return true; +} + +bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, + void *user_data /*= NULL*/, + MaterialReader *readMatFn /*= NULL*/, + std::string *err /*= NULL*/) { + std::stringstream errss; + + // material + std::map material_map; + int material_id = -1; // -1 = invalid + + std::vector indices; + std::vector materials; + std::vector names; + names.reserve(2); + std::string name; + std::vector names_out; + + std::string linebuf; + while (inStream.peek() != -1) { + safeGetline(inStream, linebuf); + + // Trim newline '\r\n' or '\n' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\n') + linebuf.erase(linebuf.size() - 1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size() - 1] == '\r') + linebuf.erase(linebuf.size() - 1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char *token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; // empty line + + if (token[0] == '#') continue; // comment line + + // vertex + if (token[0] == 'v' && IS_SPACE((token[1]))) { + token += 2; + real_t x, y, z, w; // w is optional. default = 1.0 + parseV(&x, &y, &z, &w, &token); + if (callback.vertex_cb) { + callback.vertex_cb(user_data, x, y, z, w); + } + continue; + } + + // normal + if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) { + token += 3; + real_t x, y, z; + parseReal3(&x, &y, &z, &token); + if (callback.normal_cb) { + callback.normal_cb(user_data, x, y, z); + } + continue; + } + + // texcoord + if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) { + token += 3; + real_t x, y, z; // y and z are optional. default = 0.0 + parseReal3(&x, &y, &z, &token); + if (callback.texcoord_cb) { + callback.texcoord_cb(user_data, x, y, z); + } + continue; + } + + // face + if (token[0] == 'f' && IS_SPACE((token[1]))) { + token += 2; + token += strspn(token, " \t"); + + indices.clear(); + while (!IS_NEW_LINE(token[0])) { + vertex_index vi = parseRawTriple(&token); + + index_t idx; + idx.vertex_index = vi.v_idx; + idx.normal_index = vi.vn_idx; + idx.texcoord_index = vi.vt_idx; + + indices.push_back(idx); + size_t n = strspn(token, " \t\r"); + token += n; + } + + if (callback.index_cb && indices.size() > 0) { + callback.index_cb(user_data, &indices.at(0), + static_cast(indices.size())); + } + + continue; + } + + // use mtl + if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 7; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, + static_cast(_countof(namebuf))); +#else + std::sscanf(token, "%s", namebuf); +#endif + + int newMaterialId = -1; + if (material_map.find(namebuf) != material_map.end()) { + newMaterialId = material_map[namebuf]; + } else { + // { error!! material not found } + } + + if (newMaterialId != material_id) { + material_id = newMaterialId; + } + + if (callback.usemtl_cb) { + callback.usemtl_cb(user_data, namebuf, material_id); + } + + continue; + } + + // load mtl + if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { + if (readMatFn) { + token += 7; + + std::vector filenames; + SplitString(std::string(token), ' ', filenames); + + if (filenames.empty()) { + if (err) { + (*err) += + "WARN: Looks like empty filename for mtllib. Use default " + "material. \n"; + } + } else { + bool found = false; + for (size_t s = 0; s < filenames.size(); s++) { + std::string err_mtl; + bool ok = (*readMatFn)(filenames[s].c_str(), &materials, + &material_map, &err_mtl); + if (err && (!err_mtl.empty())) { + (*err) += err_mtl; // This should be warn message. + } + + if (ok) { + found = true; + break; + } + } + + if (!found) { + if (err) { + (*err) += + "WARN: Failed to load material file(s). Use default " + "material.\n"; + } + } else { + if (callback.mtllib_cb) { + callback.mtllib_cb(user_data, &materials.at(0), + static_cast(materials.size())); + } + } + } + } + + continue; + } + + // group name + if (token[0] == 'g' && IS_SPACE((token[1]))) { + names.clear(); + + while (!IS_NEW_LINE(token[0])) { + std::string str = parseString(&token); + names.push_back(str); + token += strspn(token, " \t\r"); // skip tag + } + + assert(names.size() > 0); + + // names[0] must be 'g', so skip the 0th element. + if (names.size() > 1) { + name = names[1]; + } else { + name.clear(); + } + + if (callback.group_cb) { + if (names.size() > 1) { + // create const char* array. + names_out.resize(names.size() - 1); + for (size_t j = 0; j < names_out.size(); j++) { + names_out[j] = names[j + 1].c_str(); + } + callback.group_cb(user_data, &names_out.at(0), + static_cast(names_out.size())); + + } else { + callback.group_cb(user_data, NULL, 0); + } + } + + continue; + } + + // object name + if (token[0] == 'o' && IS_SPACE((token[1]))) { + // @todo { multiple object name? } + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 2; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + std::sscanf(token, "%s", namebuf); +#endif + std::string object_name = std::string(namebuf); + + if (callback.object_cb) { + callback.object_cb(user_data, object_name.c_str()); + } + + continue; + } + +#if 0 // @todo + if (token[0] == 't' && IS_SPACE(token[1])) { + tag_t tag; + + char namebuf[4096]; + token += 2; +#ifdef _MSC_VER + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); +#else + std::sscanf(token, "%s", namebuf); +#endif + tag.name = std::string(namebuf); + + token += tag.name.size() + 1; + + tag_sizes ts = parseTagTriple(&token); + + tag.intValues.resize(static_cast(ts.num_ints)); + + for (size_t i = 0; i < static_cast(ts.num_ints); ++i) { + tag.intValues[i] = atoi(token); + token += strcspn(token, "/ \t\r") + 1; + } + + tag.floatValues.resize(static_cast(ts.num_reals)); + for (size_t i = 0; i < static_cast(ts.num_reals); ++i) { + tag.floatValues[i] = parseReal(&token); + token += strcspn(token, "/ \t\r") + 1; + } + + tag.stringValues.resize(static_cast(ts.num_strings)); + for (size_t i = 0; i < static_cast(ts.num_strings); ++i) { + char stringValueBuffer[4096]; + +#ifdef _MSC_VER + sscanf_s(token, "%s", stringValueBuffer, + (unsigned)_countof(stringValueBuffer)); +#else + std::sscanf(token, "%s", stringValueBuffer); +#endif + tag.stringValues[i] = stringValueBuffer; + token += tag.stringValues[i].size() + 1; + } + + tags.push_back(tag); + } +#endif + + // Ignore unknown command. + } + + if (err) { + (*err) += errss.str(); + } + + return true; +} +} // namespace tinyobj + +#endif diff --git a/include/xrbds/components/component.h b/include/xrbds/components/component.h index 2d639ab..85779f2 100644 --- a/include/xrbds/components/component.h +++ b/include/xrbds/components/component.h @@ -1,8 +1,11 @@ #ifndef COMPONENT_H #define COMPONENT_H +#include "core/types.h" + struct Component { - virtual ~Component() = default; +public: + static PtrUnq Initialize(); }; #endif // COMPONENT_H \ No newline at end of file diff --git a/include/xrbds/components/mesh_filter.cpp b/include/xrbds/components/mesh_filter.cpp deleted file mode 100644 index cf2661a..0000000 --- a/include/xrbds/components/mesh_filter.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "mesh_filter.h" - -#include "resources/resource.h" - -MeshFilter::MeshFilter(const FString &name) : meshName(name) { - mesh = LoadResource(name); -} \ No newline at end of file diff --git a/include/xrbds/components/mesh_filter.h b/include/xrbds/components/mesh_filter.h index 9d49558..ee184a0 100644 --- a/include/xrbds/components/mesh_filter.h +++ b/include/xrbds/components/mesh_filter.h @@ -8,11 +8,14 @@ class Mesh; struct MeshFilter : public Component { public: - MeshFilter(const FString &name); + static PtrUnq Initialize(const FString &path); + + PtrShr getMesh() const { return mesh; } private: - FString meshName; PtrShr mesh; + + MeshFilter(const FString &path); }; #endif // MESH_FILTER_H \ No newline at end of file diff --git a/include/xrbds/resources/mesh.h b/include/xrbds/resources/mesh.h index c419aba..4d590ff 100644 --- a/include/xrbds/resources/mesh.h +++ b/include/xrbds/resources/mesh.h @@ -6,32 +6,28 @@ #include -struct Vertex { +struct FVertex { FVector3 position; FVector3 normal; FVector2 texCoords; - Vertex(const FVector3 &pos, const FVector3 &norm, const FVector2 &tex) + FVertex(const FVector3 &pos, const FVector3 &norm, const FVector2 &tex) : position(pos), normal(norm), texCoords(tex) {} }; class Mesh : public Resource { + using Super = Resource; + public: - static PtrShr Load(FString path); + static Mesh Load(const TVector> &vertices); - const TVector &getVertices() const; - const TVector &getIndices() const; - - void setVertices(const TVector &vertices); - void setIndices(const TVector &indices); + TVector> getVertices() const { return vertices; } private: - TVector m_vertices; - TVector m_indices; + TVector> vertices; - Mesh() = delete; - Mesh(const Mesh &) = delete; - Mesh(const TVector &vertices, const TVector &indices); + Mesh() = default; + Mesh(const TVector> &vertices); }; #endif // XRBDS_RESOURCES_MESH_H \ No newline at end of file diff --git a/include/xrbds/resources/resource.h b/include/xrbds/resources/resource.h index 3a97aec..478e133 100644 --- a/include/xrbds/resources/resource.h +++ b/include/xrbds/resources/resource.h @@ -4,19 +4,80 @@ #include "core/types.h" #include -template PtrShr LoadResource(Args &&...args) { - auto ptr = std::make_shared(std::forward(args)...); +/** + * @struct FResourceData + * @brief Represents resource data with a name and a file path. + * + * This structure is used to store information about a resource, including + * its name and the path to its location. + * + * @var FResourceData::name + * The name of the resource. + * + * @var FResourceData::path + * The file path to the resource. + * + * @param name The name of the resource. + * @param path The file path to the resource. + */ +struct FResourceData { + /** + * @brief Constructs an FResourceData object with the specified name and path. + * + * @param name The name of the resource. + * @param path The file path of the resource. + */ + FResourceData(const FString &name, const FString &path) + : name(name), path(path) {} - return ptr; -} - -class Resource { -public: - Resource(const FString &name, const FString &path); - -private: FString name; FString path; }; +/** + * @class Resource + * @brief Represents a resource that encapsulates data of type FResourceData. + * + * This class is used to manage and store resource data. It provides a + * constructor to initialize the resource with the given data. + * + * @note The class currently only stores the data and does not provide any + * additional functionality or accessors. + */ +class Resource { +public: + static Resource Load(); + +protected: + Resource() = default; +}; + +/** + * @brief Loads a resource by creating a shared pointer to an instance of the + * specified type. + * + * @tparam T The type of the resource to be loaded. Must be constructible + with + * the provided arguments. + * @tparam Args The types of the arguments to be forwarded to the constructor + of + * T. + * @param args The arguments to be forwarded to the constructor of T. + * @return PtrShr A shared pointer to the newly created instance of T. + * + * @note This function uses perfect forwarding to pass the arguments to the + * constructor of T. Ensure that the type T is compatible with the provided + * arguments. + */ +template PtrShr LoadResource(Args &&...args) { + if constexpr (!std::is_base_of::value) { + iprintf("Error: Type T must be derived from Resource.\n"); + return nullptr; + } + + auto ptr = std::make_shared(T::Load(std::forward(args)...)); + + return ptr; +} + #endif // RESOURCE_H \ No newline at end of file diff --git a/include/xrbds/scene/3d/mesh_instance_3d.h b/include/xrbds/scene/3d/mesh_instance_3d.h index 28a57c5..58b40d6 100644 --- a/include/xrbds/scene/3d/mesh_instance_3d.h +++ b/include/xrbds/scene/3d/mesh_instance_3d.h @@ -9,7 +9,7 @@ public: using Super = Node3D; public: - MeshInstance3D(); + MeshInstance3D(const FString &path); }; #endif // XRBDS_SCENE_3D_MESH_INSTANCE_H \ No newline at end of file diff --git a/include/xrbds/utils/file.h b/include/xrbds/utils/file.h index 256da99..376115c 100644 --- a/include/xrbds/utils/file.h +++ b/include/xrbds/utils/file.h @@ -2,11 +2,32 @@ #define XRBDS_UTILS_FILE_H #include "core/types.h" +#include "resources/mesh.h" namespace Utils::File { +/** + * @brief Reads the contents of a text file and returns it as a string. + * + * @param path The file path to the text file to be read. + * @return FString The contents of the file as a string. + * @throws std::runtime_error If the file cannot be opened or read. + */ FString ReadTextFile(const FString &path); +/** + * @brief Reads a Wavefront OBJ file and parses its contents into a nested + * vector of vertices. + * + * @param path The file path to the OBJ file as an FString. + * @return A TVector of TVector of FVertex, where each inner vector represents a + * group of vertices. + * + * @note Ensure the file at the specified path exists and is in a valid OBJ + * format. + */ +TVector> ReadObjFile(const FString &path); + } // namespace Utils::File #endif // XRBDS_UTILS_FILE_H \ No newline at end of file diff --git a/samples/hello_world/assets/meshes/mococo/cube.obj b/samples/hello_world/assets/meshes/mococo/cube.obj new file mode 100644 index 0000000..8b0a83f --- /dev/null +++ b/samples/hello_world/assets/meshes/mococo/cube.obj @@ -0,0 +1,46 @@ +# Blender v2.76 (sub 0) OBJ File: '' +# www.blender.org +mtllib cube.mtl +o Cube +v 1.000000 -1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v -1.000000 -1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -0.999999 +v 0.999999 1.000000 1.000001 +v -1.000000 1.000000 1.000000 +v -1.000000 1.000000 -1.000000 +vt 1.000000 0.333333 +vt 1.000000 0.666667 +vt 0.666667 0.666667 +vt 0.666667 0.333333 +vt 0.666667 0.000000 +vt 0.000000 0.333333 +vt 0.000000 0.000000 +vt 0.333333 0.000000 +vt 0.333333 1.000000 +vt 0.000000 1.000000 +vt 0.000000 0.666667 +vt 0.333333 0.333333 +vt 0.333333 0.666667 +vt 1.000000 0.000000 +vn 0.000000 -1.000000 0.000000 +vn 0.000000 1.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn -0.000000 0.000000 1.000000 +vn -1.000000 -0.000000 -0.000000 +vn 0.000000 0.000000 -1.000000 +usemtl Material +s off +f 2/1/1 3/2/1 4/3/1 +f 8/1/2 7/4/2 6/5/2 +f 5/6/3 6/7/3 2/8/3 +f 6/8/4 7/5/4 3/4/4 +f 3/9/5 7/10/5 8/11/5 +f 1/12/6 4/13/6 8/11/6 +f 1/4/1 2/1/1 4/3/1 +f 5/14/2 8/1/2 6/5/2 +f 1/12/3 5/6/3 2/8/3 +f 2/12/4 6/8/4 3/4/4 +f 4/13/5 3/9/5 8/11/5 +f 5/6/6 1/12/6 8/11/6 diff --git a/samples/hello_world/assets/meshes/mococo/mococo.mtl b/samples/hello_world/assets/meshes/mococo/mococo.mtl new file mode 100644 index 0000000..80767fd --- /dev/null +++ b/samples/hello_world/assets/meshes/mococo/mococo.mtl @@ -0,0 +1,12 @@ +# Blender 4.4.1 MTL File: 'mococo.blend' +# www.blender.org + +newmtl mococo_mat +Ns 0.000000 +Ka 1.000000 1.000000 1.000000 +Ks 0.000000 0.000000 0.000000 +Ke 0.000000 0.000000 0.000000 +Ni 1.450000 +illum 1 +map_Kd mococotexture_face1.png +map_d mococotexture_face1.png diff --git a/samples/hello_world/assets/meshes/mococo/mococo.obj b/samples/hello_world/assets/meshes/mococo/mococo.obj new file mode 100644 index 0000000..4fd942e --- /dev/null +++ b/samples/hello_world/assets/meshes/mococo/mococo.obj @@ -0,0 +1,1890 @@ +# Blender 4.4.1 +# www.blender.org +mtllib mococo.mtl +o mococo +v 0.000000 0.823231 -0.011167 +v 0.000000 0.794499 0.198958 +v -0.144960 0.699767 0.186263 +v 0.000000 0.671544 -0.068972 +v 0.000000 0.673928 0.193354 +v -0.258989 0.619642 0.091994 +v -0.173719 0.618638 -0.038375 +v -0.059441 0.815333 0.206733 +v -0.204319 0.819021 0.089205 +v -0.159785 0.951404 0.164029 +v -0.050720 0.984230 0.182080 +v 0.000000 1.473957 0.196196 +v -0.171771 1.348870 0.132460 +v -0.236477 1.342074 0.094327 +v 0.000000 0.984280 0.180548 +v -0.142966 1.094493 0.065272 +v 0.000000 0.878141 0.184042 +v -0.156281 1.099452 -0.026556 +v -0.149215 0.997478 0.163001 +v -0.000000 1.094468 0.118660 +v -0.000000 1.037650 -0.052538 +v 0.144960 0.699767 0.186263 +v 0.258989 0.619642 0.091994 +v 0.173719 0.618638 -0.038375 +v 0.059441 0.815333 0.206733 +v 0.050720 0.984230 0.182080 +v 0.159785 0.951404 0.164029 +v 0.163028 1.540268 0.122551 +v 0.206454 1.039741 0.039963 +v 0.000000 0.853778 -0.192446 +v -0.166569 0.841825 -0.129750 +v -0.207713 0.900581 0.075222 +v -0.206110 1.144684 0.102713 +v -0.148308 1.169202 0.088125 +v -0.195191 1.076571 0.057812 +v -0.136152 1.093371 0.038136 +v -0.168088 1.017155 0.107910 +v -0.105979 1.027223 0.093910 +v -0.162257 1.048547 0.183773 +v -0.099486 1.062171 0.178369 +v -0.185755 1.127363 0.180561 +v -0.125647 1.149919 0.174794 +v -0.127407 1.141613 -0.080483 +v -0.131157 1.180920 -0.064974 +v -0.164769 1.136894 0.085088 +v -0.165445 1.094315 0.057369 +v -0.000000 1.150041 -0.128471 +v -0.000000 1.197186 -0.113794 +v -0.053020 1.146038 -0.003633 +v -0.048138 1.142417 0.046787 +v -0.213312 1.427203 -0.218317 +v 0.000000 1.243961 0.167966 +v -0.167687 1.243272 0.103054 +v -0.000000 1.411947 -0.334016 +v -0.384477 1.029181 0.034845 +v -0.094423 1.107861 -0.026639 +v -0.094056 1.104257 0.067178 +v -0.122296 0.825828 0.014781 +v -0.110305 0.816395 0.146947 +v -0.167323 0.989321 -0.086455 +v 0.000000 0.634884 -0.037202 +v 0.000000 1.408086 0.178221 +v -0.188213 1.546349 -0.164017 +v -0.166110 1.420645 0.119245 +v -0.000000 1.594671 -0.206853 +v -0.055338 1.640299 -0.016258 +v -0.000000 1.639771 -0.028143 +v -0.218843 1.534483 -0.029825 +v -0.216584 1.715453 -0.013754 +v -0.000000 1.159039 -0.271080 +v -0.111992 1.219041 0.179210 +v -0.000000 1.210491 -0.123189 +v 0.000000 0.893413 -0.025526 +v 0.000000 0.620858 0.158693 +v 0.000000 1.240951 0.193944 +v -0.088547 1.165685 0.169344 +v -0.200970 0.934540 0.080655 +v -0.223081 0.947731 -0.062934 +v -0.284040 1.003296 -0.033734 +v -0.308011 1.016443 0.071540 +v -0.187917 0.925178 0.009956 +v -0.380538 0.932292 0.052879 +v -0.384244 0.940587 -0.036255 +v -0.285445 0.857761 -0.064139 +v -0.313462 0.856651 0.087792 +v -0.475481 0.726755 0.137458 +v -0.554968 0.817743 0.079818 +v -0.536706 0.806878 -0.052919 +v -0.448709 0.704632 -0.070441 +v -0.269558 0.825204 0.031121 +v -0.411874 0.674738 0.037256 +v -0.258569 1.371027 0.000583 +v -0.080857 1.646972 0.092587 +v -0.163028 1.540268 0.122551 +v -0.245997 1.153350 -0.041047 +v -0.001138 1.044737 -0.140702 +v -0.206454 1.039741 0.039963 +v -0.196762 1.104209 0.145196 +v -0.002836 1.186396 -0.180282 +v -0.329158 1.029188 0.105645 +v -0.265638 1.070038 -0.137061 +v -0.431867 0.755159 0.067594 +v -0.476580 0.793647 0.045619 +v -0.477702 0.797809 -0.000607 +v -0.421020 0.750656 -0.022041 +v -0.594449 0.671096 0.050484 +v -0.592611 0.678033 -0.004543 +v -0.404989 0.740539 0.029816 +v -0.523113 0.688631 0.044465 +v -0.501294 0.720686 0.092869 +v -0.281105 0.890665 0.080723 +v -0.336125 0.968954 -0.034425 +v -0.238607 0.860127 0.027480 +v -0.267246 0.883620 -0.061112 +v -0.346706 0.951655 0.055995 +v -0.241793 1.233900 -0.041997 +v -0.293104 1.507912 -0.046544 +v -0.216464 1.046830 -0.027776 +v -0.339369 1.442529 -0.066130 +v -0.116173 0.869648 0.156772 +v -0.140503 0.894201 -0.004651 +v -0.171852 0.952568 0.021786 +v -0.123407 1.144977 0.189714 +v -0.231636 1.231108 0.039455 +v -0.172792 1.344574 0.027478 +v -0.040060 1.556872 0.145021 +v -0.092448 1.551746 0.134481 +v -0.078399 1.588410 0.174666 +v -0.354701 1.134934 -0.057967 +v -0.246891 1.160263 0.012029 +v -0.400894 0.946749 0.073114 +v -0.000000 1.044555 -0.316942 +v -0.298374 1.159817 -0.118753 +v -0.337635 0.758967 -0.126360 +v -0.383627 0.776429 0.175216 +v -0.506540 0.916161 0.097710 +v -0.293543 0.706534 0.043884 +v -0.247598 1.232030 -0.137431 +v -0.163598 1.221700 0.154788 +v -0.498409 0.800334 0.023742 +v -0.593092 0.743525 0.074979 +v -0.500559 0.736174 0.132597 +v -0.735321 0.541633 0.023742 +v -0.666105 0.571155 0.064122 +v -0.628452 0.738711 0.023742 +v -0.593092 0.743525 -0.027494 +v -0.500559 0.736174 -0.085112 +v -0.666105 0.571155 -0.016637 +v -0.000000 1.149193 -0.039208 +v -0.000000 1.136252 0.083158 +v -0.020478 0.620808 -0.060571 +v -0.014344 0.597462 0.162947 +v -0.168741 0.420870 0.056308 +v -0.000000 1.132995 0.126856 +v -0.000000 1.148413 -0.085922 +v -0.097996 1.142971 0.065798 +v -0.096835 1.149667 -0.022098 +v 0.000000 0.762385 -0.052550 +v 0.000000 0.389925 -0.612132 +v 0.000000 0.437914 -0.211332 +v 0.000000 0.315956 -0.387195 +v -0.079629 0.393469 -0.423097 +v -0.183169 0.571548 -0.274655 +v 0.000000 0.448246 -0.474250 +v 0.000000 0.661658 -0.436783 +v 0.000000 0.782216 -0.272551 +v -0.130737 0.603088 0.194916 +v -0.225345 0.616051 0.085900 +v -0.046346 0.359436 0.062111 +v -0.111756 0.361810 0.123984 +v -0.161789 0.375619 -0.040102 +v -0.052093 0.370085 -0.048104 +v -0.060524 0.160855 0.059634 +v -0.070016 0.163418 -0.023480 +v -0.160039 0.425634 -0.012728 +v -0.051679 0.431956 -0.017026 +v -0.028286 0.411963 0.070881 +v -0.104316 0.409927 0.147086 +v -0.186148 0.370546 0.050435 +v -0.161883 0.159897 0.056956 +v -0.291771 1.567564 -0.045747 +v 0.166569 0.841825 -0.129750 +v 0.204319 0.819021 0.089205 +v 0.207713 0.900581 0.075222 +v 0.206110 1.144684 0.102713 +v 0.148308 1.169202 0.088125 +v 0.195191 1.076571 0.057812 +v 0.136152 1.093371 0.038136 +v 0.168088 1.017155 0.107910 +v 0.105979 1.027223 0.093910 +v 0.162257 1.048547 0.183773 +v 0.099486 1.062171 0.178369 +v 0.185755 1.127363 0.180561 +v 0.125647 1.149919 0.174794 +v 0.127407 1.141613 -0.080483 +v 0.131157 1.180920 -0.064974 +v 0.164769 1.136894 0.085088 +v 0.165445 1.094315 0.057369 +v 0.053020 1.146038 -0.003633 +v 0.048138 1.142417 0.046787 +v 0.213312 1.427203 -0.218317 +v 0.167687 1.243272 0.103054 +v 0.160781 1.194636 0.143418 +v 0.384477 1.029181 0.034845 +v 0.094423 1.107861 -0.026639 +v 0.094056 1.104257 0.067178 +v 0.122296 0.825828 0.014781 +v 0.110305 0.816395 0.146947 +v 0.167323 0.989321 -0.086455 +v 0.188213 1.546349 -0.164017 +v 0.166110 1.420645 0.119245 +v 0.055338 1.640299 -0.016258 +v 0.218843 1.534483 -0.029825 +v 0.216584 1.715453 -0.013754 +v 0.111992 1.219041 0.179210 +v 0.088547 1.165685 0.169344 +v 0.200970 0.934539 0.080655 +v 0.223081 0.947731 -0.062934 +v 0.284040 1.003296 -0.033734 +v 0.308011 1.016443 0.071540 +v 0.187917 0.925178 0.009956 +v 0.171771 1.348870 0.132460 +v 0.380538 0.932292 0.052879 +v 0.384244 0.940587 -0.036255 +v 0.285445 0.857761 -0.064139 +v 0.313462 0.856651 0.087792 +v 0.475482 0.726755 0.137458 +v 0.554968 0.817743 0.079818 +v 0.536706 0.806878 -0.052919 +v 0.448709 0.704632 -0.070441 +v 0.269558 0.825204 0.031121 +v 0.411874 0.674738 0.037256 +v 0.258569 1.371027 0.000583 +v 0.080857 1.646972 0.092587 +v 0.245997 1.153350 -0.041047 +v 0.142966 1.094493 0.065272 +v 0.196762 1.104209 0.145196 +v 0.329158 1.029188 0.105645 +v 0.265638 1.070038 -0.137061 +v 0.431867 0.755159 0.067594 +v 0.476580 0.793647 0.045619 +v 0.477702 0.797809 -0.000607 +v 0.421020 0.750656 -0.022041 +v 0.594449 0.671096 0.050484 +v 0.592611 0.678034 -0.004543 +v 0.404989 0.740539 0.029816 +v 0.523113 0.688631 0.044465 +v 0.501294 0.720686 0.092869 +v 0.156281 1.099452 -0.026556 +v 0.281105 0.890665 0.080723 +v 0.336125 0.968954 -0.034425 +v 0.238607 0.860127 0.027480 +v 0.267246 0.883620 -0.061112 +v 0.346706 0.951654 0.055995 +v 0.180126 1.009155 0.218276 +v 0.186825 0.892071 0.230973 +v 0.241793 1.233900 -0.041997 +v 0.293104 1.507912 -0.046544 +v 0.216464 1.046830 -0.027776 +v 0.339369 1.442529 -0.066130 +v 0.149215 0.997478 0.163001 +v 0.116173 0.869648 0.156772 +v 0.140503 0.894201 -0.004651 +v 0.171852 0.952568 0.021786 +v 0.123407 1.144977 0.189714 +v 0.231636 1.231108 0.039455 +v 0.172792 1.344574 0.027478 +v 0.040060 1.556872 0.145021 +v 0.092448 1.551746 0.134481 +v 0.078399 1.588410 0.174666 +v 0.354701 1.147121 -0.057967 +v 0.246891 1.160263 0.012029 +v 0.400894 0.946749 0.073114 +v 0.298374 1.159817 -0.118753 +v 0.337635 0.758967 -0.126360 +v 0.383627 0.776429 0.175216 +v 0.506540 0.916161 0.097710 +v 0.494489 0.914149 -0.088174 +v 0.293543 0.706534 0.043884 +v 0.247598 1.232030 -0.137431 +v 0.163598 1.221700 0.154788 +v 0.236477 1.342074 0.094327 +v 0.498409 0.800334 0.023742 +v 0.593092 0.743525 0.074979 +v 0.500559 0.736174 0.132597 +v 0.735321 0.541633 0.023742 +v 0.666105 0.571155 0.064122 +v 0.628452 0.738711 0.023742 +v 0.593092 0.743525 -0.027494 +v 0.500559 0.736174 -0.085112 +v 0.666105 0.571155 -0.016637 +v 0.020478 0.620808 -0.060571 +v 0.014344 0.597462 0.162947 +v 0.168741 0.420870 0.056308 +v 0.097996 1.142971 0.065798 +v 0.096835 1.149667 -0.022098 +v 0.079629 0.393469 -0.423097 +v 0.183169 0.571548 -0.274655 +v 0.130737 0.603088 0.194916 +v 0.225345 0.616051 0.085900 +v 0.046346 0.359436 0.062111 +v 0.111756 0.361809 0.123984 +v 0.161789 0.375619 -0.040102 +v 0.052093 0.370085 -0.048104 +v 0.113152 0.186648 0.104034 +v 0.148690 0.164694 -0.014404 +v 0.070016 0.163418 -0.023480 +v 0.160039 0.425634 -0.012728 +v 0.051679 0.431955 -0.017026 +v 0.028286 0.411963 0.070881 +v 0.104316 0.409927 0.147086 +v 0.186148 0.370546 0.050435 +v 0.291772 1.567564 -0.045747 +v -0.132836 1.434745 0.146338 +v 0.132836 1.434745 0.146338 +v -0.000110 1.291045 0.193476 +v -0.198584 1.183779 0.097309 +v 0.198584 1.183779 0.097309 +v -0.037405 1.309962 0.177314 +v 0.037186 1.309962 0.177314 +v -0.059541 1.274698 0.161461 +v 0.059507 1.272567 0.161461 +v 0.166769 1.359908 0.144439 +v 0.229812 1.391274 0.102473 +v 0.136373 1.435284 0.155116 +v 0.199416 1.466650 0.113150 +v -0.244145 1.404372 0.091920 +v -0.162183 1.377274 0.140941 +v -0.229084 1.456460 0.095532 +v -0.147122 1.429362 0.144553 +v -0.053020 1.162516 -0.003633 +v -0.048138 1.158895 0.046787 +v 0.000000 1.152767 0.187411 +v -0.160781 1.194636 0.143418 +v -0.164810 1.204167 -0.036589 +v -0.000000 1.165671 -0.039208 +v 0.000000 1.152731 0.083158 +v 0.053020 1.162516 -0.003633 +v 0.048138 1.158895 0.046787 +v 0.164810 1.204167 -0.036589 +v -0.108792 1.494686 0.208474 +v 0.065126 1.357963 0.226717 +v 0.000000 1.610052 0.128622 +v 0.190852 1.475639 0.195050 +v -0.014906 1.098451 -0.200394 +v -0.094144 0.775497 0.186105 +v -0.013563 0.180024 0.043727 +v -0.162416 0.256009 0.148577 +v -0.170692 0.194073 -0.078381 +v -0.044402 0.194844 -0.074120 +v -0.204427 -0.000000 -0.107489 +v -0.035378 -0.000000 -0.106876 +v -0.002600 0.069966 0.251772 +v -0.213266 0.073216 0.256636 +v -0.006705 -0.000000 0.252155 +v -0.210477 -0.000000 0.259595 +v -0.205631 0.176446 0.037212 +v -0.061328 0.254490 0.151924 +v -0.173705 0.161751 0.182508 +v -0.040818 0.159309 0.181474 +v -0.103231 0.071591 0.321407 +v -0.105406 -0.000000 0.323078 +v -0.113152 0.186648 0.104034 +v -0.148690 0.164694 -0.014404 +v 0.013563 0.180024 0.043727 +v 0.162416 0.256009 0.148577 +v 0.061328 0.254490 0.151924 +v 0.060524 0.160855 0.059634 +v 0.161883 0.159897 0.056956 +v 0.170692 0.194073 -0.078381 +v 0.044402 0.194844 -0.074120 +v 0.204427 -0.000000 -0.107489 +v 0.035378 -0.000000 -0.106876 +v 0.002600 0.069966 0.251772 +v 0.213266 0.073216 0.256636 +v 0.006705 0.000000 0.252155 +v 0.210477 0.000000 0.259595 +v 0.205631 0.176446 0.037212 +v 0.173705 0.161751 0.182508 +v 0.040818 0.159309 0.181474 +v 0.103231 0.071591 0.321407 +v 0.105406 0.000000 0.323078 +v -0.494489 0.914149 -0.088174 +v -0.384244 0.940587 -0.036255 +v 0.384244 0.940587 -0.036255 +v 0.494489 0.914149 -0.088174 +v 0.404692 1.038568 -0.093065 +v 0.517726 1.021822 -0.145272 +v -0.494489 0.914149 -0.088174 +v -0.404692 1.038568 -0.093065 +v -0.517726 1.021822 -0.145272 +v 0.170267 0.654120 -0.035883 +v -0.170267 0.654120 -0.035883 +vn -0.0000 -0.1547 0.9880 +vn -0.4718 0.2103 0.8563 +vn -0.2958 -0.1891 0.9363 +vn -0.0000 0.0717 -0.9974 +vn -0.7744 0.1287 -0.6195 +vn -0.0000 0.0881 -0.9961 +vn -0.7366 0.2278 0.6368 +vn -0.9248 -0.3689 0.0929 +vn -0.0000 0.1662 0.9861 +vn -0.5834 -0.0572 -0.8102 +vn -0.5603 0.2429 -0.7918 +vn -0.9247 -0.0845 0.3713 +vn -0.5219 0.0087 0.8529 +vn -0.6513 0.0858 0.7540 +vn -0.1998 0.1297 0.9712 +vn -0.4693 0.2343 0.8514 +vn -0.4990 0.0928 0.8616 +vn -0.4092 0.0558 0.9107 +vn -0.0000 0.4447 0.8957 +vn 0.0001 0.1775 0.9841 +vn -0.8409 0.0309 0.5403 +vn -0.1195 0.2652 0.9567 +vn -0.6853 -0.2284 0.6915 +vn -0.0000 0.0028 1.0000 +vn 0.3644 0.9292 0.0620 +vn -0.3782 0.7590 -0.5300 +vn 0.3716 0.8077 0.4577 +vn -0.0000 0.2831 0.9591 +vn -0.4242 0.8188 0.3868 +vn -0.6843 -0.2109 0.6980 +vn -0.0000 0.2703 0.9628 +vn 0.0109 0.6174 0.7866 +vn -0.0037 0.9672 -0.2540 +vn 0.2958 -0.1891 0.9363 +vn 0.4718 0.2103 0.8563 +vn 0.7744 0.1287 -0.6195 +vn 0.7366 0.2278 0.6368 +vn 0.9248 -0.3689 0.0929 +vn 0.5603 0.2429 -0.7918 +vn 0.5834 -0.0572 -0.8102 +vn 0.9394 -0.1054 0.3261 +vn 0.6513 0.0858 0.7540 +vn 0.4616 -0.0627 0.8849 +vn 0.1998 0.1297 0.9712 +vn 0.4693 0.2343 0.8514 +vn 0.4092 0.0557 0.9107 +vn 0.4990 0.0928 0.8616 +vn 0.8409 0.0309 0.5403 +vn 0.1195 0.2652 0.9567 +vn 0.6853 -0.2284 0.6915 +vn -0.3644 0.9292 0.0620 +vn -0.3716 0.8077 0.4577 +vn 0.3782 0.7590 -0.5300 +vn 0.4242 0.8188 0.3868 +vn 0.6843 -0.2109 0.6980 +vn -0.8501 0.4277 0.3072 +vn -0.7479 0.2030 -0.6320 +vn -0.9773 0.2015 0.0660 +vn -0.9328 0.3282 -0.1488 +vn 0.0003 0.2698 -0.9629 +vn -0.7857 0.1641 -0.5964 +vn -0.9080 0.3698 0.1969 +vn -0.7721 0.1842 -0.6082 +vn -0.9747 0.1139 -0.1926 +vn -0.9748 0.1323 -0.1795 +vn 0.2483 0.8882 -0.3866 +vn -0.7338 -0.2446 -0.6338 +vn -0.8534 0.5016 -0.1419 +vn -0.0000 0.3044 -0.9526 +vn -0.7224 0.1886 -0.6653 +vn 0.3786 0.0753 -0.9225 +vn -0.4369 -0.8955 -0.0850 +vn 0.7021 -0.6338 -0.3246 +vn -0.3730 -0.5516 0.7461 +vn 0.4912 0.6815 0.5425 +vn 0.7717 -0.2592 0.5808 +vn -0.6304 0.3118 0.7109 +vn 0.2360 -0.1219 0.9641 +vn -0.1794 0.8322 0.5247 +vn -0.4972 0.7010 0.5112 +vn -0.7623 0.1388 -0.6322 +vn -0.0000 0.1342 -0.9910 +vn -0.0000 -0.2103 -0.9776 +vn -0.3573 -0.8708 0.3376 +vn -0.7573 -0.2896 0.5853 +vn -0.8904 0.0552 0.4519 +vn 0.7238 -0.6734 0.1504 +vn 0.5904 -0.0597 -0.8049 +vn 0.3792 -0.2431 -0.8928 +vn -0.0000 0.0735 0.9973 +vn -0.5901 -0.0618 0.8050 +vn -0.8556 -0.5113 -0.0806 +vn -0.7202 -0.2293 0.6548 +vn -0.0000 -0.4542 -0.8909 +vn 0.2275 -0.4331 -0.8722 +vn 0.7853 -0.6162 -0.0594 +vn 0.8358 -0.5154 -0.1894 +vn -0.4512 0.2064 0.8682 +vn -0.4831 0.1302 0.8658 +vn -0.5336 0.6995 0.4754 +vn 0.7259 -0.6668 0.1688 +vn 0.4819 0.1339 0.8659 +vn -0.0776 0.9952 -0.0598 +vn -0.6330 0.5499 -0.5450 +vn -0.4276 0.8543 0.2955 +vn -0.9438 0.2935 0.1521 +vn -0.9230 -0.1456 0.3563 +vn -0.2421 -0.0206 0.9700 +vn -0.0000 0.8509 0.5253 +vn -0.0000 0.9983 -0.0589 +vn -0.0000 0.6600 0.7513 +vn -0.4621 0.6854 -0.5628 +vn -0.1852 0.8539 -0.4863 +vn -0.7138 0.0386 -0.6993 +vn -0.2057 -0.0401 -0.9778 +vn 0.3797 -0.3171 -0.8691 +vn -0.4688 0.5996 -0.6486 +vn -0.0000 -0.9511 0.3089 +vn 0.7086 -0.3921 0.5866 +vn -0.5056 0.5621 0.6545 +vn 0.1963 -0.1186 0.9733 +vn -0.2104 0.9004 0.3808 +vn -0.0000 0.8015 -0.5980 +vn -0.0000 -0.9987 0.0506 +vn -0.0000 -0.8313 0.5559 +vn 0.3443 -0.6736 0.6540 +vn -0.3766 -0.6702 0.6395 +vn -0.0000 -0.7200 0.6939 +vn -0.0000 0.6555 0.7552 +vn -0.7828 -0.3709 0.4997 +vn -0.5812 0.6252 0.5209 +vn -0.0000 -0.9346 -0.3558 +vn 0.4535 -0.3391 -0.8242 +vn 0.8900 -0.3955 0.2269 +vn 0.0118 0.9176 -0.3973 +vn -0.6478 -0.0947 -0.7559 +vn -0.3155 0.9410 0.1224 +vn -0.9117 -0.4106 0.0169 +vn -0.5701 -0.6543 0.4969 +vn -0.9413 -0.2223 0.2540 +vn -0.8161 -0.5760 -0.0462 +vn -0.8730 -0.3272 -0.3618 +vn -0.5044 -0.7889 -0.3511 +vn -0.7013 -0.6821 0.2072 +vn -0.4160 -0.9086 0.0367 +vn -0.7603 -0.6439 -0.0859 +vn -0.6162 -0.7872 -0.0229 +vn -0.7366 0.5317 0.4179 +vn -0.0000 0.1388 0.9903 +vn -0.8451 0.1803 -0.5033 +vn -0.8744 -0.1851 0.4485 +vn 0.3514 0.7145 0.6050 +vn 0.0055 -0.7132 -0.7009 +vn -0.5483 0.3570 -0.7562 +vn -0.6817 -0.7219 -0.1190 +vn -0.0283 -0.2521 -0.9673 +vn 0.3490 -0.9321 0.0969 +vn 0.0044 -0.1914 0.9815 +vn -0.7220 -0.3729 -0.5828 +vn -0.7700 -0.4841 0.4156 +vn -0.5625 0.7822 0.2678 +vn -0.5062 -0.8617 0.0340 +vn 0.0669 0.7253 -0.6851 +vn -0.1534 -0.1824 0.9712 +vn -0.0031 0.2615 -0.9652 +vn -0.0759 -0.7686 0.6352 +vn -0.3156 -0.6434 -0.6975 +vn -0.6890 -0.5494 -0.4726 +vn -0.9131 -0.4077 0.0042 +vn 0.1542 0.6348 0.7572 +vn 0.2774 0.7070 0.6506 +vn 0.4068 0.7618 0.5042 +vn -0.4911 0.7821 0.3836 +vn -0.3278 0.7312 0.5982 +vn -0.4232 0.7669 0.4825 +vn -0.7316 0.0856 0.6764 +vn -0.4275 0.3362 -0.8392 +vn -0.0000 0.3719 -0.9283 +vn -0.6846 0.7264 -0.0601 +vn -0.1815 0.0635 0.9813 +vn -0.0000 0.2972 -0.9548 +vn -0.6931 -0.0362 0.7199 +vn -0.4186 0.1309 0.8987 +vn -0.8917 -0.1129 0.4383 +vn 0.8510 -0.0234 0.5246 +vn 0.8696 -0.0039 0.4937 +vn 0.8917 -0.1129 0.4383 +vn -0.9608 0.1019 0.2579 +vn 0.7969 -0.1512 0.5848 +vn 0.6931 -0.0362 0.7199 +vn -0.7474 0.6644 -0.0000 +vn -0.5269 0.6211 -0.5802 +vn -0.8791 0.4767 -0.0000 +vn -0.5269 0.6211 0.5802 +vn -0.5120 0.1638 0.8432 +vn -0.3898 0.5960 0.7020 +vn -0.3291 0.9443 -0.0000 +vn -0.5120 0.1638 -0.8432 +vn -0.3898 0.5960 -0.7020 +vn -0.6940 0.6434 -0.3231 +vn -0.0000 0.8534 -0.5213 +vn -0.0000 0.5032 -0.8642 +vn -0.0756 -0.4203 0.9042 +vn -0.0000 0.8535 0.5212 +vn -0.7016 0.5861 0.4051 +vn -0.0000 0.4865 0.8737 +vn -0.7063 0.1061 -0.6999 +vn 0.9299 -0.3088 0.2000 +vn 0.6641 -0.0475 -0.7461 +vn -0.4811 0.8655 -0.1396 +vn -0.9568 -0.0513 0.2862 +vn -0.4719 0.8005 0.3695 +vn -0.9971 -0.0160 0.0737 +vn -0.0000 0.5365 0.8439 +vn -0.0000 0.9729 -0.2312 +vn -0.0000 -0.7038 0.7104 +vn -0.9083 -0.3424 -0.2403 +vn -0.0000 -0.9819 0.1893 +vn -0.0000 0.6326 -0.7745 +vn -0.0000 0.5219 -0.8530 +vn -0.0000 -0.0343 -0.9994 +vn -0.5968 0.1462 -0.7890 +vn 0.6174 0.1036 -0.7798 +vn 0.0151 0.9920 -0.1252 +vn -0.9647 0.0720 0.2534 +vn -0.3272 0.9085 -0.2597 +vn 0.0982 0.9911 -0.0903 +vn 0.9291 -0.1899 0.3173 +vn 0.3287 0.9166 -0.2274 +vn -0.2166 0.9575 -0.1904 +vn -0.0964 -0.2287 0.9687 +vn -0.2482 -0.0826 0.9652 +vn -0.0004 -0.6056 -0.7957 +vn 0.7479 0.2030 -0.6320 +vn 0.9773 0.2015 0.0660 +vn 0.9328 0.3282 -0.1488 +vn 0.7853 0.1656 -0.5965 +vn 0.9080 0.3698 0.1969 +vn 0.7721 0.1842 -0.6082 +vn 0.9748 0.1323 -0.1795 +vn 0.9747 0.1139 -0.1926 +vn -0.2483 0.8882 -0.3866 +vn 0.8534 0.5016 -0.1419 +vn 0.7338 -0.2446 -0.6338 +vn 0.7224 0.1886 -0.6653 +vn -0.3786 0.0753 -0.9225 +vn 0.4369 -0.8955 -0.0850 +vn -0.7021 -0.6338 -0.3246 +vn 0.3730 -0.5516 0.7461 +vn -0.7717 -0.2592 0.5808 +vn -0.4912 0.6815 0.5425 +vn 0.6304 0.3118 0.7109 +vn -0.1574 -0.8550 0.4942 +vn -0.2431 0.3374 0.9094 +vn -0.2360 -0.1219 0.9641 +vn 0.4972 0.7010 0.5112 +vn 0.1794 0.8322 0.5247 +vn 0.7623 0.1388 -0.6322 +vn 0.3573 -0.8708 0.3376 +vn 0.8904 0.0552 0.4519 +vn 0.7573 -0.2896 0.5853 +vn -0.7238 -0.6734 0.1504 +vn -0.3792 -0.2432 -0.8928 +vn -0.5904 -0.0597 -0.8049 +vn 0.5901 -0.0618 0.8050 +vn 0.8556 -0.5113 -0.0806 +vn 0.7202 -0.2293 0.6548 +vn -0.2275 -0.4331 -0.8722 +vn -0.8358 -0.5154 -0.1894 +vn -0.7853 -0.6162 -0.0594 +vn 0.4512 0.2064 0.8682 +vn 0.5342 0.6989 0.4756 +vn 0.4831 0.1302 0.8658 +vn -0.7259 -0.6668 0.1688 +vn -0.4819 0.1339 0.8659 +vn 0.0776 0.9952 -0.0598 +vn 0.4276 0.8543 0.2955 +vn 0.6330 0.5499 -0.5450 +vn 0.9438 0.2935 0.1521 +vn 0.9230 -0.1456 0.3563 +vn 0.2421 -0.0206 0.9700 +vn 0.4621 0.6854 -0.5628 +vn 0.1852 0.8539 -0.4863 +vn 0.7136 0.0386 -0.6995 +vn 0.2045 -0.0316 -0.9784 +vn 0.4688 0.5996 -0.6486 +vn -0.3797 -0.3171 -0.8691 +vn -0.7086 -0.3921 0.5866 +vn 0.5056 0.5621 0.6545 +vn -0.1963 -0.1186 0.9733 +vn 0.2104 0.9004 0.3808 +vn -0.3443 -0.6736 0.6540 +vn 0.3766 -0.6702 0.6395 +vn 0.7828 -0.3709 0.4997 +vn 0.5812 0.6252 0.5209 +vn -0.4535 -0.3391 -0.8242 +vn -0.8900 -0.3955 0.2269 +vn 0.3149 0.9415 0.1201 +vn 0.6440 -0.0925 -0.7594 +vn 0.9117 -0.4106 0.0169 +vn 0.9413 -0.2223 0.2540 +vn 0.5701 -0.6543 0.4969 +vn 0.8161 -0.5760 -0.0462 +vn 0.8730 -0.3272 -0.3618 +vn 0.7013 -0.6821 0.2072 +vn 0.5044 -0.7889 -0.3511 +vn 0.7603 -0.6439 -0.0859 +vn 0.4160 -0.9086 0.0367 +vn 0.6162 -0.7872 -0.0229 +vn 0.7366 0.5317 0.4179 +vn 0.8451 0.1803 -0.5033 +vn 0.9619 0.0038 0.2733 +vn -0.3331 0.8532 0.4014 +vn 0.5483 0.3570 -0.7562 +vn 0.7004 -0.6372 -0.3216 +vn -0.3490 -0.9321 0.0969 +vn -0.0044 -0.1914 0.9815 +vn 0.7700 -0.4841 0.4156 +vn 0.7220 -0.3729 -0.5828 +vn 0.5742 0.7715 0.2741 +vn 0.5610 0.8008 -0.2100 +vn 0.5062 -0.8617 0.0340 +vn 0.0669 -0.7253 0.6851 +vn 0.1534 -0.1824 0.9712 +vn 0.0759 -0.7686 0.6352 +vn 0.3160 -0.6432 -0.6975 +vn 0.6890 -0.5494 -0.4726 +vn 0.9131 -0.4077 0.0042 +vn -0.2238 0.6777 0.7004 +vn 0.7316 0.0856 0.6764 +vn 0.4275 0.3362 -0.8392 +vn 0.6846 0.7264 -0.0601 +vn 0.1815 0.0635 0.9813 +vn 0.5345 0.8151 -0.2235 +vn 0.4186 0.1309 0.8987 +vn -0.8510 -0.0234 0.5246 +vn 0.9608 0.1019 0.2579 +vn -0.7969 -0.1512 0.5848 +vn 0.7474 0.6644 -0.0000 +vn 0.8791 0.4767 -0.0000 +vn 0.5269 0.6211 -0.5802 +vn 0.5269 0.6211 0.5802 +vn 0.5120 0.1638 0.8432 +vn 0.3898 0.5960 0.7020 +vn 0.3291 0.9443 -0.0000 +vn 0.5120 0.1638 -0.8432 +vn 0.3898 0.5960 -0.7020 +vn 0.6940 0.6434 -0.3231 +vn 0.0756 -0.4203 0.9042 +vn 0.7016 0.5861 0.4051 +vn 0.7063 0.1061 -0.6999 +vn -0.9299 -0.3088 0.2000 +vn -0.6641 -0.0475 -0.7461 +vn 0.4811 0.8655 -0.1396 +vn 0.9568 -0.0513 0.2862 +vn 0.4719 0.8005 0.3695 +vn 0.9971 -0.0160 0.0737 +vn 0.9083 -0.3424 -0.2403 +vn 0.5968 0.1462 -0.7890 +vn -0.6174 0.1036 -0.7798 +vn -0.0151 0.9920 -0.1252 +vn 0.3272 0.9085 -0.2597 +vn 0.9647 0.0720 0.2534 +vn -0.0982 0.9911 -0.0903 +vn -0.3287 0.9166 -0.2274 +vn -0.9291 -0.1899 0.3173 +vn 0.2166 0.9575 -0.1904 +vn 0.0964 -0.2287 0.9687 +vn 0.2482 -0.0826 0.9652 +vn -0.4085 -0.0592 0.9108 +vn 0.0032 -0.0577 0.9983 +vn 0.4073 -0.0565 0.9116 +vn -0.8696 -0.0039 0.4937 +vn -0.4452 -0.1195 0.8874 +vn 0.4427 -0.1129 0.8895 +vn 0.5206 0.0897 0.8491 +vn -0.4915 0.0820 0.8670 +vn -0.6284 -0.6675 -0.3994 +vn -0.6721 -0.7045 0.2277 +vn -0.0000 -0.6292 -0.7772 +vn -0.0000 -0.8516 0.5241 +vn 0.6284 -0.6675 -0.3994 +vn 0.6721 -0.7045 0.2277 +vn 0.0567 0.2025 0.9776 +vn 0.0712 0.5214 0.8503 +vn 0.4911 0.7821 0.3836 +vn -0.5715 0.7947 -0.2045 +vn -0.6214 0.1424 0.7705 +vn 0.5522 -0.5361 -0.6385 +vn 0.8600 0.2760 0.4293 +vn 0.7004 -0.6286 0.3381 +vn -0.3830 0.7874 -0.4829 +vn -0.5910 -0.5123 -0.6231 +vn -0.6802 0.5147 0.5219 +vn -0.7395 0.6721 -0.0381 +vn 0.7537 0.6495 -0.1007 +vn 0.7188 0.4975 0.4856 +vn -0.8213 0.3152 0.4756 +vn -0.6825 -0.6138 0.3968 +vn -0.3948 0.9027 0.1711 +vn 0.3644 0.9062 0.2144 +vn 0.4397 0.7822 -0.4414 +vn 0.0431 0.3890 0.9202 +vn 0.0233 -0.5932 0.8047 +vn 0.3948 0.9027 0.1711 +vn 0.7395 0.6721 -0.0381 +vn 0.3830 0.7874 -0.4829 +vn -0.4397 0.7822 -0.4414 +vn -0.7537 0.6495 -0.1007 +vn -0.3644 0.9062 0.2144 +vn -0.5522 -0.5361 -0.6385 +vn -0.7004 -0.6286 0.3381 +vn -0.8600 0.2760 0.4293 +vn 0.5910 -0.5123 -0.6231 +vn 0.6802 0.5147 0.5219 +vn -0.7188 0.4975 0.4856 +vn 0.8213 0.3152 0.4756 +vn 0.6825 -0.6138 0.3968 +vn -0.0431 0.3890 0.9202 +vn -0.0233 -0.5932 0.8047 +vn -0.4504 0.3572 0.8183 +vn -0.4669 0.3686 0.8038 +vn -0.4514 0.3579 0.8174 +vn -0.4352 0.3467 0.8309 +vn 0.4504 0.3572 0.8183 +vn 0.4352 0.3467 0.8309 +vn 0.4514 0.3579 0.8174 +vn 0.4669 0.3686 0.8038 +vt 0.671932 0.927423 +vt 0.758677 0.923549 +vt 0.747763 0.976028 +vt 0.995373 0.946372 +vt 0.909072 0.847780 +vt 0.967888 0.849081 +vt 0.747826 0.847672 +vt 0.836732 0.976028 +vt 0.672153 0.856386 +vt 0.925248 0.977014 +vt 0.922688 0.956558 +vt 0.007812 0.796875 +vt 0.007812 0.718750 +vt 0.071266 0.774210 +vt 0.093750 0.718750 +vt 0.862814 0.350189 +vt 0.843750 0.265625 +vt 0.875000 0.296875 +vt 0.933594 0.371929 +vt 0.933594 0.313918 +vt 0.807195 0.276077 +vt 0.696964 0.758357 +vt 0.742179 0.814949 +vt 0.672012 0.821211 +vt 0.801747 0.680431 +vt 0.773791 0.632812 +vt 0.816530 0.641247 +vt 0.671964 0.682231 +vt 0.752829 0.671693 +vt 0.756564 0.743336 +vt 0.671964 0.756963 +vt 0.782742 0.606442 +vt 0.594609 0.976028 +vt 0.583694 0.923549 +vt 0.346998 0.946372 +vt 0.374483 0.849081 +vt 0.433299 0.847780 +vt 0.594598 0.847672 +vt 0.505639 0.976028 +vt 0.419684 0.956558 +vt 0.417123 0.977014 +vt 0.976562 0.093750 +vt 0.908953 0.073132 +vt 0.976562 0.015625 +vt 0.882812 0.015625 +vt 0.862814 0.350190 +vt 0.933594 0.313919 +vt 0.933594 0.371929 +vt 0.807194 0.276077 +vt 0.646964 0.758357 +vt 0.601749 0.814949 +vt 0.542180 0.680431 +vt 0.527398 0.641247 +vt 0.568978 0.633637 +vt 0.591098 0.671693 +vt 0.587364 0.743336 +vt 0.561186 0.606442 +vt 0.007812 0.937500 +vt 0.047877 0.822928 +vt 0.081720 0.846867 +vt 0.007812 0.996305 +vt 0.084956 0.903221 +vt 0.096662 0.776967 +vt 0.972123 0.760045 +vt 0.963650 0.734414 +vt 0.972228 0.731963 +vt 0.054688 0.343750 +vt 0.015625 0.375000 +vt 0.015625 0.343750 +vt 0.972228 0.784779 +vt 0.964718 0.760836 +vt 0.054688 0.375000 +vt 0.015625 0.406250 +vt 0.054688 0.406250 +vt 0.015625 0.437500 +vt 0.765559 0.453410 +vt 0.734766 0.431037 +vt 0.746528 0.394838 +vt 0.054688 0.468750 +vt 0.015625 0.468750 +vt 0.054688 0.500000 +vt 0.015625 0.500000 +vt 0.741971 0.488611 +vt 0.727228 0.443235 +vt 0.679517 0.443235 +vt 0.204730 0.644615 +vt 0.312977 0.563252 +vt 0.204730 0.572764 +vt 0.362814 0.314804 +vt 0.275320 0.341381 +vt 0.243215 0.242512 +vt 0.763615 0.070409 +vt 0.698393 0.132520 +vt 0.693302 0.101659 +vt 0.340687 0.687783 +vt 0.312977 0.738285 +vt 0.340687 0.740143 +vt 0.373139 0.145156 +vt 0.253895 0.020816 +vt 0.254360 0.141895 +vt 0.833927 0.023534 +vt 0.747990 0.023534 +vt 0.946892 0.759101 +vt 0.368582 0.762759 +vt 0.384259 0.713409 +vt 0.366228 0.712509 +vt 0.429688 0.601562 +vt 0.473362 0.597677 +vt 0.495688 0.742363 +vt 0.204730 0.687907 +vt 0.312977 0.633200 +vt 0.380350 0.453703 +vt 0.384932 0.364417 +vt 0.460938 0.421875 +vt 0.460938 0.507812 +vt 0.685490 0.023534 +vt 0.640143 0.067142 +vt 0.599552 0.046972 +vt 0.933929 0.371929 +vt 0.933929 0.447280 +vt 0.906030 0.452472 +vt 0.442536 0.353397 +vt 0.450418 0.280393 +vt 0.373118 0.019194 +vt 0.431997 0.134965 +vt 0.350951 0.811801 +vt 0.312977 0.817584 +vt 0.567470 0.779661 +vt 0.585938 0.812500 +vt 0.531250 0.812500 +vt 0.204730 0.743857 +vt 0.204730 0.818477 +vt 0.880802 0.086034 +vt 0.679711 0.965818 +vt 0.340687 0.571177 +vt 0.340687 0.638673 +vt 0.898609 0.401209 +vt 0.814518 0.463930 +vt 0.312500 0.507812 +vt 0.319396 0.414894 +vt 0.204730 0.478489 +vt 0.833927 0.109472 +vt 0.671932 0.953455 +vt 0.386498 0.670175 +vt 0.446416 0.082625 +vt 0.457744 0.019068 +vt 0.395115 0.016988 +vt 0.685490 0.062597 +vt 0.727578 0.664406 +vt 0.997691 0.966658 +vt 0.993535 0.975106 +vt 0.312977 0.680691 +vt 0.671932 0.990463 +vt 0.609426 0.172054 +vt 0.531301 0.250179 +vt 0.507864 0.211116 +vt 0.116980 0.593750 +vt 0.156228 0.642549 +vt 0.156228 0.556846 +vt 0.116980 0.531966 +vt 0.156228 0.491276 +vt 0.117174 0.741344 +vt 0.156228 0.731527 +vt 0.116980 0.686625 +vt 0.156228 0.686163 +vt 0.116980 0.656490 +vt 0.116980 0.632444 +vt 0.863149 0.350189 +vt 0.799585 0.382688 +vt 0.570503 0.086601 +vt 0.621350 0.128613 +vt 0.366780 0.228775 +vt 0.408728 0.227835 +vt 0.562500 0.476562 +vt 0.562551 0.406429 +vt 0.656250 0.406250 +vt 0.121291 0.833865 +vt 0.125000 0.992188 +vt 0.748105 0.616919 +vt 0.492239 0.297054 +vt 0.476614 0.406429 +vt 0.468801 0.297054 +vt 0.613333 0.199398 +vt 0.523489 0.375179 +vt 0.593801 0.367366 +vt 0.070088 0.637501 +vt 0.091190 0.620684 +vt 0.003338 0.529992 +vt 0.004007 0.585840 +vt 0.116980 0.508974 +vt 0.005580 0.681192 +vt 0.004863 0.625533 +vt 0.429688 0.812500 +vt 0.836650 0.991788 +vt 0.140775 0.408490 +vt 0.128930 0.429019 +vt 0.117539 0.408392 +vt 0.078327 0.404919 +vt 0.088776 0.371829 +vt 0.088776 0.436267 +vt 0.125000 0.996305 +vt 0.710989 0.297054 +vt 0.773489 0.359554 +vt 0.417935 0.668333 +vt 0.476614 0.406429 +vt 0.845078 0.722767 +vt 0.843191 0.762129 +vt 0.802585 0.733291 +vt 0.770500 0.306081 +vt 0.656250 0.476562 +vt 0.585938 0.515625 +vt 0.562551 0.476741 +vt 0.656301 0.406429 +vt 0.585989 0.515804 +vt 0.393209 0.577583 +vt 0.531301 0.273616 +vt 0.393833 0.769327 +vt 0.377193 0.181385 +vt 0.214043 0.194610 +vt 0.397245 0.512656 +vt 0.350951 0.510815 +vt 0.429688 0.507812 +vt 0.156228 0.788911 +vt 0.963559 0.784755 +vt 0.054688 0.437500 +vt 0.784591 0.394838 +vt 0.796352 0.431037 +vt 0.664774 0.488611 +vt 0.703372 0.516654 +vt 0.372038 0.468510 +vt 0.683990 0.991641 +vt 0.617280 0.227921 +vt 0.625051 0.359554 +vt 0.826592 0.206626 +vt 0.852078 0.195475 +vt 0.766489 0.234293 +vt 0.788960 0.200859 +vt 0.733361 0.209203 +vt 0.770165 0.306081 +vt 0.836729 0.161724 +vt 0.826592 0.206626 +vt 0.085313 0.152521 +vt 0.070154 0.125641 +vt 0.195199 0.152521 +vt 0.147813 0.109514 +vt 0.053259 0.083593 +vt 0.007188 0.152521 +vt 0.968750 0.507812 +vt 0.984375 0.554688 +vt 0.968750 0.554688 +vt 0.266830 0.899134 +vt 0.266213 0.829398 +vt 0.214466 0.829398 +vt 0.984375 0.410156 +vt 0.968750 0.468750 +vt 0.968750 0.410156 +vt 0.194062 0.899134 +vt 0.171089 0.829398 +vt 0.131310 0.829398 +vt 0.305022 0.899134 +vt 0.336108 0.899134 +vt 0.363461 0.829398 +vt 0.307542 0.829398 +vt 0.984375 0.507812 +vt 0.914062 0.554688 +vt 0.945312 0.507812 +vt 0.945312 0.468750 +vt 0.945312 0.410156 +vt 0.222751 0.899134 +vt 0.151660 0.899134 +vt 0.984375 0.468750 +vt 0.159174 0.289494 +vt 0.249516 0.365191 +vt 0.155407 0.382891 +vt 0.209771 0.226084 +vt 0.113566 0.220083 +vt 0.134050 0.175151 +vt 0.070570 0.239342 +vt 0.083200 0.332800 +vt 0.031736 0.187073 +vt 0.192799 0.919240 +vt 0.155499 0.919240 +vt 0.261487 0.981263 +vt 0.224026 0.919240 +vt 0.236834 0.989671 +vt 0.309402 0.989671 +vt 0.298962 0.919240 +vt 0.282169 0.989671 +vt 0.210435 0.989671 +vt 0.263935 0.919240 +vt 0.181792 0.989671 +vt 0.337200 0.919240 +vt 0.497538 0.492766 +vt 0.940224 0.728135 +vt 0.768695 0.453410 +vt 0.749664 0.394838 +vt 0.737902 0.431037 +vt 0.523489 0.406429 +vt 0.254360 0.141895 +vt 0.933928 0.371929 +vt 0.906030 0.452473 +vt 0.933928 0.447280 +vt 0.664153 0.965818 +vt 0.898609 0.401209 +vt 0.814518 0.463930 +vt 0.348836 0.975106 +vt 0.344680 0.966658 +vt 0.863149 0.350190 +vt 0.799584 0.382689 +vt 0.505721 0.991788 +vt 0.770499 0.306081 +vt 0.656301 0.476741 +vt 0.070312 0.781250 +vt 0.787727 0.394838 +vt 0.799488 0.431037 +vt 0.659874 0.991641 +vt 0.852078 0.195476 +vt 0.788960 0.200859 +vt 0.770165 0.306081 +vt 0.836729 0.161724 +vt 0.915135 0.236138 +vt 0.933594 0.229928 +vt 0.953978 0.239208 +vt 0.992188 0.296875 +vt 0.906259 0.229504 +vt 0.960977 0.227907 +vt 0.492188 0.109375 +vt 0.531250 0.109375 +vt 0.531250 0.148438 +vt 0.492188 0.148438 +vt 0.578125 0.296875 +vt 0.632812 0.296875 +vt 0.632812 0.328125 +vt 0.578125 0.328125 +vt 0.771427 0.132909 +vt 0.765133 0.139167 +vt 0.794865 0.140722 +vt 0.724552 0.187597 +vt 0.708927 0.156347 +vt 0.763615 0.164159 +vt 0.793262 0.144608 +vt 0.785798 0.146399 +vt 0.780566 0.148707 +vt 0.773467 0.159516 +vt 0.859375 0.617188 +vt 0.992188 0.617188 +vt 0.992188 0.710938 +vt 0.859375 0.710938 +vt 0.046875 0.250000 +vt 0.046875 0.328158 +vt 0.029388 0.267065 +vt 0.500000 0.554688 +vt 0.617188 0.609375 +vt 0.613281 0.628906 +vt 0.757812 0.500000 +vt 0.843750 0.515625 +vt 0.804688 0.554688 +vt 0.679688 0.562500 +vt 0.726318 0.529782 +vt 0.583731 0.529528 +vt 0.625000 0.562500 +vt 0.687500 0.609375 +vt 0.691406 0.628906 +vt 0.818858 0.578125 +vt 0.865733 0.515625 +vt 0.093750 0.039062 +vt 0.085938 0.015625 +vt 0.109375 0.015625 +vt 0.632812 0.523438 +vt 0.078125 0.039062 +vt 0.062500 0.015625 +vt 0.023438 0.039062 +vt 0.203125 0.039062 +vt 0.156250 0.015625 +vt 0.210938 0.015625 +vt 0.140625 0.039062 +vt 0.132812 0.015625 +vt 0.109375 0.039062 +vt 0.015625 0.015625 +vt 0.789062 0.468750 +vt 0.546875 0.500000 +vt 0.671875 0.523438 +vt 0.652605 0.615460 +vt 0.865733 0.562500 +vt 0.652560 0.633328 +vt 0.204730 0.437274 +vt 0.312500 0.500000 +vt 0.312500 0.429688 +vt 0.234861 0.401680 +s 1 +usemtl mococo_mat +f 5/1/1 3/2/2 167/3/3 +f 4/4/4 58/5/5 1/6/6 +f 59/7/7 58/5/5 6/8/8 +f 6/8/8 3/2/2 59/7/7 +f 2/9/9 59/7/7 3/2/2 +f 5/1/1 2/9/9 3/2/2 +f 4/4/4 7/10/10 393/11/11 +f 6/8/8 167/3/3 3/2/2 +f 393/11/11 7/10/10 6/8/8 +f 9/12/12 8/13/13 10/14/14 +f 8/13/13 11/15/15 10/14/14 +f 94/16/16 13/17/17 314/18/18 +f 343/19/19 94/16/16 12/20/20 +f 13/17/17 94/16/16 14/21/21 +f 11/22/22 120/23/23 17/24/24 +f 97/25/25 18/26/26 118/27/27 +f 20/28/28 16/29/29 11/22/22 +f 11/22/22 19/30/30 120/23/23 +f 15/31/31 11/22/22 17/24/24 +f 15/31/31 20/28/28 11/22/22 +f 16/29/29 19/30/32 11/22/22 +f 97/25/25 19/30/32 16/29/29 +f 118/27/27 18/26/26 21/32/33 +f 97/25/25 16/29/29 18/26/26 +f 5/1/1 299/33/34 22/34/35 +f 4/35/4 1/36/6 207/37/36 +f 208/38/37 23/39/38 207/37/36 +f 23/39/38 208/38/37 22/34/35 +f 2/9/9 22/34/35 208/38/37 +f 5/1/1 22/34/35 2/9/9 +f 4/35/4 207/37/36 392/40/39 +f 23/39/38 22/34/35 299/33/34 +f 23/39/38 24/41/40 392/40/39 +f 183/42/41 27/43/42 25/44/43 +f 25/44/43 27/43/42 26/45/44 +f 28/46/45 12/47/20 315/18/46 +f 343/48/19 12/47/20 28/46/45 +f 222/17/47 282/49/48 28/46/45 +f 26/50/49 17/24/24 262/51/50 +f 29/52/51 259/53/52 249/54/53 +f 20/28/28 26/50/49 236/55/54 +f 26/50/49 262/51/50 261/56/55 +f 15/31/31 17/24/24 26/50/49 +f 15/31/31 26/50/49 20/28/28 +f 236/55/54 26/50/49 261/56/56 +f 29/52/51 236/55/54 261/56/56 +f 259/53/52 21/57/33 249/54/53 +f 29/52/51 249/54/53 236/55/54 +f 31/58/57 9/12/12 32/59/58 +f 31/58/57 32/59/58 122/60/59 +f 30/61/60 31/58/57 60/62/61 +f 19/63/62 122/60/59 32/59/58 +f 60/62/61 31/58/57 122/60/59 +f 44/64/63 46/65/64 45/66/65 +f 34/67/66 35/68/67 33/69/68 +f 48/70/69 43/71/70 44/64/63 +f 36/72/71 37/73/72 35/68/67 +f 38/74/73 39/75/74 37/73/72 +f 34/76/66 42/77/75 40/78/76 +f 39/75/74 42/79/75 41/80/77 +f 41/80/77 34/81/66 33/82/68 +f 35/83/67 37/84/72 39/85/74 +f 135/86/78 82/87/79 136/88/80 +f 51/89/81 54/90/82 70/91/83 +f 335/92/84 334/93/85 53/94/86 +f 113/95/87 84/96/88 114/97/89 +f 53/98/86 62/99/90 64/100/91 +f 17/24/24 59/7/7 2/9/9 +f 133/101/92 335/92/84 116/102/93 +f 58/5/5 73/103/94 1/6/6 +f 78/104/95 122/105/96 81/106/97 +f 120/23/23 58/5/5 59/7/7 +f 10/107/98 19/108/99 80/109/100 +f 137/110/101 85/111/102 135/86/78 +f 66/112/103 63/113/104 69/114/105 +f 68/115/106 66/112/103 69/114/105 +f 92/116/107 64/117/91 94/118/108 +f 343/119/109 67/120/110 66/121/103 +f 68/122/106 51/89/81 92/123/107 +f 52/124/111 53/98/86 334/125/85 +f 112/126/112 84/96/88 83/127/113 +f 79/128/114 60/129/115 112/130/112 +f 83/127/113 134/131/116 383/132/117 +f 335/92/84 133/101/92 70/133/118 +f 5/1/1 167/3/3 152/134/119 +f 63/113/104 54/90/82 51/89/81 +f 115/135/120 85/111/102 111/136/121 +f 93/137/122 66/121/103 68/138/106 +f 115/135/120 83/139/113 82/87/79 +f 66/112/103 65/140/123 63/113/104 +f 82/87/79 383/141/117 136/88/80 +f 72/142/124 335/92/84 70/133/118 +f 74/143/125 5/1/1 152/134/119 +f 113/95/87 77/144/126 111/136/121 +f 76/145/127 333/146/128 75/147/129 +f 334/125/85 76/145/127 75/147/129 +f 75/147/129 52/124/111 334/125/85 +f 64/117/91 92/116/107 125/148/130 +f 57/149/131 16/29/29 20/28/28 +f 93/137/122 343/119/109 66/121/103 +f 4/4/4 61/150/132 151/151/133 +f 134/131/116 90/152/134 137/110/101 +f 63/113/104 68/122/106 69/114/105 +f 152/134/119 61/153/132 74/143/125 +f 99/154/135 101/155/136 95/156/137 +f 103/157/138 86/158/139 87/159/140 +f 104/160/141 87/159/140 88/161/142 +f 104/162/141 89/163/143 105/164/144 +f 105/164/144 91/165/145 108/166/146 +f 102/167/147 91/165/145 86/158/139 +f 94/168/148 93/137/122 68/169/106 +f 343/170/149 64/117/91 62/171/90 +f 93/137/122 94/168/148 343/119/109 +f 51/89/81 138/172/150 116/173/93 +f 77/144/126 81/106/97 122/105/96 +f 100/174/151 98/175/152 95/176/137 +f 60/62/115 79/177/114 96/178/153 +f 56/179/154 16/29/29 57/149/131 +f 80/180/155 19/181/32 100/182/151 +f 101/155/136 99/154/135 345/183/156 +f 98/175/152 19/184/32 97/185/25 +f 80/180/155 100/182/151 101/155/136 +f 109/186/157 102/167/147 110/187/158 +f 103/157/138 107/188/159 106/189/160 +f 105/190/144 107/188/159 104/160/141 +f 105/164/144 109/186/157 107/191/159 +f 110/187/158 106/189/160 109/186/157 +f 106/192/160 107/191/159 109/186/157 +f 103/157/138 110/187/158 102/167/147 +f 108/166/146 109/186/157 105/164/144 +f 106/189/160 110/187/158 103/157/138 +f 90/152/134 111/136/121 85/111/102 +f 10/107/98 80/109/100 131/193/161 +f 81/106/97 114/97/89 78/104/95 +f 168/194/162 167/3/3 6/8/8 +f 127/195/163 128/196/163 126/197/163 +f 117/198/164 119/199/164 68/200/164 +f 30/61/60 60/62/61 96/201/165 +f 97/185/25 95/176/137 98/175/152 +f 95/176/137 21/202/33 99/203/135 +f 16/29/29 56/179/154 18/26/26 +f 19/204/166 77/144/126 122/105/96 +f 98/175/152 100/174/151 19/205/32 +f 60/206/167 73/103/94 121/207/168 +f 60/206/167 121/207/168 122/208/169 +f 122/208/169 121/207/168 120/23/23 +f 122/208/169 120/23/23 19/30/30 +f 18/26/26 56/179/154 21/32/33 +f 68/169/106 92/209/107 94/168/148 +f 64/117/91 343/170/149 94/118/108 +f 53/94/86 64/117/91 125/148/130 +f 335/92/84 53/94/86 125/148/130 +f 51/89/81 70/91/83 138/172/150 +f 7/10/10 4/4/4 151/151/133 +f 7/10/10 168/194/162 6/8/8 +f 101/155/136 100/182/151 95/156/137 +f 271/210/170 235/176/171 204/211/172 +f 100/212/173 95/213/174 55/214/175 +f 77/144/126 19/204/166 80/215/176 +f 111/136/121 77/144/126 115/135/120 +f 79/216/114 80/180/155 101/155/136 +f 112/126/112 78/104/95 114/97/89 +f 60/217/115 78/104/95 112/126/112 +f 335/92/84 125/148/130 92/116/107 +f 335/92/84 92/116/107 116/102/93 +f 133/218/177 70/91/178 132/219/178 +f 92/123/107 51/89/81 116/173/93 +f 32/59/58 9/12/12 10/14/14 +f 115/135/120 77/144/126 80/215/176 +f 115/135/120 80/215/176 79/220/179 +f 112/221/112 115/135/120 79/220/179 +f 11/222/15 19/108/180 10/107/14 +f 10/14/14 19/63/62 32/59/58 +f 89/163/143 137/110/101 91/165/145 +f 136/88/80 88/161/142 87/159/140 +f 88/223/142 134/131/116 89/163/143 +f 91/165/145 135/86/78 86/158/139 +f 135/86/78 87/159/140 86/158/139 +f 116/173/93 138/172/150 133/218/92 +f 138/172/150 70/91/83 133/218/92 +f 44/64/63 43/71/70 46/65/64 +f 34/67/66 36/72/71 35/68/67 +f 48/70/69 47/224/181 43/71/70 +f 36/72/71 38/74/73 37/73/72 +f 38/74/73 40/225/76 39/75/74 +f 38/226/73 36/227/71 40/78/76 +f 36/227/71 34/76/66 40/78/76 +f 39/75/74 40/225/76 42/79/75 +f 41/80/77 42/79/75 34/81/66 +f 41/228/77 33/229/68 39/85/74 +f 33/229/68 35/83/67 39/85/74 +f 135/86/78 85/111/102 82/87/79 +f 113/95/87 90/152/134 84/96/88 +f 53/98/86 52/124/111 62/99/90 +f 17/24/24 120/23/23 59/7/7 +f 58/5/5 121/207/168 73/103/94 +f 78/104/95 60/217/115 122/105/96 +f 120/23/23 121/207/168 58/5/5 +f 137/110/101 90/152/134 85/111/102 +f 68/122/106 63/113/104 51/89/81 +f 112/126/112 114/97/89 84/96/88 +f 83/127/113 84/96/88 134/131/116 +f 63/113/104 65/140/123 54/90/82 +f 115/135/120 82/87/79 85/111/102 +f 115/135/120 112/221/112 83/139/113 +f 66/112/103 67/230/110 65/140/123 +f 82/87/79 83/139/113 383/141/117 +f 113/95/87 81/106/97 77/144/126 +f 134/131/116 84/96/88 90/152/134 +f 152/134/119 151/231/133 61/153/132 +f 103/157/138 102/167/147 86/158/139 +f 104/160/141 103/157/138 87/159/140 +f 104/162/141 88/223/142 89/163/143 +f 105/164/144 89/163/143 91/165/145 +f 102/167/147 108/166/146 91/165/145 +f 96/232/153 79/216/114 101/155/136 +f 109/186/157 108/166/146 102/167/147 +f 103/157/138 104/160/141 107/188/159 +f 90/152/134 113/95/87 111/136/121 +f 81/106/97 113/95/87 114/97/89 +f 97/185/25 118/233/27 95/176/137 +f 95/176/137 118/233/27 21/202/33 +f 89/163/143 134/131/116 137/110/101 +f 136/88/80 383/141/117 88/161/142 +f 88/223/142 383/132/117 134/131/116 +f 91/165/145 137/110/101 135/86/78 +f 135/86/78 136/88/80 87/159/140 +f 13/17/17 139/234/182 71/235/183 +f 124/236/184 139/234/182 14/21/21 +f 318/237/185 272/238/186 266/236/187 +f 94/16/16 92/239/188 14/21/21 +f 14/21/21 92/239/188 124/236/184 +f 139/234/182 13/17/17 14/21/21 +f 265/240/189 318/237/185 281/241/190 +f 145/242/191 146/243/192 143/244/193 +f 145/242/191 143/244/193 141/243/194 +f 144/245/195 142/246/196 141/243/194 +f 140/247/197 145/242/191 141/243/194 +f 142/246/196 140/247/197 141/243/194 +f 148/245/198 146/243/192 147/246/199 +f 140/247/197 146/243/192 145/242/191 +f 147/246/199 146/243/192 140/247/197 +f 157/248/200 149/249/201 155/250/202 +f 178/251/203 167/252/3 168/253/162 +f 150/254/204 156/255/205 154/256/206 +f 175/257/207 7/258/10 151/259/133 +f 177/260/208 176/261/209 151/262/133 +f 152/263/119 167/252/3 178/251/203 +f 168/253/162 7/258/10 175/257/207 +f 156/255/205 49/264/210 157/248/200 +f 177/260/208 152/263/119 178/251/203 +f 21/265/33 56/266/154 155/250/202 +f 156/255/205 56/266/154 57/267/131 +f 154/256/206 57/267/131 20/268/28 +f 56/266/154 157/248/200 155/250/202 +f 153/269/211 178/251/203 168/253/162 +f 153/269/211 168/253/162 175/257/207 +f 176/270/209 175/257/207 151/259/133 +f 152/263/119 177/260/208 151/262/133 +f 157/248/200 49/264/210 149/249/201 +f 150/254/204 50/271/212 156/255/205 +f 156/255/205 50/271/212 49/264/210 +f 156/255/205 157/248/200 56/266/154 +f 154/256/206 156/255/205 57/267/131 +f 163/272/213 158/273/214 166/274/215 +f 160/275/216 162/276/217 161/277/218 +f 158/273/214 163/272/213 160/275/216 +f 164/278/219 163/272/213 165/279/220 +f 162/276/217 159/280/221 161/277/218 +f 164/278/219 159/280/221 162/276/217 +f 160/275/216 163/272/213 162/276/217 +f 163/272/213 166/274/215 165/279/220 +f 164/278/219 162/276/217 163/272/213 +f 171/281/222 176/270/209 172/282/223 +f 363/283/224 179/284/225 180/285/226 +f 174/286/227 169/287/228 173/288/229 +f 169/287/228 363/283/224 173/288/229 +f 179/284/225 364/289/230 180/285/226 +f 179/284/225 175/257/207 171/281/222 +f 177/260/208 170/290/231 169/287/228 +f 169/287/228 176/261/209 177/260/208 +f 170/290/231 153/269/211 179/284/225 +f 171/281/222 174/291/227 364/289/230 +f 171/281/222 175/257/207 176/270/209 +f 363/283/224 170/290/231 179/284/225 +f 174/286/227 172/292/223 169/287/228 +f 169/287/228 170/290/231 363/283/224 +f 179/284/225 171/281/222 364/289/230 +f 179/284/225 153/269/211 175/257/207 +f 177/260/208 178/251/203 170/290/231 +f 169/287/228 172/292/223 176/261/209 +f 170/290/231 178/251/203 153/269/211 +f 171/281/222 172/282/223 174/291/227 +f 69/114/232 181/293/232 68/115/232 +f 60/206/167 96/294/233 73/103/94 +f 182/58/234 184/59/235 183/12/41 +f 182/58/234 264/60/236 184/59/235 +f 30/61/60 209/62/237 182/58/234 +f 261/63/238 184/59/235 264/60/236 +f 209/62/237 264/60/236 182/58/234 +f 196/64/239 197/66/240 198/65/241 +f 186/67/242 185/69/243 187/68/244 +f 48/70/69 196/64/239 195/71/245 +f 188/72/246 187/68/244 189/73/247 +f 190/74/248 189/73/247 191/75/249 +f 186/295/242 192/296/250 194/297/251 +f 191/75/249 193/80/252 194/79/251 +f 193/80/252 185/82/243 186/81/242 +f 187/83/244 191/85/249 189/84/247 +f 261/184/56 256/181/253 255/298/254 +f 276/86/255 277/88/256 223/87/257 +f 201/89/258 70/91/83 54/90/82 +f 340/92/259 202/94/260 203/93/261 +f 252/95/262 253/97/263 225/96/264 +f 202/98/260 211/299/265 62/99/90 +f 17/24/24 2/9/9 208/38/37 +f 274/101/266 257/102/267 340/92/259 +f 207/5/36 1/6/6 73/103/94 +f 218/104/268 221/106/269 264/105/270 +f 262/23/50 208/7/37 207/5/36 +f 27/107/271 220/109/272 261/108/273 +f 279/110/274 276/86/255 226/111/275 +f 212/112/276 214/114/277 210/113/278 +f 213/115/279 214/114/277 212/112/276 +f 233/116/280 28/118/281 211/117/265 +f 343/300/109 212/301/276 67/302/110 +f 213/122/279 233/123/280 201/89/258 +f 52/124/111 203/125/261 202/98/260 +f 251/126/282 224/127/283 225/96/264 +f 219/128/284 251/130/282 209/129/285 +f 224/127/283 278/132/286 275/131/287 +f 340/92/259 70/133/118 274/101/266 +f 5/1/1 293/303/288 299/33/34 +f 210/113/278 201/89/258 54/90/82 +f 254/135/289 250/136/290 226/111/275 +f 234/304/291 213/305/279 212/301/276 +f 254/135/289 223/87/257 224/139/283 +f 212/112/276 210/113/278 65/140/123 +f 223/87/257 277/88/256 278/141/286 +f 72/142/124 70/133/118 340/92/259 +f 74/143/125 293/303/288 5/1/1 +f 252/95/262 250/136/290 217/144/292 +f 216/145/293 75/147/129 333/146/128 +f 203/125/261 75/147/129 216/145/293 +f 75/147/129 203/125/261 52/124/111 +f 211/117/265 267/148/294 233/116/280 +f 206/149/295 20/28/28 236/29/54 +f 234/304/291 212/301/276 343/300/109 +f 4/35/4 292/306/296 61/307/132 +f 275/131/287 279/110/274 231/152/297 +f 210/113/278 214/114/277 213/122/279 +f 293/303/288 74/143/125 61/153/132 +f 99/154/135 235/156/298 239/155/299 +f 241/157/300 228/159/301 227/158/302 +f 242/160/303 229/161/304 228/159/301 +f 242/162/303 243/164/305 230/163/306 +f 243/164/305 246/166/307 232/165/308 +f 240/167/309 227/158/302 232/165/308 +f 28/308/310 213/309/279 234/304/291 +f 343/170/149 62/171/90 211/117/265 +f 234/304/291 343/300/109 28/308/310 +f 201/89/258 257/173/267 280/172/311 +f 217/144/292 264/105/270 221/106/269 +f 238/174/312 235/176/298 237/175/313 +f 209/62/285 96/178/153 219/177/284 +f 205/179/314 206/149/295 236/29/54 +f 220/180/315 238/182/312 256/181/253 +f 345/183/156 99/154/135 239/155/299 +f 237/175/313 29/185/51 261/184/56 +f 220/180/315 239/155/299 238/182/312 +f 247/186/316 248/187/317 240/167/309 +f 241/157/300 244/189/318 245/188/319 +f 243/190/305 242/160/303 245/188/319 +f 243/164/305 245/191/319 247/186/316 +f 248/187/317 247/186/316 244/189/318 +f 244/192/318 247/186/316 245/191/319 +f 241/157/300 240/167/309 248/187/317 +f 246/166/307 243/164/305 247/186/316 +f 244/189/318 241/157/300 248/187/317 +f 231/152/297 226/111/275 250/136/290 +f 220/109/272 273/193/320 251/130/321 +f 27/107/271 273/193/320 220/109/272 +f 221/106/269 218/104/268 253/97/263 +f 300/310/322 23/39/38 299/33/34 +f 269/195/323 270/196/323 268/197/323 +f 258/198/324 213/200/324 260/199/324 +f 30/61/60 96/201/165 209/62/237 +f 29/185/51 237/175/313 235/176/298 +f 235/176/298 99/203/135 21/202/33 +f 236/29/54 249/26/53 205/179/314 +f 261/204/325 264/105/270 217/144/292 +f 237/175/313 255/298/254 238/174/312 +f 209/206/326 263/207/327 73/103/94 +f 209/206/326 264/208/328 263/207/327 +f 264/208/328 262/23/50 263/207/327 +f 264/208/328 261/30/55 262/23/50 +f 249/26/53 21/32/33 205/179/314 +f 213/309/279 28/308/310 233/311/280 +f 211/117/265 28/118/281 343/170/149 +f 202/94/260 267/148/294 211/117/265 +f 340/92/259 267/148/294 202/94/260 +f 201/89/258 280/172/311 70/91/83 +f 24/41/40 292/306/296 4/35/4 +f 24/41/40 23/39/38 300/310/322 +f 239/155/299 235/156/298 238/182/312 +f 95/213/174 129/312/329 55/214/175 +f 217/144/292 220/215/330 261/204/325 +f 250/136/290 254/135/289 217/144/292 +f 219/216/284 239/155/299 220/180/315 +f 251/126/282 253/97/263 218/104/268 +f 209/217/285 251/126/282 218/104/268 +f 340/92/259 233/116/280 267/148/294 +f 340/92/259 257/102/267 233/116/280 +f 274/218/331 132/219/178 70/91/178 +f 233/123/280 257/173/267 201/89/258 +f 184/59/235 27/313/42 183/12/41 +f 254/135/289 220/215/330 217/144/292 +f 254/135/289 219/220/332 220/215/330 +f 251/221/282 219/220/332 254/135/289 +f 26/222/44 27/107/42 261/108/333 +f 27/313/42 184/59/235 261/63/238 +f 230/163/306 232/165/308 279/110/274 +f 277/88/256 228/159/301 229/161/304 +f 229/223/304 230/163/306 275/131/287 +f 232/165/308 227/158/302 276/86/255 +f 276/86/255 227/158/302 228/159/301 +f 257/173/267 274/218/266 280/172/311 +f 280/172/311 274/218/266 70/91/83 +f 196/64/239 198/65/241 195/71/245 +f 186/67/242 187/68/244 188/72/246 +f 48/70/69 195/71/245 47/224/181 +f 188/72/246 189/73/247 190/74/248 +f 190/74/248 191/75/249 192/225/250 +f 190/314/248 192/296/250 188/315/246 +f 188/315/246 192/296/250 186/295/242 +f 191/75/249 194/79/251 192/225/250 +f 193/80/252 186/81/242 194/79/251 +f 193/228/252 191/85/249 185/229/243 +f 185/229/243 191/85/249 187/83/244 +f 276/86/255 223/87/257 226/111/275 +f 252/95/262 225/96/264 231/152/297 +f 202/98/260 62/99/90 52/124/111 +f 17/24/24 208/38/37 262/51/50 +f 207/5/36 73/103/94 263/207/327 +f 218/104/268 264/105/270 209/217/285 +f 262/23/50 207/5/36 263/207/327 +f 279/110/274 226/111/275 231/152/297 +f 213/122/279 201/89/258 210/113/278 +f 251/126/282 225/96/264 253/97/263 +f 224/127/283 275/131/287 225/96/264 +f 210/113/278 54/90/82 65/140/123 +f 254/135/289 226/111/275 223/87/257 +f 254/135/289 224/139/283 251/221/282 +f 212/112/276 65/140/123 67/230/110 +f 223/87/257 278/141/286 224/139/283 +f 252/95/262 217/144/292 221/106/269 +f 275/131/287 231/152/297 225/96/264 +f 293/303/288 61/153/132 292/316/296 +f 241/157/300 227/158/302 240/167/309 +f 242/160/303 228/159/301 241/157/300 +f 242/162/303 230/163/306 229/223/304 +f 243/164/305 232/165/308 230/163/306 +f 240/167/309 232/165/308 246/166/307 +f 220/180/315 256/181/253 261/184/56 +f 96/232/153 239/155/299 219/216/284 +f 237/175/313 261/184/56 255/298/254 +f 247/186/316 240/167/309 246/166/307 +f 241/157/300 245/188/319 242/160/303 +f 231/152/297 250/136/290 252/95/262 +f 220/109/272 251/130/321 219/128/334 +f 221/106/269 253/97/263 252/95/262 +f 29/185/51 235/176/298 259/233/52 +f 235/176/298 21/202/33 259/233/52 +f 230/163/306 279/110/274 275/131/287 +f 277/88/256 229/161/304 278/141/286 +f 229/223/304 275/131/287 278/132/286 +f 232/165/308 276/86/255 279/110/274 +f 276/86/255 228/159/301 277/88/256 +f 222/17/47 215/317/335 281/241/190 +f 266/236/187 282/49/48 281/241/190 +f 139/234/182 124/236/184 317/318/336 +f 28/46/45 282/49/48 233/319/337 +f 282/49/48 266/236/187 233/319/337 +f 281/241/190 282/49/48 222/17/47 +f 123/320/338 139/234/182 317/318/336 +f 238/174/312 255/298/254 256/181/253 +f 288/242/339 286/244/340 289/243/341 +f 288/242/339 284/243/342 286/244/340 +f 287/245/343 284/243/342 285/246/344 +f 283/247/345 284/243/342 288/242/339 +f 285/246/344 284/243/342 283/247/345 +f 291/245/346 290/246/347 289/243/341 +f 283/247/345 288/242/339 289/243/341 +f 290/246/347 283/247/345 289/243/341 +f 296/248/348 155/250/202 149/249/201 +f 311/251/349 300/253/322 299/252/34 +f 150/254/204 154/256/206 295/255/350 +f 308/257/351 292/259/296 24/258/40 +f 310/260/352 292/262/296 309/261/353 +f 293/263/288 311/251/349 299/252/34 +f 300/253/322 308/257/351 24/258/40 +f 295/255/350 296/248/348 199/264/354 +f 310/260/352 311/251/349 293/263/288 +f 21/265/33 155/250/202 205/266/314 +f 295/255/350 206/267/295 205/266/314 +f 154/256/206 20/268/28 206/267/295 +f 205/266/314 155/250/202 296/248/348 +f 294/269/355 300/253/322 311/251/349 +f 294/269/355 308/257/351 300/253/322 +f 309/270/353 292/259/296 308/257/351 +f 293/263/288 292/262/296 310/260/352 +f 296/248/348 149/249/201 199/264/354 +f 150/254/204 295/255/350 200/271/356 +f 295/255/350 199/264/354 200/271/356 +f 295/255/350 205/266/314 296/248/348 +f 154/256/206 206/267/295 295/255/350 +f 298/272/357 166/274/215 158/273/214 +f 160/275/216 161/277/218 297/276/358 +f 158/273/214 160/275/216 298/272/357 +f 164/278/219 165/279/220 298/272/357 +f 297/276/358 161/277/218 159/280/221 +f 164/278/219 297/276/358 159/280/221 +f 160/275/216 297/276/358 298/272/357 +f 298/272/357 165/279/220 166/274/215 +f 164/278/219 298/272/357 297/276/358 +f 303/281/359 304/282/360 309/270/353 +f 305/283/361 369/285/362 312/284/363 +f 307/286/364 368/288/365 301/287/366 +f 301/287/366 368/288/365 305/283/361 +f 312/284/363 369/285/362 306/289/367 +f 312/284/363 303/281/359 308/257/351 +f 310/260/352 301/287/366 302/290/368 +f 301/287/366 310/260/352 309/261/353 +f 302/290/368 312/284/363 294/269/355 +f 303/281/359 306/289/367 307/291/364 +f 303/281/359 309/270/353 308/257/351 +f 305/283/361 312/284/363 302/290/368 +f 307/286/364 301/287/366 304/292/360 +f 301/287/366 305/283/361 302/290/368 +f 312/284/363 306/289/367 303/281/359 +f 312/284/363 308/257/351 294/269/355 +f 310/260/352 302/290/368 311/251/349 +f 301/287/366 309/261/353 304/292/360 +f 302/290/368 294/269/355 311/251/349 +f 303/281/359 307/291/364 304/282/360 +f 214/114/369 213/115/369 313/293/369 +f 209/206/326 73/103/94 96/294/233 +f 12/20/20 94/16/16 314/18/18 +f 222/17/47 28/46/45 315/18/46 +f 12/20/20 314/18/18 319/321/370 +f 12/20/20 316/322/371 320/323/372 +f 124/236/184 130/238/373 317/318/336 +f 281/241/190 318/237/185 266/236/187 +f 316/322/371 12/20/20 319/321/370 +f 315/324/46 12/20/20 320/323/372 +f 316/322/371 319/321/370 321/325/374 +f 320/323/372 316/322/371 322/326/375 +s 0 +f 323/327/376 324/328/376 326/329/376 325/330/376 +f 327/331/377 328/332/377 330/333/377 329/334/377 +s 1 +f 331/335/378 335/92/84 72/142/124 +f 332/336/379 334/93/85 335/92/84 +f 72/142/124 336/337/380 331/335/378 +f 333/338/128 76/339/127 337/340/381 +f 332/336/379 335/92/84 331/335/378 +f 332/336/379 76/339/127 334/93/85 +f 332/336/379 337/340/381 76/339/127 +f 338/335/382 72/142/124 340/92/259 +f 339/336/383 340/92/259 203/93/261 +f 72/142/124 338/335/382 336/337/380 +f 333/338/128 337/340/381 216/339/293 +f 339/336/383 338/335/382 340/92/259 +f 339/336/383 203/93/261 216/339/293 +f 339/336/383 216/339/293 337/340/381 +f 331/335/378 149/341/201 49/342/210 +f 338/335/382 149/341/201 336/337/380 +f 339/336/383 199/342/354 338/335/382 +f 337/340/381 200/343/356 339/336/383 +f 337/340/381 50/343/212 150/344/204 +f 332/336/379 49/342/210 50/343/212 +f 331/335/378 336/337/380 149/341/201 +f 338/335/382 199/342/354 149/341/201 +f 339/336/383 200/343/356 199/342/354 +f 337/340/381 150/344/204 200/343/356 +f 337/340/381 332/336/379 50/343/212 +f 332/336/379 331/335/378 49/342/210 +s 0 +f 341/345/384 342/346/384 344/347/384 +f 343/348/385 341/345/385 344/347/385 +s 1 +f 96/232/153 345/183/156 239/155/299 +f 96/232/153 101/155/136 345/183/156 +f 235/176/171 238/174/386 204/211/172 +f 112/130/387 131/193/161 80/109/100 79/128/387 +f 8/349/13 9/350/12 346/351/388 +f 352/352/389 353/353/390 355/354/391 +f 349/355/392 352/356/389 351/357/393 +f 359/358/394 357/359/395 351/357/393 +f 352/352/389 347/360/396 360/361/397 +f 354/362/398 351/357/393 356/363/399 +f 356/364/399 352/356/389 355/365/391 +f 180/366/226 357/367/395 348/368/400 +f 358/369/401 359/358/394 360/361/397 +f 364/370/230 349/371/392 357/367/395 +f 174/372/227 349/371/392 364/370/230 +f 174/373/227 347/374/396 350/375/402 +f 173/376/229 358/377/401 347/374/396 +f 363/378/224 348/368/400 358/377/401 +f 180/366/226 364/370/230 357/367/395 +f 174/372/227 350/379/402 349/371/392 +f 174/373/227 173/376/229 347/374/396 +f 173/376/229 363/378/224 358/377/401 +f 363/378/224 180/366/226 348/368/400 +f 353/353/390 352/352/389 360/361/397 +f 349/355/392 350/380/402 352/356/389 +f 354/362/398 359/358/394 351/357/393 +f 350/381/402 347/360/396 352/352/389 +f 356/364/399 351/357/393 352/356/389 +f 357/359/395 349/355/392 351/357/393 +f 359/358/394 348/382/400 357/359/395 +f 359/358/394 354/362/398 361/383/403 +f 347/360/396 358/369/401 360/361/397 +f 361/383/403 355/354/391 353/353/390 +f 356/363/399 361/383/403 354/362/398 +f 353/353/390 360/361/397 361/383/403 +f 360/361/397 359/358/394 361/383/403 +f 362/384/404 356/364/399 355/365/391 +f 358/369/401 348/382/400 359/358/394 +f 361/383/403 362/385/404 355/354/391 +f 356/363/399 362/385/404 361/383/403 +f 369/366/362 366/368/405 378/367/406 +f 306/370/367 378/367/406 370/371/407 +f 307/372/364 306/370/367 370/371/407 +f 307/373/364 371/375/408 365/374/409 +f 368/376/365 365/374/409 367/377/410 +f 305/378/361 367/377/410 366/368/405 +f 369/366/362 378/367/406 306/370/367 +f 307/372/364 370/371/407 371/379/408 +f 307/373/364 365/374/409 368/376/365 +f 368/376/365 367/377/410 305/378/361 +f 305/378/361 366/368/405 369/366/362 +f 373/352/411 376/354/412 374/353/413 +f 370/355/407 372/357/414 373/356/411 +f 379/358/415 372/357/414 378/359/406 +f 373/352/411 380/361/416 365/360/409 +f 375/362/417 377/363/418 372/357/414 +f 377/364/418 376/365/412 373/356/411 +f 367/369/410 380/361/416 379/358/415 +f 374/353/413 380/361/416 373/352/411 +f 370/355/407 373/356/411 371/380/408 +f 375/362/417 372/357/414 379/358/415 +f 371/381/408 373/352/411 365/360/409 +f 377/364/418 373/356/411 372/357/414 +f 378/359/406 372/357/414 370/355/407 +f 379/358/415 378/359/406 366/382/405 +f 379/358/415 381/383/419 375/362/417 +f 365/360/409 380/361/416 367/369/410 +f 381/383/419 374/353/413 376/354/412 +f 377/363/418 375/362/417 381/383/419 +f 374/353/413 381/383/419 380/361/416 +f 380/361/416 381/383/419 379/358/415 +f 382/384/420 376/365/412 377/364/418 +f 367/369/410 379/358/415 366/382/405 +f 381/383/419 376/354/412 382/385/420 +f 377/363/418 381/383/419 382/385/420 +f 389/386/421 384/387/422 390/388/423 +f 389/386/421 390/388/423 391/389/424 +f 386/386/425 388/389/426 387/388/427 +f 386/386/425 387/388/427 385/387/428 +f 207/37/36 23/39/38 392/40/39 +f 24/41/40 4/35/4 392/40/39 +f 58/5/5 393/11/11 6/8/8 +f 58/5/5 4/4/4 393/11/11 diff --git a/samples/hello_world/assets/meshes/mococo/textures/mococotexture_face1.png b/samples/hello_world/assets/meshes/mococo/textures/mococotexture_face1.png new file mode 100644 index 0000000..b83055d Binary files /dev/null and b/samples/hello_world/assets/meshes/mococo/textures/mococotexture_face1.png differ