#include "ofbx.h" #include "miniz.h" #include #include #include #include #include #include #include #include namespace ofbx { struct Allocator { struct Page { struct { Page* next = nullptr; u32 offset = 0; } header; u8 data[4096 * 1024 - 12]; }; Page* first = nullptr; ~Allocator() { while (first) { Page* page = first; first = first->header.next; delete page; } } template T* allocate(Args&&... args) { assert(sizeof(T) <= sizeof(first->data)); if (!first) { first = new Page; } Page* p = first; if (p->header.offset % alignof(T) != 0) { p->header.offset += alignof(T) - p->header.offset % alignof(T); } if (p->header.offset + sizeof(T) > sizeof(p->data)) { p = new Page; p->header.next = first; first = p; } T* res = new (p->data + p->header.offset) T(args...); p->header.offset += sizeof(T); return res; } // store temporary data, can be reused std::vector tmp; std::vector int_tmp; std::vector vec3_tmp; std::vector double_tmp; std::vector vec3_tmp2; }; struct Temporaries { std::vector f; std::vector i; std::vector v2; std::vector v3; std::vector v4; }; struct Video { DataView filename; DataView content; DataView media; }; struct Error { Error() {} Error(const char* msg) { s_message = msg; } static const char* s_message; }; const char* Error::s_message = ""; template struct OptionalError { OptionalError(Error error) : is_error(true) { } OptionalError(T _value) : value(_value) , is_error(false) { } T getValue() const { #ifdef _DEBUG assert(error_checked); #endif return value; } bool isError() { #ifdef _DEBUG error_checked = true; #endif return is_error; } private: T value; bool is_error; #ifdef _DEBUG bool error_checked = false; #endif }; #pragma pack(1) struct Header { u8 magic[21]; u8 reserved[2]; u32 version; }; #pragma pack() struct Cursor { const u8* current; const u8* begin; const u8* end; }; static void setTranslation(const Vec3& t, Matrix* mtx) { mtx->m[12] = t.x; mtx->m[13] = t.y; mtx->m[14] = t.z; } static Vec3 operator-(const Vec3& v) { return {-v.x, -v.y, -v.z}; } static Matrix operator*(const Matrix& lhs, const Matrix& rhs) { Matrix res; for (int j = 0; j < 4; ++j) { for (int i = 0; i < 4; ++i) { double tmp = 0; for (int k = 0; k < 4; ++k) { tmp += lhs.m[i + k * 4] * rhs.m[k + j * 4]; } res.m[i + j * 4] = tmp; } } return res; } static Matrix makeIdentity() { return {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; } static Matrix rotationX(double angle) { Matrix m = makeIdentity(); double c = cos(angle); double s = sin(angle); m.m[5] = m.m[10] = c; m.m[9] = -s; m.m[6] = s; return m; } static Matrix rotationY(double angle) { Matrix m = makeIdentity(); double c = cos(angle); double s = sin(angle); m.m[0] = m.m[10] = c; m.m[8] = s; m.m[2] = -s; return m; } static Matrix rotationZ(double angle) { Matrix m = makeIdentity(); double c = cos(angle); double s = sin(angle); m.m[0] = m.m[5] = c; m.m[4] = -s; m.m[1] = s; return m; } static Matrix getRotationMatrix(const Vec3& euler, RotationOrder order) { const double TO_RAD = 3.1415926535897932384626433832795028 / 180.0; Matrix rx = rotationX(euler.x * TO_RAD); Matrix ry = rotationY(euler.y * TO_RAD); Matrix rz = rotationZ(euler.z * TO_RAD); switch (order) { default: case RotationOrder::EULER_XYZ: return rz * ry * rx; case RotationOrder::EULER_XZY: return ry * rz * rx; case RotationOrder::EULER_YXZ: return rz * rx * ry; case RotationOrder::EULER_YZX: return rx * rz * ry; case RotationOrder::EULER_ZXY: return ry * rx * rz; case RotationOrder::EULER_ZYX: return rx * ry * rz; case RotationOrder::SPHERIC_XYZ: assert(false); Error::s_message = "Unsupported rotation order."; return rx * ry * rz; } } double fbxTimeToSeconds(i64 value) { return double(value) / 46186158000L; } i64 secondsToFbxTime(double value) { return i64(value * 46186158000L); } static Vec3 operator*(const Vec3& v, float f) { return {v.x * f, v.y * f, v.z * f}; } static Vec3 operator+(const Vec3& a, const Vec3& b) { return {a.x + b.x, a.y + b.y, a.z + b.z}; } template static bool copyString(char (&destination)[SIZE], const char* source) { const char* src = source; char* dest = destination; int length = SIZE; if (!src) return false; while (*src && length > 1) { *dest = *src; --length; ++dest; ++src; } *dest = 0; return *src == '\0'; } u64 DataView::toU64() const { if (is_binary) { assert(end - begin == sizeof(u64)); return *(u64*)begin; } static_assert(sizeof(unsigned long long) >= sizeof(u64), "can't use strtoull"); return strtoull((const char*)begin, nullptr, 10); } i64 DataView::toI64() const { if (is_binary) { assert(end - begin == sizeof(i64)); return *(i64*)begin; } static_assert(sizeof(long long) >= sizeof(i64), "can't use atoll"); return atoll((const char*)begin); } int DataView::toInt() const { if (is_binary) { assert(end - begin == sizeof(int)); return *(int*)begin; } return atoi((const char*)begin); } u32 DataView::toU32() const { if (is_binary) { assert(end - begin == sizeof(u32)); return *(u32*)begin; } return (u32)atoll((const char*)begin); } double DataView::toDouble() const { if (is_binary) { assert(end - begin == sizeof(double)); return *(double*)begin; } return atof((const char*)begin); } float DataView::toFloat() const { if (is_binary) { assert(end - begin == sizeof(float)); return *(float*)begin; } return (float)atof((const char*)begin); } bool DataView::operator==(const char* rhs) const { if (!begin) return !rhs[0]; const char* c = rhs; const char* c2 = (const char*)begin; while (*c && c2 != (const char*)end) { if (*c != *c2) return false; ++c; ++c2; } return c2 == (const char*)end && *c == '\0'; } struct Property; template static bool parseArrayRaw(const Property& property, T* out, int max_size); template static bool parseBinaryArray(const Property& property, std::vector* out); static bool parseDouble(Property& property, double* out); struct Property : IElementProperty { Type getType() const override { return (Type)type; } IElementProperty* getNext() const override { return next; } DataView getValue() const override { return value; } int getCount() const override { assert(type == ARRAY_DOUBLE || type == ARRAY_INT || type == ARRAY_FLOAT || type == ARRAY_LONG); if (value.is_binary) { return int(*(u32*)value.begin); } return count; } bool getValues(double* values, int max_size) const override { return parseArrayRaw(*this, values, max_size); } bool getValues(float* values, int max_size) const override { return parseArrayRaw(*this, values, max_size); } bool getValues(u64* values, int max_size) const override { return parseArrayRaw(*this, values, max_size); } bool getValues(i64* values, int max_size) const override { return parseArrayRaw(*this, values, max_size); } bool getValues(int* values, int max_size) const override { return parseArrayRaw(*this, values, max_size); } int count = 0; u8 type = INTEGER; DataView value; Property* next = nullptr; }; struct Element : IElement { IElement* getFirstChild() const override { return child; } IElement* getSibling() const override { return sibling; } DataView getID() const override { return id; } IElementProperty* getFirstProperty() const override { return first_property; } IElementProperty* getProperty(int idx) const { IElementProperty* prop = first_property; for (int i = 0; i < idx; ++i) { if (prop == nullptr) return nullptr; prop = prop->getNext(); } return prop; } DataView id; Element* child = nullptr; Element* sibling = nullptr; Property* first_property = nullptr; }; static const Element* findChild(const Element& element, const char* id) { Element* const* iter = &element.child; while (*iter) { if ((*iter)->id == id) return *iter; iter = &(*iter)->sibling; } return nullptr; } static IElement* resolveProperty(const Object& obj, const char* name) { const Element* props = findChild((const Element&)obj.element, "Properties70"); if (!props) return nullptr; Element* prop = props->child; while (prop) { if (prop->first_property && prop->first_property->value == name) { return prop; } prop = prop->sibling; } return nullptr; } static int resolveEnumProperty(const Object& object, const char* name, int default_value) { Element* element = (Element*)resolveProperty(object, name); if (!element) return default_value; Property* x = (Property*)element->getProperty(4); if (!x) return default_value; return x->value.toInt(); } static Vec3 resolveVec3Property(const Object& object, const char* name, const Vec3& default_value) { Element* element = (Element*)resolveProperty(object, name); if (!element) return default_value; Property* x = (Property*)element->getProperty(4); if (!x || !x->next || !x->next->next) return default_value; return {x->value.toDouble(), x->next->value.toDouble(), x->next->next->value.toDouble()}; } Object::Object(const Scene& _scene, const IElement& _element) : scene(_scene) , element(_element) , is_node(false) , node_attribute(nullptr) { auto& e = (Element&)_element; if (e.first_property && e.first_property->next) { e.first_property->next->value.toString(name); } else { name[0] = '\0'; } } static bool decompress(const u8* in, size_t in_size, u8* out, size_t out_size) { mz_stream stream = {}; mz_inflateInit(&stream); stream.avail_in = (int)in_size; stream.next_in = in; stream.avail_out = (int)out_size; stream.next_out = out; int status = mz_inflate(&stream, Z_SYNC_FLUSH); if (status != Z_STREAM_END) return false; return mz_inflateEnd(&stream) == Z_OK; } template static OptionalError read(Cursor* cursor) { if (cursor->current + sizeof(T) > cursor->end) return Error("Reading past the end"); T value = *(const T*)cursor->current; cursor->current += sizeof(T); return value; } static OptionalError readShortString(Cursor* cursor) { DataView value; OptionalError length = read(cursor); if (length.isError()) return Error(); if (cursor->current + length.getValue() > cursor->end) return Error("Reading past the end"); value.begin = cursor->current; cursor->current += length.getValue(); value.end = cursor->current; return value; } static OptionalError readLongString(Cursor* cursor) { DataView value; OptionalError length = read(cursor); if (length.isError()) return Error(); if (cursor->current + length.getValue() > cursor->end) return Error("Reading past the end"); value.begin = cursor->current; cursor->current += length.getValue(); value.end = cursor->current; return value; } static OptionalError readProperty(Cursor* cursor, Allocator& allocator) { if (cursor->current == cursor->end) return Error("Reading past the end"); Property* prop = allocator.allocate(); prop->next = nullptr; prop->type = *cursor->current; ++cursor->current; prop->value.begin = cursor->current; switch (prop->type) { case 'S': { OptionalError val = readLongString(cursor); if (val.isError()) return Error(); prop->value = val.getValue(); break; } case 'Y': cursor->current += 2; break; case 'C': cursor->current += 1; break; case 'I': cursor->current += 4; break; case 'F': cursor->current += 4; break; case 'D': cursor->current += 8; break; case 'L': cursor->current += 8; break; case 'R': { OptionalError len = read(cursor); if (len.isError()) return Error(); if (cursor->current + len.getValue() > cursor->end) return Error("Reading past the end"); cursor->current += len.getValue(); break; } case 'b': case 'f': case 'd': case 'l': case 'i': { OptionalError length = read(cursor); OptionalError encoding = read(cursor); OptionalError comp_len = read(cursor); if (length.isError() || encoding.isError() || comp_len.isError()) return Error(); if (cursor->current + comp_len.getValue() > cursor->end) return Error("Reading past the end"); cursor->current += comp_len.getValue(); break; } default: return Error("Unknown property type"); } prop->value.end = cursor->current; return prop; } static OptionalError readElementOffset(Cursor* cursor, u16 version) { if (version >= 7500) { OptionalError tmp = read(cursor); if (tmp.isError()) return Error(); return tmp.getValue(); } OptionalError tmp = read(cursor); if (tmp.isError()) return Error(); return tmp.getValue(); } static OptionalError readElement(Cursor* cursor, u32 version, Allocator& allocator) { OptionalError end_offset = readElementOffset(cursor, version); if (end_offset.isError()) return Error(); if (end_offset.getValue() == 0) return nullptr; OptionalError prop_count = readElementOffset(cursor, version); OptionalError prop_length = readElementOffset(cursor, version); if (prop_count.isError() || prop_length.isError()) return Error(); OptionalError id = readShortString(cursor); if (id.isError()) return Error(); Element* element = allocator.allocate(); element->first_property = nullptr; element->id = id.getValue(); element->child = nullptr; element->sibling = nullptr; Property** prop_link = &element->first_property; for (u32 i = 0; i < prop_count.getValue(); ++i) { OptionalError prop = readProperty(cursor, allocator); if (prop.isError()) { return Error(); } *prop_link = prop.getValue(); prop_link = &(*prop_link)->next; } if (cursor->current - cursor->begin >= (ptrdiff_t)end_offset.getValue()) return element; int BLOCK_SENTINEL_LENGTH = version >= 7500 ? 25 : 13; Element** link = &element->child; while (cursor->current - cursor->begin < ((ptrdiff_t)end_offset.getValue() - BLOCK_SENTINEL_LENGTH)) { OptionalError child = readElement(cursor, version, allocator); if (child.isError()) { return Error(); } *link = child.getValue(); if (child.getValue() == 0) break; link = &(*link)->sibling; } if (cursor->current + BLOCK_SENTINEL_LENGTH > cursor->end) { return Error("Reading past the end"); } cursor->current += BLOCK_SENTINEL_LENGTH; return element; } static bool isEndLine(const Cursor& cursor) { return *cursor.current == '\n'; } static void skipInsignificantWhitespaces(Cursor* cursor) { while (cursor->current < cursor->end && isspace(*cursor->current) && *cursor->current != '\n') { ++cursor->current; } } static void skipLine(Cursor* cursor) { while (cursor->current < cursor->end && !isEndLine(*cursor)) { ++cursor->current; } if (cursor->current < cursor->end) ++cursor->current; skipInsignificantWhitespaces(cursor); } static void skipWhitespaces(Cursor* cursor) { while (cursor->current < cursor->end && isspace(*cursor->current)) { ++cursor->current; } while (cursor->current < cursor->end && *cursor->current == ';') skipLine(cursor); } static bool isTextTokenChar(char c) { return isalnum(c) || c == '_' || c == '-'; } static DataView readTextToken(Cursor* cursor) { DataView ret; ret.begin = cursor->current; while (cursor->current < cursor->end && isTextTokenChar(*cursor->current)) { ++cursor->current; } ret.end = cursor->current; return ret; } static OptionalError readTextProperty(Cursor* cursor, Allocator& allocator) { Property* prop = allocator.allocate(); prop->value.is_binary = false; prop->next = nullptr; if (*cursor->current == '"') { prop->type = 'S'; ++cursor->current; prop->value.begin = cursor->current; while (cursor->current < cursor->end && *cursor->current != '"') { ++cursor->current; } prop->value.end = cursor->current; if (cursor->current < cursor->end) ++cursor->current; // skip '"' return prop; } if (isdigit(*cursor->current) || *cursor->current == '-') { prop->type = 'L'; prop->value.begin = cursor->current; if (*cursor->current == '-') ++cursor->current; while (cursor->current < cursor->end && isdigit(*cursor->current)) { ++cursor->current; } prop->value.end = cursor->current; if (cursor->current < cursor->end && *cursor->current == '.') { prop->type = 'D'; ++cursor->current; while (cursor->current < cursor->end && isdigit(*cursor->current)) { ++cursor->current; } if (cursor->current < cursor->end && (*cursor->current == 'e' || *cursor->current == 'E')) { // 10.5e-013 ++cursor->current; if (cursor->current < cursor->end && *cursor->current == '-') ++cursor->current; while (cursor->current < cursor->end && isdigit(*cursor->current)) ++cursor->current; } prop->value.end = cursor->current; } else if (cursor->current < cursor->end && (*cursor->current == 'e' || *cursor->current == 'E')) { prop->type = 'D'; // 10e-013 ++cursor->current; if (cursor->current < cursor->end && *cursor->current == '-') ++cursor->current; while (cursor->current < cursor->end && isdigit(*cursor->current)) ++cursor->current; prop->value.end = cursor->current; } return prop; } if (*cursor->current == 'T' || *cursor->current == 'Y' || *cursor->current == 'W' || *cursor->current == 'C') { // WTF is this prop->type = *cursor->current; prop->value.begin = cursor->current; ++cursor->current; prop->value.end = cursor->current; return prop; } if (*cursor->current == '*') { prop->type = 'l'; ++cursor->current; // Vertices: *10740 { a: 14.2760353088379,... } while (cursor->current < cursor->end && *cursor->current != ':') { ++cursor->current; } if (cursor->current < cursor->end) ++cursor->current; // skip ':' skipInsignificantWhitespaces(cursor); prop->value.begin = cursor->current; prop->count = 0; bool is_any = false; while (cursor->current < cursor->end && *cursor->current != '}') { if (*cursor->current == ',') { if (is_any) ++prop->count; is_any = false; } else if (!isspace(*cursor->current) && *cursor->current != '\n') is_any = true; if (*cursor->current == '.') prop->type = 'd'; ++cursor->current; } if (is_any) ++prop->count; prop->value.end = cursor->current; if (cursor->current < cursor->end) ++cursor->current; // skip '}' return prop; } assert(false); return Error("TODO"); } static OptionalError readTextElement(Cursor* cursor, Allocator& allocator) { DataView id = readTextToken(cursor); if (cursor->current == cursor->end) return Error("Unexpected end of file"); if (*cursor->current != ':') return Error("Unexpected character"); ++cursor->current; skipInsignificantWhitespaces(cursor); if (cursor->current == cursor->end) return Error("Unexpected end of file"); Element* element = allocator.allocate(); element->id = id; Property** prop_link = &element->first_property; while (cursor->current < cursor->end && *cursor->current != '\n' && *cursor->current != '{') { OptionalError prop = readTextProperty(cursor, allocator); if (prop.isError()) { return Error(); } if (cursor->current < cursor->end && *cursor->current == ',') { ++cursor->current; skipWhitespaces(cursor); } skipInsignificantWhitespaces(cursor); *prop_link = prop.getValue(); prop_link = &(*prop_link)->next; } Element** link = &element->child; if (*cursor->current == '{') { ++cursor->current; skipWhitespaces(cursor); while (cursor->current < cursor->end && *cursor->current != '}') { OptionalError child = readTextElement(cursor, allocator); if (child.isError()) { return Error(); } skipWhitespaces(cursor); *link = child.getValue(); link = &(*link)->sibling; } if (cursor->current < cursor->end) ++cursor->current; // skip '}' } return element; } static OptionalError tokenizeText(const u8* data, size_t size, Allocator& allocator) { Cursor cursor; cursor.begin = data; cursor.current = data; cursor.end = data + size; Element* root = allocator.allocate(); root->first_property = nullptr; root->id.begin = nullptr; root->id.end = nullptr; root->child = nullptr; root->sibling = nullptr; Element** element = &root->child; while (cursor.current < cursor.end) { if (*cursor.current == ';' || *cursor.current == '\r' || *cursor.current == '\n') { skipLine(&cursor); } else { OptionalError child = readTextElement(&cursor, allocator); if (child.isError()) { return Error(); } *element = child.getValue(); if (!*element) return root; element = &(*element)->sibling; } } return root; } static OptionalError tokenize(const u8* data, size_t size, u32& version, Allocator& allocator) { Cursor cursor; cursor.begin = data; cursor.current = data; cursor.end = data + size; const Header* header = (const Header*)cursor.current; cursor.current += sizeof(*header); version = header->version; Element* root = allocator.allocate(); root->first_property = nullptr; root->id.begin = nullptr; root->id.end = nullptr; root->child = nullptr; root->sibling = nullptr; Element** element = &root->child; for (;;) { OptionalError child = readElement(&cursor, header->version, allocator); if (child.isError()) { return Error(); } *element = child.getValue(); if (!*element) return root; element = &(*element)->sibling; } } static void parseTemplates(const Element& root) { const Element* defs = findChild(root, "Definitions"); if (!defs) return; std::unordered_map templates; Element* def = defs->child; while (def) { if (def->id == "ObjectType") { Element* subdef = def->child; while (subdef) { if (subdef->id == "PropertyTemplate") { DataView prop1 = def->first_property->value; DataView prop2 = subdef->first_property->value; std::string key((const char*)prop1.begin, prop1.end - prop1.begin); key += std::string((const char*)prop1.begin, prop1.end - prop1.begin); templates[key] = subdef; } subdef = subdef->sibling; } } def = def->sibling; } // TODO } struct Scene; Mesh::Mesh(const Scene& _scene, const IElement& _element) : Object(_scene, _element) { } struct MeshImpl : Mesh { MeshImpl(const Scene& _scene, const IElement& _element) : Mesh(_scene, _element) { is_node = true; } Matrix getGeometricMatrix() const override { Vec3 translation = resolveVec3Property(*this, "GeometricTranslation", {0, 0, 0}); Vec3 rotation = resolveVec3Property(*this, "GeometricRotation", {0, 0, 0}); Vec3 scale = resolveVec3Property(*this, "GeometricScaling", {1, 1, 1}); Matrix scale_mtx = makeIdentity(); scale_mtx.m[0] = (float)scale.x; scale_mtx.m[5] = (float)scale.y; scale_mtx.m[10] = (float)scale.z; Matrix mtx = getRotationMatrix(rotation, RotationOrder::EULER_XYZ); setTranslation(translation, &mtx); return scale_mtx * mtx; } Type getType() const override { return Type::MESH; } const Pose* getPose() const override { return pose; } const Geometry* getGeometry() const override { return geometry; } const Material* getMaterial(int index) const override { return materials[index]; } int getMaterialCount() const override { return (int)materials.size(); } const Pose* pose = nullptr; const Geometry* geometry = nullptr; std::vector materials; }; Material::Material(const Scene& _scene, const IElement& _element) : Object(_scene, _element) { } struct MaterialImpl : Material { MaterialImpl(const Scene& _scene, const IElement& _element) : Material(_scene, _element) { for (const Texture*& tex : textures) tex = nullptr; } Type getType() const override { return Type::MATERIAL; } const Texture* getTexture(Texture::TextureType type) const override { return textures[type]; } Color getDiffuseColor() const override { return diffuse_color; } Color getSpecularColor() const override { return specular_color; } Color getReflectionColor() const override { return reflection_color; }; Color getAmbientColor() const override { return ambient_color; }; Color getEmissiveColor() const override { return emissive_color; }; double getDiffuseFactor() const override { return diffuse_factor; }; double getSpecularFactor() const override { return specular_factor; }; double getReflectionFactor() const override { return reflection_factor; }; double getShininess() const override { return shininess; }; double getShininessExponent() const override { return shininess_exponent; }; double getAmbientFactor() const override { return ambient_factor; }; double getBumpFactor() const override { return bump_factor; }; double getEmissiveFactor() const override { return emissive_factor; }; const Texture* textures[Texture::TextureType::COUNT]; Color diffuse_color; Color specular_color; Color reflection_color; Color ambient_color; Color emissive_color; double diffuse_factor; double specular_factor; double reflection_factor; double shininess; double shininess_exponent; double ambient_factor; double bump_factor; double emissive_factor; }; struct LimbNodeImpl : Object { LimbNodeImpl(const Scene& _scene, const IElement& _element) : Object(_scene, _element) { is_node = true; } Type getType() const override { return Type::LIMB_NODE; } }; struct NullImpl : Object { NullImpl(const Scene& _scene, const IElement& _element) : Object(_scene, _element) { is_node = true; } Type getType() const override { return Type::NULL_NODE; } }; NodeAttribute::NodeAttribute(const Scene& _scene, const IElement& _element) : Object(_scene, _element) { } struct NodeAttributeImpl : NodeAttribute { NodeAttributeImpl(const Scene& _scene, const IElement& _element) : NodeAttribute(_scene, _element) { } Type getType() const override { return Type::NODE_ATTRIBUTE; } DataView getAttributeType() const override { return attribute_type; } DataView attribute_type; }; Geometry::Geometry(const Scene& _scene, const IElement& _element) : Object(_scene, _element) { } struct GeometryImpl : Geometry { enum VertexDataMapping { BY_POLYGON_VERTEX, BY_POLYGON, BY_VERTEX }; struct NewVertex { ~NewVertex() { delete next; } int index = -1; NewVertex* next = nullptr; }; std::vector vertices; std::vector normals; std::vector uvs[s_uvs_max]; std::vector colors; std::vector tangents; std::vector materials; const Skin* skin = nullptr; const BlendShape* blendShape = nullptr; std::vector indices; std::vector to_new_vertices; GeometryImpl(const Scene& _scene, const IElement& _element) : Geometry(_scene, _element) { } Type getType() const override { return Type::GEOMETRY; } int getVertexCount() const override { return (int)vertices.size(); } const int* getFaceIndices() const override { return indices.empty() ? nullptr : &indices[0]; } int getIndexCount() const override { return (int)indices.size(); } const Vec3* getVertices() const override { return &vertices[0]; } const Vec3* getNormals() const override { return normals.empty() ? nullptr : &normals[0]; } const Vec2* getUVs(int index = 0) const override { return index < 0 || index >= s_uvs_max || uvs[index].empty() ? nullptr : &uvs[index][0]; } const Vec4* getColors() const override { return colors.empty() ? nullptr : &colors[0]; } const Vec3* getTangents() const override { return tangents.empty() ? nullptr : &tangents[0]; } const Skin* getSkin() const override { return skin; } const BlendShape* getBlendShape() const override { return blendShape; } const int* getMaterials() const override { return materials.empty() ? nullptr : &materials[0]; } }; Shape::Shape(const Scene& _scene, const IElement& _element) : Object(_scene, _element) { } struct ShapeImpl : Shape { std::vector vertices; std::vector normals; ShapeImpl(const Scene& _scene, const IElement& _element) : Shape(_scene, _element) { } bool postprocess(GeometryImpl* geom, Allocator& allocator); Type getType() const override { return Type::SHAPE; } int getVertexCount() const override { return (int)vertices.size(); } const Vec3* getVertices() const override { return &vertices[0]; } const Vec3* getNormals() const override { return normals.empty() ? nullptr : &normals[0]; } }; Cluster::Cluster(const Scene& _scene, const IElement& _element) : Object(_scene, _element) { } struct ClusterImpl : Cluster { ClusterImpl(const Scene& _scene, const IElement& _element) : Cluster(_scene, _element) { } const int* getIndices() const override { return &indices[0]; } int getIndicesCount() const override { return (int)indices.size(); } const double* getWeights() const override { return &weights[0]; } int getWeightsCount() const override { return (int)weights.size(); } Matrix getTransformMatrix() const override { return transform_matrix; } Matrix getTransformLinkMatrix() const override { return transform_link_matrix; } Object* getLink() const override { return link; } bool postprocess(Allocator& allocator) { assert(skin); GeometryImpl* geom = (GeometryImpl*)skin->resolveObjectLinkReverse(Object::Type::GEOMETRY); if (!geom) return false; allocator.int_tmp.clear(); // old indices const Element* indexes = findChild((const Element&)element, "Indexes"); if (indexes && indexes->first_property) { if (!parseBinaryArray(*indexes->first_property, &allocator.int_tmp)) return false; } allocator.double_tmp.clear(); // old weights const Element* weights_el = findChild((const Element&)element, "Weights"); if (weights_el && weights_el->first_property) { if (!parseBinaryArray(*weights_el->first_property, &allocator.double_tmp)) return false; } if (allocator.int_tmp.size() != allocator.double_tmp.size()) return false; indices.reserve(allocator.int_tmp.size()); weights.reserve(allocator.int_tmp.size()); int* ir = allocator.int_tmp.empty() ? nullptr : &allocator.int_tmp[0]; double* wr = allocator.double_tmp.empty() ? nullptr : &allocator.double_tmp[0]; for (int i = 0, c = (int)allocator.int_tmp.size(); i < c; ++i) { int old_idx = ir[i]; double w = wr[i]; GeometryImpl::NewVertex* n = &geom->to_new_vertices[old_idx]; if (n->index == -1) continue; // skip vertices which aren't indexed. while (n) { indices.push_back(n->index); weights.push_back(w); n = n->next; } } return true; } Object* link = nullptr; Skin* skin = nullptr; std::vector indices; std::vector weights; Matrix transform_matrix; Matrix transform_link_matrix; Type getType() const override { return Type::CLUSTER; } }; AnimationStack::AnimationStack(const Scene& _scene, const IElement& _element) : Object(_scene, _element) { } AnimationLayer::AnimationLayer(const Scene& _scene, const IElement& _element) : Object(_scene, _element) { } AnimationCurve::AnimationCurve(const Scene& _scene, const IElement& _element) : Object(_scene, _element) { } AnimationCurveNode::AnimationCurveNode(const Scene& _scene, const IElement& _element) : Object(_scene, _element) { } struct AnimationStackImpl : AnimationStack { AnimationStackImpl(const Scene& _scene, const IElement& _element) : AnimationStack(_scene, _element) { } const AnimationLayer* getLayer(int index) const override { return resolveObjectLink(index); } Type getType() const override { return Type::ANIMATION_STACK; } }; struct AnimationCurveImpl : AnimationCurve { AnimationCurveImpl(const Scene& _scene, const IElement& _element) : AnimationCurve(_scene, _element) { } int getKeyCount() const override { return (int)times.size(); } const i64* getKeyTime() const override { return ×[0]; } const float* getKeyValue() const override { return &values[0]; } std::vector times; std::vector values; Type getType() const override { return Type::ANIMATION_CURVE; } }; Skin::Skin(const Scene& _scene, const IElement& _element) : Object(_scene, _element) { } struct SkinImpl : Skin { SkinImpl(const Scene& _scene, const IElement& _element) : Skin(_scene, _element) { } int getClusterCount() const override { return (int)clusters.size(); } const Cluster* getCluster(int idx) const override { return clusters[idx]; } Type getType() const override { return Type::SKIN; } std::vector clusters; }; BlendShapeChannel::BlendShapeChannel(const Scene& _scene, const IElement& _element) : Object(_scene, _element) { } struct BlendShapeChannelImpl : BlendShapeChannel { BlendShapeChannelImpl(const Scene& _scene, const IElement& _element) : BlendShapeChannel(_scene, _element) { } double getDeformPercent() const override { return deformPercent; } int getShapeCount() const override { return (int)shapes.size(); } const Shape* getShape(int idx) const override { return shapes[idx]; } Type getType() const override { return Type::BLEND_SHAPE_CHANNEL; } bool postprocess(Allocator& allocator) { assert(blendShape); GeometryImpl* geom = (GeometryImpl*)blendShape->resolveObjectLinkReverse(Object::Type::GEOMETRY); if (!geom) return false; const Element* deform_percent_el = findChild((const Element&)element, "DeformPercent"); if (deform_percent_el && deform_percent_el->first_property) { if (!parseDouble(*deform_percent_el->first_property, &deformPercent)) return false; } const Element* full_weights_el = findChild((const Element&)element, "FullWeights"); if (full_weights_el && full_weights_el->first_property) { if (!parseBinaryArray(*full_weights_el->first_property, &fullWeights)) return false; } for (int i = 0; i < shapes.size(); i++) { auto shape = (ShapeImpl*)shapes[i]; if (!shape->postprocess(geom, allocator)) return false; } return true; } BlendShape* blendShape = nullptr; double deformPercent = 0; std::vector fullWeights; std::vector shapes; }; BlendShape::BlendShape(const Scene& _scene, const IElement& _element) : Object(_scene, _element) { } struct BlendShapeImpl : BlendShape { BlendShapeImpl(const Scene& _scene, const IElement& _element) : BlendShape(_scene, _element) { } int getBlendShapeChannelCount() const override { return (int)blendShapeChannels.size(); } const BlendShapeChannel* getBlendShapeChannel(int idx) const override { return blendShapeChannels[idx]; } Type getType() const override { return Type::BLEND_SHAPE; } std::vector blendShapeChannels; }; Texture::Texture(const Scene& _scene, const IElement& _element) : Object(_scene, _element) { } Pose::Pose(const Scene& _scene, const IElement& _element) : Object(_scene, _element) { } struct PoseImpl : Pose { PoseImpl(const Scene& _scene, const IElement& _element) : Pose(_scene, _element) { } bool postprocess(Scene* scene); Matrix getMatrix() const override { return matrix; } const Object* getNode() const override { return node; } Type getType() const override { return Type::POSE; } Matrix matrix; Object* node = nullptr; DataView node_id; }; struct TextureImpl : Texture { TextureImpl(const Scene& _scene, const IElement& _element) : Texture(_scene, _element) { } DataView getRelativeFileName() const override { return relative_filename; } DataView getFileName() const override { return filename; } DataView getEmbeddedData() const override; DataView media; DataView filename; DataView relative_filename; Type getType() const override { return Type::TEXTURE; } }; struct Root : Object { Root(const Scene& _scene, const IElement& _element) : Object(_scene, _element) { copyString(name, "RootNode"); is_node = true; } Type getType() const override { return Type::ROOT; } }; struct Scene : IScene { struct Connection { enum Type { OBJECT_OBJECT, OBJECT_PROPERTY }; Type type = OBJECT_OBJECT; u64 from = 0; u64 to = 0; DataView property; }; struct ObjectPair { const Element* element; Object* object; }; int getAnimationStackCount() const override { return (int)m_animation_stacks.size(); } int getMeshCount() const override { return (int)m_meshes.size(); } float getSceneFrameRate() const override { return m_scene_frame_rate; } const GlobalInfo* getGlobalInfo() const override { return &m_info; } const GlobalSettings* getGlobalSettings() const override { return &m_settings; } const Object* const* getAllObjects() const override { return m_all_objects.empty() ? nullptr : &m_all_objects[0]; } int getAllObjectCount() const override { return (int)m_all_objects.size(); } int getEmbeddedDataCount() const override { return (int)m_videos.size(); } DataView getEmbeddedData(int index) const override { return m_videos[index].content; } DataView getEmbeddedFilename(int index) const override { return m_videos[index].filename; } const AnimationStack* getAnimationStack(int index) const override { assert(index >= 0); assert(index < m_animation_stacks.size()); return m_animation_stacks[index]; } const Mesh* getMesh(int index) const override { assert(index >= 0); assert(index < m_meshes.size()); return m_meshes[index]; } const TakeInfo* getTakeInfo(const char* name) const override { for (const TakeInfo& info : m_take_infos) { if (info.name == name) return &info; } return nullptr; } const IElement* getRootElement() const override { return m_root_element; } const Object* getRoot() const override { return m_root; } void destroy() override { delete this; } ~Scene() override { for(auto ptr : m_all_objects) ptr->~Object(); } Element* m_root_element = nullptr; Root* m_root = nullptr; float m_scene_frame_rate = -1; GlobalInfo m_info; GlobalSettings m_settings; std::unordered_map m_object_map; std::vector m_all_objects; std::vector m_meshes; std::vector m_animation_stacks; std::vector m_connections; std::vector m_data; std::vector m_take_infos; std::vector