gltf 的sdk地址

这份代码的原始例子比较不好参考,创建一个三角形还比较好参考,创建多个三角形或者元素就有点懵。所以我做了个更通用的例子(即创建多个mesh)作为使用这个gltf sdk的样例。

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#include <GLTFSDK/GLTF.h>
#include <GLTFSDK/BufferBuilder.h>
#include <GLTFSDK/GLTFResourceWriter.h>
#include <GLTFSDK/GLBResourceWriter.h>
#include <GLTFSDK/IStreamWriter.h>
#include <GLTFSDK/Serialize.h>

// Replace this with <filesystem> (and use std::filesystem rather than
// std::experimental::filesystem) if your toolchain fully supports C++17
#include <experimental/filesystem>

#include <fstream>
#include <sstream>
#include <iostream>

#include <cassert>
#include <cstdlib>
#include <map>

using namespace std;

using namespace Microsoft::glTF;

namespace
{
    // The glTF SDK is decoupled from all file I/O by the IStreamWriter (and IStreamReader)
    // interface(s) and the C++ stream-based I/O library. This allows the glTF SDK to be used in
    // sandboxed environments, such as WebAssembly modules and UWP apps, where any file I/O code
    // must be platform or use-case specific.
    class StreamWriter : public IStreamWriter
    {
    public:
        StreamWriter(std::experimental::filesystem::path pathBase) : m_pathBase(std::move(pathBase))
        {
            assert(m_pathBase.has_root_path());
        }

        // Resolves the relative URIs of any external resources declared in the glTF manifest
        std::shared_ptr<std::ostream> GetOutputStream(const std::string& filename) const override
        {
            // In order to construct a valid stream:
            // 1. The filename argument will be encoded as UTF-8 so use filesystem::u8path to
            //    correctly construct a path instance.
            // 2. Generate an absolute path by concatenating m_pathBase with the specified filename
            //    path. The filesystem::operator/ uses the platform's preferred directory separator
            //    if appropriate.
            // 3. Always open the file stream in binary mode. The glTF SDK will handle any text
            //    encoding issues for us.
            auto streamPath = m_pathBase / std::experimental::filesystem::u8path(filename);
            auto stream = std::make_shared<std::ofstream>(streamPath, std::ios_base::binary);

            // Check if the stream has no errors and is ready for I/O operations
            if (!stream || !(*stream))
            {
                throw std::runtime_error("Unable to create a valid output stream for uri: " + filename);
            }

            return stream;
        }

    private:
        std::experimental::filesystem::path m_pathBase;
    };	    
	
	std::string AddMaterial(Document& document, Color4& color4) {
		// Construct a Material
		Material material;
		material.metallicRoughness.baseColorFactor = color4;
		material.metallicRoughness.metallicFactor = 0.2f;
		material.metallicRoughness.roughnessFactor = 0.4f;
		material.doubleSided = true;

		// Add it to the Document and store the generated ID
		auto materialId = document.materials.Append(std::move(material), AppendIdPolicy::GenerateOnEmpty).id;
		return materialId;
	}

	//添加顶点索引Accessor
	string AddIndexAccessor(BufferBuilder& bufferBuilder, const vector<uint16_t> &indices) {
		// Create a BufferView with a target of ELEMENT_ARRAY_BUFFER (as it will reference index
		// data) - it will be the 'current' BufferView that all the Accessors created by this
		// BufferBuilder will automatically reference
		bufferBuilder.AddBufferView(BufferViewTarget::ELEMENT_ARRAY_BUFFER);

		// Add an Accessor for the indices

		// Copy the Accessor's id - subsequent calls to AddAccessor may invalidate the returned reference
		return bufferBuilder.AddAccessor(indices, { TYPE_SCALAR, COMPONENT_UNSIGNED_SHORT }).id;
	}

