【GIS开发】glTF三维模型文件格式读取(C++,Python)

【GIS开发】glTF三维模型文件格式读取(C++,Python),第1张

文章目录
  • 1、简介
    • 1.1 层级结构
    • 1.2 术语定义
  • 2、glTF文件预览
    • 2.1 VSCode
  • 3、tinygltf(C++)
    • 3.1 下载和编译
    • 3.2 官网代码示例
  • 4、libgltf(C++)
    • 4.1 下载和编译
    • 4.2 官网代码示例1
    • 4.3 官网代码示例2
  • 5、gltflib(python)
    • 5.1 Parsing a glTF 2.0 Model
    • 5.2 Exporting a glTF 2.0 Model
    • 5.3 Converting Between glTF and GLB
  • 后记

1、简介

官网地址:
https://www.khronos.org/gltf/
glTF™ 是一种免版税规范,用于通过引擎和应用程序高效传输和加载 3D 场景和模型。


glTF 定义了一种可扩展的发布格式,通过在整个行业中实现 3D 内容的互 *** 作使用来简化创作工作流程和交互式服务。


glTF™(GL 传输格式)用于在 Web 和本机应用程序中传输和加载 3D 模型。


glTF 减少了 3D 模型的大小以及解包和渲染这些模型所需的运行时处理。


这种格式在 Web 上很常用,并且在 Unity3D、Unreal Engine 4 和 Godot 等各种 3D 引擎中都有支持。


glTF 的内部结构模仿了图形芯片在实时渲染时常用的内存缓冲区,因此可以将资产交付到桌面、Web 或移动客户端,并以最少的处理迅速显示。


因此,在导出到 glTF 时,四边形和 n 边形会自动转换为三角形。



1.1 层级结构
  • glTF Object Hierarchy
1.2 术语定义

glTF 规范使用常见的工程和图形术语,如image、buffer、texture等来识别和描述某些glTF结构及其属性、状态和行为。


本节在规范的上下文中定义了这些术语的基本含义。


规范文本提供了更完整的术语定义,并详细阐述、扩展或澄清了这些定义。


当本节中定义的术语在规范中以规范语言使用时,规范中的定义支配并取代这些术语在其他技术上下文中(即规范之外)可能具有的任何含义。



  • accessor
    An object describing the number and the format of data elements stored in a binary buffer.

  • animation
    An object describing the keyframe data, including timestamps, and the target property affected by it.

  • back-facing
    See facingness.

  • buffer
    An external or embedded resource that represents a linear array of bytes.

  • buffer view
    An object that represents a range of a specific buffer, and optional metadata that controls how the buffer’s content is interpreted.

  • camera
    An object defining the projection parameters that are used to render a scene.

  • facingness
    A classification of a triangle as either front-facing or back-facing, depending on the orientation (winding order) of its vertices.

  • front-facing
    See facingness.

  • image
    A two dimensional array of pixels encoded as a standardized bitstream, such as PNG.

  • indexed geometry
    A mesh primitive that uses a separate source of data (index values) to assemble the primitive’s topology.

  • linear blend skinning
    A skinning method that computes a per-vertex transformation matrix as a linear weighted sum of transformation matrices of the designated nodes.

  • material
    A parametrized approximation of visual properties of the real-world object being represented by a mesh primitive.

  • mesh
    A collection of mesh primitives.

  • mesh primitive
    An object binding indexed or non-indexed geometry with a material.

  • mipmap
    A set of image representations consecutively reduced by the factor of 2 in each dimension.

  • morph target
    An altered state of a mesh primitive defined as a set of difference values for its vertex attributes.

  • node
    An object defining the hierarchy relations and the local transform of its content.

  • non-indexed geometry
    A mesh primitive that uses linear order of vertex attribute values to assemble the primitive’s topology.

  • normal
    A unit XYZ vector defining the perpendicular to the surface.

  • root node
    A node that is not a child of any other node.

  • sampler
    An object that controls how image data is sampled.

  • scene
    An object containing a list of root nodes to render.

  • skinning
    The process of computing and applying individual transforms for each vertex of a mesh primitive.

  • tangent
    A unit XYZ vector defining a tangential direction on the surface.

  • texture
    An object that combines an image and its sampler.

  • topology type
    State that controls how vertices are assembled, e.g. as lists of triangles, strips of lines, etc.

  • vertex attribute
    A property associated with a vertex.

  • winding order
    The relative order in which vertices are defined within a triangle

  • wrapping
    A process of selecting an image pixel based on normalized texture coordinates.

