官网地址:
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 地理空间内容交互。
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.
https://gltf-viewer.donmccurdy.com/
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.
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示例编译依赖库需要额外下载。本身就是几个头文件直接使用。
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
/ 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;
}
Tiny glTF loader, header only C++ glTF 1.x parsing library.
https://github.com/syoyo/tinygltfloader
注意:目前该库仅支持glTF 1.0格式。
这个库不需要编译,直接引用头文件.h就可以了。
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;
}
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;
}
根据前面贴的结构图,编写代码遍历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 data
tinygltf::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.indices
const 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 data
std::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 5126
accessor.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;
}
在头文件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 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;
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;
}
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格式。
它的编译依赖库不不不需要额外下载。
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
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;
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");
}
#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
运行结果如下:
//***********************************************************************
// 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];
}
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 格式。
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)
从它的GitHub官网上下载代码,用cmake默认直接编译即可。
#include
#include
/* assimp include files. These three are usually needed. */
#include
#include
#include
#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);
else
printf(" 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_POLYGON
int 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 avoid
spelling 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);
// Relese
aiReleaseImport(scene);
aiDetachAllLogStreams();
return EXIT_SUCCESS;
}
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
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)
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')
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')
相关测试代码见网址链接:
https://download.csdn.net/download/hhy321/85182743
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
import * as THREE from 'three';
// init
const 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 );
// animation
function animation( time ) {
mesh.rotation.x = time / 2000;
mesh.rotation.y = time / 1000;
renderer.render( scene, camera );
}
<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???
如果您需要相关功能的代码定制化开发,可以留言私信作者;
(✿◡‿◡)
感谢各位童鞋们的支持!
( ´ ▽´ )ノ ( ´ ▽´)っ!!!