	//添加顶点Accessor
	string AddPositionsAccessor(BufferBuilder& bufferBuilder,const vector<float> &positions) {
		// Create a BufferView with target ARRAY_BUFFER (as it will reference vertex attribute data)
		bufferBuilder.AddBufferView(BufferViewTarget::ARRAY_BUFFER);

		// Add an Accessor for the positions
		//std::vector<float> positions = {
		//	0.0f, 0.0f, 0.0f, // Vertex 0
		//	1.0f, 0.0f, 0.0f, // Vertex 1
		//	0.0f, 1.0f, 0.0f  // Vertex 2
		//};

		std::vector<float> minValues(3U, std::numeric_limits<float>::max());
		std::vector<float> maxValues(3U, std::numeric_limits<float>::lowest());

		const size_t positionCount = positions.size();

		// Accessor min/max properties must be set for vertex position data so calculate them here
		for (size_t i = 0U, j = 0U; i < positionCount; ++i, j = (i % 3U))
		{
			minValues[j] = std::min(positions[i], minValues[j]);
			maxValues[j] = std::max(positions[i], maxValues[j]);
		}

		return bufferBuilder.AddAccessor(positions, { TYPE_VEC3, COMPONENT_FLOAT, false, std::move(minValues), std::move(maxValues) }).id;
	}

	void AddNode(Document& document, string &meshId, Scene &scene) {
		Node node;
		node.meshId = meshId;
		// Add it to the Document and store the generated ID
		auto nodeId = document.nodes.Append(std::move(node), AppendIdPolicy::GenerateOnEmpty).id;
		scene.nodes.push_back(nodeId);
	}
	void AddMesh(Document& document,string &materialId,string &accessorIdIndices,string &accessorIdPositions, Scene &scene) {
		MeshPrimitive meshPrimitive;
		meshPrimitive.materialId = materialId;
		meshPrimitive.indicesAccessorId = accessorIdIndices;
		meshPrimitive.attributes[ACCESSOR_POSITION] = accessorIdPositions;

		// Construct a Mesh and add the MeshPrimitive as a child
		Mesh mesh;
		mesh.primitives.push_back(std::move(meshPrimitive));
		// Add it to the Document and store the generated ID
		auto meshId = document.meshes.Append(std::move(mesh), AppendIdPolicy::GenerateOnEmpty).id;
		AddNode(document, meshId, scene);
	}

	void CreateTrianglesResources(Document& document, BufferBuilder& bufferBuilder)
	{
		std::string accessorIdIndices;
		std::string accessorIdPositions;
		std::string accessorIdIndices2;
		std::string accessorIdPositions2;
		// Create all the resource data (e.g. triangle indices and
		// vertex positions) that will be written to the binary buffer
		const char* bufferId = nullptr;

		// Specify the 'special' GLB buffer ID. This informs the GLBResourceWriter that it should use
		// the GLB container's binary chunk (usually the desired buffer location when creating GLBs)
		if (dynamic_cast<const GLBResourceWriter*>(&bufferBuilder.GetResourceWriter()))
		{
			bufferId = GLB_BUFFER_ID;
		}

		// Create a Buffer - it will be the 'current' Buffer that all the BufferViews
		// created by this BufferBuilder will automatically reference
		bufferBuilder.AddBuffer(bufferId);

#pragma region 添加索引Accessor
		std::vector<uint16_t> indices = {
			0U, 1U, 2U
		};
		accessorIdIndices = AddIndexAccessor(bufferBuilder,indices);
#pragma endregion

#pragma region 添加顶点Accessor

		// Add an Accessor for the positions
		std::vector<float> positions = {
			0.0f, 0.0f, 0.0f, // Vertex 0
			1.0f, 0.0f, 0.0f, // Vertex 1
			0.0f, 1.0f, 0.0f  // Vertex 2
		};
		accessorIdPositions = AddPositionsAccessor(bufferBuilder, positions);
#pragma endregion

#pragma region 添加索引Accessor
		std::vector<uint16_t> indices2 = {
			0U, 1U, 2U
		};
		accessorIdIndices2 = AddIndexAccessor(bufferBuilder, indices2);
#pragma endregion

#pragma region 添加顶点Accessor

		// Add an Accessor for the positions
		std::vector<float> positions2 = {
			1.0f, 0.0f, 0.0f, // Vertex 0
			2.0f, 0.0f, 0.0f, // Vertex 1
			1.0f, 1.0f, 0.0f  // Vertex 2
		};
		accessorIdPositions2 = AddPositionsAccessor(bufferBuilder, positions2);
#pragma endregion
			

		// Add all of the Buffers, BufferViews and Accessors that were created using BufferBuilder to
		// the Document. Note that after this point, no further calls should be made to BufferBuilder
		bufferBuilder.Output(document);

		// Construct a Material
		Color4 color(1.0f, 0.0f, 0.0f, 1.0f);
		auto materialId = AddMaterial(document, color);

		Color4 color2(0.0f, 1.0f, 0.0f, 1.0f);
		auto materialId2 = AddMaterial(document, color2);

		// Construct a MeshPrimitive. Unlike most types in glTF, MeshPrimitives are direct children
		// of their parent Mesh entity rather than being children of the Document. This is why they
		// don't have an ID member.
		Scene scene;
		AddMesh(document, materialId, accessorIdIndices, accessorIdPositions, scene);
		AddMesh(document, materialId2, accessorIdIndices2, accessorIdPositions2, scene);
		// Add it to the Document, using a utility method that also sets the Scene as the Document's default
		document.SetDefaultScene(std::move(scene), AppendIdPolicy::GenerateOnEmpty);
	}

	

