Esempio n. 1
0
void glTFExporter::ExportMeshes()
{
    // Not for
    //     using IndicesType = decltype(aiFace::mNumIndices);
    // But yes for
    //     using IndicesType = unsigned short;
    // because "ComponentType_UNSIGNED_SHORT" used for indices. And it's a maximal type according to glTF specification.
    typedef unsigned short IndicesType;

    // Variables needed for compression. BEGIN.
    // Indices, not pointers - because pointer to buffer is changing while writing to it.
    size_t idx_srcdata_begin = 0; // Index of buffer before writing mesh data. Also, index of begin of coordinates array in buffer.
    size_t idx_srcdata_normal = SIZE_MAX;// Index of begin of normals array in buffer. SIZE_MAX - mean that mesh has no normals.
    std::vector<size_t> idx_srcdata_tc;// Array of indices. Every index point to begin of texture coordinates array in buffer.
    size_t idx_srcdata_ind;// Index of begin of coordinates indices array in buffer.
    bool comp_allow;// Point that data of current mesh can be compressed.
    // Variables needed for compression. END.

    std::string fname = std::string(mFilename);
    std::string bufferIdPrefix = fname.substr(0, fname.rfind(".gltf"));
    std::string bufferId = mAsset->FindUniqueID("", bufferIdPrefix.c_str());

    Ref<Buffer> b = mAsset->GetBodyBuffer();
    if (!b) {
       b = mAsset->buffers.Create(bufferId);
    }

    //----------------------------------------
    // Initialize variables for the skin
    bool createSkin = false;
    for (unsigned int idx_mesh = 0; idx_mesh < mScene->mNumMeshes; ++idx_mesh) {
        const aiMesh* aim = mScene->mMeshes[idx_mesh];
        if(aim->HasBones()) {
            createSkin = true;
            break;
        }
    }

    Ref<Skin> skinRef;
    std::string skinName = mAsset->FindUniqueID("skin", "skin");
    std::vector<aiMatrix4x4> inverseBindMatricesData;
    if(createSkin) {
        skinRef = mAsset->skins.Create(skinName);
        skinRef->name = skinName;
    }
    //----------------------------------------

	for (unsigned int idx_mesh = 0; idx_mesh < mScene->mNumMeshes; ++idx_mesh) {
		const aiMesh* aim = mScene->mMeshes[idx_mesh];

		// Check if compressing requested and mesh can be encoded.
#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
		comp_allow = mProperties->GetPropertyBool("extensions.Open3DGC.use", false);
#else
		comp_allow = false;
#endif

		if(comp_allow && (aim->mPrimitiveTypes == aiPrimitiveType_TRIANGLE) && (aim->mNumVertices > 0) && (aim->mNumFaces > 0))
		{
			idx_srcdata_tc.clear();
			idx_srcdata_tc.reserve(AI_MAX_NUMBER_OF_TEXTURECOORDS);
		}
		else
		{
			std::string msg;

			if(aim->mPrimitiveTypes != aiPrimitiveType_TRIANGLE)
				msg = "all primitives of the mesh must be a triangles.";
			else
				msg = "mesh must has vertices and faces.";

            ASSIMP_LOG_WARN_F("GLTF: can not use Open3DGC-compression: ", msg);
            comp_allow = false;
		}

        std::string meshId = mAsset->FindUniqueID(aim->mName.C_Str(), "mesh");
        Ref<Mesh> m = mAsset->meshes.Create(meshId);
        m->primitives.resize(1);
        Mesh::Primitive& p = m->primitives.back();

        p.material = mAsset->materials.Get(aim->mMaterialIndex);

		/******************* Vertices ********************/
		// If compression is used then you need parameters of uncompressed region: begin and size. At this step "begin" is stored.
		if(comp_allow) idx_srcdata_begin = b->byteLength;

        Ref<Accessor> v = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mVertices, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);
		if (v) p.attributes.position.push_back(v);

		/******************** Normals ********************/
		if(comp_allow && (aim->mNormals != 0)) idx_srcdata_normal = b->byteLength;// Store index of normals array.

		Ref<Accessor> n = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mNormals, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);
		if (n) p.attributes.normal.push_back(n);

		/************** Texture coordinates **************/
        for (int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
            // Flip UV y coords
            if (aim -> mNumUVComponents[i] > 1) {
                for (unsigned int j = 0; j < aim->mNumVertices; ++j) {
                    aim->mTextureCoords[i][j].y = 1 - aim->mTextureCoords[i][j].y;
                }
            }

            if (aim->mNumUVComponents[i] > 0) {
                AttribType::Value type = (aim->mNumUVComponents[i] == 2) ? AttribType::VEC2 : AttribType::VEC3;

				if(comp_allow) idx_srcdata_tc.push_back(b->byteLength);// Store index of texture coordinates array.

				Ref<Accessor> tc = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mTextureCoords[i], AttribType::VEC3, type, ComponentType_FLOAT, false);
				if (tc) p.attributes.texcoord.push_back(tc);
			}
		}

		/*************** Vertices indices ****************/
		idx_srcdata_ind = b->byteLength;// Store index of indices array.

		if (aim->mNumFaces > 0) {
			std::vector<IndicesType> indices;
			unsigned int nIndicesPerFace = aim->mFaces[0].mNumIndices;
            indices.resize(aim->mNumFaces * nIndicesPerFace);
            for (size_t i = 0; i < aim->mNumFaces; ++i) {
                for (size_t j = 0; j < nIndicesPerFace; ++j) {
                    indices[i*nIndicesPerFace + j] = uint16_t(aim->mFaces[i].mIndices[j]);
                }
            }

			p.indices = ExportData(*mAsset, meshId, b, unsigned(indices.size()), &indices[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_UNSIGNED_SHORT, true);
		}

        switch (aim->mPrimitiveTypes) {
            case aiPrimitiveType_POLYGON:
                p.mode = PrimitiveMode_TRIANGLES; break; // TODO implement this
            case aiPrimitiveType_LINE:
                p.mode = PrimitiveMode_LINES; break;
            case aiPrimitiveType_POINT:
                p.mode = PrimitiveMode_POINTS; break;
            default: // aiPrimitiveType_TRIANGLE
                p.mode = PrimitiveMode_TRIANGLES;
        }

    /*************** Skins ****************/
    if(aim->HasBones()) {
        ExportSkin(*mAsset, aim, m, b, skinRef, inverseBindMatricesData);
    }

		/****************** Compression ******************/
		///TODO: animation: weights, joints.
		if(comp_allow)
		{
#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
			// Only one type of compression supported at now - Open3DGC.
			//
			o3dgc::BinaryStream bs;
			o3dgc::SC3DMCEncoder<IndicesType> encoder;
			o3dgc::IndexedFaceSet<IndicesType> comp_o3dgc_ifs;
			o3dgc::SC3DMCEncodeParams comp_o3dgc_params;

			//
			// Fill data for encoder.
			//
			// Quantization
			unsigned quant_coord = mProperties->GetPropertyInteger("extensions.Open3DGC.quantization.POSITION", 12);
			unsigned quant_normal = mProperties->GetPropertyInteger("extensions.Open3DGC.quantization.NORMAL", 10);
			unsigned quant_texcoord = mProperties->GetPropertyInteger("extensions.Open3DGC.quantization.TEXCOORD", 10);

			// Prediction
			o3dgc::O3DGCSC3DMCPredictionMode prediction_position = o3dgc::O3DGC_SC3DMC_PARALLELOGRAM_PREDICTION;
			o3dgc::O3DGCSC3DMCPredictionMode prediction_normal =  o3dgc::O3DGC_SC3DMC_SURF_NORMALS_PREDICTION;
			o3dgc::O3DGCSC3DMCPredictionMode prediction_texcoord = o3dgc::O3DGC_SC3DMC_PARALLELOGRAM_PREDICTION;

			// IndexedFacesSet: "Crease angle", "solid", "convex" are set to default.
			comp_o3dgc_ifs.SetCCW(true);
			comp_o3dgc_ifs.SetIsTriangularMesh(true);
			comp_o3dgc_ifs.SetNumFloatAttributes(0);
			// Coordinates
			comp_o3dgc_params.SetCoordQuantBits(quant_coord);
			comp_o3dgc_params.SetCoordPredMode(prediction_position);
			comp_o3dgc_ifs.SetNCoord(aim->mNumVertices);
			comp_o3dgc_ifs.SetCoord((o3dgc::Real* const)&b->GetPointer()[idx_srcdata_begin]);
			// Normals
			if(idx_srcdata_normal != SIZE_MAX)
			{
				comp_o3dgc_params.SetNormalQuantBits(quant_normal);
				comp_o3dgc_params.SetNormalPredMode(prediction_normal);
				comp_o3dgc_ifs.SetNNormal(aim->mNumVertices);
				comp_o3dgc_ifs.SetNormal((o3dgc::Real* const)&b->GetPointer()[idx_srcdata_normal]);
			}

			// Texture coordinates
			for(size_t num_tc = 0; num_tc < idx_srcdata_tc.size(); num_tc++)
			{
				size_t num = comp_o3dgc_ifs.GetNumFloatAttributes();

				comp_o3dgc_params.SetFloatAttributeQuantBits(static_cast<unsigned long>(num), quant_texcoord);
				comp_o3dgc_params.SetFloatAttributePredMode(static_cast<unsigned long>(num), prediction_texcoord);
				comp_o3dgc_ifs.SetNFloatAttribute(static_cast<unsigned long>(num), aim->mNumVertices);// number of elements.
				comp_o3dgc_ifs.SetFloatAttributeDim(static_cast<unsigned long>(num), aim->mNumUVComponents[num_tc]);// components per element: aiVector3D => x * float
				comp_o3dgc_ifs.SetFloatAttributeType(static_cast<unsigned long>(num), o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_TEXCOORD);
				comp_o3dgc_ifs.SetFloatAttribute(static_cast<unsigned long>(num), (o3dgc::Real* const)&b->GetPointer()[idx_srcdata_tc[num_tc]]);
				comp_o3dgc_ifs.SetNumFloatAttributes(static_cast<unsigned long>(num + 1));
			}

			// Coordinates indices
			comp_o3dgc_ifs.SetNCoordIndex(aim->mNumFaces);
			comp_o3dgc_ifs.SetCoordIndex((IndicesType* const)&b->GetPointer()[idx_srcdata_ind]);
			// Prepare to encoding
			comp_o3dgc_params.SetNumFloatAttributes(comp_o3dgc_ifs.GetNumFloatAttributes());
			if(mProperties->GetPropertyBool("extensions.Open3DGC.binary", true))
				comp_o3dgc_params.SetStreamType(o3dgc::O3DGC_STREAM_TYPE_BINARY);
			else
				comp_o3dgc_params.SetStreamType(o3dgc::O3DGC_STREAM_TYPE_ASCII);

			comp_o3dgc_ifs.ComputeMinMax(o3dgc::O3DGC_SC3DMC_MAX_ALL_DIMS);
			//
			// Encoding
			//
			encoder.Encode(comp_o3dgc_params, comp_o3dgc_ifs, bs);
			// Replace data in buffer.
			b->ReplaceData(idx_srcdata_begin, b->byteLength - idx_srcdata_begin, bs.GetBuffer(), bs.GetSize());
			//
			// Add information about extension to mesh.
			//
			// Create extension structure.
			Mesh::SCompression_Open3DGC* ext = new Mesh::SCompression_Open3DGC;

			// Fill it.
			ext->Buffer = b->id;
			ext->Offset = idx_srcdata_begin;
			ext->Count = b->byteLength - idx_srcdata_begin;
			ext->Binary = mProperties->GetPropertyBool("extensions.Open3DGC.binary");
			ext->IndicesCount = comp_o3dgc_ifs.GetNCoordIndex() * 3;
			ext->VerticesCount = comp_o3dgc_ifs.GetNCoord();
			// And assign to mesh.
			m->Extension.push_back(ext);
#endif
		}// if(comp_allow)
	}// for (unsigned int i = 0; i < mScene->mNumMeshes; ++i)

    //----------------------------------------
    // Finish the skin
    // Create the Accessor for skinRef->inverseBindMatrices
    if (createSkin) {
        mat4* invBindMatrixData = new mat4[inverseBindMatricesData.size()];
        for ( unsigned int idx_joint = 0; idx_joint < inverseBindMatricesData.size(); ++idx_joint) {
            CopyValue(inverseBindMatricesData[idx_joint], invBindMatrixData[idx_joint]);
        }

        Ref<Accessor> invBindMatrixAccessor = ExportData(*mAsset, skinName, b, static_cast<unsigned int>(inverseBindMatricesData.size()), invBindMatrixData, AttribType::MAT4, AttribType::MAT4, ComponentType_FLOAT);
        if (invBindMatrixAccessor) skinRef->inverseBindMatrices = invBindMatrixAccessor;

        // Identity Matrix   =====>  skinRef->bindShapeMatrix
        // Temporary. Hard-coded identity matrix here
        skinRef->bindShapeMatrix.isPresent = true;
        IdentityMatrix4(skinRef->bindShapeMatrix.value);

        // Find node that contains this mesh and add "skeletons" and "skin" attributes to that node.
        Ref<Node> rootNode = mAsset->nodes.Get(unsigned(0));
        Ref<Node> meshNode;
        std::string meshID = mAsset->meshes.Get(unsigned(0))->id;
        FindMeshNode(rootNode, meshNode, meshID);

        Ref<Node> rootJoint = FindSkeletonRootJoint(skinRef);
        meshNode->skeletons.push_back(rootJoint);
        meshNode->skin = skinRef;
    }
}