2、glTF文件预览 2.1 VSCode

安装第三方扩展glTF Tool。



  • 打开和预览gltf文件
  • 打开和预览glb文件(gltf二进制格式)
3、tinygltf(C++)

https://github.com/syoyo/tinygltf

Header only C++11 tiny glTF 2.0 library
Header only C++ tiny glTF library(loader/saver).

TinyGLTF is a header only C++11 glTF 2.0 https://github.com/KhronosGroup/glTF library.

TinyGLTF uses Niels Lohmann’s json library(https://github.com/nlohmann/json), so now it requires C++11 compiler. If you are looking for old, C++03 version, please use devel-picojson branch(but not maintained anymore).
注意:目前该库仅支持glTF 2.0格式。



它的编译依赖库需要额外下载。



3.1 下载和编译
cd C:\Users\tomcat\Desktop\test
git clone https://github.com/syoyo/tinygltf.git
cd tinygltf
mkdir bin
cd bin
cmake ..
## or 
cmake -G "Visual Studio 15 2017" .. -A x64
.\\tools\\windows\\premake5.exe vs2017
  • 从GitHub下载该库的源代码
  • 通过CMake生成该库的vs2017的工程文件
  • 生成的vs2017的工程文件的相关文件截图
  • vs2017打开工程文件,进行编译库文件
  • 也可以通过premake5来生成该库vs2017的工程文件,如下图所示:
  • tinygltf自带例子中一个小例子编译后的运行结果
3.2 官网代码示例
/ Define these only in *one* .cc file.
#define TINYGLTF_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
// #define TINYGLTF_NOEXCEPTION // optional. disable exception handling.
#include "tiny_gltf.h"

using namespace tinygltf;

Model model;
TinyGLTF loader;
std::string err;
std::string warn;

bool ret = loader.LoadASCIIFromFile(&model, &err, &warn, argv[1]);
//bool ret = loader.LoadBinaryFromFile(&model, &err, &warn, argv[1]); // for binary glTF(.glb)

if (!warn.empty()) {
  printf("Warn: %s\n", warn.c_str());
}

if (!err.empty()) {
  printf("Err: %s\n", err.c_str());
}

if (!ret) {
  printf("Failed to parse glTF\n");
  return -1;
}
4、libgltf(C++)

https://github.com/code4game/libgltf

libgltf:glTF 2.0 parser/loader for C++11, supports many extensions
likes KHR_draco_mesh_compression, KHR_lights_punctual,
KHR_materials_clearcoat, and more.

注意:目前该库仅支持glTF 2.0格式。



它的编译依赖库不不不需要额外下载。



4.1 下载和编译
cd C:\Users\tomcat\Desktop\test
git clone https://github.com/code4game/libgltf.git
cd libgltf
git submodule update --init
mkdir bin
cd bin
cmake ..
## or 
cmake -G "Visual Studio 15 2017" .. -A x64
  • 从github上下载libgltf的源代码,如下图所示:

  • 下载libgltf的源代码的文件夹,如下所示:

  • 通过cmake生成vs2017的工程文件。




  • vs2017打开上面生成的工程文件,编译生成libgltf的库文件,如下图所示:

4.2 官网代码示例1
std::shared_ptr<libgltf::IglTFLoader> gltf_loader = libgltf::IglTFLoader::Create(/*your gltf file*/);
std::shared_ptr<libgltf::SGlTF> loaded_gltf = gltf_loader->glTF().lock();
if (!loaded_gltf)
{
    printf("failed to load your gltf file");
}
4.3 官网代码示例2
#include "runtest.h"
#include 

#include 
#include 
#include 

#if defined(LIBGLTF_PLATFORM_WINDOWS)
#include 
#include 
#endif

#if defined(LIBGLTF_CHARACTOR_ENCODING_IS_UNICODE) && defined(LIBGLTF_PLATFORM_WINDOWS)
int _tmain(int _iArgc, wchar_t* _pcArgv[])
#else
int main(int _iArgc, char* _pcArgv[])
#endif
{
#if defined(LIBGLTF_PLATFORM_WINDOWS) && defined(_DEBUG)
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif

#if defined(LIBGLTF_BUILD_COVERAGE)
    int error_code = 0;
#else
    int error_code = 1;
#endif

#if defined(LIBGLTF_CHARACTOR_ENCODING_IS_UNICODE) && defined(LIBGLTF_PLATFORM_WINDOWS)
    std::wstring input_file_path;
#else
    std::string input_file_path;
#endif

    {
#if defined(LIBGLTF_CHARACTOR_ENCODING_IS_UNICODE) && defined(LIBGLTF_PLATFORM_WINDOWS)
        std::wstringstream argument;
#else
        std::stringstream argument;
#endif
        argument << _pcArgv[1];
        input_file_path = argument.str();
    }

    if (input_file_path.length() == 0)
    {
        printf("Command line format: runtest input_file_path\n");
        return error_code;
    }

#if defined(LIBGLTF_CHARACTOR_ENCODING_IS_UTF16)
    std::shared_ptr<libgltf::IglTFLoader> gltf_loader = libgltf::IglTFLoader::Create(libgltf::UTF8ToUTF16(input_file_path));
#elif defined(LIBGLTF_CHARACTOR_ENCODING_IS_UTF32)
    std::shared_ptr<libgltf::IglTFLoader> gltf_loader = libgltf::IglTFLoader::Create(libgltf::UTF8ToUTF32(input_file_path));
#elif defined(LIBGLTF_CHARACTOR_ENCODING_IS_UNICODE)
#if defined(LIBGLTF_PLATFORM_WINDOWS)
    std::shared_ptr<libgltf::IglTFLoader> gltf_loader = libgltf::IglTFLoader::Create(input_file_path);
#else
    std::shared_ptr<libgltf::IglTFLoader> gltf_loader = libgltf::IglTFLoader::Create(libgltf::UTF8ToUNICODE(input_file_path));
#endif
#else
    std::shared_ptr<libgltf::IglTFLoader> gltf_loader = libgltf::IglTFLoader::Create(input_file_path);
#endif
    
    std::shared_ptr<libgltf::SGlTF> loaded_gltf = gltf_loader->glTF().lock();
    if (loaded_gltf)
    {
        printf("operator << Success\n");
    }
    else
    {
        printf("operator << Failed\n");
        return error_code;
    }

	int num_node = loaded_gltf->nodes.size();
	int num_mesh = loaded_gltf->meshes.size();
	const std::shared_ptr<libgltf::SMesh>& mesh = loaded_gltf->meshes[0];
	int num_pri = mesh->primitives.size();

    libgltf::TDimensionVector<1, size_t> triangle_data;
    std::shared_ptr<libgltf::TAccessorStream<libgltf::TDimensionVector<1, size_t> > > triangle_stream = std::make_shared<libgltf::TAccessorStream<libgltf::TDimensionVector<1, size_t> > >(triangle_data);
    gltf_loader->GetOrLoadMeshPrimitiveIndicesData(0, 0, triangle_stream);

    libgltf::TDimensionVector<3, float> position_data;
    std::shared_ptr<libgltf::TAccessorStream<libgltf::TDimensionVector<3, float> > > position_stream = std::make_shared<libgltf::TAccessorStream<libgltf::TDimensionVector<3, float> > >(position_data);
    gltf_loader->GetOrLoadMeshPrimitiveAttributeData(0, 0, GLTFTEXT("position"), position_stream);

    libgltf::TDimensionVector<3, float> normal_data;
    std::shared_ptr<libgltf::TAccessorStream<libgltf::TDimensionVector<3, float> > > normal_stream = std::make_shared<libgltf::TAccessorStream<libgltf::TDimensionVector<3, float> > >(normal_data);
    gltf_loader->GetOrLoadMeshPrimitiveAttributeData(0, 0, GLTFTEXT("normal"), normal_stream);

    libgltf::TDimensionVector<2, float> texcoord_0_data;
    std::shared_ptr<libgltf::TAccessorStream<libgltf::TDimensionVector<2, float> > > texcoord_0_stream = std::make_shared<libgltf::TAccessorStream<libgltf::TDimensionVector<2, float> > >(texcoord_0_data);
    gltf_loader->GetOrLoadMeshPrimitiveAttributeData(0, 0, GLTFTEXT("texcoord_0"), texcoord_0_stream);

    std::vector<uint8_t> image0_data;
    libgltf::string_t image0_data_type;
    gltf_loader->GetOrLoadImageData(0, image0_data, image0_data_type);

    //TODO: just convert to json, save the mesh or image data to file in future
    libgltf::string_t output_content;
    if (loaded_gltf >> output_content)
    {
        printf("nodes: %d\n", num_node);
		printf("meshes: %d\n", num_mesh);
		printf("meshes[0]'s primitives: %d\n", num_pri);

		printf("triangle_data: %d\n", triangle_data.size());
		printf("position_data: %d\n", position_data.size());
		printf("normal_data: %d\n", normal_data.size());
		printf("texcoord_0_data: %d\n", texcoord_0_data.size());
		printf("image0_data: %d\n", image0_data.size());
        printf("operator >> Success\n");
    }
    else
    {
        printf("operator >> Failed\n");
        return error_code;
    }

    return 0;
}

加载三维模型文件:C:\Users\tomcat\Desktop\dtiles_3\BlockBAB\BlockBAB.glb

运行结果如下:

5、gltflib(python)

https://pypi.org/project/gltflib/

Library for parsing, creating, and converting glTF 2.0 files in Python 3.6+.

This library is intended for working with glTF 2.0 at a fairly low level, meaning you are responsible for managing the actual geometry data yourself. This library facilitates saving this data into a properly formatted glTF/GLB file. It also helps with converting resources inside a glTF/GLB file between external files or web URLs, data URLs, and embedded GLB resources.

pip install gltflib

5.1 Parsing a glTF 2.0 Model
from gltflib import GLTF

gltf = GLTF.load('D:/glTF-Sample-Models/2.0/BoxTextured/gltf/BoxTextured.gltf')
# print(gltf.model)

print(gltf.model.buffers[0].uri)
print(gltf.resources)
resource = gltf.resources[0]
print(resource)

5.2 Exporting a glTF 2.0 Model
import struct
import operator
from gltflib import (
    GLTF, GLTFModel, Asset, Scene, Node, Mesh, Primitive, Attributes, Buffer, BufferView, Accessor, AccessorType,
    BufferTarget, ComponentType, GLBResource, FileResource)

vertices = [
    (-4774424.719997984, 4163079.2597148907, 671001.6353722484),
    (-4748098.650098154, 4163079.259714891, 837217.8990777463),
    (-4689289.5292739635, 4246272.966707474, 742710.4976137652)
]

vertex_bytearray = bytearray()
for vertex in vertices:
    for value in vertex:
        vertex_bytearray.extend(struct.pack('f', value))
bytelen = len(vertex_bytearray)
mins = [min([operator.itemgetter(i)(vertex) for vertex in vertices]) for i in range(3)]
maxs = [max([operator.itemgetter(i)(vertex) for vertex in vertices]) for i in range(3)]
model = GLTFModel(
    asset=Asset(version='2.0'),
    scenes=[Scene(nodes=[0])],
    nodes=[Node(mesh=0)],
    meshes=[Mesh(primitives=[Primitive(attributes=Attributes(POSITION=0))])],
    buffers=[Buffer(byteLength=bytelen, uri='vertices.bin')],
    bufferViews=[BufferView(buffer=0, byteOffset=0, byteLength=bytelen, target=BufferTarget.ARRAY_BUFFER.value)],
    accessors=[Accessor(bufferView=0, byteOffset=0, componentType=ComponentType.FLOAT.value, count=len(vertices),
                        type=AccessorType.VEC3.value, min=mins, max=maxs)]
)

resource = FileResource('vertices.bin', data=vertex_bytearray)
gltf = GLTF(model=model, resources=[resource])
gltf.export('triangle.gltf')
# gltf.export('triangle.glb')
5.3 Converting Between glTF and GLB
from gltflib import GLTF

gltf = GLTF.load('D:/glTF-Sample-Models/2.0/BoxTextured/gltf/BoxTextured.gltf')
gltf.export('D:/BoxTextured.glb')
from gltflib import GLTF

gltf = GLTF.load('D:/glTF-Sample-Models/2.0/BoxTextured/glTF-Binary/BoxTextured.glb')
glb_resource = gltf.get_glb_resource()
gltf.convert_to_file_resource(glb_resource, 'BoxTextured.bin')
gltf.export('D:/BoxTextured.gltf')
后记

如果你觉得该方法或代码有一点点用处,可以给作者点个赞;╮( ̄▽ ̄)╭
如果你感觉方法或代码不咋地//(ㄒoㄒ)//,就在评论处留言,作者继续改进。


o_O???
谢谢各位童鞋们啦( ´ ▽´ )ノ ( ´ ▽´)っ!!!

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/585109.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-12
下一篇 2022-04-12

发表评论

登录后才能评论

评论列表(0条)

保存