/** * Copyright (c) 2014-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ #ifndef __RAWMODEL_H__ #define __RAWMODEL_H__ #include #include enum RawVertexAttribute { RAW_VERTEX_ATTRIBUTE_POSITION = 1 << 0, RAW_VERTEX_ATTRIBUTE_NORMAL = 1 << 1, RAW_VERTEX_ATTRIBUTE_TANGENT = 1 << 2, RAW_VERTEX_ATTRIBUTE_BINORMAL = 1 << 3, RAW_VERTEX_ATTRIBUTE_COLOR = 1 << 4, RAW_VERTEX_ATTRIBUTE_UV0 = 1 << 5, RAW_VERTEX_ATTRIBUTE_UV1 = 1 << 6, RAW_VERTEX_ATTRIBUTE_JOINT_INDICES = 1 << 7, RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS = 1 << 8, RAW_VERTEX_ATTRIBUTE_AUTO = 1 << 31 }; struct RawBlendVertex { Vec3f position {}; Vec3f normal {}; Vec4f tangent {}; bool operator==(const RawBlendVertex &other) const { return position == other.position && normal == other.normal && tangent == other.tangent; } }; struct RawVertex { RawVertex() : polarityUv0(false), pad1(false), pad2(false), pad3(false) {} Vec3f position { 0.0f }; Vec3f normal { 0.0f }; Vec3f binormal { 0.0f }; Vec4f tangent { 0.0f }; Vec4f color { 0.0f }; Vec2f uv0 { 0.0f }; Vec2f uv1 { 0.0f }; Vec4i jointIndices { 0, 0, 0, 0 }; Vec4f jointWeights { 0.0f }; // end of members that directly correspond to vertex attributes // if this vertex participates in a blend shape setup, the surfaceIx of its dedicated mesh; otherwise, -1 int blendSurfaceIx = -1; // the size of this vector is always identical to the size of the corresponding RawSurface.blendChannels std::vector blends { }; bool polarityUv0; bool pad1; bool pad2; bool pad3; bool operator==(const RawVertex &other) const; size_t Difference(const RawVertex &other) const; }; class VertexHasher { public: size_t operator()(const RawVertex &v) const { size_t seed = 5381; const auto hasher = std::hash{}; seed ^= hasher(v.position[0]) + 0x9e3779b9 + (seed<<6) + (seed>>2); seed ^= hasher(v.position[1]) + 0x9e3779b9 + (seed<<6) + (seed>>2); seed ^= hasher(v.position[2]) + 0x9e3779b9 + (seed<<6) + (seed>>2); return seed; } }; struct RawTriangle { int verts[3]; int materialIndex; int surfaceIndex; }; enum RawShadingModel { RAW_SHADING_MODEL_UNKNOWN = -1, RAW_SHADING_MODEL_CONSTANT, RAW_SHADING_MODEL_LAMBERT, RAW_SHADING_MODEL_BLINN, RAW_SHADING_MODEL_PHONG, RAW_SHADING_MODEL_PBR_MET_ROUGH, RAW_SHADING_MODEL_MAX }; static inline std::string Describe(RawShadingModel model) { switch(model) { case RAW_SHADING_MODEL_UNKNOWN: return ""; case RAW_SHADING_MODEL_CONSTANT: return "Constant"; case RAW_SHADING_MODEL_LAMBERT: return "Lambert"; case RAW_SHADING_MODEL_BLINN: return "Blinn"; case RAW_SHADING_MODEL_PHONG: return "Phong"; case RAW_SHADING_MODEL_PBR_MET_ROUGH: return "Metallic/Roughness"; case RAW_SHADING_MODEL_MAX: default: return ""; } } enum RawTextureUsage { RAW_TEXTURE_USAGE_NONE = -1, RAW_TEXTURE_USAGE_AMBIENT, RAW_TEXTURE_USAGE_DIFFUSE, RAW_TEXTURE_USAGE_NORMAL, RAW_TEXTURE_USAGE_SPECULAR, RAW_TEXTURE_USAGE_SHININESS, RAW_TEXTURE_USAGE_EMISSIVE, RAW_TEXTURE_USAGE_REFLECTION, RAW_TEXTURE_USAGE_ALBEDO, RAW_TEXTURE_USAGE_OCCLUSION, RAW_TEXTURE_USAGE_ROUGHNESS, RAW_TEXTURE_USAGE_METALLIC, RAW_TEXTURE_USAGE_MAX }; static inline std::string Describe(RawTextureUsage usage) { switch (usage) { case RAW_TEXTURE_USAGE_NONE: return ""; case RAW_TEXTURE_USAGE_AMBIENT: return "ambient"; case RAW_TEXTURE_USAGE_DIFFUSE: return "diffuse"; case RAW_TEXTURE_USAGE_NORMAL: return "normal"; case RAW_TEXTURE_USAGE_SPECULAR: return "specuar"; case RAW_TEXTURE_USAGE_SHININESS: return "shininess"; case RAW_TEXTURE_USAGE_EMISSIVE: return "emissive"; case RAW_TEXTURE_USAGE_REFLECTION: return "reflection"; case RAW_TEXTURE_USAGE_OCCLUSION: return "occlusion"; case RAW_TEXTURE_USAGE_ROUGHNESS: return "roughness"; case RAW_TEXTURE_USAGE_METALLIC: return "metallic"; case RAW_TEXTURE_USAGE_MAX:default: return "unknown"; } }; enum RawTextureOcclusion { RAW_TEXTURE_OCCLUSION_OPAQUE, RAW_TEXTURE_OCCLUSION_TRANSPARENT }; struct RawTexture { std::string name; // logical name in FBX file int width; int height; int mipLevels; RawTextureUsage usage; RawTextureOcclusion occlusion; std::string fileName; // original filename in FBX file std::string fileLocation; // inferred path in local filesystem, or "" }; enum RawMaterialType { RAW_MATERIAL_TYPE_OPAQUE, RAW_MATERIAL_TYPE_TRANSPARENT, RAW_MATERIAL_TYPE_SKINNED_OPAQUE, RAW_MATERIAL_TYPE_SKINNED_TRANSPARENT, }; struct RawMatProps { explicit RawMatProps(RawShadingModel shadingModel) : shadingModel(shadingModel) {} const RawShadingModel shadingModel; virtual bool operator!=(const RawMatProps &other) const { return !(*this == other); } virtual bool operator==(const RawMatProps &other) const { return shadingModel == other.shadingModel; }; }; struct RawTraditionalMatProps : RawMatProps { RawTraditionalMatProps( RawShadingModel shadingModel, const Vec3f &&ambientFactor, const Vec4f &&diffuseFactor, const Vec3f &&emissiveFactor, const Vec3f &&specularFactor, const float shininess ) : RawMatProps(shadingModel), ambientFactor(ambientFactor), diffuseFactor(diffuseFactor), emissiveFactor(emissiveFactor), specularFactor(specularFactor), shininess(shininess) {} const Vec3f ambientFactor; const Vec4f diffuseFactor; const Vec3f emissiveFactor; const Vec3f specularFactor; const float shininess; bool operator==(const RawMatProps &other) const override { if (RawMatProps::operator==(other)) { const auto &typed = (RawTraditionalMatProps &) other; return ambientFactor == typed.ambientFactor && diffuseFactor == typed.diffuseFactor && specularFactor == typed.specularFactor && emissiveFactor == typed.emissiveFactor && shininess == typed.shininess; } return false; } }; struct RawMetRoughMatProps : RawMatProps { RawMetRoughMatProps( RawShadingModel shadingModel, const Vec4f &&diffuseFactor, const Vec3f &&emissiveFactor, float emissiveIntensity, float metallic, float roughness ) : RawMatProps(shadingModel), diffuseFactor(diffuseFactor), emissiveFactor(emissiveFactor), emissiveIntensity(emissiveIntensity), metallic(metallic), roughness(roughness) {} const Vec4f diffuseFactor; const Vec3f emissiveFactor; const float emissiveIntensity; const float metallic; const float roughness; bool operator==(const RawMatProps &other) const override { if (RawMatProps::operator==(other)) { const auto &typed = (RawMetRoughMatProps &) other; return diffuseFactor == typed.diffuseFactor && emissiveFactor == typed.emissiveFactor && emissiveIntensity == typed.emissiveIntensity && metallic == typed.metallic && roughness == typed.roughness; } return false; } }; struct RawMaterial { std::string name; RawMaterialType type; std::shared_ptr info; int textures[RAW_TEXTURE_USAGE_MAX]; }; struct RawBlendChannel { float defaultDeform; bool hasNormals; bool hasTangents; }; struct RawSurface { long id; std::string name; // The name of this surface std::string skeletonRootName; // The name of the root of the skeleton. Bounds bounds; std::vector jointNames; std::vector jointGeometryMins; std::vector jointGeometryMaxs; std::vector inverseBindMatrices; std::vector blendChannels; bool discrete; }; struct RawChannel { int nodeIndex; std::vector translations; std::vector rotations; std::vector scales; std::vector weights; }; struct RawAnimation { std::string name; std::vector times; std::vector channels; }; struct RawCamera { std::string name; std::string nodeName; enum { CAMERA_MODE_PERSPECTIVE, CAMERA_MODE_ORTHOGRAPHIC } mode; struct { float aspectRatio; float fovDegreesX; float fovDegreesY; float nearZ; float farZ; } perspective; struct { float magX; float magY; float nearZ; float farZ; } orthographic; }; struct RawNode { bool isJoint; std::string name; std::string parentName; std::vector childNames; Vec3f translation; Quatf rotation; Vec3f scale; long surfaceId; }; class RawModel { public: RawModel(); // Add geometry. void AddVertexAttribute(const RawVertexAttribute attrib); int AddVertex(const RawVertex &vertex); int AddTriangle(const int v0, const int v1, const int v2, const int materialIndex, const int surfaceIndex); int AddTexture(const std::string &name, const std::string &fileName, const std::string &fileLocation, RawTextureUsage usage); int AddMaterial(const RawMaterial &material); int AddMaterial( const char *name, const RawMaterialType materialType, const int textures[RAW_TEXTURE_USAGE_MAX], std::shared_ptr materialInfo); int AddSurface(const RawSurface &suface); int AddSurface(const char *name, long surfaceId); int AddAnimation(const RawAnimation &animation); int AddCameraPerspective( const char *name, const char *nodeName, const float aspectRatio, const float fovDegreesX, const float fovDegreesY, const float nearZ, const float farZ); int AddCameraOrthographic(const char *name, const char *nodeName, const float magX, const float magY, const float nearZ, const float farZ); int AddNode(const RawNode &node); int AddNode(const char *name, const char *parentName); void SetRootNode(const char *name) { rootNodeName = name; } const char *GetRootNode() const { return rootNodeName.c_str(); } // Remove unused vertices, textures or materials after removing vertex attributes, textures, materials or surfaces. void Condense(); void TransformTextures(const std::vector> &transforms); // Get the attributes stored per vertex. int GetVertexAttributes() const { return vertexAttributes; } // Iterate over the vertices. int GetVertexCount() const { return (int) vertices.size(); } const RawVertex &GetVertex(const int index) const { return vertices[index]; } // Iterate over the triangles. int GetTriangleCount() const { return (int) triangles.size(); } const RawTriangle &GetTriangle(const int index) const { return triangles[index]; } // Iterate over the textures. int GetTextureCount() const { return (int) textures.size(); } const RawTexture &GetTexture(const int index) const { return textures[index]; } // Iterate over the materials. int GetMaterialCount() const { return (int) materials.size(); } const RawMaterial &GetMaterial(const int index) const { return materials[index]; } // Iterate over the surfaces. int GetSurfaceCount() const { return (int) surfaces.size(); } const RawSurface &GetSurface(const int index) const { return surfaces[index]; } RawSurface &GetSurface(const int index) { return surfaces[index]; } int GetSurfaceById(const long id) const; // Iterate over the animations. int GetAnimationCount() const { return (int) animations.size(); } const RawAnimation &GetAnimation(const int index) const { return animations[index]; } // Iterate over the cameras. int GetCameraCount() const { return (int) cameras.size(); } const RawCamera &GetCamera(const int index) const { return cameras[index]; } // Iterate over the nodes. int GetNodeCount() const { return (int) nodes.size(); } const RawNode &GetNode(const int index) const { return nodes[index]; } RawNode &GetNode(const int index) { return nodes[index]; } int GetNodeByName(const char *name) const; // Create individual attribute arrays. // Returns true if the vertices store the particular attribute. template void GetAttributeArray(std::vector<_attrib_type_> &out, const _attrib_type_ RawVertex::* ptr) const; // Create an array with a raw model for each material. // Multiple surfaces with the same material will turn into a single model. // However, surfaces that are marked as 'discrete' will turn into separate models. void CreateMaterialModels( std::vector &materialModels, const int maxModelVertices, const int keepAttribs, const bool forceDiscrete) const; private: std::string rootNodeName; int vertexAttributes; std::unordered_map vertexHash; std::vector vertices; std::vector triangles; std::vector textures; std::vector materials; std::vector surfaces; std::vector animations; std::vector cameras; std::vector nodes; }; template void RawModel::GetAttributeArray(std::vector<_attrib_type_> &out, const _attrib_type_ RawVertex::* ptr) const { out.resize(vertices.size()); for (size_t i = 0; i < vertices.size(); i++) { out[i] = vertices[i].*ptr; } } #endif // !__RAWMODEL_H__