【CAD开发】glTF和b3dm文件格式读取(C++,Python)
文章目录
- 1、简介
- 1.1 层级结构
- 1.2 术语定义
- 2、glTF文件预览
- 2.1 VSCode(v2.0)
- 2.2 glTF Viewer(v2.0, js)
- 2.3 babylon.js(v2.0,js)
- 3、tinygltf (v2.0, C++)
- 3.1 下载和编译
- 3.2 官网代码示例
- 4、TinyGLTFLoader (v1.0, C++)
- 4.1 下载和编译
- 4.2 picojson库
- 4.3 官网代码示例
- 4.4 自己测试代码1
- 4.5 自己测试代码2
- 5、libgltf (v2.0, C++)
- 5.1 下载和编译
- 5.2 nlohmann/json库
- 5.3 官网代码示例1
- 5.4 官网代码示例2
- 5.5 自己测试代码
- 6、assimp (v1.0, v2.0, C++)
- 6.1 下载和编译
- 6.2 官网代码示例
- 7、gltflib (v2.0, python)
- 7.1 Parsing a glTF 2.0 Model
- 7.2 Exporting a glTF 2.0 Model
- 7.3 Converting Between glTF and GLB
- 8、three.js(v2.0, javascript)
- 8.1 下载和编译
- 8.2 官网代码示意
- 结语
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 边形会自动转换为三角形。
b3dm就是在原来gltf单个模型的基础之上,做了批量化的数据组织方式,多了feature table和batch table两个文件。3D Tiles 是一种开放规范,用于在桌面、Web 和移动应用程序中共享、可视化、融合和与大量异构 3D 地理空间内容交互。
1.1 层级结构
- glTF Object Hierarchy
这里来几张作者收集的结构图说明gltf的各种对象组成。
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 trianglewrapping
A process of selecting an image pixel based on normalized texture coordinates.
2、glTF文件预览
2.1 VSCode(v2.0)
安装第三方扩展glTF Tool。
- 打开和预览gltf文件
- 打开和预览glb文件(gltf二进制格式)
2.2 glTF Viewer(v2.0, js)
https://gltf-viewer.donmccurdy.com/
2.3 babylon.js(v2.0,js)
https://sandbox.babylonjs.com/
Babylon.js Sandbox - View glTF, glb, obj and babylon files. Drag and drop gltf, glb, obj or babylon files to view them.
3、tinygltf (v2.0, 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格式。
它的OpenGL示例编译依赖库需要额外下载。本身就是几个头文件直接使用。
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、TinyGLTFLoader (v1.0, C++)
Tiny glTF loader, header only C++ glTF 1.x parsing library.
4.1 下载和编译
https://github.com/syoyo/tinygltfloader
注意:目前该库仅支持glTF 1.0格式。
这个库不需要编译,直接引用头文件.h就可以了。
4.2 picojson库
https://github.com/kazuho/picojson/
a header-file-only, JSON parser serializer in C++
copyright © 2009-2010 Cybozu Labs, Inc. Copyright © 2011-2015 Kazuho Oku
代码示例如下:
const char* json = "{\"a\":1}";
picojson::value v;
std::string err;
const char* json_end = picojson::parse(v, json, json + strlen(json), &err);
if (! err.empty()) {std::cerr << err << std::endl;
}
picojson::value v;// parse the input
std::cin >> v;
std::string err = picojson::get_last_error();
if (! err.empty()) {std::cerr << err << std::endl;exit(1);
}// check if the type of the value is "object"
if (! v.is<picojson::object>()) {std::cerr << "JSON is not an object" << std::endl;exit(2);
}// obtain a const reference to the map, and print the contents
const picojson::value::object& obj = v.get<picojson::object>();
for (picojson::value::object::const_iterator i = obj.begin();i != obj.end();++i) {std::cout << i->first << ': ' << i->second.to_str() << std::endl;
}
4.3 官网代码示例
Copy stb_image.h, picojson.h and tiny_gltf_loader.h to your project.
// Define these only in *one* .cc file.
#define TINYGLTF_LOADER_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#include "tiny_gltf_loader.h"using namespace tinygltf;Scene scene;
TinyGLTFLoader loader;
std::string err;bool ret = loader.LoadASCIIFromFile(scene, err, argv[1]);
//bool ret = loader.LoadBinaryFromFile(scene, err, argv[1]); // for binary glTF(.glb)
if (!err.empty()) {printf("Err: %s\n", err.c_str());
}if (!ret) {printf("Failed to parse glTF\n");return -1;
}
4.4 自己测试代码1
根据前面贴的结构图,编写代码遍历gltf1.0文件包含的所有几何数据(顶点、法线、面、纹理坐标、纹理图片数据等)。
//***********************************************************************
// Purpose: tiny_gltf_loader遍历gltf v1.0文件的几何数据
// Author: 爱看书的小沐
// Date: 2022-4-19
// Languages: C++
// Platform: Visual Studio 2017
// OS: Win10 win64
// ***********************************************************************#define TINYGLTF_LOADER_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#include "tiny_gltf_loader.h"void DumpNode(const tinygltf::Node &node, tinygltf::Scene &scene) {for (size_t i = 0; i < node.meshes.size(); i++) {std::map<std::string, tinygltf::Mesh>::const_iterator it_mesh = scene.meshes.find(node.meshes[i]);if (it_mesh == scene.meshes.end()) {continue;}const tinygltf::Mesh& mesh = it_mesh->second;for (size_t i = 0; i < mesh.primitives.size(); i++) {const tinygltf::Primitive &primitive = mesh.primitives[i];if (primitive.indices.empty()) return;///// get texture datatinygltf::Material &mat = scene.materials[primitive.material];if (mat.values.find("diffuse") != mat.values.end()) {std::string diffuseTexName = mat.values["diffuseTex"].string_value;//std::string diffuseTexName2 = mat.values["diffuse"].string_value;if (scene.textures.find(diffuseTexName) != scene.textures.end()) {tinygltf::Texture &tex = scene.textures[diffuseTexName];if (scene.images.find(tex.source) != scene.images.end()) {tinygltf::Image &image = scene.images[tex.source];image.image;image.width;image.height;image.component;image.image.size();}}}///// get face data primitive.indicesconst tinygltf::Accessor indexAccessor = scene.accessors[primitive.indices];indexAccessor.type;indexAccessor.componentType;indexAccessor.byteStride;indexAccessor.byteOffset;indexAccessor.count;const tinygltf::BufferView &bufferView = scene.bufferViews[indexAccessor.bufferView];const tinygltf::Buffer &buffer = scene.buffers[bufferView.buffer];printf("\n");float* pointArr = NULL;float* normalArr = NULL;float* textureArr = NULL;unsigned short* faceIndex = NULL;int faceCount = 0;int pointCount = 0;int normalCount = 0;int texCount = 0;faceCount = indexAccessor.count;faceIndex = new unsigned short[faceCount];//assert(bufferView.byteLength == faceCount *sizeof(unsigned short));memcpy(faceIndex, &buffer.data[0] + bufferView.byteOffset+ indexAccessor.byteOffset, faceCount * sizeof(unsigned short));///// get point and texture datastd::map<std::string, std::string>::const_iterator it(primitive.attributes.begin());std::map<std::string, std::string>::const_iterator itEnd(primitive.attributes.end());for (; it != itEnd; it++) {assert(scene.accessors.find(it->second) != scene.accessors.end());const tinygltf::Accessor &accessor = scene.accessors[it->second];const tinygltf::BufferView &bufferView_pt = scene.bufferViews[accessor.bufferView];const tinygltf::Buffer &buffer_pt = scene.buffers[bufferView_pt.buffer];int count = 1;if (accessor.type == TINYGLTF_TYPE_SCALAR) {count = 1;}else if (accessor.type == TINYGLTF_TYPE_VEC2) {count = 2;}else if (accessor.type == TINYGLTF_TYPE_VEC3) {count = 3;}else if (accessor.type == TINYGLTF_TYPE_VEC4) {count = 4;}else {assert(0);}if (it->first.compare("POSITION") == 0) {pointCount = accessor.count * count;pointArr = new float[pointCount];memcpy(pointArr, &buffer_pt.data[0]+ bufferView_pt.byteOffset + accessor.byteOffset, pointCount* sizeof(float));}else if (it->first.compare("NORMAL") == 0) {normalCount = accessor.count * count;normalArr = new float[normalCount];memcpy(normalArr, &buffer_pt.data[0] + bufferView_pt.byteOffset + accessor.byteOffset, normalCount * sizeof(float));}else if (it->first.compare("TEXCOORD_0") == 0) {texCount = accessor.count * count;textureArr = new float[texCount];memcpy(textureArr, &buffer_pt.data[0] + bufferView_pt.byteOffset + accessor.byteOffset, texCount * sizeof(float));}if ((it->first.compare("POSITION") == 0) ||(it->first.compare("NORMAL") == 0) ||(it->first.compare("TEXCOORD_0") == 0)) {//#define GL_BYTE 0x1400 5120//#define GL_UNSIGNED_BYTE 0x1401 5121//#define GL_SHORT 0x1402 5122//#define GL_UNSIGNED_SHORT 0x1403 5123//#define GL_INT 0x1404 5124//#define GL_UNSIGNED_INT 0x1405 5125//#define GL_FLOAT 0x1406 5126accessor.type;accessor.componentType;accessor.byteStride;accessor.byteOffset;accessor.count;const tinygltf::BufferView &bufferView = scene.bufferViews[accessor.bufferView];const tinygltf::Buffer &buffer = scene.buffers[bufferView.buffer];printf("\n");}}}}for (size_t i = 0; i < node.children.size(); i++) {std::map<std::string, tinygltf::Node>::const_iterator it =scene.nodes.find(node.children[i]);if (it != scene.nodes.end()) {//DrawNode(scene, it->second);}}
}static void Dump(const tinygltf::Scene &scene) {std::cout << "=== Dump glTF ===" << std::endl;std::cout << "asset.generator : " << scene.asset.generator<< std::endl;std::cout << "asset.premultipliedAlpha : " << scene.asset.premultipliedAlpha<< std::endl;std::cout << "asset.version : " << scene.asset.version<< std::endl;std::cout << "asset.profile.api : " << scene.asset.profile_api<< std::endl;std::cout << "asset.profile.version : " << scene.asset.profile_version<< std::endl;std::cout << std::endl;std::cout << "=== Dump scene ===" << std::endl;std::cout << "defaultScene: " << scene.defaultScene << std::endl;{std::map<std::string, tinygltf::Node>::const_iterator it(scene.nodes.begin());std::map<std::string, tinygltf::Node>::const_iterator itEnd(scene.nodes.end());std::cout << "nodes(items=" << scene.nodes.size() << ")" << std::endl;for (; it != itEnd; it++) {DumpNode(it->second, (tinygltf::Scene &)scene);}}
}int main(int argc, char **argv) {/*if (argc < 2) {printf("Needs input.gltf\n");exit(1);}*/tinygltf::Scene scene;tinygltf::TinyGLTFLoader loader;std::string err;std::string input_filename("c:\\Users\\tomcat\\Desktop\\BlockB.b3dm");std::string ext = GetFilePathExtension(input_filename);bool ret = false;if (ext.compare("glb") == 0) {// assume binary glTF.ret = loader.LoadBinaryFromFile(&scene, &err, input_filename.c_str());}else if (ext.compare("gltf") == 0) {// assume ascii glTF.ret = loader.LoadASCIIFromFile(&scene, &err, input_filename.c_str());}else if (ext.compare("b3dm") == 0) {ret = loader.LoadB3dmFromFile(&scene, &err, input_filename.c_str());}else {return false;}if (!err.empty()) {printf("Err: %s\n", err.c_str());}if (!ret) {printf("Failed to parse glTF\n");return -1;}Dump(scene);return 0;
}
4.5 自己测试代码2
在头文件tiny_gltf_loader.h中,增加读取b3dm文件格式(3d地图瓦片)的接口。
//***********************************************************************
// Purpose: tiny_gltf_loader增加读取b3dm文件格式的接口
// Author: 爱看书的小沐
// Date: 2022-4-19
// Languages: C++
// Platform: Visual Studio 2017
// OS: Win10 win64
// ***********************************************************************// 读取b3dm文件的文件头,返回值为头部的长度
int ReadB3dmHeader(std::ifstream &f, bool &rtcCenterEnable, float *rtcCenterPos)
{rtcCenterEnable = false;if (rtcCenterPos) {rtcCenterPos[0] = 0;rtcCenterPos[1] = 0;rtcCenterPos[2] = 0;}int header[7];int len_header = 7 * sizeof(int);f.seekg(0, f.beg);std::vector<char> buf(len_header);f.read(&buf.at(0), static_cast<std::streamsize>(len_header));::memcpy(header, &buf.at(0), len_header);int len_featureTable = header[3];if (len_featureTable > 20 && rtcCenterPos != nullptr) {char *json = new char[len_featureTable + 1];f.read(json, len_featureTable);json[len_featureTable] = '\0';picojson::value v;std::string err;const char* json_end = picojson::parse(v, json, json + strlen(json), &err);if (!err.empty()) {std::cerr << err << std::endl;}// obtain a const reference to the map, and print the contentsconst picojson::value::object& obj = v.get<picojson::object>();for (picojson::value::object::const_iterator i = obj.begin(); i != obj.end() ; ++i) {std::cout << i->first << ': ' << i->second.to_str() << std::endl;if (i->first.compare("RTC_CENTER") == 0) {const picojson::value::array& center = i->second.get<picojson::array>();rtcCenterPos[0] = (float)center[0].get<double>();rtcCenterPos[1] = (float)center[1].get<double>();rtcCenterPos[2] = (float)center[2].get<double>();rtcCenterEnable = true;}}delete[] json;}return len_header + header[3] + header[4] + header[5] + header[6];
}// 读取gltf文件的文件头,返回值为gltf的版本号
int ReadGltfHeader(std::ifstream &f, int offset)
{std::vector<char> buf(16);f.seekg(offset, f.beg);f.read(&buf.at(0), static_cast<std::streamsize>(buf.size()));//std::string strBuf;//strBuf.clear();//strBuf.assign(buf.begin(), buf.end());return buf[4];
}// 读取b3dm文件的b3dm头部和gltf头部
int TinyGLTFLoader::ReadB3dmVersion(const std::string &filename, bool &rtcCenterEnable, float *rtcCenterPos) {std::stringstream ss;std::ifstream f(filename.c_str(), std::ios::binary);if (!f) {return 0;}int len_b3dm = ReadB3dmHeader(f, rtcCenterEnable, rtcCenterPos);int ver = ReadGltfHeader(f, len_b3dm);f.close();return ver;
}// 读取b3dm文件的所有内容
bool TinyGLTFLoader::LoadB3dmFromFile(Scene *scene, std::string *err,const std::string &filename,unsigned int check_sections) {std::stringstream ss;std::ifstream f(filename.c_str(), std::ios::binary);if (!f) {ss << "Failed to open file: " << filename << std::endl;if (err) {(*err) = ss.str();}return false;}bool rtcCenterEnable = false;float rtcCenterPos[3] = {0};int len_b3dm = ReadB3dmHeader(f, rtcCenterEnable, rtcCenterPos);int ver = ReadGltfHeader(f, len_b3dm);if (ver != 1) return false;f.seekg(0, f.end);size_t sz = static_cast<size_t>(f.tellg());std::vector<char> buf(sz - len_b3dm);f.seekg(len_b3dm, f.beg);f.read(&buf.at(0), static_cast<std::streamsize>(sz - len_b3dm));f.close();std::string basedir = GetBaseDir(filename);bool ret = LoadBinaryFromMemory(scene, err, reinterpret_cast<unsigned char *>(&buf.at(0)),static_cast<unsigned int>(buf.size()), basedir, check_sections);return ret;
}
5、libgltf (v2.0, C++)
https://github.com/code4game/libgltf
libgltf:glTF 2.0 parser/loader for C++11, supports many extensions
likesKHR_draco_mesh_compression
,KHR_lights_punctual
,
KHR_materials_clearcoat
, and more.
注意:目前该库仅支持glTF 2.0格式。
它的编译依赖库不不不需要额外下载。
5.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的库文件,如下图所示:
5.2 nlohmann/json库
https://github.com/nlohmann/json
JSON for Modern C++
代码示例如下:
// write a JSON file// create an empty structure (null)
json j;// add a number that is stored as double (note the implicit conversion of j to an object)
j["pi"] = 3.141;// add a Boolean that is stored as bool
j["happy"] = true;// add a string that is stored as std::string
j["name"] = "Niels";// add another null object by passing nullptr
j["nothing"] = nullptr;// add an object inside the object
j["answer"]["everything"] = 42;// add an array that is stored as std::vector (using an initializer list)
j["list"] = { 1, 0, 2 };// add another object (using an initializer list of pairs)
j["object"] = { {"currency", "USD"}, {"value", 42.99} };// instead, you could also write (which looks very similar to the JSON above)
json j2 = {{"pi", 3.141},{"happy", true},{"name", "Niels"},{"nothing", nullptr},{"answer", {{"everything", 42}}},{"list", {1, 0, 2}},{"object", {{"currency", "USD"},{"value", 42.99}}}
};
// read a JSON file
std::ifstream i("file.json");
json j;
i >> j;// write prettified JSON to another file
std::ofstream o("pretty.json");
o << std::setw(4) << j << std::endl;
5.3 官网代码示例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");
}
5.4 官网代码示例2
#include "runtest.h"
#include <libgltf/libgltf_ext.h>#include <string>
#include <sstream>
#include <fstream>#if defined(LIBGLTF_PLATFORM_WINDOWS)
#include <tchar.h>
#include <crtdbg.h>
#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;
#elseint error_code = 1;
#endif#if defined(LIBGLTF_CHARACTOR_ENCODING_IS_UNICODE) && defined(LIBGLTF_PLATFORM_WINDOWS)std::wstring input_file_path;
#elsestd::string input_file_path;
#endif{
#if defined(LIBGLTF_CHARACTOR_ENCODING_IS_UNICODE) && defined(LIBGLTF_PLATFORM_WINDOWS)std::wstringstream argument;
#elsestd::stringstream argument;
#endifargument << _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);
#elsestd::shared_ptr<libgltf::IglTFLoader> gltf_loader = libgltf::IglTFLoader::Create(libgltf::UTF8ToUNICODE(input_file_path));
#endif
#elsestd::shared_ptr<libgltf::IglTFLoader> gltf_loader = libgltf::IglTFLoader::Create(input_file_path);
#endifstd::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 futurelibgltf::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.5 自己测试代码
//***********************************************************************
// Purpose: 在libgltf代码中增加读取b3dm文件格式的接口
// Author: 爱看书的小沐
// Date: 2022-4-19
// Languages: C++
// Platform: Visual Studio 2017
// OS: Win10 win64
// ***********************************************************************int CFileLoader::ReadB3dmHeader(const string_t& _sFilePath) const
{float RTC_CENTER[3] = { 0,0,0 };bool RTC_CENTER_enable = false;const std::vector<uint8_t>& file_data = (*this)[_sFilePath];if (file_data.empty()) return 0;int header[7];::memcpy(header, file_data.data(), 7*sizeof(int));int len_featureTable = header[3];if (len_featureTable > 20) {char *buffer = new char[len_featureTable + 1];::memcpy(buffer, file_data.data() + 7 * sizeof(int), len_featureTable * sizeof(char));buffer[len_featureTable] = '\0';auto j = json::parse(buffer);bool ret = j.contains(json::json_pointer("/RTC_CENTER"));if (ret) {auto center = j["RTC_CENTER"];RTC_CENTER[0] = center[0];RTC_CENTER[1] = center[1];RTC_CENTER[2] = center[2];RTC_CENTER_enable = true;}else {RTC_CENTER[0] = 0;RTC_CENTER[1] = 0;RTC_CENTER[2] = 0;RTC_CENTER_enable = false;}delete[] buffer;}return 7*sizeof(int)+ header[3] + header[4] + header[5] + header[6];
}
6、assimp (v1.0, v2.0, C++)
https://github.com/assimp/assimp
https://assimp-docs.readthedocs.io/en/v5.1.0/
The official Open-Asset-Importer-Library Repository. Loads 40+ 3D-file-formats into one unified and clean data structure.
Asset-Importer-Lib(简称 assimp)是一个库,用于加载和处理来自各种 3D 数据格式的几何场景。通过支持节点层次结构、静态或蒙皮网格、材质、骨骼动画和潜在纹理数据,它主要针对典型的游戏场景进行定制。但也支持一些 3D 打印和 CAD 格式。
- 目前assimp库支持如下模型格式的导入:
3D Manufacturing Format (.3mf)
Collada (.dae, .xml)
Blender (.blend)
Biovision BVH (.bvh)
3D Studio Max 3DS (.3ds)
3D Studio Max ASE (.ase)
glTF (.glTF)
glTF2.0 (.glTF)
FBX-Format, as ASCII and binary (.fbx)
Stanford Polygon Library (.ply)
AutoCAD DXF (.dxf)
IFC-STEP (.ifc)
Neutral File Format (.nff)
Sense8 WorldToolkit (.nff)
Valve Model (.smd, .vta)
Quake I (.mdl)
Quake II (.md2)
Quake III (.md3)
Quake 3 BSP (.pk3)
RtCW (.mdc)
Doom 3 (.md5mesh, .md5anim, .md5camera)
DirectX X (.x)
Quick3D (.q3o, .q3s)
Raw Triangles (.raw)
AC3D (.ac, .ac3d)
Stereolithography (.stl)
Autodesk DXF (.dxf)
Irrlicht Mesh (.irrmesh, .xml)
Irrlicht Scene (.irr, .xml)
Object File Format ( .off )
Wavefront Object (.obj)
Terragen Terrain ( .ter )
3D GameStudio Model ( .mdl )
3D GameStudio Terrain ( .hmp )
Ogre ( .mesh.xml, .skeleton.xml, .material )
OpenGEX-Fomat (.ogex)
Milkshape 3D ( .ms3d )
LightWave Model ( .lwo )
LightWave Scene ( .lws )
Modo Model ( .lxo )
CharacterStudio Motion ( .csm )
Stanford Ply ( .ply )
TrueSpace (.cob, .scn)
XGL-3D-Format (.xgl)
6.1 下载和编译
从它的GitHub官网上下载代码,用cmake默认直接编译即可。
6.2 官网代码示例
- 代码示例:这是一段加载gltf模型文件的代码。
#include <stdlib.h>
#include <stdio.h>/* assimp include files. These three are usually needed. */
#include <assimp/cimport.h>
#include <assimp/scene.h>
#include <assimp/postprocess.h>#pragma comment(lib,"..\\..\\lib\\Debug\\assimp-vc141-mtd.lib")
#pragma comment(lib,"..\\..\\contrib\\zlib\\Debug\\zlibstaticd.lib")/* ---------------------------------------------------------------------------- */
inline static void print_error(const char* msg) {printf("ERROR: %s\n", msg);
}#define NEW_LINE "\n"
#define DOUBLE_NEW_LINE NEW_LINE NEW_LINE/* the global Assimp scene object */
const C_STRUCT aiScene* scene = NULL;
C_STRUCT aiVector3D scene_min, scene_max, scene_center;/* current rotation angle */
static float angle = 0.f;#define aisgl_min(x,y) (x<y?x:y)
#define aisgl_max(x,y) (y>x?y:x)/* ---------------------------------------------------------------------------- */
void get_bounding_box_for_node (const C_STRUCT aiNode* nd,C_STRUCT aiVector3D* min,C_STRUCT aiVector3D* max,C_STRUCT aiMatrix4x4* trafo
){C_STRUCT aiMatrix4x4 prev;unsigned int n = 0, t;prev = *trafo;aiMultiplyMatrix4(trafo,&nd->mTransformation);for (; n < nd->mNumMeshes; ++n) {const C_STRUCT aiMesh* mesh = scene->mMeshes[nd->mMeshes[n]];for (t = 0; t < mesh->mNumVertices; ++t) {C_STRUCT aiVector3D tmp = mesh->mVertices[t];aiTransformVecByMatrix4(&tmp,trafo);min->x = aisgl_min(min->x,tmp.x);min->y = aisgl_min(min->y,tmp.y);min->z = aisgl_min(min->z,tmp.z);max->x = aisgl_max(max->x,tmp.x);max->y = aisgl_max(max->y,tmp.y);max->z = aisgl_max(max->z,tmp.z);}}for (n = 0; n < nd->mNumChildren; ++n) {get_bounding_box_for_node(nd->mChildren[n],min,max,trafo);}*trafo = prev;
}/* ---------------------------------------------------------------------------- */
void get_bounding_box(C_STRUCT aiVector3D* min, C_STRUCT aiVector3D* max)
{C_STRUCT aiMatrix4x4 trafo;aiIdentityMatrix4(&trafo);min->x = min->y = min->z = 1e10f;max->x = max->y = max->z = -1e10f;get_bounding_box_for_node(scene->mRootNode,min,max,&trafo);
}/* ---------------------------------------------------------------------------- */
void color4_to_float4(const C_STRUCT aiColor4D *c, float f[4])
{f[0] = c->r;f[1] = c->g;f[2] = c->b;f[3] = c->a;
}/* ---------------------------------------------------------------------------- */
void set_float4(float f[4], float a, float b, float c, float d)
{f[0] = a;f[1] = b;f[2] = c;f[3] = d;
}/* ---------------------------------------------------------------------------- */
void apply_material(const C_STRUCT aiMaterial *mtl)
{float c[4];int fill_mode;int ret1, ret2;C_STRUCT aiColor4D diffuse;C_STRUCT aiColor4D specular;C_STRUCT aiColor4D ambient;C_STRUCT aiColor4D emission;ai_real shininess, strength;int two_sided;int wireframe;unsigned int max;set_float4(c, 0.8f, 0.8f, 0.8f, 1.0f);if (AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_DIFFUSE, &diffuse))color4_to_float4(&diffuse, c);printf(" GL_DIFFUSE: %.3f, %.3f, %.3f, %.3f\n", c[0], c[1], c[2], c[3]);set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);if (AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_SPECULAR, &specular))color4_to_float4(&specular, c);printf(" GL_SPECULAR: %.3f, %.3f, %.3f, %.3f\n", c[0], c[1], c[2], c[3]);set_float4(c, 0.2f, 0.2f, 0.2f, 1.0f);if (AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_AMBIENT, &ambient))color4_to_float4(&ambient, c);printf(" GL_AMBIENT: %.3f, %.3f, %.3f, %.3f\n", c[0], c[1], c[2], c[3]);set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);if (AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_EMISSIVE, &emission))color4_to_float4(&emission, c);printf(" GL_EMISSION:%.3f, %.3f, %.3f, %.3f\n", c[0], c[1], c[2], c[3]);max = 1;ret1 = aiGetMaterialFloatArray(mtl, AI_MATKEY_SHININESS, &shininess, &max);if (ret1 == AI_SUCCESS) {max = 1;ret2 = aiGetMaterialFloatArray(mtl, AI_MATKEY_SHININESS_STRENGTH, &strength, &max);if (ret2 == AI_SUCCESS)printf(" GL_SHININESS: %.3f\n", shininess * strength);elseprintf(" GL_SHININESS: %.3f\n", shininess);}max = 1;if (AI_SUCCESS == aiGetMaterialIntegerArray(mtl, AI_MATKEY_ENABLE_WIREFRAME, &wireframe, &max))printf(" fill_mode: %s\n", wireframe ? "GL_LINE" : "GL_FILL");max = 1;if ((AI_SUCCESS == aiGetMaterialIntegerArray(mtl, AI_MATKEY_TWOSIDED, &two_sided, &max)) && two_sided)printf(" two_sided: true\n");
}/* ---------------------------------------------------------------------------- */
void recursive_render (const C_STRUCT aiScene *sc, const C_STRUCT aiNode* nd)
{unsigned int i;unsigned int n = 0, t;C_STRUCT aiMatrix4x4 m = nd->mTransformation;/* update transform */aiTransposeMatrix4(&m);/* draw all meshes assigned to this node */printf("Node(name: %s, mNumMeshes: %d)\n", nd->mName.data, nd->mNumMeshes);for (; n < nd->mNumMeshes; ++n) {const C_STRUCT aiMesh* mesh = scene->mMeshes[nd->mMeshes[n]];apply_material(sc->mMaterials[mesh->mMaterialIndex]);printf(" mNumFaces(%d): %d\n", n, mesh->mNumFaces);for (t = 0; t < mesh->mNumFaces; ++t) {const C_STRUCT aiFace* face = &mesh->mFaces[t];//1:GL_POINTS, 2:GL_LINES, 3:GL_TRIANGLES, 0:GL_POLYGONint face_mode = face->mNumIndices;for(i = 0; i < face->mNumIndices; i++) {int index = face->mIndices[i];//if(mesh->mColors[0] != NULL)// glColor4fv((GLfloat*)&mesh->mColors[0][index]);//if(mesh->mNormals != NULL)// glNormal3fv(&mesh->mNormals[index].x);//glVertex3fv(&mesh->mVertices[index].x);}}}/* draw all children */for (n = 0; n < nd->mNumChildren; ++n) {recursive_render(sc, nd->mChildren[n]);}
}/* ---------------------------------------------------------------------------- */
int loadasset (const char* path)
{/* we are taking one of the postprocessing presets to avoidspelling out 20+ single postprocessing flags here. */scene = aiImportFile(path,aiProcessPreset_TargetRealtime_MaxQuality);if (scene) {get_bounding_box(&scene_min,&scene_max);scene_center.x = (scene_min.x + scene_max.x) / 2.0f;scene_center.y = (scene_min.y + scene_max.y) / 2.0f;scene_center.z = (scene_min.z + scene_max.z) / 2.0f;printf("scene_center: (%.3f, %.3f, %.3f)\n", scene_center.x, scene_center.y, scene_center.z);return 0;}return 1;
}/* ---------------------------------------------------------------------------- */
int main(int argc, char **argv)
{const char* model_file = NULL;C_STRUCT aiLogStream stream;// Check and validate the specified model file extension.model_file = "D:\\test_3dxml\\Duck1.glb";const char* extension = strrchr(model_file, '.');if (!extension) {print_error("Please provide a file with a valid extension.");return EXIT_FAILURE;}if (AI_FALSE == aiIsExtensionSupported(extension)) {print_error("The specified model file extension is currently ""unsupported in Assimp " ASSIMP_VERSION ".");return EXIT_FAILURE;}stream = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT,NULL);aiAttachLogStream(&stream);stream = aiGetPredefinedLogStream(aiDefaultLogStream_FILE,"assimp_log.txt");aiAttachLogStream(&stream);// Load the model file.if(0 != loadasset(model_file)) {print_error("Failed to load model. Please ensure that the specified file exists.");aiDetachAllLogStreams();return EXIT_FAILURE;}// Print the model data.recursive_render(scene, scene->mRootNode);// ReleseaiReleaseImport(scene);aiDetachAllLogStreams();return EXIT_SUCCESS;
}
- 运行结果如下:
(1)加载gltf1.0的模型文件:Duck1.glb
(2)加载gltf2.0的模型文件:Avocado2.glb
7、gltflib (v2.0, 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
7.1 Parsing a glTF 2.0 Model
from gltflib import GLTFgltf = 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)
7.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')
7.3 Converting Between glTF and GLB
from gltflib import GLTFgltf = GLTF.load('D:/glTF-Sample-Models/2.0/BoxTextured/gltf/BoxTextured.gltf')
gltf.export('D:/BoxTextured.glb')
from gltflib import GLTFgltf = 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')
相关测试代码见网址链接:
https://download.csdn.net/download/hhy321/85182743
8、three.js(v2.0, javascript)
8.1 下载和编译
https://github.com/mrdoob/three.js
下载GitHub代码:
git clone --depth=1 https://github.com/mrdoob/three.js.git
使用npm编译代码:
npm install
npm start
or
npm run dev
8.2 官网代码示意
- 代码1:
import * as THREE from 'three';// initconst camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
camera.position.z = 1;const scene = new THREE.Scene();const geometry = new THREE.BoxGeometry( 0.2, 0.2, 0.2 );
const material = new THREE.MeshNormalMaterial();const mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );const renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animation );
document.body.appendChild( renderer.domElement );// animationfunction animation( time ) {mesh.rotation.x = time / 2000;mesh.rotation.y = time / 1000;renderer.render( scene, camera );}
- 代码2:
<script type="module">import * as THREE from 'three';import Stats from './jsm/libs/stats.module.js';import { GUI } from './jsm/libs/lil-gui.module.min.js';import { GLTFLoader } from './jsm/loaders/GLTFLoader.js';import { GPUComputationRenderer } from './jsm/misc/GPUComputationRenderer.js';/* TEXTURE WIDTH FOR SIMULATION */const WIDTH = 64;const BIRDS = WIDTH * WIDTH;/* BAKE ANIMATION INTO TEXTURE and CREATE GEOMETRY FROM BASE MODEL */const BirdGeometry = new THREE.BufferGeometry();let textureAnimation, durationAnimation, birdMesh, materialShader, indicesPerBird;function nextPowerOf2( n ) {return Math.pow( 2, Math.ceil( Math.log( n ) / Math.log( 2 ) ) );}Math.lerp = function ( value1, value2, amount ) {amount = Math.max( Math.min( amount, 1 ), 0 );return value1 + ( value2 - value1 ) * amount;};const gltfs = [ 'models/gltf/Parrot.glb', 'models/gltf/Flamingo.glb' ];const colors = [ 0xccFFFF, 0xffdeff ];const sizes = [ 0.2, 0.1 ];const selectModel = Math.floor( Math.random() * gltfs.length );new GLTFLoader().load( gltfs[ selectModel ], function ( gltf ) {//.........//.........//.........}//.........
//.........
//.........
</script>
结语
如果您觉得该方法或代码有一点点用处,可以给作者点个赞,或打赏杯咖啡;
╮( ̄▽ ̄)╭
如果您感觉方法或代码不咋地
//(ㄒoㄒ)//,就在评论处留言,作者继续改进;
o_O???
如果您需要相关功能的代码定制化开发,可以留言私信作者;
(✿◡‿◡)
感谢各位童鞋们的支持!
( ´ ▽´ )ノ ( ´ ▽´)っ!!!
【CAD开发】glTF和b3dm文件格式读取(C++,Python)相关推荐
- 【CAD开发】gltf文件格式的转换工具汇总(js、python、c++)
文章目录 1.gltf格式简介 2.C# 代码 2.1 3dsMax/maya导出插件(Babylon.js) 2.python代码 2.1 gltflib (v2.0) 3.JavaScript代码 ...
- JavaCV音视频开发宝典:使用JavaCV读取海康平台或海康网络摄像头sdk回调视频TS码流并解析预览图像
<JavaCV音视频开发宝典>专栏目录导航 <JavaCV音视频开发宝典>专栏介绍和目录 前言 两年前博主写了如何利用JavaCV解析各种h264裸流,<JavaCV ...
- 2021-02-08【Web作业开发记录】Java代码读取文件问题
[Web作业开发记录]Java代码读取文件问题 1.问题分析 在项目中需要读取properties文件,而对于gradle项目,在java文件夹底下的properties文件在编译打包时会自动忽略,最 ...
- 【车载开发系列】UDS诊断---读取内存($0x23)
[车载开发系列]UDS诊断-读取内存($0x23) UDS诊断---读取内存($0x23) [车载开发系列]UDS诊断---读取内存($0x23) 一.概念定义 二.注意事项 三.报文格式 1)报文请 ...
- NX二次开发-ug表达式函数ug_find_file读取当前prt所在路径
NX二次开发-ug表达式函数ug_find_file读取当前prt所在路径 ug_find_file(ug_askCurrentWorkPart()) 该方法最早是看唐工在QQ群里发的,被我收集了.
- 【车载开发系列】UDS诊断---读取DTC信息($0x19)
[车载开发系列]UDS诊断-读取DTC信息($0x19) UDS诊断---读取DTC信息($0x19) [车载开发系列]UDS诊断---读取DTC信息($0x19) 一.概念定义 1)DTC概念 2) ...
- JavaCV音视频开发宝典:使用JavaCV读取海康平台或海康网络摄像头sdk回调录像回放视频PS码流并解析预览图像
<JavaCV音视频开发宝典>专栏目录导航 <JavaCV音视频开发宝典>专栏介绍和目录 前言 上一章中<JavaCV音视频开发宝典:使用JavaCV读取海康平台或海 ...
- 【沁恒WCH CH32V307V-R1开发板两路ADC读取实验】
[沁恒WCH CH32V307V-R1开发板两路ADC读取实验] 1. 前言 2. 软件配置 2.1 安装MounRiver Studio 3. ADC项目测试 3.1 打开ADC工程 3.2 编译项 ...
- 【车载开发系列】UDS诊断---读取周期标识符($0x2A)
[车载开发系列]UDS诊断-读取周期标识符($0x2A) UDS诊断---读取周期标识符($0x2A) [车载开发系列]UDS诊断---读取周期标识符($0x2A) 一.概念定义 二.报文格式 1)请 ...
最新文章
- CVE-2018-4407 苹果设备远程溢出漏洞
- windosw7 Hosts文件的位置
- Play Framework 的模板引擎
- Android 功耗(9)---MTK功耗基础知识
- 从0打卡leetcode之day 3 -- 最大子序列和
- 150分试卷c语言,连续5道C语言题目一共送150分啊,题目2.一个农场有头母牛,现 爱问知识人...
- Memcached、Redis、RabbitMQ
- openssl加解密原理
- 【产业互联网周报】AWS 全球换帅,中国区发布战略;杨杰回应回A,中移动计划千亿投5G;全球缺芯潮持续...
- 银联手机POS,支付黑科技
- 吸引力法则~助攻遇到自己的另一半
- matlab代码:基于主从博弈的智能小区代理商定价策略及电动汽车充电管理
- 怎样的100位区块链开发者,入选这份严苛至极的特训名单?
- HotSpot的新生代三区划分好在哪?
- php 跳转邮箱,JS点击跳转登陆邮箱功能简单的实现方法
- 我的博客也开通了,誓要戒骄,戒躁,每天学习,每天总结
- arm服务器运行onlyoffice
- bluedroid key miss问题
- 【操作系统】FIFO先进先出页面置换算法(C语言实现)
- 注册宝网络验证系统,安全免费的网络验证系统