    void SerializeTriangle(const std::experimental::filesystem::path& path)
    {
        // Pass the absolute path, without the filename, to the stream writer
        auto streamWriter = std::make_unique<StreamWriter>(path.parent_path());

        std::experimental::filesystem::path pathFile = path.filename();
        std::experimental::filesystem::path pathFileExt = pathFile.extension();

        auto MakePathExt = [](const std::string& ext)
        {
            return "." + ext;
        };

        std::unique_ptr<ResourceWriter> resourceWriter;

        // If the file has a '.gltf' extension then create a GLTFResourceWriter
        if (pathFileExt == MakePathExt(GLTF_EXTENSION))
        {
            resourceWriter = std::make_unique<GLTFResourceWriter>(std::move(streamWriter));
        }

        // If the file has a '.glb' extension then create a GLBResourceWriter. This class derives
        // from GLTFResourceWriter and adds support for writing manifests to a GLB container's
        // JSON chunk and resource data to the binary chunk.
        if (pathFileExt == MakePathExt(GLB_EXTENSION))
        {
            resourceWriter = std::make_unique<GLBResourceWriter>(std::move(streamWriter));
        }

        if (!resourceWriter)
        {
            throw std::runtime_error("Command line argument path filename extension must be .gltf or .glb");
        }

        // The Document instance represents the glTF JSON manifest
        Document document;

        // Use the BufferBuilder helper class to simplify the process of
        // constructing valid glTF Buffer, BufferView and Accessor entities
        BufferBuilder bufferBuilder(std::move(resourceWriter));

        CreateTrianglesResources(document, bufferBuilder);
        //CreateTriangle2Entities(document, accessorIdIndices, accessorIdPositions);

		//CreateTriangle2Resources(document, bufferBuilder, accessorIdIndices, accessorIdPositions);
		//CreateTriangle2Entities(document, accessorIdIndices, accessorIdPositions);

        std::string manifest;

        try
        {
            // Serialize the glTF Document into a JSON manifest
            manifest = Serialize(document, SerializeFlags::Pretty);
        }
        catch (const GLTFException& ex)
        {
            std::stringstream ss;

            ss << "Microsoft::glTF::Serialize failed: ";
            ss << ex.what();

            throw std::runtime_error(ss.str());
        }

        auto& gltfResourceWriter = bufferBuilder.GetResourceWriter();

        if (auto glbResourceWriter = dynamic_cast<GLBResourceWriter*>(&gltfResourceWriter))
        {
            glbResourceWriter->Flush(manifest, pathFile.u8string()); // A GLB container isn't created until the GLBResourceWriter::Flush member function is called
        }
        else
        {
            gltfResourceWriter.WriteExternal(pathFile.u8string(), manifest); // Binary resources have already been written, just need to write the manifest
        }
    }
}

int main()
{
	const char* argv[1] = {"D:\\test.gltf" };
    try
    {
        std::experimental::filesystem::path path = argv[0U];

        if (path.is_relative())
        {
            auto pathCurrent = std::experimental::filesystem::current_path();

            // Convert the relative path into an absolute path by appending the command line argument to the current path
            pathCurrent /= path;
            pathCurrent.swap(path);
        }

        if (!path.has_filename())
        {
            throw std::runtime_error("Command line argument path has no filename");
        }

        if (!path.has_extension())
        {
            throw std::runtime_error("Command line argument path has no filename extension");
        }

        SerializeTriangle(path);
    }
    catch (const std::runtime_error& ex)
    {
        std::cerr << "Error! - ";
        std::cerr << ex.what() << "\n";

        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